【详解LinkedList与链表】

news2024/11/20 13:18:05

🌠作者:@TheMythWS.

🎆专栏:《集合与数据结构》

🎇座右铭:不走心的努力都是在敷衍自己,让自己所做的选择,熠熠发光。

目录

链表

概念

图解链表

链表的实现 

1.创建链表

2.遍历链表 

3.查找是否包含关键字key是否在单链表当中 

4.获取单链表的长度 

5.头插法 

6.尾插法 

7.任意位置插入(假如第一个数据结点为0下标) 

8.删除第一次出现关键字key的结点 

9.清空链表 

10.删除所有值为key的结点 

※链表OJ面试题   

LinkedList的模拟实现 

头插法: 

尾插法: 

任意位置插入元素: 

删除第一次出现关键字key的结点: 

删除所有关键字key的结点: 

清空双链表: 

LinkedList的使用

什么是LinkedList?

LinkedList的使用 

1.LinkedList的构造  

2. LinkedList的其他常用方法介绍   

3. LinkedList的遍历 

ArrayList和LinkedList的区别   


链表

概念

        是一种物理存储结构上非连续存储结构,数据元素的逻辑顺序是通过链表中的引用链接次序实现的,也就是线性表用链式存储结构形成的表。
特点:用一组任意的存储单元存储线性表的数据元素(这组存储单元可以是连续的,也可以是不连续的)
因此,为了表示每个数据元素ai与其直接后继数据元素ai+1之间的逻辑关系,对数据元素ai来说,除了存储其本身的信息之外,还需要存储一个指示(指针)其直接后继(即直接后继的存储位置)这两部分信息组成数据元素ai的存储映像,称为节点/结点(node),它包括两个域:其中存储数据元素信息的域称为数据域;存储直接后继位置的域称为指针域。指针域存储的信息称作指针或链。n个结点(ai(1≤i≤n)的存储映像)链结成一个链表,即为线性表(a1,a2,...,an)的链式存储结构。如果此链表的每个结点中只包含一个指针域,故又称此链表为线性链表或单链表

        根据链表结点所含指针个数、指针指向和指针链接方式,可以将链表分为单链表、循环链表、双向链表、二叉链表、十字链表、邻接表、邻接多重表等等。
        其中单链表、循环链表、双向链表用于实现线性表的链式存储结构,其他形式多用于实现树和图等非线性结构。
        类似我们生活中的火车,一节一节相连的。        

下面用单链表作为例子: 

图解链表

实际中链表的结构非常多样,以下情况组合起来就有8种链表结构:
分为单向和双向,其次是带头和不带头、循环和非循环 

所以排列组合有2^3=8种,如下图所示:

单向 不带头 非循环链表:结构简单,一般不会单独用来存数据。实际中更多是作为其他数据结构的子结构,如:哈希桶、图的邻接表等等。另外这种结构在笔试面试中出现很多
双向 不带头 非循环链表:在Java的集合框架库中LinkedList底层实现就是 双向 不带头 非循环 链表。 

注意:
单向就是一个方向一直走。
双向就是两个方向都走。 

链表的实现 

不带头 非循环 单链表作为例子,下面我们先要明白链表和结点是怎么用代码实现的:

1.创建链表

public class MySingleLinkedList {
    static class Node {//加static说明创建这个结点的时候不依赖外部对象
        public int val;//存储的数据
        public Node next;//存储下一个结点的地址
        /*
            给val构造方法,因为如果不设置(相当于没有这个结点)那么我们不知道结点的next域要存什么地址,
            为什么不给next赋值呢? 因为next是一个引用类型,默认是null,这就是结点类
             */
        public Node(int val) {
            this.val = val;
        }
    }
    public Node head;//代表当前链表的头结点的引用
    //创建结点
    public void create() {
        Node node1 = new Node(10);
        Node node2 = new Node(12);
        Node node3 = new Node(11);
        Node node4 = new Node(9);
        node1.next = node2;
        node2.next = node3;
        node3.next = node4;
        head = node1;
    }
}
public class Test {
    //这是一个main方法,是程序的入口:
    public static void main(String[] args) {
        MySingleLinkedList mySingleLinkedList = new MySingleLinkedList();
        mySingleLinkedList.create();
    }
}

通过断点调试查看链表是否创建成功: 

2.遍历链表 

/*
    遍历链表
    注意点1:
    如果是遍历完成整个链表,那么head == null
    如果是遍历到链表的最后一个结点(尾结点),那么head.next == null
     */
    public void display() {
        Node cur = head;//注意点2:让head引用指向头结点,cur来临时指向每个结点,如果不设置的话,那么head = null,连续打印两次链表,只能显示第一次打印的链表
        while (cur != null) {//如果判断条件是head.next != null,因为无法进入循环则不能打印最后结点的val
            System.out.print(cur.val + " ");
            cur = cur.next;
        }
        System.out.println();
    }
public class Test {
    //这是一个main方法,是程序的入口:
    public static void main(String[] args) {
        MySingleLinkedList mySingleLinkedList = new MySingleLinkedList();
        mySingleLinkedList.create();
        mySingleLinkedList.display();
    }
}

3.查找是否包含关键字key是否在单链表当中 

//查找是否包含关键字key是否在单链表当中
    public boolean contains(int key) {
        Node cur = head;
        while (cur != null) {
            if (cur.val == key) {
                return true;
            }
            cur = cur.next;
        }
        return false;
    }
public class Test {
    //这是一个main方法,是程序的入口:
    public static void main(String[] args) {
        MySingleLinkedList mySingleLinkedList = new MySingleLinkedList();
        mySingleLinkedList.create();
        mySingleLinkedList.display();
        System.out.println(mySingleLinkedList.contains(9));
    }
}

4.获取单链表的长度 

//得到单链表的长度 O(N)
    public int size() {
        int count = 0;
        Node cur = head;
        while (cur != null) {
            count++;
            cur = cur.next;
        }
        return count;
    }

5.头插法 

//头插法
    public void addFirst(int data) {
        Node node = new Node(data);//1.插入结点,必须先得有结点
        node.next = head;//2.先连
        head = node;//3.后断
    }
public class Test {
    //这是一个main方法,是程序的入口:
    public static void main(String[] args) {
        MySingleLinkedList mySingleLinkedList = new MySingleLinkedList();
        mySingleLinkedList.create();
        mySingleLinkedList.display();
        System.out.println(mySingleLinkedList.contains(9));
        System.out.println(mySingleLinkedList.size());
        mySingleLinkedList.addFirst(9);
        mySingleLinkedList.addFirst(8);
        mySingleLinkedList.display();
    }
}

6.尾插法 

//尾插法 O(N)
    public void addLast(int data) {
        Node node = new Node(data);
        if (head == null) {//该链表是否是第一次插入数据
            head = node;
            return;
        }
        Node cur = head;
        while (cur.next != null) {//cur指向最后一个结点(尾结点)
            cur = cur.next;
        }
        cur.next = node;
    }
public class Test {
    //这是一个main方法,是程序的入口:
    public static void main(String[] args) {
        MySingleLinkedList mySingleLinkedList = new MySingleLinkedList();
        mySingleLinkedList.create();
        mySingleLinkedList.display();
        System.out.println(mySingleLinkedList.contains(9));
        System.out.println(mySingleLinkedList.size());
        System.out.println("《---测试头插法---》");
        mySingleLinkedList.addFirst(9);
        mySingleLinkedList.addFirst(8);
        mySingleLinkedList.display();
        System.out.println("《---测试尾插法---》");
        mySingleLinkedList.addLast(22);
        mySingleLinkedList.addLast(33);
        mySingleLinkedList.display();
    }
}

7.任意位置插入(假如第一个数据结点为0下标) 

//任意位置插入,第一个数据节点为0号下标
    public void addIndex(int index, int data) throws LinekdListIndexOutOfException {
        checkIndex(index);
        if (index == 0) {
            addFirst(data);
            return;
        }
        if (index == size()) {
            addLast(data);
            return;
        }
        Node cur = findIndexSubOne(index);
        Node node = new Node(data);
        node.next = cur.next;
        cur.next = node;
    }
    //找到index - 1位置的结点的地址
    private Node findIndexSubOne(int index) {
        Node cur = head;
        int count = 0;
        while (count != index - 1) {
            cur = cur.next;
            count++;
        }
        return cur;
    }
    private void checkIndex(int index) throws LinekdListIndexOutOfException {
        if (index < 0 || index > size()) {
            throw new LinekdListIndexOutOfException("index位置不合法!");
        }
    }
public class LinekdListIndexOutOfException extends RuntimeException {
    public LinekdListIndexOutOfException() {
    }
    public LinekdListIndexOutOfException(String message) {
        super(message);
    }
}
public class Test {
    //这是一个main方法,是程序的入口:
    public static void main(String[] args) {
        MySingleLinkedList mySingleLinkedList = new MySingleLinkedList();
        mySingleLinkedList.create();
        mySingleLinkedList.display();
        System.out.println(mySingleLinkedList.contains(9));
        System.out.println(mySingleLinkedList.size());
        System.out.println("《---测试头插法---》");
        mySingleLinkedList.addFirst(9);
        mySingleLinkedList.addFirst(8);
        mySingleLinkedList.display();
        System.out.println("《---测试尾插法---》");
        mySingleLinkedList.addLast(22);
        mySingleLinkedList.addLast(33);
        mySingleLinkedList.display();
        System.out.println("《---测试插入---》");
        mySingleLinkedList.addIndex(3, 11);
        mySingleLinkedList.addIndex(0, 7);
        mySingleLinkedList.addIndex(10, 44);
        try {
            mySingleLinkedList.addIndex(12, 44);
        } catch (LinekdListIndexOutOfException e) {
            e.printStackTrace();
        }
        mySingleLinkedList.display();
    }
}

8.删除第一次出现关键字key的结点 

//删除第一次出现关键字为key的结点
    public void remove(int key) {
        if (head == null) {
            return;//一个结点也没有
        }
        if (head.val == key) {
            head = head.next;
            return;
        }
        Node cur = searchPrev(key);//key的前驱结点
        if (cur == null) {//没有找到key是要被删除的
            return;
        }
        Node del = cur.next;//要删除的结点
        cur.next = del.next;
        //cur.next = cur.next.next;
    }
    //找到关键字key的前一个结点
    private Node searchPrev(int key) {
        Node cur = head;
        while (cur.next != null) {//cur.next == null,它没有下一个结点也就不可能是要找的key结点的前驱,这样就说明了一直没有找到要删除的结点
            if (cur.next.val == key) {//如果cur指向的下一个结点的值和key相等,就返回当前cur,当前的cur指向的结点就是key的前一个结点(前驱结点)
                return cur;
            }
            cur = cur.next;
        }
        return null;//没有要删除的结点
    }
public class Test {
    //这是一个main方法,是程序的入口:
    public static void main(String[] args) {
        MySingleLinkedList mySingleLinkedList = new MySingleLinkedList();
        mySingleLinkedList.create();
        mySingleLinkedList.display();
        System.out.println(mySingleLinkedList.contains(9));
        System.out.println(mySingleLinkedList.size());
        System.out.println("《---测试头插法---》");
        mySingleLinkedList.addFirst(9);
        mySingleLinkedList.addFirst(8);
        mySingleLinkedList.display();
        System.out.println("《---测试尾插法---》");0
        mySingleLinkedList.addLast(22);
        mySingleLinkedList.addLast(33);
        mySingleLinkedList.display();
        System.out.println("《---测试插入---》");
        mySingleLinkedList.addIndex(3, 11);
        mySingleLinkedList.addIndex(0, 7);
        mySingleLinkedList.addIndex(10, 44);
        /*try {
            mySingleLinkedList.addIndex(12, 44);
        } catch (LinekdListIndexOutOfException e) {
            e.printStackTrace();
        }*/
        mySingleLinkedList.display();
        System.out.println("《---测试删除---》");
        mySingleLinkedList.remove(7);
        mySingleLinkedList.remove(8);
        mySingleLinkedList.remove(9);
        mySingleLinkedList.remove(11);
        mySingleLinkedList.display();
    }
}

9.清空链表 

//清空链表
    public void clear() {
        head = null;
    }
public class Test {
    //这是一个main方法,是程序的入口:
    public static void main(String[] args) {
        MySingleLinkedList mySingleLinkedList = new MySingleLinkedList();
        mySingleLinkedList.create();
        mySingleLinkedList.display();
        System.out.println(mySingleLinkedList.contains(9));
        System.out.println(mySingleLinkedList.size());
        System.out.println("《---测试头插法---》");
        mySingleLinkedList.addFirst(9);
        mySingleLinkedList.addFirst(8);
        mySingleLinkedList.display();
        System.out.println("《---测试尾插法---》");
        mySingleLinkedList.addLast(22);
        mySingleLinkedList.addLast(33);
        mySingleLinkedList.display();
        System.out.println("《---测试插入---》");
        mySingleLinkedList.addIndex(3, 11);
        mySingleLinkedList.addIndex(0, 7);
        mySingleLinkedList.addIndex(10, 44);
        /*try {
            mySingleLinkedList.addIndex(12, 44);
        } catch (LinekdListIndexOutOfException e) {
            e.printStackTrace();
        }*/
        mySingleLinkedList.display();
        System.out.println("《---测试删除---》");
        mySingleLinkedList.remove(7);
        mySingleLinkedList.remove(8);
        mySingleLinkedList.remove(9);
        mySingleLinkedList.remove(11);
        mySingleLinkedList.display();
        System.out.println("《---清空链表---》");
        mySingleLinkedList.clear();
        mySingleLinkedList.display();
        System.out.println("《---更新数据---》");
        mySingleLinkedList.addIndex(0 ,11);
        mySingleLinkedList.addIndex(1 ,22);
        mySingleLinkedList.addIndex(2 ,33);
        mySingleLinkedList.addIndex(3 ,22);
        mySingleLinkedList.addIndex(4 ,44);
        mySingleLinkedList.display();
    }
}

10.删除所有值为key的结点 

//删除所有值为key的结点
    public void removeAllKey(int key) {
        if (head == null) {
            return;
        }
        Node prev = head;
        Node cur = head.next;
        while (cur != null) {
            /*while (head.val == key) {
                head = head.next;
            }*/
            if (cur.val == key) {
                prev.next = cur.next;
                //cur = cur.next;
            } else {
                prev = cur;
                //cur = cur.next;
            }
            cur = cur.next;
        }
        if (head.val == key) {//处理头结点也是key的情况
            head = head.next;
        }
    }
public class Test {
    //这是一个main方法,是程序的入口:
    public static void main(String[] args) {
        MySingleLinkedList mySingleLinkedList = new MySingleLinkedList();
        mySingleLinkedList.create();
        mySingleLinkedList.display();
        System.out.println(mySingleLinkedList.contains(9));
        System.out.println(mySingleLinkedList.size());
        System.out.println("《---测试头插法---》");
        mySingleLinkedList.addFirst(9);
        mySingleLinkedList.addFirst(8);
        mySingleLinkedList.display();
        System.out.println("《---测试尾插法---》");
        mySingleLinkedList.addLast(22);
        mySingleLinkedList.addLast(33);
        mySingleLinkedList.display();
        System.out.println("《---测试插入---》");
        mySingleLinkedList.addIndex(3, 11);
        mySingleLinkedList.addIndex(0, 7);
        mySingleLinkedList.addIndex(10, 44);
        /*try {
            mySingleLinkedList.addIndex(12, 44);
        } catch (LinekdListIndexOutOfException e) {
            e.printStackTrace();
        }*/
        mySingleLinkedList.display();
        System.out.println("《---测试删除---》");
        mySingleLinkedList.remove(7);
        mySingleLinkedList.remove(8);
        mySingleLinkedList.remove(9);
        mySingleLinkedList.remove(11);
        mySingleLinkedList.display();
        System.out.println("《---清空链表---》");
        mySingleLinkedList.clear();
        mySingleLinkedList.display();
        System.out.println("《---更新数据---》");
        mySingleLinkedList.addIndex(0 ,11);
        mySingleLinkedList.addIndex(1 ,22);
        mySingleLinkedList.addIndex(2 ,33);
        mySingleLinkedList.addIndex(3 ,22);
        mySingleLinkedList.addIndex(4 ,44);
        mySingleLinkedList.display();
        System.out.println("《---删除所有值为key的结点---》");
        mySingleLinkedList.removeAllKey(22);
        mySingleLinkedList.display();
    }
}

总的代码: 

public class MySingleLinkedList {
    static class Node {//加static说明创建这个结点的时候不依赖外部对象
        public int val;//存储的数据
        public Node next;//存储下一个结点的地址
        /*
            给val构造方法,因为如果不设置,那么我们不知道结点的next域要存什么地址,
            为什么不给next赋值呢? 因为next是一个引用类型,默认是null,这就是结点类
             */
        public Node(int val) {
            this.val = val;
        }
    }
    public Node head;//代表当前链表的头结点的引用
    //创建结点
    public void create() {
        Node node1 = new Node(10);
        Node node2 = new Node(12);
        Node node3 = new Node(11);
        Node node4 = new Node(9);
        node1.next = node2;
        node2.next = node3;
        node3.next = node4;
        head = node1;
    }
    /*
    遍历链表
    注意点1:
    如果是遍历完成整个链表,那么head == null
    如果是遍历到链表的最后一个结点(尾结点),那么head.next == null
     */
    public void display() {
        Node cur = head;//注意点2:让head引用指向头结点,cur来临时指向每个结点,如果不设置的话,那么head = null,连续打印两次链表,只能显示第一次打印的链表
        while (cur != null) {//如果判断条件是head.next != null,因为无法进入循环则不能打印最后结点的val
            System.out.print(cur.val + " ");
            cur = cur.next;
        }
        System.out.println();
    }
    //查找是否包含关键字key是否在单链表当中
    public boolean contains(int key) {
        Node cur = head;
        while (cur != null) {
            if (cur.val == key) {
                return true;
            }
            cur = cur.next;
        }
        return false;
    }
    //得到单链表的长度 O(N)
    public int size() {
        int count = 0;
        Node cur = head;
        while (cur != null) {
            count++;
            cur = cur.next;
        }
        return count;
    }
    //头插法 O(1)
    public void addFirst(int data) {
        Node node = new Node(data);//1.插入结点,必须先得有结点
        node.next = head;//2.先连
        head = node;//3.后断
    }
    //尾插法 O(N)
    public void addLast(int data) {
        Node node = new Node(data);
        if (head == null) {//该链表是否是第一次插入数据
            head = node;
            return;
        }
        Node cur = head;
        while (cur.next != null) {//cur指向最后一个结点(尾结点)
            cur = cur.next;
        }
        cur.next = node;
        /*node = cur;无意义*/
    }
    //任意位置插入,第一个数据节点为0号下标
    public void addIndex(int index, int data) throws LinekdListIndexOutOfException {
        checkIndex(index);
        if (index == 0) {
            addFirst(data);
            return;
        }
        if (index == size()) {
            addLast(data);
            return;
        }
        Node cur = findIndexSubOne(index);
        Node node = new Node(data);
        node.next = cur.next;
        cur.next = node;
    }
    //找到index - 1位置的结点的地址
    private Node findIndexSubOne(int index) {
        Node cur = head;
        int count = 0;
        while (count != index - 1) {
            cur = cur.next;
            count++;
        }
        return cur;
    }
    private void checkIndex(int index) throws LinekdListIndexOutOfException {
        if (index < 0 || index > size()) {
            throw new LinekdListIndexOutOfException("index位置不合法!");
        }
    }
    //删除第一次出现关键字为key的结点
    public void remove(int key) {
        if (head == null) {
            return;//一个结点也没有
        }
        if (head.val == key) {
            head = head.next;
            return;
        }
        Node cur = searchPrev(key);//key的前驱结点
        if (cur == null) {
            return;
        }
        Node del = cur.next;//要删除的结点
        cur.next = del.next;
        //cur.next = cur.next.next;
    }
    //找到关键字key的前一个结点
    private Node searchPrev(int key) {
        Node cur = head;
        while (cur.next != null) {//cur.next == null说明一直没有要删除的结点
            if (cur.next.val == key) {//如果cur指向的下一个结点的值和key相等,就返回当前cur,当前的cur指向的结点就是key的前一个结点(前驱结点)
                return cur;
            }
            cur = cur.next;
        }
        return null;//没有要删除的结点
    }
    //删除所有值为key的结点
    public void removeAllKey(int key) {
        if (head == null) {
            return;
        }
        Node prev = head;
        Node cur = head.next;
        while (cur != null) {
            /*while (head.val == key) {
                head = head.next;
            }*/
            if (cur.val == key) {
                prev.next = cur.next;
                //cur = cur.next;
            } else {
                prev = cur;
                //cur = cur.next;
            }
            cur = cur.next;
        }
        if (head.val == key) {//处理头结点也是key的情况
            head = head.next;
        }
    }
    //清空链表
    public void clear() {
        head = null;
    }
}
public class Test {
    //这是一个main方法,是程序的入口:
    public static void main(String[] args) {
        MySingleLinkedList mySingleLinkedList = new MySingleLinkedList();
        mySingleLinkedList.create();
        mySingleLinkedList.display();
        System.out.println(mySingleLinkedList.contains(9));
        System.out.println(mySingleLinkedList.size());
        System.out.println("《---测试头插法---》");
        mySingleLinkedList.addFirst(9);
        mySingleLinkedList.addFirst(8);
        mySingleLinkedList.display();
        System.out.println("《---测试尾插法---》");
        mySingleLinkedList.addLast(22);
        mySingleLinkedList.addLast(33);
        mySingleLinkedList.display();
        System.out.println("《---测试插入---》");
        mySingleLinkedList.addIndex(3, 11);
        mySingleLinkedList.addIndex(0, 7);
        mySingleLinkedList.addIndex(10, 44);
        /*try {
            mySingleLinkedList.addIndex(12, 44);
        } catch (LinekdListIndexOutOfException e) {
            e.printStackTrace();
        }*/
        mySingleLinkedList.display();
        System.out.println("《---测试删除---》");
        mySingleLinkedList.remove(7);
        mySingleLinkedList.remove(8);
        mySingleLinkedList.remove(9);
        mySingleLinkedList.remove(11);
        mySingleLinkedList.display();
        System.out.println("《---清空链表---》");
        mySingleLinkedList.clear();
        mySingleLinkedList.display();
        System.out.println("《---更新数据---》");
        mySingleLinkedList.addIndex(0 ,11);
        mySingleLinkedList.addIndex(1 ,22);
        mySingleLinkedList.addIndex(2 ,33);
        mySingleLinkedList.addIndex(3 ,22);
        mySingleLinkedList.addIndex(4 ,44);
        mySingleLinkedList.display();
        System.out.println("《---删除所有值为key的结点---》");
        mySingleLinkedList.removeAllKey(22);
        mySingleLinkedList.display();
    }
}

※链表OJ面试题   

注意:从这儿开始将前面代码里面Node全部替换了ListNode,方便后续刷题改进代码

1. 删除链表中等于给定值 val 的所有节点。

/**
 * Definition for singly-linked list.
 * public class ListNode {
 *     int val;
 *     ListNode next;
 *     ListNode() {}
 *     ListNode(int val) { this.val = val; }
 *     ListNode(int val, ListNode next) { this.val = val; this.next = next; }
 * }
 */
class Solution {
    public ListNode removeElements(ListNode head, int val) {
        if (head == null) {
            return null;
        }
        ListNode prev = head;
        ListNode cur = head.next;
        while (cur != null) {
            /*while (head.val == key) {
                head = head.next;
            }*/
            if (cur.val == val) {
                prev.next = cur.next;
                //cur = cur.next;
            } else {
                prev = cur;
                //cur = cur.next;
            }
            cur = cur.next;
        }
        if (head.val == val) {
                head = head.next;
            }
        return head;
    }
}

2. 反转一个单链表。

/**
 * Definition for singly-linked list.
 * public class ListNode {
 *     int val;
 *     ListNode next;
 *     ListNode() {}
 *     ListNode(int val) { this.val = val; }
 *     ListNode(int val, ListNode next) { this.val = val; this.next = next; }
 * }
 */
class Solution {
    public ListNode reverseList(ListNode head) {
        if (head == null) {
            return null;
        }
        if (head.next == null) {
            return head;
        }
        ListNode cur = head.next;
                                                                            //先将头结点置空
        head.next = null;
        while (cur != null) {
            ListNode curNext = cur.next;
            //头插法 插入cur
            cur.next = head;
            head = cur;
            cur = curNext;
        }
        return head;
    }
}

3. 给定一个带有头结点 head 的非空单链表,返回链表的中间结点。如果有两个中间结点,则返回第二个中间结点。 

/**
 * Definition for singly-linked list.
 * public class ListNode {
 *     int val;
 *     ListNode next;
 *     ListNode() {}
 *     ListNode(int val) { this.val = val; }
 *     ListNode(int val, ListNode next) { this.val = val; this.next = next; }
 * }
 */
class Solution {
    public ListNode middleNode(ListNode head) {
        ListNode fast = head;
        ListNode slow = head;
        while (fast != null && fast.next != null) {//偶数结点和奇数结点, fast走到的位置不一样, 所以都要考虑
            fast = fast.next.next;//fast走两步
            slow = slow.next;//slow走一步
        }
        return slow;//此时slow指向的结点就是中间结点
    }
}

4.输入一个链表,输出该链表中倒数第k个结点。 

/**
 * Definition for singly-linked list.
 * public class ListNode {
 *     int val;
 *     ListNode next;
 *     ListNode(int x) { val = x; }
 * }
 */
class Solution {
    public ListNode getKthFromEnd(ListNode head, int k) {
//        if (k <= 0 || k > size() || head == null) {//判断是否合法
//            return null;
//        }
        //优化条件:去掉k > size()的条件
        if (k <= 0 || head == null) {//判断是否合法
            return null;
        }
        ListNode fast = head;
        ListNode slow = head;
        //第一步:让fast走k-1步
        while (k - 1 != 0) {
            fast = fast.next;
            //优化条件
            if (fast == null) {//没走完k-1步之前fast==null,说明k给大了.
                return null;
            }
            k--;
        }
//        for (k = k - 1; k > 0; k--) {
//            fast = fast.next;
//            //优化条件
//            if (fast == null) {//没走完k-1步之前fast==null,说明k给大了.
//                return null;
//            }
//        }
        //第二步 和 第三步
        while (fast.next != null) {
            fast = fast.next;
            slow = slow.next;
        }
        return slow;
    }
}

5. (重点!!!)将两个有序链表合并为一个新的有序链表并返回。新链表是通过拼接给定的两个链表的所有节点组成的。

/**
 * Definition for singly-linked list.
 * public class ListNode {
 *     int val;
 *     ListNode next;
 *     ListNode() {}
 *     ListNode(int val) { this.val = val; }
 *     ListNode(int val, ListNode next) { this.val = val; this.next = next; }
 * }
 */
class Solution {
    public ListNode mergeTwoLists(ListNode head1, ListNode head2) {
        ListNode newHead = new ListNode(0);
        ListNode tmp = newHead;
        while (head1 != null && head2 != null) {
            if (head1.val < head2.val) {
                tmp.next = head1;
                head1 = head1.next;
                //tmp = tmp.next;
            } else {
                tmp.next = head2;
                head2 = head2.next;
                //tmp = tmp.next;
            }
            tmp = tmp.next;
        }
        if (head1 != null) {//说明head2 == null
            tmp.next = head1;
        }
        if (head2 != null) {//说明head1 == null
            tmp.next = head2;
        }
        return newHead.next;
    }
}

6.力扣链接 :编写代码,以给定值x为基准将链表分割成两部分,所有小于x的结点排在大于或等于x的结点之前 。
现有一链表的头指针 ListNode* pHead,给一定值x,编写一段代码将所有小于x的结点排在其余结点之前,且不能改变原来的数据顺序,返回重新排列后的链表的头指针。牛客链接:n链表分割_牛客题霸_牛客网 (nowcoder.com) 

/**
 * Definition for singly-linked list.
 * public class ListNode {
 *     int val;
 *     ListNode next;
 *     ListNode(int x) { val = x; }
 * }
 */
class Solution {
    public ListNode partition(ListNode head, int x) {
        // write code here
        // bs : before start, be: before end
        // as : after start, ae : after end
        ListNode bs = null;//相当于左半部分的头结点, 不能动
        ListNode be = null;
        ListNode as = null;//相当于右半部分的头结点, 不能动
        ListNode ae = null;
        ListNode cur = head;
        while (cur != null) {
            if (cur.val < x) {
                if (bs == null) {//左半部分第一次插入
                    bs = cur;
                    be = cur;
                    //cur = cur.next;
                } else { //不是第一次插入了
                    be.next = cur;
                    be = be.next;
                    //cur = cur.next;
                }
            } else {//cur.val >= x
                if (as == null) {//右半部分第一次插入
                    as = cur;
                    ae = cur;
                    //cur = cur.next;
                } else {//不是第一次插入了
                    ae.next = cur;
                    ae = ae.next;
                    //cur = cur.next;
                }
            }
            cur = cur.next;
        }
        //此时链表已经遍历完成了, 且分成了两部分, 左半边部分<x, 右半部分>=x
        //需要考虑ae是否需要置空???
        //有可能不会同时存在小于x 和 大于等于x的数据, 所以需要考虑所有的值都小于x 或者 所有的值都大于等于x的情况.
        //注意:下面这个if判断只能判断左半部分不为空, 可以拿到右半部分的数据
//        if (bs != null) {//说明左半部分一定有值<x的.
//            be.next = as;
//        }
        //而下面这种更好的写法可以判断左半部分为空和右半部分为空的情况, 从而分别拿到右半部分和左半部分的数据
        if (bs == null) {//说明左半部分为空, 只有右半部分的值,即数据都是大于等于x的.
            return as;//直接返回右半部分的头结点, 也包含了右半部分为空的情况.如果as == null, 也是返回null.
        }
        //左半部分不为空
        be.next = as;
        //右半部分不为空
        if (as != null) {
            ae.next = null;
        }
        return bs;
    }
}

7. 链表的回文结构。

/**
 * Definition for singly-linked list.
 * public class ListNode {
 *     int val;
 *     ListNode next;
 *     ListNode() {}
 *     ListNode(int val) { this.val = val; }
 *     ListNode(int val, ListNode next) { this.val = val; this.next = next; }
 * }
 */
class Solution {
    public boolean isPalindrome(ListNode head) {
        if (head == null) {
            return false;
        }
        if (head.next == null) {//一个结点
            return true;
        }
        ListNode fast = head;
        ListNode slow = head;
        //1.找中间结点
        while (fast != null && fast.next != null) {//奇数结点和偶数结点, fast走到的位置不一样, 所以都要考虑
            fast = fast.next.next;//fast走两步
            slow = slow.next;//slow走一步
        }
                                                                                //此时slow指向的结点就是中间结点
        //2.反转
        ListNode cur = slow.next;//cur代表当前要反转的结点
        while (cur != null) {
            ListNode curNext = cur.next;
            cur.next = slow;
            slow = cur;
            cur = curNext;
        }
        //3.一个从头往后, 一个从后往前遍历, 比较值
        while (slow != head) {//直到slow和head相遇 slow == head
            if (head.val != slow.val) {
                return false;
            }
            //偶数结点的情况:
            if (head.next == slow) {
                return true;
            }
            head = head.next;
            slow = slow.next;
        }
        return true;
    }
}

8. 输入两个链表,找出它们的第一个公共结点。 

/**
 * Definition for singly-linked list.
 * public class ListNode {
 *     int val;
 *     ListNode next;
 *     ListNode(int x) {
 *         val = x;
 *         next = null;
 *     }
 * }
 */
public class Solution {
    public ListNode getIntersectionNode(ListNode headA, ListNode headB) {
         if (headA == null && headB == null) {
            return null;
        }
        if (headA == null) {
            return null;
        }
        if (headB == null) {
            return null;
        }
        
        //1.分别两个链表的长度.
        int lenA = 0;
        int lenB = 0;
        ListNode pl = headA;//假设pl是指向长的链表
        ListNode ps = headB;//假设ps是指向短的链表
        while (pl != null) {
            lenA++;
            pl = pl.next;
        }
        while (ps != null) {
            lenB++;
            ps = ps.next;
        }
        //因为上面判断链表的长度, pl 和 ps都指向null, 所以需要将它们指回来.
        pl = headA;
        ps = headB;
        //3.根据len的值 修改指向.
        int len = lenA - lenB;
        if (len < 0) {
            pl = headB;
            ps = headA;
            len = lenB - lenA;
        }
        /*
        通过上述步骤得到:
        1.len一定是一个正数.
        2.pl指向的链表一定是最长的, ps指向的链表一定是最短的.
         */
        //4.让 pl 走 len 步
        while (len != 0) {
            pl = pl.next;
            len--;
        }
        while (pl != ps) {
            pl = pl.next;
            ps = ps.next;
        }
          //pl == ps
//        if (pl == ps && pl == null) {//不需要考虑ps == null, 因为pl已经和ps相等了.
//            return null;
//        }
        //优化:
        /*if (pl == null) {//上面while结束之后, 已经是pl == ps的条件了
            return null;
        }*/
        //或者直接注释, 因为返回null, 就说明没有相交.
        return pl;
    }
}

9. 给定一个链表,判断链表中是否有环。 

/**
 * Definition for singly-linked list.
 * class ListNode {
 *     int val;
 *     ListNode next;
 *     ListNode(int x) {
 *         val = x;
 *         next = null;
 *     }
 * }
 */
public class Solution {
    public boolean hasCycle(ListNode head) {
        ListNode fast = head;
        ListNode slow = head;
        while (fast != null && fast.next != null) {
            fast = fast.next.next;
            slow = slow.next;
            if (fast == slow) {
                return true;
            }
        }
        return false;
        /*ListNode fast = head;
        ListNode slow = head;
        while (fast != null && fast.next != null) {//条件1
            fast = fast.next.next;
            slow = slow.next;
            if (fast == slow) {//条件2
                break;
            }
        }
        //走到下面if语句有两种情况, 一种是不满足条件1, 另一种是不满足条件2
        if (fast ==null || fast.next==null) {
            return false;
        }
        //其余情况: fast == slow 条件2
        return true;*/
    }
}

10.给定一个链表,返回链表开始入环的第一个节点。 如果链表无环,则返回 NULL。

/**
 * Definition for singly-linked list.
 * class ListNode {
 *     int val;
 *     ListNode next;
 *     ListNode(int x) {
 *         val = x;
 *         next = null;
 *     }
 * }
 */
public class Solution {
    public ListNode detectCycle(ListNode head) {
        ListNode fast = head;
        ListNode slow = head;
        while (fast != null && fast.next != null) {
            fast = fast.next.next;
            slow = slow.next;
            if (fast == slow) {
                break;
            }
        }
        if (fast == null || fast.next == null) {
            return null;
        }
        //找到相遇点之后, 让slow指向起始点.
        slow = head;
        //fast和slow同时每次走1步.
        while (fast != slow) {
            fast = fast.next;
            slow = slow.next;
        }
        return fast;//返回fast和slow都一样的
        //优化写法:
        /*ListNode fast = head;
        ListNode slow = head;
        while (fast != null && fast.next != null) {
            fast = fast.next.next;
            slow = slow.next;
            if (fast == slow) {
                slow = head;//找到相遇点之后, 让slow指向起始点, 并结束循环.
                break;
            }
        }
        if (fast == null || fast.next == null) {
            return null;
        }
        while (fast != slow) {
            fast = fast.next;
            slow = slow.next;
        }
        return slow;//返回fast和slow都一样*/
    }
}

LinkedList的模拟实现 

底层是:双向 不带头 非循环 链表 

static class ListNode {
        public int val;
        public ListNode prev;//前驱
        public ListNode next;//后继
        public ListNode(int val) {
            this.val = val;
        }
    }
    public ListNode head;
    public ListNode last;

头插法: 

//头插法 O(1)
    public void addFirst(int data) {
        ListNode node = new ListNode(data);
        if (head == null) {
            head = node;
            last = node;
        } else {
            node.next = head;
            head.prev = node;
            head = node;
        }
    }

尾插法: 

//尾插法 O(1)
    public void addLast(int data) {
        ListNode node = new ListNode(data);
        if (head == null) {
            head = node;
            last = node;
        } else {
            last.next = node;
            node.prev = last;
            last = node;
        }
    }

任意位置插入元素: 

//任意位置插入,第一个数据节点为0号下标
    public void addIndex(int index, int data) {
        if (index < 0 || index > size()) {
            throw new LinekdListIndexOutOfException("index位置不合法!");
        }
        if (index == 0) {
            addFirst(data);
            return;
        }
        if (index == size()) {
            addLast(data);
            return;
        }
        ListNode node = new ListNode(data);
        ListNode cur = findIndex(index);
        node.next = cur;
        cur.prev.next = node;
        node.prev = cur.prev;
        cur.prev = node;
    }
    private ListNode findIndex(int index) {
        ListNode cur = head;
        while (index != 0) {
            cur = cur.next;
            index--;
        }
        return cur;
    }

删除第一次出现关键字key的结点: 

//删除第一次出现关键字为key的结点
    public void remove(int key) {
        ListNode cur = head;
        while (cur != null) {
            if (cur.val == key) {
                //删除头结点
                if (cur == head) {
                    head = head.next;
                    //只有一个结点
                    if (head != null) {
                        head.prev = null;
                    }
                } else {
                    //删除中间或者尾巴结点
                    cur.prev.next = cur.next;
                    if (cur.next != null) {
                        //删除中间结点
                        cur.next.prev = cur.prev;
                    } else {
                        //删除尾巴结点
                        last = last.prev;
                    }
                }
                return;
            }
            cur = cur.next;
        }
    }

删除所有关键字key的结点: 

//删除所有值为key的节点
    public void removeAllKey(int key) {
        ListNode cur = head;
        while (cur != null) {
            if (cur.val == key) {
                //删除头结点
                if (cur == head) {
                    head = head.next;
                    //只有一个结点
                    if (head != null) {
                        head.prev = null;
                    }
                } else {
                    //删除中间或者尾巴结点
                    cur.prev.next = cur.next;
                    if (cur.next != null) {
                        //删除中间结点
                        cur.next.prev = cur.prev;
                    } else {
                        //删除尾巴结点
                        last = last.prev;
                    }
                }
                //return; 去掉return这样就可以接着删
            }
            cur = cur.next;
        }
    }

清空双链表: 

//清空双链表
    public void clear() {
        ListNode cur = head;
        while (cur != null) {
            ListNode curNext = cur.next;
            cur.prev = null;
            cur.next = null;
            cur = curNext;
        }
        head = null;
        last = null;
    }

LinkedList的使用

什么是LinkedList?

LinkedList 的官方文档
LinkedList的底层是双向链表结构(链表后面介绍),由于链表没有将元素存储在连续的空间中,元素存储在单独的节点中,然后通过引用将节点连接起来了,因此在在任意位置插入或者删除元素时,不需要搬移元素,效率比较高

在集合框架中,LinkedList也实现了List接口,具体如下:

【说明】

  1. LinkedList实现了List接口
  2. LinkedList的底层使用了双向链表
  3. LinkedList没有实现RandomAccess接口,因此LinkedList不支持随机访问  
  4. LinkedList的任意位置插入和删除元素时效率比较高,时间复杂度为O(1)
  5. LinkedList比较适合任意位置插入的场景   

LinkedList的使用 

1.LinkedList的构造  

2. LinkedList的其他常用方法介绍   

public static void main(String[] args) {
        LinkedList<Integer> list = new LinkedList<>();
        list.add(1); // add(elem): 表示尾插
        list.add(2);
        list.add(3);
        list.add(4);
        list.add(5);
        list.add(6);
        list.add(7);
        System.out.println(list.size());
        System.out.println(list);
        // 在起始位置插入0
        list.add(0, 0); // add(index, elem): 在index位置插入元素elem
        System.out.println(list);
        list.remove(); // remove(): 删除第一个元素,内部调用的是removeFirst()
        list.removeFirst(); // removeFirst(): 删除第一个元素
        list.removeLast(); // removeLast(): 删除最后元素
        list.remove(1); // remove(index): 删除index位置的元素
        System.out.println(list);
        // contains(elem): 检测elem元素是否存在,如果存在返回true,否则返回false
        if (!list.contains(1)) {
            list.add(0, 1);
        }
        list.add(1);
        System.out.println(list);
        System.out.println(list.indexOf(1)); // indexOf(elem): 从前往后找到第一个elem的位置
        System.out.println(list.lastIndexOf(1)); // lastIndexOf(elem): 从后往前找第一个1的位置
        int elem = list.get(0); // get(index): 获取指定位置元素
        list.set(0, 100); // set(index, elem): 将index位置的元素设置为elem
        System.out.println(list);
        // subList(from, to): 用list中[from, to)之间的元素构造一个新的LinkedList返回
        List<Integer> copy = list.subList(0, 3);
        System.out.println(list);
        System.out.println(copy);
        list.clear(); // 将list中元素清空
        System.out.println(list.size());
    }

3. LinkedList的遍历 

public static void main(String[] args) {
        LinkedList<Integer> list = new LinkedList<>();
        list.add(1); // add(elem): 表示尾插
        list.add(2);
        list.add(3);
        list.add(4);
        list.add(5);
        list.add(6);
        list.add(7);
        System.out.println(list.size());
        // foreach遍历
        for (Integer e : list) {
            System.out.print(e + " ");
        }
        System.out.println();
        // 使用迭代器遍历---正向遍历
        ListIterator<Integer> it = list.listIterator();
        while (it.hasNext()) {
            System.out.print(it.next() + " ");
        }
        System.out.println();
        // 使用反向迭代器---反向遍历
        ListIterator<Integer> rit = list.listIterator(list.size());
        while (rit.hasPrevious()) {
            System.out.print(rit.previous() + " ");
        }
        System.out.println();
    }

ArrayList和LinkedList的区别   

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/541116.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

CST-FSS/周期谐振单元的仿真

引言 这几天要仿真超表面,上下求索CST有关相关内容的教程,视频倒是有不少,不过发现很多人忽略了官方帮助文档。本文以官方帮助文档为基础,写一个有关使用CST实现FSS/超表面这类周期结构的笔记。 官方帮助文档 CST有关FSS的内容使用了一个金属谐振圆环作为例子,这是由于…

pod的基本介绍| harbor仓库的搭建 tomcat镜像拉取

pod的基本介绍| harbor仓库的搭建 tomcat镜像拉取 一 Pod基础概念&#xff1a;二 通常把Pod分为两类&#xff1a;三 Pod容器的分类&#xff1a;四 应用容器&#xff08;Maincontainer&#xff09;五 镜像拉取策略&#xff08;image PullPolicy&#xff09;六 部署 harbor 创建私…

无序列表标签<ul>和<li>

HTML5中提供了3种列表。 无序列表 无序列表是没有刻意顺序的列表。 比如我们如果想去超市买东西&#xff0c;想买的东西又比较多&#xff0c;为了避免忘记漏买某些东西&#xff0c;就会列一个购物清单&#xff0c;购物清单就是一个无序列表&#xff0c;因为要买的东西只要没有…

如何为博客选择目标受众(+例子)

要创建免费网站和博客&#xff1f;从易服客建站平台开始 500M免费空间&#xff0c;可升级为20GB电子商务网站 创建免费网站 您是否正在寻找为您的博客选择目标受众的最佳实践&#xff1f; 选择目标受众可以让您创建更好的内容&#xff0c;引起用户的共鸣。这有助于您获得更…

[学习笔记]解决因C#8.0的语言特性导致EFCore实体类型映射的错误

今天下午在排查一个EF问题时&#xff0c;遇到了个很隐蔽的坑&#xff0c;特此记录。 问题 使用ef执行Insert对象到某表时报错&#xff0c;此对象的Address为空&#xff1a; 不能将值 NULL 插入列 Address&#xff0c;表 dbo.xxx&#xff1b;列不允许有 Null 值。INSERT 失败。…

基于云服务器的博客和靶场搭建-经验教训

搭这两个东西还是走了很多弯路 我就不给你们讲方法过程了&#xff0c;直接参考我给的链接&#xff0c;我主要说经验教训 博客搭建参考&#xff1a;https://zhuanlan.zhihu.com/p/37896471 靶场搭建参考&#xff1a;https://zhuanlan.zhihu.com/p/86409304 https://www.free…

LAMP搭建Discuz

文章目录 一、关闭防火墙二、安装apache三、安装Mysql四、安装PHP五、安装Discuz 一、关闭防火墙 [rootlocalhost ~]# systemctl status firewalld.service还要关闭selinux&#xff0c;要不然后面web页安装Discuz时会过不去。 [rootlocalhost ~]# setenforce 0编辑 /etc/seli…

堆排序——我欲修仙(功法篇)

个人主页&#xff1a;【&#x1f60a;个人主页】 系列专栏&#xff1a;【❤️我欲修仙】 学习名言&#xff1a;学习和研究好比爬梯子&#xff0c;要一步一步地往上爬&#xff0c;企图一脚跨上四五步&#xff0c;平地登天&#xff0c;那就必须会摔跤了。——华罗庚 系列文章目录…

国产游戏引擎,竟然用来搞民航

​本文源自量子位 | 公众号 QbitAI 只是给飞行员做个“装备”&#xff0c;竟然突破了国内民用航空领域的一大技术难题&#xff1f;&#xff01; 这是一群游戏技术开发者的真实经历。 他们用自研游戏引擎开发了一个飞行模拟软件&#xff0c;能够第一视角模拟飞行员起飞、着陆…

Html中使用jquery通过Ajax请求WebService接口以及跨域问题解决

场景 VS2019新建WebService/Web服务/asmx并通过IIS实现发布和调用&#xff1a; VS2019新建WebService/Web服务/asmx并通过IIS实现发布和调用_霸道流氓气质的博客-CSDN博客 在上面实现发布WebService的基础上&#xff0c;怎样在html中通过jquery对接口发起 请求和解析数据。…

【航空和卫星图像中检测建筑物】使用gabor特征和概率的城市区域和建筑物检测研究(Matlab代码实现)

&#x1f4a5;&#x1f4a5;&#x1f49e;&#x1f49e;欢迎来到本博客❤️❤️&#x1f4a5;&#x1f4a5; &#x1f3c6;博主优势&#xff1a;&#x1f31e;&#x1f31e;&#x1f31e;博客内容尽量做到思维缜密&#xff0c;逻辑清晰&#xff0c;为了方便读者。 ⛳️座右铭&a…

领域驱动设计DDD架构解析和绘图模板分享

DDD整洁架构 DDD整洁架构为了解决强调用的关系&#xff0c;出现了洋葱架构&#xff08;六边形&#xff09;架构&#xff0c;就是为了实现依赖倒置 它的思想就是把领域模型放到核心的位置&#xff0c;领域模型是独立的&#xff0c;不会直接强依赖其他层&#xff0c;而通过适配…

mmtrack mmdet mmcv环境安装 版本匹配 2023.5.18

一、参考官网&#xff1a; https://mmtracking.readthedocs.io/zh_CN/latest/install.html# mmtracking&#xff0c;mmcv&#xff0c;mmdetection版本匹配关系&#xff1a; MMTracking versionMMCV versionMMDetection versionmastermmcv-full>1.3.17, \<2.0.0MMDetec…

100 个 Go 错误以及如何避免:9~12

协议&#xff1a;CC BY-NC-SA 4.0 译者&#xff1a;飞龙 本文来自【OpenDocCN 饱和式翻译计划】&#xff0c;采用译后编辑&#xff08;MTPE&#xff09;流程来尽可能提升效率。 真相一旦入眼&#xff0c;你就再也无法视而不见。——《黑客帝国》 九、并发实践 本章涵盖 防止 …

vue3的学习【超详细】

目录 一、vue3的优点1、vue3的优点 二、常用的API1、setup&#xff08;Composition API&#xff09;2、生命周期&#xff08;Composition API&#xff09;3、ref函数和reactive函数用法和区别&#xff08;Composition API&#xff09;1、ref2、reactive3、ref和reactive的区别 …

linux + ros 使用 catkin 从源码编译安装并运行 rocon_rtsp_camera_relay 订阅 rtsp 视频流

1. rocon_rtsp_camera_relay 介绍 最主要的功能在于把相机的 rtsp 视频流 转换为 ros topic 发布出来&#xff0c;使其他节点可以通过订阅的形式获取视频流数据。 2. 编译安装 注&#xff1a;官网的安装命令 sudo apt-get install ros-<distro>-rocon-rtsp-camera-rel…

leetcode 1557. Minimum Number of Vertices to Reach All Nodes(到达所有顶点的最少顶点集)

给出一个有向无环图&#xff08;DAG&#xff09;&#xff0c;顶点有n个&#xff1a;0&#xff5e;n - 1, 边[from, to]为从顶点from到to的边。 找出最小的顶点集合&#xff0c;从这些顶点出发能到达图中的所有顶点&#xff08;集合里不一定每个点都能到达所有顶点&#xff0c;而…

kettle——处理缺失值

目录 一、删除缺失值 1、文本文件输入 2、字段选择 3、过滤记录 4、输出excel文件 5、运行 二、填充缺失值 1、添加文件 2、过滤记录 3、替换NULL值 4、合并记录 5、替换NULL值2 6、字段选择 7、Excel输出 8、运行并查看执行结果 一、删除缺失值 1、文本文件输入…

MMOE - 经典多任务模型(谷歌)

文章目录 1、动机&#xff1a;2、模型结构&#xff1a; Modeling Task Relationships in Multi-task Learning with Multi-gate Mixture-of-Expertsmmoe: Multi-gate Mixture-of-Expertsmmoe由谷歌发表在KDD-2018【和阿里的ESMM同年发表&#xff0c;SIGIR-2018】&#xff1b;模…

华为手环8添加门禁卡操作指导

不得不说&#xff0c;华为基于手机/手环NFC和蓝牙等技术应用&#xff0c;结合门禁卡灵活、安全、便利的优势&#xff0c;给社区场景提供更优质和更多样的技术支持与服务&#xff0c;为广大用户创造美好的数字化生活体验。 目前华为手环8支持模拟市面上未经加密过的、频率为13.5…