永远没有人力可以击退一个坚决强毅的希望 —— 金斯莱
这个专栏就是我自己进行数据结构和算法的总结和学习,我打算将常用的数据结构,比如链表和树等这种还有常见的算法都进行整理和分享,还会在后续的文章中进行解题技巧整理
1. 什么是链表
1.1 定义
数组结构
下面的这些1、2、3、5、6、7、88、111数字用于数组存储的情况下就是,向内存申请一个长度能放下这些元素的空间【这些控件必须是连续的】,系统剩下的空间没有这么多的话那么就申请失败,但是链表有可能申请成空因为它可以不是连续的内存结构
链表结构
链表是一种通过指针串联在一起的线性结构,每一个节点由两部分组成,一个是数据域一个是指针域【存放指向下一个节点的指针】,最后一个节点的指针域指向null【空指针的意思,也是尾部指针】,第一个指针就是链表的头指针
1.2 分类
1.2.1 单向链表
图解
单向链表就是这种,一个节点中包含了数据域和指针域,之后指针指向的是单方向的,这就是单向链表【每个节点的指针域都是存放的后继指针,就是指向下一个节点的指针】
代码结构定义【java】
public class ListNode {
int val; // 节点的值
ListNode next; // 记录下一个节点的位置指针
ListNode(int val, ListNode next) {
this.val = val;
this.next = next;
}
}
1.2.2 双向链表
图解
双向链表就是这种,一个节点中包含了数据域和指针域,之后指针指向的是双方向的【指针一共包含了两个指针,一个是指向前面节点的指针叫前驱指针,一个指向后面的指针叫后继指针】
代码结构定义【java】
class Node {
public int val; // 节点的值
public Node prev; // 记录上一个节点的位置指针
public Node next; // 记录下一个节点的位置指针
ListNode(int val, ListNode prev,ListNode next) {
this.val = val;
this.prev = prev;
this.next = next;
}
};
2. 链表的基本操作
其实吧,链表的操作无非就是链表指针的操作,我们找工作也好,日常开发也好常用的也就是单向的链表,双向的场景还是比较少的,所以我们重点讲解一下单项链表的基本操作方式
假设基础的链表结构就是这个样子的,里面包含了3个节点,1,2,5
2.1 增加节点
图解
中间位置添加
在上面原有的链表中间添加一个节点6的操作 【也就是在1和2两个节点中加一个节点6】
- 第一步先将节点6的指针指向节点2的位置
- 第二步将1节点的指针指向节点6的位置
此时链表的结构就成了下图的样子
注意为什么第一步和第二步不能进行调换呢【如果在不借用其他节点进行记录的情况下】,因为如果第二步先走的话就是指针1的指针先指向节点6,那么节点2就丢了,因为原来记录节点2的指针目前已经指向了节点6了所以这块需要注意一下
头部位置添加
头部添加的话,相对比较简单比如将一个新的节点8 在链表的头部加入
- 第一步就是将节点8的指针指向现有链表的头节点1
- 第二部就是头节点的指针指向节点8的位置就可以了
尾部位置添加
尾部添加的话就是比较简单的了,比如添加一个新的节点7 在链表的尾部
- 第一步直接将节点5的指针,也就是最后一个节点的指针指向新接入的节点就好了,
- 第二步直接将尾部节点指向新添加的节点
2.2 删除节点
图解
中间位置删除
在上面原有的链表中间删除一个节点2的操作
- 第一步就是将指向当前节点2的指针指向节点2指针指向的节点位置
- 断开节点2指针指向的位置,为了方便垃圾回收【特定的语言会有这个操作,比如 java】
尾部位置删除
尾部节点删除其实就是将尾部节点的前一个节点的指针指向空就可以了,之后改变一下尾部节点的指针指向
2.3 修改节点
图解
修改节点其实就是修改节点的值,那么就直接利用遍历操作,获取到要修改的节点位置直接修改其值就可以了 将节点6的值修改为3,那么就直接利用遍历操作修改其val的值即可
2.4 查询节点
查询的话,链表不像数组那样,直接就能进行数组下标进行遍历后者,直接利用数组的索引进行get获取值,链表只能进行遍历查询,所以数组通过索引的方式获取值的时间复杂度是O(1),而链表获取元素的时间复杂度就是O(n)了,它需要进行全部的遍历
总结
这篇文章主要讲解了单项链表的基本操作,而且链表也是我们常见的数据结构,我们只有知道了并掌握了链表结构才能去解决lettcode上面的算法题,如果数据结构都不清楚的话那么根本就无法进行算法的学习,后面会进行讲解一些链表的常见算法的解题技巧帮助大家进行链表相关题型的算法解决