链表的介绍
链表是什么?
一种线性数据结构,由一系列节点组成,每个节点包含数据和指向下一个节点的引用(指针)。
- 通常一个节点包含数据域和指针域两个部分
const obj = {
val: 'a',
next: null
}
链表有哪几种?
链表又分为:单链表、双链表、和循环链表。
链表和数组有哪几种明显的区别?
数组:
- 数组是有序和线性的
- 内部每一个数据都可以直接进行访问
- 查找节点的效率很高
链表:
- 有序但离散的,节点都是离散型存储的
- 只能通过前一个节点访问到后一个节点 (指针)
- 增删节点的效率很高
链表的实现
链表实现实例:
前面我们提到:链表必须是通过前一个结点才能访问到这个节点
错误示范(这是创建两个不同的节点,没有链接起来)
正确示范:
const list = { // 链表的一个节点
val: 'a',
next: {
val: 'b',
next: {
val: 'c',
next: null
}
}
}
将链表定义成这样的结果就可以实现将不同的节点连接起来。
那么我们知道这个原理那就能编写一个简单的遍历链表的程序了:
function ListNode(val, next) {
this.val = val
this.next = next ? next : null
}
const head = new ListNode('a')
let node = head
for (let i = 1; i <= 10; i++) {
node = node.next
}
console.log(node.val);
接下来我们就以leetcode的一道题来讲解: 我们可以在上面链表遍历的基础上去实现这道题
首先我们需要创建一个头结点:
通过头节点不断访问下一个节点,到最后返回这个头节点就相当于返回了所需求的新链表。
然后就需要一个可以判断两个链表之间值大小的程序。
if (list1.val <= list2.val) {
cur.next = list1
list1 = list1.next
} else {
cur.next = list2
list2 = list2.next
}
接下来就是应该设置怎么样一个循环可以让节点来回访问并且判断什么时候结束循环呢?
while (list1 && list2) {}
显然需要一直判断那就需要两个链表最后的节点是否有值(不为 null),那为什么需要list1 && list2呢?因为只要一个值为 null 了,那就不需要判断两者之间的关系直接 .next 访问就好了。
var mergeTwoLists = function(list1, list2) {
let head = new ListNode() // {val: 0, next: null}
let cur = head // 后面 cur 访问到的就是 head 访问到的,因为他们访问的值都是引用地址,地址相同,访问的内容那就一样的。
while (list1 && list2) {
if (list1.val <= list2.val) {
cur.next = list1
list1 = list1.next
} else {
cur.next = list2
list2 = list2.next
}
cur = cur.next //让这个变量代表的是自己的下一个节点
}
// 当某一个链表已经穿完了仍需要访问完另一个还未遍历完的链表。
if (list1) {
cur.next = list1
}
if (list2) {
cur.next = list2
}
return head.next
};
这就已经实现一个完整简易的链表合并代码了,其他类型都可以以这个为基础展开解答。
链表的注意事项:
- 空链表检查:所有操作前先判断
head是否为null - 单节点处理:删除/操作时检查
head === tail - 头尾指针更新:插入/删除头尾节点时同步更新
head和tail - 插入:先连新节点 → 后断原指针
-
- 删除:先备份后节点 → 再修改前驱指针
- 双链表对称修改:同时维护
prev和next,保持双向链接一致性 - 设置终止条件(如回到起点或计数器)