持续创作,加速成长!这是我参与「掘金日新计划 · 10 月更文挑战」的第5天,点击查看活动详情
在我自己的开发生涯中,其实用到链表的情况很少,但是不可否认的是,链表对于 插入、删除元素 比较频繁的场景,是有较大性能上的提升,所以我认为,对于链表这种数据结构,作为前端工程师的我们也应该熟练掌握,本篇文章就准备聊聊关于它的一道较为经典的算法题,废话不多说,开搞!
什么是链表
在开始讲解算法题之前,我们先看看链表的定义与特征
链表是一种 线性结构,它的每一个元素除了存放本身的值以外,还存放了 指向其他元素的指针,通过指针来将所有元素串联起来,与数组的区别有如下几点:
- 链表的元素存储空间 不一定连续,而数组中的元素存储空间 一定是连续的
- 链表的 查询效率较低,因为必须从头开始查找,而数组可以直接根据索引进行查找
- 链表的 插入与删除 操作效率高,因为只需要调整指针指向即可,而数组是会将原本的元素 依次进行移动
合并两个有序链表
在知道了链表的定义之后,我们来做下这道经典的 合并两个有序链表,题目如下:
将两个升序链表合并为一个新的升序链表并返回。新链表是通过拼接给定的两个链表的所有节点组成的
我们从题目中可以提取中两个关键点
- 两个列表是 升序的
- 合并后的列表也是 升序的
对于该题目,一共有两种解决方式
- 双指针:为了充分利用已经有序的特性,我们应该很自然的想到双指针的方式进行合并,通过双指针分别指向两个列表,我们可以通过一次遍历,依次将两个列表中相对较小的节点放在前面,以达到升序的目的
- 递归:通过不断把需要合并的链表规模缩小,最终实现链表的整合
下面依次给出具体代码
双指针:
/**
* Definition for singly-linked list.
* function ListNode(val, next) {
* this.val = (val===undefined ? 0 : val)
* this.next = (next===undefined ? null : next)
* }
*/
/**
* @param {ListNode} list1
* @param {ListNode} list2
* @return {ListNode}
*/
var mergeTwoLists = function(list1, list2) {
if(list1 === null) return list2
if(list2 === null) return list1
const head = new ListNode(0)
let 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 = list2
}
if(!list2) {
cur.next = list1
}
return head.next
}
递归:
/**
* Definition for singly-linked list.
* function ListNode(val, next) {
* this.val = (val===undefined ? 0 : val)
* this.next = (next===undefined ? null : next)
* }
*/
/**
* @param {ListNode} list1
* @param {ListNode} list2
* @return {ListNode}
*/
var mergeTwoLists = function(list1, list2) {
if(list1 === null) return list2
if(list2 === null) return list1
if(list1.val <= list2.val) {
list1.next = mergeTwoLists(list1.next,list2)
return list1
}else {
list2.next = mergeTwoLists(list1,list2.next)
return list2
}
}