6. 合并两个有序链表
将两个升序链表合并为一个新的 升序 链表并返回。新链表是通过拼接给定的两个链表的所有节点组成的。
示例 1:
输入:l1 = [1,2,4], l2 = [1,3,4]
输出:[1,1,2,3,4,4]
示例 2:
输入:l1 = [], l2 = []
输出:[]
示例 3:
输入:l1 = [], l2 = [0]
输出:[0]
提示:
- 两个链表的节点数目范围是
[0, 50] -100 <= Node.val <= 100l1和l2均按 非递减顺序 排列
在做这个题目的时候先了解一下数据结构--链表
单链表
概念介绍
链表中的数据是以结点来表示的,每个结点的构成:元素(数据元素的映象) + 指针(指示后继元素存储位置),元素就是存储数据的存储单元,指针就是连接每个结点的地址数据。以“结点的序列”表示线性表称作线性链表(单链表),单链表是链式存取的结构。
链接存储方法
链接方式存储的线性表简称为链表(Linked List)。
链表的具体存储表示为:
① 用一组任意的存储单元来存放线性表的结点(这组存储单元既可以是连续的,也可以是不连续的)
② 链表中结点的逻辑次序和物理次序不一定相同。为了能正确表示结点间的逻辑关系,在存储每个结点值的同时,还必须存储指示其后继结点的地址(或位置)信息(称为指针(pointer)或链(link))
链式存储是最常用的存储方式之一,它不仅可用来表示线性表,而且可用来表示各种非线性的数据结构。
结点结构
┌───┬───┐
│data │next │
└───┴───┘
data域--存放结点值的数据域
next域--存放结点的直接后继的地址(位置)的指针域(链域)
链表通过每个结点的链域将线性表的n个结点按其逻辑顺序链接在一起的,每个结点只有一个链域的链表称为单链表(Single Linked List)。
头指针head和终端结点
单链表中每个结点的存储地址是存放在其前趋结点next域中,而开始结点无前趋,故应设头指针head指向开始结点。链表由头指针唯一确定,单链表可以用头指针的名字来命名。终端结点无后继,故终端结点的指针域为空,即NULL。
了解以上基本知识之后实现上面题目的代码如下:
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;
}
}
思路: 两个链表的当前的节点的值相互比较,保留较小的,再将较小的节点的下一个节点A和另一个链表的当前
节点B比较,如果A小于B,list1.next 还是指向 list1 否则指向list2。重复以上的操作直至其中有一个链表为空。
根据代码显而易见重复以操作采用的是递归
遇到这个问题总是第一反应使用递归,但是做完之后又感觉递归不完美,就非常解决,如果你也有这种感受 可以看下下面的思路
var mergeTwoLists = function(list1, list2) {
const preHead = new ListNode(-1)
let pre = preHead
while(list1 && list2){
if(list1.val <= list2.val){
pre.next = list1
list1 = list1.next
}else{
pre.next = list2
list2 = list2.next
}
pre = pre.next
}
pre.next = list1 === null ? list2 : list1
return preHead.next
};
思路: 建立了一个空链表preHead 并且同时创建了一个头指针pre 指向创建的空链表
使用while 代替递归循环比较两个链表的当前节点,那个小就将pre.next指向谁,相当于向preHead链表中添加节点。