详解LinkedList与链表

1. ArrayList的缺陷

当在ArrayList任意位置插入或者删除元素时,就需要将后序元素整体往前或者往后搬移,时间复杂度为O(n),效率比较低,因此ArrayList不适合做任意位置插入和删除比较多的场景。

因此:java 集合中又引入了LinkedList,即链表结构。

2. 链表的概念及结构

链表是一种物理存储结构上非连续存储结构,数据元素的逻辑顺序是通过链表中的引用链接次序实现的。

实际中链表的结构非常多样,以下情况组合起来就有8种链表结构:

1. 单向或者双向

2. 带头或者不带头

3. 循环或者非循环

重点掌握两种:

  • 无头单向非循环链表:结构简单,一般不会单独用来存数据。实际中更多是作为其他数据结构的子结构,如 哈希桶、图的邻接表等等。

  • 无头双向链表:在Java的集合框架库中LinkedList底层实现就是无头双向循环链表。

3. 链表的实现

无头单向非循环链表实现

public class MySingleLinkedList {

    // 节点内部类
    static class ListNode {
        public int val;
        public ListNode next;

        public ListNode(int val) {
            this.val = val;
        }
    }

    public ListNode head;// 不初始化,默认为null

    public void dispaly() {
        ListNode cur = this.head;
        while (cur != null) {
            System.out.print(cur.val + " ");
            cur = cur.next;
        }
        System.out.println(" ");
    }

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

    // 查找是否包含关键字key是否在单链表当中
    public boolean contains(int key) {
        ListNode cur = this.head;
        while (cur != null) {
            if (cur.val == key) {
                return true;
            }
            cur = cur.next;
        }
        return false;
    }

    // 头插法
    // 先绑定和后面节点的关系
    public void addFirst(int data) {
        ListNode node = new ListNode(data);
        node.next = head;
        head = node;
    }

    // 尾插法
    public void addLast(int data) {
        ListNode node = new ListNode(data);
        ListNode cur = this.head;
        if(cur == null) {
            this.head = node;
        }else {
            while (cur.next != null) {
                cur = cur.next;
            }
            //cur已经是尾节点了
            cur.next = node;
        }
    }

    // 任意位置插入,第一个数据节点为0号下标
    public void addIndex(int index, int data) {
        if (index < 0 || index > size()) {
            System.out.println("index位置不合法");
            throw new IndexWrongFulException("index位置不合法");
        }
        if (index == 0) {
            addFirst(data);
            return;
        }
        if (index == size()) {
            addLast(data);
            return;
        }
        // 先走index-1步,找到cur
        ListNode cur = findIndexSubOne(index);
        ListNode node = new ListNode(data);
        //
        node.next = cur.next;
        cur.next = node;
    }
    private ListNode findIndexSubOne(int index) {
        ListNode cur = this.head;
        while (index - 1 != 0) {
            cur = cur.next;
            index--;
        }
        return cur;
    }

    // 删除第一次出现关键字为key的节点
    public void remove(int key) {
        // 判断链表为空
        if (this.head == null) {
            return;
        }
        // 删除头节点
        if (this.head.val == key) {
            this.head = this.head.next;
            return;
        }
        ListNode cur = findPrevOfKey(key);
        if (cur == null) {
            System.out.println("没有你要删除的数字");
            return;
        }
        ListNode del = cur.next;
        cur.next = del.next;
    }
    private ListNode findPrevOfKey(int key) {
        ListNode cur = this.head;
        while (cur.next != null) {
            if (cur.next.val == key) {
                return cur;
            }
            cur = cur.next;

        }
        return null;
    }

    // 删除所有值为key的节点
    public void removeAllKey(int key) {
        if (this.head == null) {
            return;
        }
        ListNode cur = this.head.next;
        ListNode pre = this.head;
        while (cur != null) {
            if (cur.val == key) {
                pre.next = cur.next;
                cur = cur.next;
            } else {
                pre = cur;
                cur = cur.next;
            }
        }
        if (this.head.val == key) {
            head = head.next;
        }
    }

    public void clear() {
        this.head = null;
    }

4.什么是LinkedList

LinkedList官方文档

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

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

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

5.LinkedList的模拟实现

LinkedList底层就是一个双向链表

public class MyLinkedList {
    static class ListNode {
        public int val;
        public ListNode next;
        public ListNode pre;

        public ListNode(int val) {
            this.val = val;
        }
    }

    public ListNode head;
    public ListNode tail;       

    // 头插法
    public void addFirst(int data) {
        ListNode node = new ListNode(data);
        if (this.head == null) {
            this.head = node;
            this.tail = node;
        } else {
            node.next = this.head;
            this.head.pre = node;
            head = node;
        }

    }

    // 尾插法
    public void addLast(int data) {
        ListNode node = new ListNode(data);
        if (this.head == null) {
            this.head = node;
            this.tail = node;
        } else {
            node.pre = this.tail;
            this.tail.next = node;
            tail = node;
        }
    }

    // 任意位置插入,第一个数据节点为0号下标
    public void addIndex(int index, int data) {
        if (index < 0 || index > size()) {
            throw new IndexWrongFulException("index位置不合法");
        }

        if (index == 0) {
            addFirst(data);
            return;
        }
        if (index == size()) {
            addLast(data);
            return;
        }

        ListNode cur = findIndeListNode(index);

        ListNode node = new ListNode(data);
        node.next = cur;
        node.pre = cur.pre;
        cur.pre.next = node;
        cur.pre = node;

    }

    private ListNode findIndeListNode(int index) {
        ListNode cur = this.head;
        while (index != 0) {
            index--;
            cur = cur.next;
        }
        return cur;
    }

    // 查找是否包含关键字key是否在单链表当中
    public boolean contains(int key) {

        return false;
    }

    // 删除第一次出现关键字为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.pre=null;
                    }else {
                        tail=null;
                    }
                    
                } else {
                    cur.pre.next = cur.next;
                    if (cur.next != null) {
                        cur.next.pre = cur.pre;
                    } else {
                        this.tail = cur.pre;
                    }
                }
                 return;
            }
            cur = cur.next;
        }
    }

    // 删除所有值为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.pre=null;
                    }else {
                        tail=null;
                    }
                    
                } else {
                    cur.pre.next = cur.next;
                    if (cur.next != null) {
                        cur.next.pre = cur.pre;
                    } else {
                        this.tail = cur.pre;
                    }
                }

            }
            cur = cur.next;
        }
    }

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

    public void display() {
        ListNode cur = this.head;
        while (cur != null) {
            System.out.print(cur.val + " ");
            cur = cur.next;
        }
        System.out.println();
    }

    public void clear() {
        
        ListNode cur=this.head;
        
        while(cur!=null) {
            ListNode curNext = cur.next;
            
            cur.next=null;
            cur.pre=null;
            cur=curNext;
        }

            head=null;
            tail=null;
    }
}

6.LinkedList的使用

6.1LinkedList的构造

LinkedList()

无参构造

public LinkedList(Collection<? extends E> c)

使用其他集合容器中元素构造List

public static void main(String[] args) { 
   // 构造一个空的LinkedList
   List<Integer> list1 = new LinkedList<>();
   List<String> list2 = new ArrayList<>();
   list2.add("JavaSE");
   list2.add("JavaWeb");
   list2.add("JavaEE");
   // 使用ArrayList构造LinkedList
   List<String> list3 = new LinkedList<>(list2); 
}

6.2常用方法

方法

解释

boolean add(E e)

尾插e

void add(int index, E element)

将e插入到index位置

boolean addAll(Collection<? extends E> c)

尾插c中的元素

E remove(int index)

删除index位置元素

boolean remove(Object o)

删除遇到的第一个o

E get(int index)

获取下标index位置元素

E set(int index,E element)

将下标index位置元素设置为element

void clear()

清空

boolean contains(Object o)

判断o是否在线性表中

int indexOf(Object o)

返回第一个o所在下标

int lastlndexOf(Object o)

返回最后一个o的下标

List<E> subList(int fromIndex, int tolndex)

截取部分list

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());
}

6.3LinkedList的遍历

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 (int 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(); 
}

7.ArrayList和LinkedList的区别

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值