当前位置: 首页 > news >正文

[数据结构] 链表

一.链表

1.链表概念

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

注意:

  • 从上图中可以看出 , 链表结构在逻辑上是连续的 , 但是在物理上是不连续的
  • 现实中的结点一般都是从 堆上申请出来的
  • 从堆上申请的空间 , 是按照一定的策略来分配的 , 两次申请的 空间可能连续 , 也可能不连续


2.链表分类(8种)

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

  • 单向或双向
  • 带头结点或者不带头结点
  • 循环或非循环

类型结构特点核心引用域
单向链表每个节点只指向后一个节点,尾节点nextnull,只能从头部遍历next
双向链表每个节点同时指向前后节点,支持双向遍历,尾节点nextnullprev + next
循环链表尾节点的next指向头节点(单向循环)或头节点的prev指向尾节点(双向循环),可循环遍历同单向 / 双向

头结点可以存放数据 , 但是无任何意义


3.链表的实现

  • 无头单项非循环链表的实现
  • 头插法 , 尾插法 , 任意位置插入 , 查找 , 删除 , 长度 , 清空 , 打印等功能 

①接口的实现

public interface IList {//头插法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 clear();public void display();
}

②功能的实现

//无头单项非循环链表的实现
public class MyLinkedList implements IList {static class ListNode {public int val;public ListNode next;public ListNode(int val) {this.val = val;}}public ListNode head;//求链表的长度@Overridepublic int size() {if(head == null) {return 0;}else {int len = 0;ListNode cur = head;while (cur.next != null){cur = cur.next;len++;}return len;}}//头插法@Overridepublic void addFirst(int data) {ListNode node = new ListNode(data);if(head == null){head =  node;}else {node.next = head;head = node;}}//尾插法@Overridepublic void addLast(int data) {ListNode node = new ListNode(data);if(head == null){head =  node;}else{ListNode cur = head;while(cur.next!=null){cur = cur.next;}cur.next = node;}}//在下标 index 位置 , 插入 data@Overridepublic void addIndex(int index, int data) {ListNode node = new ListNode(data);int len = size();if(index<0||index>len){throw new IndexOutOfBoundsException("下标越界");}if(index == 0){addFirst(data);}if(index == len){addLast(data);}ListNode cur = head;for (int i = 0; i < index; i++) {cur = cur.next;}node.next = cur.next;cur.next = node;}//查找是否包含关键字key是否在单链表当中@Overridepublic boolean contains(int key) {int len = size();if(len == 0) {return false;}ListNode cur = head;while (cur.next!=null)if(cur.val == key) {return true;}cur = cur.next;return false;}//删除第一次出现关键字为key的节点@Overridepublic void remove(int key) {// 空链表直接返回if(head == null) {return;}// 处理头节点是目标节点的情况if(head.val == key) {head = head.next;return;}// 查找并删除中间/尾部的目标节点ListNode cur = head;while (cur.next != null) {if (cur.next.val == key) {// 找到目标节点,进行删除cur.next = cur.next.next;return; // 只删除第一个匹配的节点}cur = cur.next;}// 循环结束仍未找到,说明链表中没有该节点,无需操作}//删除所有值为key的节点@Overridepublic void removeAllKey(int key) {if(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;}}//清空链表@Overridepublic void clear() {ListNode cur = head;while (cur!=null){ListNode curN = cur.next;cur = null;cur = curN;}head =null;}//打印链表@Overridepublic void display() {ListNode cur = head;while (cur!=null){System.out.println(cur.val+" ");cur = cur.next;}}
}


4.链表与数组的对比

  • 内存分配:链表动态分配内存,数组需连续内存。
  • 访问效率:数组随机访问O(1),链表需遍历O(n)。
  • 插入/删除:链表在已知位置操作更高效(O(1)或O(n)),数组需移动元素(O(n))。
对比维度链表(LinkedList)数组(ArrayList)
内存存储非连续内存,通过引用连接连续内存,元素按索引存储
增删效率头部 / 尾部增删 O (1),中间增删 O (n)(需遍历)尾部增删 O (1),头部 / 中间增删 O (n)(需移动元素)
查询效率按索引查询 O (n)(需遍历)按索引查询 O (1)(直接通过地址偏移获取)
动态扩容无需扩容(节点可动态创建)需扩容(默认扩容为原容量 1.5 倍,浪费内存)
线程安全非线程安全非线程安全
适用场景频繁增删(尤其是头部 / 尾部)、不确定长度频繁查询、已知大致长度


5.总结

  • Java 中链表分为自定义链表和内置LinkedListLinkedList是双向循环链表,功能强大;
  • 链表的核心是节点和引用,增删灵活但查询效率低,适合频繁增删的场景;
  • 需掌握链表的基本操作(增、删、反转)和经典算法问题,理解指针(引用)的移动逻辑。


例1:

  • 给你单链表的头结点 head ,请你找出并返回链表的中间结点。
  • 如果有两个中间结点,则返回第二个中间结点。
class Solution {public ListNode middleNode(ListNode head) {ListNode cur1 = head;//快指针ListNode cur2 = head;//慢指针while(cur1!=null&&cur1.next!=null){//cur1 = cur1.next.next;cur2 = cur2.next;}return cur2;}
}


例2:

  • 链表的逆置
   //链表的逆置
public ListNode reverseList() {if(head == null||head.next == null) {return head;}ListNode cur = head.next;head.next = null;while(cur != null) {ListNode curN = cur.next;cur.next = head;head = cur;cur = curN;}return head;
}


例3:

  •     获取倒数第 k 个结点的数据
    //获取倒数第 k 个结点的数据public int kthToLast( int k) {if(head == null) return -1;if(k <= 0) {return -1;}ListNode fast = head;ListNode slow = head;int count = 0;while(count != k-1) {fast = fast.next;if(fast == null) {return -1;}count++;}while(fast.next != null) {fast = fast.next;slow = slow.next;}return slow.val;}


http://www.xdnf.cn/news/1460467.html

相关文章:

  • 深度学习之第七课卷积神经网络 (CNN)调整学习率
  • MySQL子查询的分类讲解与实战
  • 从基础到实践:Web核心概念与Nginx入门全解析
  • 前端url参数拼接和提取
  • 嵌入式基础 -- I²C 信号与位层规则
  • Swift 解法详解:LeetCode 371《两整数之和》
  • 漏洞绕过方式
  • 从零到一:人工智能应用技术完全学习指南与未来展望
  • ClickHouse 分片、 Distributed 表、副本机制
  • flowable基础入门
  • 【c/c++】深度DFS
  • MATLAB平台实现人口预测和GDP预测
  • 美国教授提出的布鲁姆法,结合AI直击学术科研痛点,写作与创新效率直接翻倍!
  • 漫谈《数字图像处理》之实时美颜技术
  • Java并行计算详解
  • 解决 Rollup failed to resolve import “vue3-json-viewer/dist/index.css“ from xxx
  • 【Docker】P1 前言:容器化技术发展之路
  • JS本地存储
  • Java String vs StringBuilder vs StringBuffer:一个性能优化的探险故事
  • C++学习记录(6)string部分操作的模拟实现
  • push pop 和 present dismiss
  • Leetcode 206. 反转链表 迭代/递归
  • 拦截器和过滤器(理论+实操)
  • Websocket链接如何配置nginx转发规则?
  • NV169NV200美光固态闪存NV182NV184
  • 云数据库服务(参考自腾讯云计算工程师认证课程)更新中......
  • 阿里云 ESA 实时log 发送没有quta的解决
  • 【机器学习】HanLP+Weka+Java=Random Forest算法模型
  • 【CS32L015C8T6】配置单片机时基TimeBase(内附完整代码及注释)
  • Mysql杂志(九)