【Java 数据结构】LinkedList与链表

LinkedList与链表

  • [1. ArrayList的缺陷](#1. ArrayList的缺陷)
  • [2. 链表](#2. 链表)
    • [2.1 链表的概念及结构](#2.1 链表的概念及结构)
    • [2.2 链表的实现](#2.2 链表的实现)
  • [3. LinkedList的模拟实现](#3. LinkedList的模拟实现)
  • 4.LinkedList的使用
  • [5. ArrayList和LinkedList的区别](#5. ArrayList和LinkedList的区别)

1. ArrayList的缺陷

上节课已经熟悉了ArrayList的使用,并且进行了简单模拟实现。通过源码知道,ArrayList底层使用数组来存储元素:

dart 复制代码
public class ArrayList<E> extends AbstractList<E>
	implements List<E>, RandomAccess, Cloneable, java.io.Serializable
	{
	// ...
	// 默认容量是10
	private static final int DEFAULT_CAPACITY = 10;
	//...
	// 数组:用来存储元素
	transient Object[] elementData; // non-private to simplify nested class access
	// 有效元素个数
	private int size;
	public ArrayList(int initialCapacity) {
	if (initialCapacity > 0) {
	this.elementData = new Object[initialCapacity];
	} else if (initialCapacity == 0) {
	this.elementData = EMPTY_ELEMENTDATA;
	} else {
	throw new IllegalArgumentException("Illegal Capacity: "+
	initialCapacity);
	}
	}
	// ...
}

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

2. 链表

2.1 链表的概念及结构

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

实际中链表的结构非常多样

  1. 单向或者双向
  • 带头或者不带头
  • 循环或者非循环

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

2.2 链表的实现

自己实现接口ILIST

dart 复制代码
public interface IList {
    //头插法
    void addFirst(int data);
    //尾插法
    void addLast(int data);
    //任意位置插入,第一个数据节点为0号下标
    void addIndex(int index,int data);
    //查找是否包含关键字key是否在单链表当中
    boolean contains(int key);
    //删除第一次出现关键字为key的节点
    void remove(int key);
    //删除所有值为key的节点
    void removeAllKey(int key);
    //得到单链表的长度
    int size();
    void clear();
    void display();
}

链表实现

dart 复制代码
public class MySingleList implements IList {

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

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


    public ListNode head;
    //public int usedSize;//可以定义

    public void createList() {
        ListNode node1 = new ListNode(12);
        ListNode node2 = new ListNode(23);
        ListNode node3 = new ListNode(34);
        ListNode node4 = new ListNode(45);
        ListNode node5 = new ListNode(56);

        node1.next = node2;
        node2.next = node3;
        node3.next = node4;
        node4.next = node5;

        this.head = node1;
    }

    @Override
    public void addFirst(int data) {
        ListNode node = new ListNode(data);
        if (this.head == null) {
            this.head = node;
        } else {
            node.next = this.head;
            this.head = node;
        }
    }

    @Override
    public void addLast(int data) {

        ListNode node = new ListNode(data);

        ListNode cur = this.head;
        if (this.head == null) {
            this.head = node;
        } else {
            //找到尾巴
            while (cur.next != null) {
                cur = cur.next;
            }
            //cur 现在指向了最后一个节点
            cur.next = node;
        }

    }

    @Override
    public void addIndex(int index, int data) {
        if (index < 0 || index > size()) {
            //抛自定义的异常
            return;
        }
        if (index == 0) {
            addFirst(data);
            return;
        }
        if (index == size()) {
            addLast(data);
            return;
        }
        ListNode cur = searchPrev(index);
        ListNode node = new ListNode(data);
        node.next = cur.next;
        cur.next = node;
    }

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

    @Override
    public boolean contains(int key) {
        ListNode cur = this.head;
        while (cur != null) {
            if (cur.val == key) {
                return true;
            }
            cur = cur.next;
        }
        return false;
    }

    @Override
    public void remove(int key) {
        if (this.head == null) {
            //一个节点都没有 无法删除!
            return;
        }
        if (this.head.val == key) {
            this.head = this.head.next;
            return;
        }
        //1. 找到前驱
        ListNode cur = findPrev(key);
        //2、判断返回值是否为空?
        if (cur == null) {
            System.out.println("没有你要删除的数字");
            return;
        }
        //3、删除
        ListNode del = cur.next;
        cur.next = del.next;


    }

    /**
     * 找到关键字key的前一个节点的地址
     *
     * @param key
     * @return
     */
    private ListNode findPrev(int key) {
        ListNode cur = this.head;
        while (cur.next != null) {
            if (cur.next.val == key) {
                return cur;
            }
            cur = cur.next;
        }
        return null;
    }

    @Override
    public void removeAllKey(int key) {
        if (this.head == null) {
            return;
        }
        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;
        }
    }

    @Override
    public int size() {
        int count = 0;
        ListNode cur = this.head;
        while (cur != null) {
            count++;
            cur = cur.next;
        }
        return count;
    }

    @Override
    public void clear() {
        ListNode cur = head;
        while (cur != null) {
            ListNode curNext = cur.next;
            //cur.val = null;
            cur.next = null;
            cur = curNext;
        }
        head = null;
    }

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


    /**
     * 这个是从指定位置开始打印
     *
     * @param newHead
     */
    public void display(ListNode newHead) {
        ListNode cur = newHead;
        while (cur != null) {
            System.out.print(cur.val + " ");
            cur = cur.next;
        }
        System.out.println();
    }
}

3. LinkedList的模拟实现

dart 复制代码
// 2、无头双向链表实现
public class MyLinkedList {
	//头插法
	public void addFirst(int data){ }
	//尾插法
	public void addLast(int data){}
	//任意位置插入,第一个数据节点为0号下标
	public void addIndex(int index,int data){}
	//查找是否包含关键字key是否在单链表当中
	public boolean contains(int key){}
	//删除第一次出现关键字为key的节点
	public void remove(int key){}
	//删除所有值为key的节点
	public void removeAllKey(int key){}
	//得到单链表的长度
	public int size(){}
	public void display(){}
	public void clear(){}
}

4.LinkedList的使用

4.1 什么是LinkedList

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

在集合框架中,LinkedList也实现了List接口

【说明】

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

4.2LinkedList的使用

  1. LinkedList的构造
dart 复制代码
public static void main(String[] args) {
	// 构造一个空的LinkedList
	List<Integer> list1 = new LinkedList<>();
	List<String> list2 = new java.util.ArrayList<>();
	list2.add("JavaSE");
	list2.add("JavaWeb");
	list2.add("JavaEE");
	// 使用ArrayList构造LinkedList
	List<String> list3 = new LinkedList<>(list2);
}
  1. LinkedList的其他常用方法介绍
  2. LinkedList的遍历
dart 复制代码
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();
    }

5. ArrayList和LinkedList的区别

相关推荐
seventeennnnn5 分钟前
Java大厂面试真题:谢飞机的技术挑战
java·spring boot·面试·aigc·技术挑战·电商场景·内容社区
wkj00116 分钟前
接口实现类向上转型和向上转型解析
java·开发语言·c#
qqxhb16 分钟前
零基础设计模式——行为型模式 - 观察者模式
java·观察者模式·设计模式·go
寒士obj43 分钟前
类加载的过程
java·开发语言
无名之逆1 小时前
大三自学笔记:探索Hyperlane框架的心路历程
java·开发语言·前端·spring boot·后端·rust·编程
Chuck1sn1 小时前
我把 Cursor AI 整合到 Ruoyi 中,从此让 Java 脚手架脱离人工!
java·vue.js·后端
水木石画室1 小时前
Spring Boot 常用注解面试题深度解析
java·spring boot·后端
hweiyu001 小时前
tomcat指定使用的jdk版本
java·开发语言·tomcat
百锦再1 小时前
.NET 类库开发详细指南c
java·log4j·.net·net·dot
黎䪽圓2 小时前
【Java多线程从青铜到王者】阻塞队列(十)
java·开发语言