📝个人主页:爱吃炫迈
💌系列专栏:数据结构与算法
🧑💻座右铭:道阻且长,行则将至💗
文章目录
- 反转链表
- 移除链表元素
- 两两交换链表中的节点
- 删除链表的倒数第 N 个结点
- 💞总结💞
反转链表
LeetCode题目:反转链表
思路 :
改变链表的next指针的指向,直接将链表反转 ,而不用重新定义一个新的链表。如下图所示:
步骤 :
pre
:表示当前需要反转节点的前一个节点node
:表示当前到达的节点
- 定义两个指针
pre
和node
:pre
在前,node
在后 - 每次让
node.next
指向pre
,实现一次局部反转 - 局部反转完成之后,
pre
和node
都向前移动一个位置 - 循环上述过程,直到
node
到达链表尾部
代码 :
var reverseList = function (head) {
let pre = null; //当前需要翻转节点的前一个节点
let node = head; //当前需要翻转的节点
while (node) {
let nextNode = node.next;
// 翻转指针
node.next = pre;
// pre和node都往后移动
pre = node;
node = nextNode;
}
// 此时pre是新的头结点,所以返回
return pre;
};
移除链表元素
LeetCode题目:移除链表元素
思路 :
用迭代的方法删除链表中所有节点值等于特定值的节点。如下图所示:
- 这种情况下的移除操作,就是让节点next指针直接指向下下一个节点就可以了,
- 那么因为单链表的特殊性,只能指向下一个节点,刚刚删除的是链表的中第二个,和第四个节点,那么如果删除的是头结点又该怎么办呢?
🌷🌷可以设置一个虚拟头结点在进行删除操作:使得头结点和其他节点删除操作相同。
步骤 :
dummy node
:虚拟头节点
- 创建一个虚拟的头节点,并将该节点的
next
指针指向原链表的头节点。 - 判断
node.next.val===val
是否成立,若成立将node的next指针指向下下个节点,即ndoe.next=node.next.next
node
向后移动,直到迭代结束- 最后函数返回虚拟头节点的
next
指针,真正的头结点
代码 :
var removeElements = function (head, val) {
// 虚拟头结点,值为0,指向head
const dummyNode = new ListNode(0, head);
let node = dummyNode;
while (node) {
if (node.next.val === val) {
node.next = node.next.next;
continue;
}
node = node.next;
}
return dummyNode.next;
};
两两交换链表中的节点
LeetCode题目:两两交换链表中的节点
思路 :
通过迭代的方式实现两两交换链表中的节点。
步骤 :
- 创建虚拟头结点 dummyHead,令 dummyHead.next = head。
temp.next=node2
,令temp->node2节点
temp
:表示当前到达的节点node1
:表示temp
的后一个节点node2
:表示temp
的后两个节点
node1.next=node2.next
,令node1->node2的下一个节点
node2.next=node1
,令node2->node1,完成这步操作后,节点关系变成temp->node2->node1
temp=node1
,对链表中的其余节点进行两两交换,直到全部节点都被两两交换。
- 重复上面操作,直到 temp 的后面没有节点或者只有一个节点,则没有更多的节点需要交换,因此结束交换。
代码 :
var swapPairs = function (head) {
// 虚拟头结点
const temp = new ListNode(0, head);
let node = temp;
while (node.next && node.next.next) {
let node1 = node.next;
let node2 = node.next.next;
node.next = node2;
node1.next = node2.next;
node2.next = node1;
node = node1;
}
return temp.next;
};
删除链表的倒数第 N 个结点
LeetCode题目:删除链表的倒数第N个结点
思路1 :
两次遍历链表,第一次遍历得到链表的长度len,第二次遍历删除倒数第n个结点
- 倒数第n个结点,正向下标为:len-n
- 若创建了虚拟头结点,则正向下标为:len-n+1
- 此方法遍历链表两次
代码 :
var removeNthFromEnd = function (head, n) {
let len = 0;
const temp = new ListNode(0, head);
let node = temp;
// 链表的节点个数为len
while (node) {
node = node.next;
len++;
}
// 被删节点的下标为deleNode
let deleNode = len - n;
let lastNode = null;
node = temp;
for (let i = 0; i < deleNode; i++) {
//将当前节点赋值给上一个节点
lastNode = node;
//将当前节点更新为下一个节点
node = node.next;
}
// 赋值上一个节点的下一个节点为当前节点的下一个节点即可删除该节点
lastNode.next = node.next;
return temp.next;
};
思路2
双指针:
- 创建虚拟结点
- 我们可以使用两个指针
first
和second
同时对链表进行遍历;second
比first
超前且相隔n个结点。当second
遍历到链表的末尾(null)时,first
就恰好处于倒数第 n+1(3)个节点。- 此方法遍历链表一次
动图演示
步骤
- 设置虚拟节点
dummyHead
指向 head - 设定双指针
first
和second
,初始都指向虚拟节点dummyHead
- 移动
second
,直到first
与second
之间相隔的元素个数为 n - 同时移动
first
与second
,直到second
指向的为 NULL - 将
first
的下一个节点指向下下个节点
代码
var removeNthFromEnd = function (head, n) {
const dummyHead = new ListNode(0, head);
let first = dummyHead;
let second = dummyHead;
for (let i = 0; i <= n; i++) {
//n2移动n+1次
second = second.next;
}
while (second) {
// first和second同时移动
first = first.next;
second = second.next;
}
// 令first指向first的下下个结点,即删除first的下个结点
first.next = first.next.next;
return dummyHead.next;
};
💞总结💞
希望我的文章能对你学习链表的知识有所帮助!