🧑💻JavaScript算法与数据结构-HowieCong
务必要熟悉JavaScript使用再来学!
一、链表三种方向
- 链表的处理:合并、删除等(删除操作画个记号)
- 链表的反转及其衍生题目
- 链表成环问题及其衍生题目
二、链表的合并
原题:
将两个有序链表合并为一个新的有序链表并返回。新链表是通过拼接给定的两个链表的所有结点组成的。
示例:
输入:1->2->4, 1->3->4 输出:1->1->2->3->4->4
(1)思路分析
- 中心思想:处理链表的本质,是处理链表结点之间的指针关系
- 两个链表如果想要合并为一个链表,我们恰当地补充双方之间结点next指针地指向关系,就能达到目的
- 先比较一下眼前的两个结点,选择其中值较少的那个,优先把它串进去,一次串一个,直到所有结点被串起来,同时我们还要考虑l1和l2两个链表长度不等的情况:若其中一个链表已经完全被串进新链表里了,而另外一个链表还还有剩余结点,考虑到该链表本身就是有序的,可以直接把它整个拼到目标链表的尾部
(2)编码实现
const mergeTwoLists = function(l1,l2){
// 定义头结点,确保链表可以被访问到
let head = new ListNode()
// cur 这里就是咱们那根线
let cur = head
// 线开始在l1和l2间行走
while(l1 && l2){
// 如果l1的结点值较小
if(l1.val <= l2.val){
// 先串起 l1 的结点
cur.next = l1
// l1指针向前一步
l1 = l1.next
}else{
// l2较小时,串起l2结点
cur.next = l2
l2 = l2.next
}
// 线在串起一个结点后,也会向前一步
cur = cur.next
}
// 处理链表不等长的情况
cur.next = l1 !== null ? l1:l2
// 返回起始结点
return head.next
}
三、链表结点的删除
原题:
给定一个排序链表,删除所有重复的元素,使得每个元素只出现一次
示例:
输入: 1->1->2\
输出: 1->2\
示例 2:\
输入: 1->1->2->3->3\
输出: 1->2->3
(1)思路分析
- 链表的删除是一个基础且关键的操作,这里直接复用删除能力,将需要删除的目标结点的前驱结点next指针往后指一格
- 判断两个元素是否重复,由于此处是已排序的链表,我们直接判断前后两个元素值是否相等即可
- 考察了链表的遍历,while循环,也考察了链表的CRUD的删除操作
(2)编码实现
const deleteDuplicates = function(head){
// cur指针,初始位置为链表第一个结点
let cur = head;
// 遍历链表
while(cur != null && cur.next != null){
// 若当前结点和它后面一个结点值相等(重复)
if(cur.val === cur.next.val){
// 删除靠后的那个结点(去重)
cur.next = cur.next.next;
}else{
// 若不重复,继续遍历
cur = cur.next;
}
}
return head;
}
四、删除问题——dummy结点
原题:
给定一个排序链表,删除所有含有重复数字的结点,只保留原始链表中 没有重复出现的数字
示例:
输入: 1->2->3->3->4->4->5\
输出: 1->2->5\
示例 2:\
输入: 1->1->1->2->3\
输出: 2->3
(1)思路分析
- 和上道题的区别在于,上题是删到没有重复元素就行,这个题要求只要一个元素发生了重复,就要彻底从链表去掉
- 链表的第一个结点,没有前驱节点,可以用一个
dummy
结点来解决这个问题 - dummy结点就是自己去new的第一个结点的前驱节点,确保了链表的所有结点都有一个前驱结点,能帮助我们降低链表处理过程的复杂度
- 处理链表时,不设dummy结点思路可能会打不开,设了dummy结点,不用到,也不会出错
- 定义一个dummy结点,指向链表的起始位置
- 如果想删除两个连续重复的值为1的结点,只需要把dummy结点的next指针直接指向2
- 由于重复的结点可能不止一个两个,需要使用一个while循环来反复进行重复结点的判断和删除判断
(2)编码实现
const deleteDuplicates = function(head){
// 特殊:0个或1个结点,不会重复,直接返回
if(!head || !head.next){
return head
}
// dummy出现
let dummy = new ListNode()
// dummy永远指向头结点
dummy.next = head
// cur从dummy开始遍历
let cur = dummy
// 当cur的后面有至少两个结点时
whilt(cur.next && cur.next.next){
// 对cur的后面至少有两个结点时
if(cur.next.val === cur.next.next.val){
// 若值重复,则记下这个值
let val = cur.next.val
// 反复地排查后面的元素是否存在多次重复该值的情况
while(cur.next && cur.next.val===val){
// 若有,则删除
cur.next = cur.next.next
}else{
// 若不重复,则正常遍历
cur = cur.next
}
}
}
// 返回链表的起始结点
return dummy.next;
}
❓其他
1. 疑问与作者HowieCong声明
- 如有疑问、出错的知识,请及时点击下方链接添加作者HowieCong的其中一种联系方式或发送邮件到下方邮箱告知作者HowieCong
- 若想让作者更新哪些方面的技术文章或补充更多知识在这篇文章,请及时点击下方链接添加里面其中一种联系方式或发送邮件到下方邮箱告知作者HowieCong
- 声明:作者HowieCong目前只是一个前端开发小菜鸟,写文章的初衷只是全面提高自身能力和见识;如果对此篇文章喜欢或能帮助到你,麻烦给作者HowieCong点个关注/给这篇文章点个赞/收藏这篇文章/在评论区留下你的想法吧,欢迎大家来交流!
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。