数据结构——LinkedList和链表 - 实践

目录

一:ArrayList的缺陷

二:链表

2.1 :链表的概念及结构

2.2:链表的实现

三:LinkedList模拟实现

四:总结


一:ArrayList的缺陷

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

二:链表

2.1 :链表的概念及结构

链表是⼀种物理存储结构上⾮连续存储结构,数据元素的逻辑顺序是通过链表中的引⽤链接次序实现的 。
白话一点:
链表不要求连续的内存空间(离散的结构)
元素和元素之间,内存是不连续的,并且这些元素的空间是没啥规律的(顺序上没有要求,内存上也没有要求)
如何知道链表中包含哪些元素、如何遍历链表中所以元素?
  此处就把每个节点上面都引入一个引用变量,称为next
   使用直观引用保存下一个元素对应的内存空间。

就是在a中存放一个next如何存放b的地址,以此类推,到d中就存放一个null,后面没有元素了。

  链表的特点:
  1.链表的元素在离散的内存空间上
  2.每个元素中记录下一个元素地址(引用)
  需要知道第一个元素是谁,后续整个链表就能拿到了

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

1:单向或者双向

单链表只能指知道下一个,不知道上一个
双向链表,每个节点包含两个引用,prev、next(前一个元素地址、后一个元素地址)功能多了,但是消耗的空间更多了

 2.带头或者不带头(是否带有“傀儡节点”)

头节点:应该是链表第一个节点
  傀儡节点:这个节点不存储数据,而是占位置,来简化后续代码

  其实带头也有傀儡节点,只是不显示,如图带头,其实head的next指向傀儡节点,但是傀儡节点不存储数据,那么head的next就指向d1

  3.循环或者⾮循环

重点关注:
⽆头单向⾮循环链表:结构简单,⼀般不会单独⽤来存数据。实际中更多是作为其他数据结构的⼦结构,如哈希桶、图的邻接表等等。另外这种结构在笔试⾯试中出现很多。
⽆头双向链表:在Java的集合框架库中LinkedList底层实现就是⽆头双向循环链表

在java标准库中,LinkedList就是现成的实现,都是实现List的接口。

2.2:链表的实现

package linkedlist;
import java.util.LinkedList;
public class Test1 {
    public static void main(String[] args) {
        LinkedList list = new LinkedList<>();
        list.add(1);
        list.add(2);
        list.add(3);
        list.add(4);
        list.add(0,5);
        list.addFirst(6);
        System.out.println(list);//[6, 5, 1, 2, 3, 4]
        list.remove(2);// 删除索引为2的元素 1        System.out.println(list);//[6, 5, 2, 3, 4]
        list.remove(Integer.valueOf(2));//删除值为2的元素 2        System.out.println(list);//[6, 5, 3, 4]
        //头删
        list.removeFirst();
        System.out.println(list);//[5, 3, 4]
        //尾删
        list.removeLast();
       System.out.println(list);//[5, 3]
        list.add(1);
        list.add(2);
        list.add(3);
        list.add(4);
        list.add(5);
        //通过get方法获取元素
        System.out.println(list.get(2));//3
        //通过set方法修改元素
        list.set(2, 10);
        System.out.println(list);//[1, 2, 10, 4, 1, 2, 3, 4, 5]
        //通过contians方法判断是否包含元素
        list.add(1);
        list.add(2);
        list.add(3);
        list.add(4);
        list.add(5);
        System.out.println(list.contains(3));//true
        System.out.println(list.contains(6));//false
    }
}

三:LinkedList模拟实现

以下代码模拟实现都有注释,如果有不理解的可以评论O~~~

package linkedlist;
//表示链表的节点
class Node {
    // 节点保存值
    public String value;
    // 指向下一个节点
    public Node next;
    public Node(String value) {
        this.value = value;
        this.next = null;
    }
}
public class MyLinkedList {
         //把链表的头节点表示出来,此时整个链表就能获取了
    //此时不包含傀儡节点,head==null的时候表示空链表
    private Node head=null;
    //不像顺序表使用size表示区间的长度,链表使用length表示链表的长度
    //但也可以使用size表示个数。
    //插入元素
    //1:尾插(先找到尾巴,再插入)
    public void addlast(String value){
        //如果链表为空
        if(head==null){
            //新建一个节点
            Node newNode=new Node(value);
            //把新节点放到头部
            head=newNode;
            return;        }
        //先找道尾巴,把新的节点加道尾巴后面
        //先创建一个头节点
        Node tail=head;
        for(;tail.next!=null;tail=tail.next){
            if(tail.next==null){
                break;
            }
        }//找到尾巴
        //新建一个节点
        Node newNode=new Node(value);
        tail.next=newNode;
        newNode.next=null;
    }
    //2:头插(
    public void addfirst(String value) {
            //新建一个节点
            Node newNode = new Node(value);
            //把新节点放到头部
            //1:就要将newNode的next指向head指向的节点
            newNode.next = head;
            //2:然后我们的head在指向(新节点)newNode
            head = newNode;
    }
    //3:指定位置插入
    //在链表中没有下标的概念
    //链表需要遍历,找位置。
    //但是java标准库,LinkedList同样引入下标这个概念,使用List统一作为ArrayList和LinkedList的接口
    public void add(int index,String value) {
        //先判断index是否合法
        if (index < 0 || index > size()) {//index==size等于尾插,没必要判断
            throw new IndexOutOfBoundsException("Index is out of bounds");
        }
        //针对头插出现的情况
        if(index==0){
            addfirst(value);
            return;        }
        //根据当前value值,创建新的节点
        Node cur = new Node(value);
        //找到index位置的前一个节点
        //由于当前链表是单向链表,每个节点稚只能找到next节点
        //需要修改前一个节点next的值,让它指向当前节点
        //插入新节点,需要找到index-1位置的节点
        Node prev = head;
        for(int i=0;i= size()){
            throw new IndexOutOfBoundsException("Index is out of bounds");
        }
        //特殊处理index为0的情况
        if(index==0){
            head=head.next;
            return;        }
        //找到被删除元素前一个节点位置
        Node prev=head;
        for(int i=0;i

关于clear:
一旦head==null,此时1就没有无人指向
1这个节点,就会被GC给释放掉了
1被释放之后,2就没有人指向了,2也会被GC释放,后面也差不多,依此类推。

四:总结

本篇博客讲述了链表的概念以及使用方法,最后模拟实现了链表。

如果有不明白的可以评论哦。

posted @ 2025-10-27 08:58  yangykaifa  阅读(2)  评论(0)    收藏  举报