LinkedList与链表

前言

本篇博客主要讲述 - 以下内容

  1. ArrayList的缺陷
  2. 链表
  3. 链表相关oj
  4. LinkedList的模拟实现
  5. LinkedList的使用
  6. ArrayList和LinkedList的区别

ArrayList的缺陷

上篇博客讲述了ArrayList的使用 ,我们知道ArrayList底层使用数组来存储元

由于其底层是一段连续空间,当在ArrayList任意位置插入或者删除元素时,就需要将后序元素整体往前或者往后搬移,时间复杂度为O(n),效率比较低,因此ArrayList不适合做任意位置插入和删除比较多的场景。因此:java集合中又引入了LinkedList,即链表结构。

链表

链表的概念及结构

链表是一种物理存储结构上非连续存储结构,数据元素的逻辑顺序是通过链表中的引用链接次序实现的 。
在这里插入图片描述
实际中链表的结构非常多样,以下情况组合起来就有8种链表结构:

  1. 单向或者双向
  2. 带头或者不带头节点
  3. 循环或者非循环链表

我们主要讲解俩种链表结构

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

链表的实现

import java.util.List;


public class MySingleList {
    
    

    static class ListNode {
    
    
        public int val;//存储的数据
        public ListNode next;//存储下一个节点的地址
        public ListNode() {
    
    

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

    public ListNode head;// 代表当前链表的头节点的引用


    public void createLink() {
    
    
        ListNode listNode1 = new ListNode(12);
        ListNode listNode2 = new ListNode(45);
        ListNode listNode3 = new ListNode(23);
        ListNode listNode4 = new ListNode(90);
        listNode1.next = listNode2;
        listNode2.next = listNode3;
        listNode3.next = listNode4; /* */
        head = listNode1;
    }


    /**
     * 遍历链表
     */
    public void display() {
    
    
        //如果说 把整个链表 遍历完成 那么 就需要 head == null
        // 如果说 你遍历到链表的尾巴  head.next == null
        ListNode cur = head;
        while (cur != null) {
    
    
            System.out.print(cur.val+" ");
            cur = cur.next;
        }
        System.out.println();
    }

    /**
     * 从指定位置开始打印链表
     * @param newHead
     */
    public void display(ListNode newHead) {
    
    
        //如果说 把整个链表 遍历完成 那么 就需要 head == null
        // 如果说 你遍历到链表的尾巴  head.next == null
        ListNode cur = newHead;
        while (cur != null) {
    
    
            System.out.print(cur.val+" ");
            cur = cur.next;
        }
        System.out.println();
    }

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

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

    //头插法 O(1)
    public void addFirst(int data){
    
    
        ListNode listNode = new ListNode(data);
        listNode.next = head;
        head = listNode;
    }
    //尾插法 O(N)    找尾巴的过程
    public void addLast(int data){
    
    
        ListNode listNode = new ListNode(data);
        if(head == null) {
    
    
            head = listNode;
            return;
        }
        ListNode cur = head;
        while (cur.next != null) {
    
    
            cur = cur.next;
        }
        cur.next = listNode;
    }
    //任意位置插入,第一个数据节点为0号下标
    public void addIndex(int index,int data)
            throws ListIndexOutOfException{
    
    
        checkIndex(index);
        if(index == 0) {
    
    
            addFirst(data);
            return;
        }
        if(index == size()) {
    
    
            addLast(data);
            return;
        }
        ListNode cur = findIndexSubOne(index);
        ListNode listNode = new ListNode(data);
        listNode.next = cur.next;
        cur.next = listNode;
    }

    /**
     * 找到 index-1位置的节点的地址
     * @param index
     * @return
     */
    private ListNode findIndexSubOne(int index) {
    
    
        ListNode cur = head;
        int count = 0;
        while (count != index-1) {
    
    
            cur = cur.next;
            count++;
        }
        return cur;
    }
    private void checkIndex(int index) throws ListIndexOutOfException{
    
    
        if(index < 0 || index > size()) {
    
    
            throw new ListIndexOutOfException("index位置不合法");
        }
    }

    //删除第一次出现关键字为key的节点 O(N)
    public void remove(int key){
    
    
        if(head == null) {
    
    
            return ;//一个节点都没有
        }
        if(head.val == key) {
    
    
            head = head.next;
            return;
        }
        ListNode cur = searchPrev(key);
        if(cur == null) {
    
    
            return;
        }
        ListNode del = cur.next;//要删除的节点
        cur.next = del.next;
    }

    /**
     * 找到关键字key的前一个节点
     * @param key
     * @return
     */
    private ListNode searchPrev(int key) {
    
    
        ListNode cur = head;
        while (cur.next != null) {
    
    
            if(cur.next.val == key) {
    
    
                return cur;
            }
            cur = cur.next;
        }
        return null;//没有你要删除的节点
    }

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

    /**
     * 保证链表当中 所有的节点 都可以被回收
     */
    public void clear() {
    
    
        head = null;

        //StringBuilder sb = "fdfa";
    }

  


}

说明: 链表的习题将会出一篇博客,尽量将结构与题型分开

LinkedList的使用

LinkedList的官方文档

https://docs.oracle.com/javase/8/docs/api/java/util/LinkedList.html

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

在集合框架中,LinkedList也实现了List接口
在这里插入图片描述

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

LinkedList的其他常用方法介绍

方法 解释
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 lastIndexOf(Object o) 返回最后一个 o 的下标
List subList(int fromIndex, int toIndex) 截取部分 list

LinkedList的遍历

// foreach遍历
for (int e:list) {
    
    
	System.out.print(e + " ");
}
// 使用迭代器遍历---正向遍历
ListIterator<Integer> it = list.listIterator();
	while(it.hasNext()){
    
    
		System.out.print(it.next()+ " ");
}
// 使用反向迭代器---反向遍历
ListIterator<Integer> rit = list.listIterator(list.size());
while (rit.hasPrevious()){
    
    
	System.out.print(rit.previous() +" ");
}

ArrayList和LinkedList的区别

ArrayList LinkedList
物理上一定连续 逻辑上连续 物理上不一定连续
随机访问 支持O(1) 随机访问 支持O(N)
插入O(N) 插入(1)
应用场景 元素高效存储+频繁访问 任意位置插入和删除频繁

猜你喜欢

转载自blog.csdn.net/qq_56454895/article/details/131839163