一、23 合并k个升序链表
问题描述
给你一个链表数组,每个链表都已经按升序排列。
请你将所有链表合并到一个升序链表中,返回合并后的链表。
解题思路
这个题的最基础的思路就是合并有序链表,和21题方法是一样的,难点在于现在的链表数是k个,而不是简单的两个。首先最自然可以想到的就是按顺序合并给定的k个链表,其次就是有点归并排序的思想,用分治法。
分治法
自底向上归并,第一次归并2个链表,第二次归并4个链表,以此类推,每次归并不断合并两个有序链表,直到合并完所以分治后的链表。代码如下:
var mergeKLists =function(lists){
//数组为空的情况
if(!lists.length){
return null;
}
//合并两个列表
const merge=(head1,head2)=>{
let dummy=new ListNode(0);
let cur=dummy;
//新的链表,谁小就先接谁
while(head1&&head2){
if(head1.val<=head2.val){
cur.next=head1;
head1=head1.next;
}else{
cur.next=head2;
head2=head2.next;
}
cur=cur.next;
}
/*while(head1){
cur.next=head1;
head1=head1.next;
cur=cur.next;
}
while(head2){
cur.next=head2;
head2=head2.next;
cur=cur.next;
}*/
cur.next=head1==null?head2:head1;
return dummy.next;
};
const mergeLists=(lists,start,end)=>{
if(start+1==end){
return lists[start];
}
//输入的k个排序链表,可以分为两个部分,前k/2个链表和后k/2个链表
//如果将这前k/2个链表和后k/2个链表分别合并成两个排序的链表,再将两个排序的链表合并,那么所有链表都合并了
let mid=(start+end)>>1;
let head1=mergeLists(lists,start.mid);
let head2=mergeLists(lists,mid,end);
return merge(head1,head2);
};
return mergeLists(lists,0,lists.length);
};
//自底向上合并
var mergeKLists=function(lists){
//当归并的节点只有一个时,返回这个节点
if(lists.length<=1) return lists[0]||null;
const newLists=[];
//自底向上归并,第一次归并大小为2的链表,第二次归并大小为4的链表....
for(let i=0;i<lists.length;i+=2){
newLists.push(merge(lists[i],lists[i+1]||null));
}
return mergeKLists(newLists);
};
const merge=(list_1,list_2)=>{ //合并两个有序链表
const dummyNode=new ListNode(0);
let p=dummyNode;
while(list_1&&list_2){
if(list_1.val<=list_2.val){
p.next=list_1;
list_1=list_1.next;
}else{
p.next=list_2;
list_2=list_2.next;
}
p=p.next;
}
p.next=list_1?list_1:list_2;
return dummyNode.next;
};
优先队列
这个看到别人的思路,用最小堆做的,我先偷个懒,之后再看。还看到有先把值取出来再新建链表的,属于是直接逃了,略过面试官期待的解法哈哈哈哈。
二、 24 两两交换链表中的节点
问题描述
给你一个链表,两两交换其中相邻的节点,并返回交换后链表的头节点。你必须在不修改节点内部的值的情况下完成本题(即,只能进行节点交换)。
解题思路
第一个简单的思路就是先对原链表做拆分,第1、3、5…个元素作为一个单独的队列,第2、4、6…元素作为一个单独的队列,然后再将偶数位置的数列作为前置数列拼接起来。但是这样需要设置很多的指针,代码实现也比较复杂,所以转换为采用递归的思想。
递归结束的条件就是列表中没有元素或者只含有一个元素,递归内部的操作就是对元素进行互换,原来的头节点head变为列表中的第二个节点,原来的第二个节点是新的头节点newhead。newhead.next=head,head.next=swapPairs(newHead.next)。代码如下:
var swapPairs = function(head) {
if(!head||!head.next) return null||head;
let newHead=head.next;
head.next=swapPairs(newHead.next);
newHead.next=head;
return newHead;
};
还有一种就是直接做调换,这里需要特别注意要从调换的前一个位置开始记录指针,不然链表会断开的。具体代码如下:
var swapPairs=function(head){
if(!head||!head.next) return head||null;
let dummyhead=new ListNode(0,head);
let temp=dummyhead;
while(temp.next!=null&&temp.next.next!=null){
let n1=temp.next;
let n2=temp.next.next;
temp.next=n2;
n1.next=n2.next;
n2.next=n1;
temp=n1;
}
return dummyhead.next;
};
82 删除排序链表中重复的元素
问题描述
给定一个已排序的链表的头 head , 删除原始链表中所有重复数字的节点,只留下不同的数字 。返回 已排序的链表 。
解题思路
虚拟头节点+双指针
首先初始化哑节点dummyhead的next指向head,初始化快慢指针fast、slow的值分别为dummyhead、head;fast指针用来寻找重复节点,只要fast和fast.next值相等,fast则向后移动;slow.next=fast.next删除重复值,最后返回dummyhead.next;
var deleteDuplicates=function(head){
//防止删除头节点,所以设置一个哑节点
if(!head||!head.next) return head||null;
const dummyhead=new ListNode(0,head);
let fast=dummyhead.next,slow=dummyhead;
while(fast){
while(fast.next!==null&&fast.val==fast.next.val) fast=fast.next;
if(slow.next==fast){
slow=slow.next;
}else{
slow.next=fast.next;
}
fast=fast.next;
}
return dummyhead.next;
}
递归
A、终止条件:
1、若head为空,则肯定没有值出现重复的节点,直接返回head;
2、若head.next为空,说明链表中只存在一个节点,也不可能出现重复的节点,可直接返回head;
B、如果head.val!=head.next.val,说明头节点的值不等于下一个节点的值,所以当前的head节点必须保留,但是head.next的节点是否需要保留还不确定,需要对head.next进行递归,即对以head.next为头节点的链表进行去除重复值的操作,所以head.next=self.deleDuplicates(head.next);
C、如果head.val==head.next.val,说明头节点的值对于下一个节点的值,所以当前head节点必须删除,并且之后和head.val相等的节点也都需要删除,直到找到与head.val不等的节点,在这里需要设置一个move指针来向后遍历寻找这个不等的节点。此时move之前的系欸但都不要保留,只返回deleDuplicates(move);
var deleteDuplicates=function(head){
if(head==null||head.next==null) return head;
if(head.next.val==head.val){
let temp=head.next;
while(temp!=null&&head.val==temp.val)
temp=temp.next;
return deleteDuplicates(temp);
}else{
head.next=deleteDuplicates(head.next);
}
return head;
};