今天要分享的内容是反转链表。这是一个很经典的问题,将一个单指针链表从头指向尾
的顺序改成从尾指向头
像下面这样:
变成:
问题很清晰,也很简单,我们来看有哪些解法:
准备数据
javascript
const data = [1, 2, 3, 4, 5];
const generateList = (data) => {
const root = { next: null };
let node = root;
data.forEach((item) => {
const temp = { value: item, next: null };
node.next = temp;
node = node.next;
});
return root.next;
};
const linkList = generateList(data);
上面代码生成了有五个节点的单链表。链表从 1 到 5。
再准备一个输出链表的方法,用来验证链表是否正确
javascript
const printLink = (data) => {
let node = data;
while (node) {
console.log(node.value);
node = node.next;
}
};
printLink(linkList);
// 1
// 2
// 3
// 4
// 5
没有问题
反转链表
方法一
javascript
// 定义一个函数 reverseLink,接收一个链表作为参数
const reverseLink = (linkList) => {
// 初始化一个节点指针 node 指向链表的头结点
let node = linkList;
// 初始化一个空节点指针 pre 指向 null
let pre = null;
// 使用 while 循环遍历链表,直到链表为空
while (node) {
// 保存当前节点的下一个节点指针 next
const next = node.next;
// 将当前节点的下一个节点指针指向 pre
node.next = pre;
// 将 pre 指针指向当前节点
pre = node;
// 将 node 指针指向下一个节点
node = next;
}
// 返回反转后的链表头结点
return pre;
};
逻辑比较简单,从头到尾依次遍历,依次让 node 的 next 指针指向 pre,然后 pre 和 node 分别往后挪动一个位置。直到 node 挪成 null,也就是 pre 挪到了链表的最后一个位置,就停止遍历。并且返回反转之后链表的头节点
测试代码:
javascript
printLink(reverseLink(linkList));
// 5
// 4
// 3
// 2
// 1
方法二:
还可以用递归的方式来解决:
javascript
// 定义一个函数 reverseLink2,接收一个链表作为参数
const reverseLink2 = (linkList) => {
// 如果链表为空或只有一个节点,直接返回链表
if (!linkList || !linkList.next) return linkList;
// 递归调用 reverseLink 函数,直到链表的最后一个节点
const lastNode = reverseLink(linkList.next);
// 将链表的下一个节点的下一个节点指针指向当前节点
linkList.next.next = linkList;
// 将当前节点的下一个节点指针指向 null
linkList.next = null;
// 返回反转后的链表的头结点
return lastNode;
};
这个方法的构思就比较巧妙。将调整的位置直接从链表的尾部开始,将一个个节点由后指向前。
帮助理解:
- 假设我们已经来到了最后一个 node,也就是
linkList == 节点 5
,这时候第一个判断会直接将节点 5
返回 - 然后我们就回到了上一层调用的第 6 行代码。在这一层
linkList == 节点 4
,lastNode 会是节点 5
。 继续往下执行。下面两行代码的作用是将节点 5
的 next 指针指向 4,并且节点 4
的指针指向null
。最后返回节点 5
- 然后又回到了上一层调用的第 6 行代码。在这一层
linkList == 节点 3
。lastNode 依旧是节点 5
。继续往下执行。下面两行代码的作用是将节点 4
的 next 指针指向 3,并且节点 3
的指针指向null
。最后返回节点 5
- 依次类推
测试代码:
javascript
printLink(reverseLink2(linkList));
// 5
// 4
// 3
// 2
// 1
方法三
还有种方法,就是遍历 linkList,然后将遍历到的节点头插法
插到新的链表中。这个方法简单,就不展示代码了
总结
这篇文章分享了经典的算法--反转链表,文中提供了三种代码编写方式,都不难,代码注释和解释也都很清晰,方便大家理解
你觉得这篇文章怎么样?喜欢就点赞+关注吧