链表的概念和单向链表的实现

文章目录

一:链表的概念及结构

1.1链表是⼀种物理存储结构上⾮连续 存储结构,数据元素的逻辑顺序 是通过链表中的引⽤链接 次序实现

的。

(1))物理非连续 :链表的节点在内存中不一定连续,每个节点包含数据域(val)和引用域(next)(指向其他节点)。

(2)逻辑连续 :通过引用域,节点之间形成链式关系,保证了数据在逻辑上的连续。

(3))节点来源 :现实中,链表的节点一般从堆内存中申请,两次申请的空间可能连续也可能不连续。

1.2 链表的常见结构

链表的结构多样,通过以下三个维度组合,可形成 8 种不同的链表结构:

(1))单向 / 双向 :单向链表节点只有一个引用域,指向后继节点;双向链表节点有两个引用域,分别指向前驱和后继节点。

(2))带头 / 不带头 :带头链表有一个头节点(不存储实际数据),用于简化操作;不带头链表直接从存储数据的节点开始。

(3))循环 / 非循环 :循环链表的尾节点引用指向头节点(或头节点相关节点),形成闭环;非循环链表的尾节点引用为 null。
示例:

(1)单向不带头非循环

(2)单向带头非循环
(3)单向不带头循环

(4)单向带头循环

在实际应用中,我们重点掌握两种核心结构:

无头单向非循环链表 :结构简单,一般不单独用于存储数据,常作为其他数据结构(如哈希桶、图的邻接表)的子结构,也是笔试面试中的高频考点。
无头双向非循环链表:Java 中 LinkedList 的底层实现就是这种结构,能兼顾插入、删除和查询操作的性能。

二:单向链表

复制代码
                         我们自己实现一个⽆头单向⾮循环链表

(1)基础结构定义

java 复制代码
public class MySingleList {
    // 内部静态类:链表节点
    static class ListNode{
        public int val;    // 数据域
        public ListNode next; // 引用域(下一个节点引用)

        // 初始化数据域
        public ListNode(int val) {
            this.val = val;
        }
    }

    ListNode head; // 链表的头引用,是访问链表的入口
}

(2)手动创建链表

java 复制代码
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;


}

(3)打印链表

java 复制代码
public void display() {
    //不要让head动
    ListNode cur = this.head;
    while (cur != null) {
        System.out.print(cur.val + " ");
        cur = cur.next;
    }
    System.out.println();
}

测试:

java 复制代码
public class Test {
    public static void main(String[] args) {
        MySingleList mySingleList = new MySingleList();
        mySingleList.createList();
        mySingleList.display();
    }
}

在这里我们会思考如果我们把循环条件改为cur.next !=null会怎么样

根据上面的链表图,我们可以推出不会打印最后一个val;

我们来修改一下代码来看结果

确实少打印了56 ,说明我们的推断是正确的

所以我们可以总结出一个结论:
如果要停在最后一个节点,那么cur.next !=null,如果要遍历完所有节点,那么cur !=null

(4)得到单链表的⻓度

java 复制代码
public int size(){
     int count = 0;
     ListNode cur = this.head;
     while(cur !=null){
          count++;
          cur = cur.next;
     }
     return count;
}

(5)查找是否包含关键字key是否在单链表当中

java 复制代码
 public boolean contains(int key){
        ListNode cur = this.head;
        while(cur != null){
             if(cur.val == key){
                 return true;
             }
             cur = cur.next;
        }
        return false;

 }

(6)头插法

1实例化一个节点对象node

2.修改指向

java 复制代码
//时间复杂度为O(1)
public void addFirst(int data){
       ListNode node = new ListNode(data);//实例化一个节点对象node

       node.next = this.head;//修改指向
       this.head = node;//修改指向

}


注意:修改指向这两步不能调换,因为调换之后相当于自己指向自己
结论:所有插入的时候先绑定后面

这时我们会有一个疑问,如果我们在一个空链表里使用头插法要不要特殊处理?

答案是不用!

因为你一开始节点的引用域本来就是null,你的head也是null,第一步修改指向没什么变化,第二步修改指向,head就存node的地址了,也就是正常插入了!

(7)尾插法

1看一下链表是不是空链表(一个节点都没有)head==null

2.找尾巴

java 复制代码
//时间复杂度O(N)
public void addLast(int data){
       ListNode node = new ListNode(data);
       if(this.head == null){
           this.head = node;
           return;
       }
       //找尾巴
       ListNode cur = this.head;
       while(cur.next != null){
            cur = cur.next;
       }
       cur.next = node;
}

(8)下标不合法异常

java 复制代码
public class illegalIndexException extends RuntimeException {
    public illegalIndexException() {
    }
    public illegalIndexException(String message) {
        super(message);
    }
}

(9)任意位置前面插⼊,第一个数据节点为0号下标

假如插入2位置,那就是插入2之前,所以要找到index位置的前一个节点

java 复制代码
//任意位置前面插⼊,第一个数据节点为0号下标
public void addIndex(int index,int data){
            int len = size();
            if(index < 0 || index > len){
                throw new illegalIndexException("下标不合法");
            }
            //头插法
            if(index == 0){
                addFirst(data);
                return;
            }
            //尾插法
            if(index == len){
                addLast(data);
                return;
            }
            //中间位置
            ListNode cur = searchIndex(index);
            if(cur == null){
                return;
            }
            ListNode node = new ListNode(data);
            node.next = cur.next;
            cur.next = node;



 }

    /**
     * 找到index位置的前一个节点
     * @param index
     * @return
     */
    private ListNode searchIndex(int index){
        int len = size();
        if(index < 0 || index >len){
            return null;
        }
        ListNode cur = this.head;
        int count = 0;
        while(count !=index-1){
            cur = cur.next;
            count++;
        }
        return cur;
    }

(10)删除第⼀次出现关键字为key的节点

java 复制代码
/**
 * 查找关键字key的前一个节点,找到返回地址
 * 找不到返回null
 * @param key
 * @return
 */
private ListNode findNode(int key){
     if(this.head == null){
            return null;
     }
     ListNode prev = this.head;
     while(prev.next != null){
          if(prev.next.val == key){
               return prev;
          }
          prev = prev.next;
     }
     return null;

}
//删除第⼀次出现关键字为key的节点
public void remove(int key){
      if(this.head == null){
            return;
      }
      if(this.head.val == key){
            this.head = this.head.next;
            return;
      }
      //走到这里 第一个节点如果是要删除的节点 此时已经删除完毕
      ListNode prev = findNode(key);
      if(prev == null){
          return;
      }
      ListNode del = prev.next;
      prev.next = del.next;
}

(11)删除所有值为key的结点

java 复制代码
public ListNode removeAllKey(int key) {
    if(this.head == null) {
        return null;
    }
    ListNode prev = this.head;
    ListNode cur = this.head.next;
    
    while(cur != null) {
        if(cur.val == key) {
            prev.next = cur.next;
            cur = cur.next;
        } else {
            prev = cur;
            cur = cur.next;
        }
    }
        //最后处理头
    if(this.head.val == key) {
        this.head = this.head.next;
    }
    return this.head;
}

(12)清空链表中所有元素:

java 复制代码
public void clear() {
    while (this.head != null) {
        ListNode curNext = head.next;
        head.next = null;
        head.prev = null;
        head = curNext;
    }
    last = null;
}
相关推荐
INGNIGHT5 小时前
单词搜索 II · Word Search II
数据结构·c++·算法
QuantumLeap丶7 小时前
《数据结构:从0到1》-06-单链表&双链表
数据结构·算法
violet-lz7 小时前
数据结构八大排序:快速排序-挖坑法(递归与非递归)及其优化
数据结构
Mrliu__7 小时前
Python数据结构(七):Python 高级排序算法:希尔 快速 归并
数据结构·python·排序算法
大数据张老师9 小时前
数据结构——广度优先搜索
数据结构·图论·宽度优先
小梁努力敲代码9 小时前
java数据结构--LinkedList与链表
java·数据结构·链表
qq_433554549 小时前
C++ 双向循环链表
开发语言·c++·链表
再睡一夏就好10 小时前
【C++闯关笔记】深究继承
java·数据结构·c++·stl·学习笔记
那我掉的头发算什么10 小时前
【数据结构】反射、枚举、lambda表达式以及补充知识
java·jvm·数据结构·intellij idea