链表应用
链表实现栈和队列
思路:
- 只要了解栈和队列的特性,实现起来并不难。完整代码
- 栈具有先进后出的特性。也可以看看另一篇短文数据结构和算法---栈
- 队列具有先进先出的特性。也可以看看另一篇短文数据结构和算法---队列
反转链表
反转一个单链表。
示例:
输入: 1->2->3->4->5->null
输出: 5->4->3->2->1->null
思路:
- 递归方法反转链表 递归的精髓在于甩锅, 自己不能完成的事情,让别人先做,别人完成之后,再在别人的基础上完成。
- 明确函数的功能,既然让别人做就要告诉它做什么,函数 reverse_digui(head)是从head 开始反转链表,函数的返回值new_head是反转后链表的首节点
- 原本是从head 开始反转, 而你不会啊,就从 head.next 开始。head.next 完成反转后head.next 就是新链表的尾节点 连接上 head 就可以了。head.next.next = head; head.next = null, 这样就完成了反转
- 递归必须有终止条件,不然就会无限循环下去。函数最终要返回新链表的头, 而新链表的头就是旧链表的尾。所以该题的终止条件就是到尾节点时直接返回尾节点。
function reverse_digui(head) {
if(!head) {
return null
}
if(!head.next) {
return head
}
const new_head = reverse_digui(head.next) // 先让 head.next 进行反转
head.next.next = head // 反转完成后, head.next 是新链表的尾节点, 再连接上 head
head.next = null // head 成为新链表的尾节点
return new_head // 返回新链表的首节点
}
- 非递归反转链表
直接看上面的示例的化, 有点难以分析, 我们可以换个思路
输入: 1->2->3->4->5->null
输出: null<-1<-2<-3<-4<-5
思路:
- 如上面的示例, 我们只需要把当前节点指向上一个节点就行了
function reverse_link(head) {
if(!head) {
return null
}
let curr_node = head
let pre_node = null
while(curr_node) {
const next_node = curr_node.next // 先拿到当前节点的下一个节点
curr_node.next = pre_node // 当前节点指向上一个节点
// pre_node 和 curr_node 向后滑动,让下一个节点指向上一个节点
pre_node = curr_node
curr_node = next_node
}
return pre_node
}
从尾到头打印链表
- 递归打印
当你拿到一个链表,得到是头节点, 只有头节点后面的节点打印完成了,头节点才能打印,这不正是递归甩锅吗
思路:
- 拿到一个链表从 head 开始反向打印链表,但你不知道怎么打印, 甩锅给head.next 打印, head.next 打印完成后,再打印head 就可以了。
function reverse_print(head) {
if(!head) {
console.log(null)
return null
}
reverse_print(head.next)
console.log(head.data)
}
- 非递归反向打印链表 思路:
- 遍历整个链表
- 先遍历的后打印,这是明显的 栈的先进后出的特性
function reverse_print(head) {
let stack = new Stack()
while(head) {
stack.push(head)
head = head.next
}
}
合并两个有序链表
思路:
- 对于两个链表,各自设置一个游标节点指向首节点,比较首节点数值,数值小的那个,拿出来放入合并链表,同时游标节点向后滑动,继续比较游标节点数值。
- 为了实现滑动,需要用到 while 循环,一个游标节点为null 时,循环终止,这时另一个游标可能还没有到达尾节点,需要把这段没有遍历结束的链表添加到合并链表上。
function merge_link(head1, head2){
if(!head1) return head2
if(!head2) return head1
let merge_head = null // 合并链表的首节点
let merge_tail = null // 合并链表的尾节点
let min_node = null
let curr1 = head1
let curr2 = head2
while(curr1 && curr2) {
if(curr1.data < curr2.data) { // 值小的节点作为最小节点
min_node = curr1
curr1 = curr1.next
}else {
min_node = curr2
curr2 = curr2.next
}
if(!merge_head) { // merge_head 为null ,说明这时第一个最小节点
merge_head = min_node
merge_tail = min_node
}else {
merge_tail.next = min_node
merge_tail = min_node
}
// 连接上剩下还没有到尾节点的链表
let rest_link = null
if(curr1) {
rest_link = curr1
}
if(curr2) {
rest_link = curr2
}
while(rest_link) {
merge_tail.next = rest_link
merge_tail = rest_link
rest_link = rest_link.next
}
return merge_head
}
}