一、160 相交链表
问题描述
给你两个单链表的头节点headA和headB,请你找出并返回两个点链表相交的起始节点。如果两个链表不存在相交节点,返回null。
图示两个链表在节点c1开始相交:
题目数据保证整个链式结构中不存在环。
注意,函数返回结果后,链表必须保持其原始结构。
自定义评测:
评测系统的输入如下(你设计的程序不适用此输入):
- intersectVal-相交的起始节点的值。如果不存在相交节点,这一值为0;
- listA-第一个链表
- listB-第二个链表
- skipA-在listA中(从头节点开始)跳到交叉点的节点数
- skipB-在listB中(从头节点开始)跳到交叉点的节点数
测评系统根据这些输入创建链式数据结构,并将两个头节点headA和headB传递给你的程序。如果程序能够正确返回相交节点,那么你的解决方案将被视作正确答案。
解答思路
1、借助数组
a、将链表A和链表B的节点(不是节点的值)分别存储在数组lista和listb中;
b、从后往前依次弹出数组值,进行比较,直到出现不相等的数值或者数组中元素全部弹出为止;
c、记录最后一个共同的节点并返回节点值。
注意:这里需要存储的是节点本身而不是节点的值,因为节点值相同时两个列表中的节点可能指向的是不同的位置,不属于相交节点,具体可看示例1。
代码如下:
var getIntersectionNode = function(headA, headB) {
let lista=[],listb=[];
let p1=headA,p2=headB;
while(p1){
lista.push(p1);
p1=p1.next;
}
while(p2){
listb.push(p2);
p2=p2.next;
}
let a=null;
while(lista.length&&listb.length){
let p1=lista.pop();
let p2=listb.pop();
if(p1==p2){
a=p1;
continue;
}
if(p1!=p2){
break;
}
}
return a;
};
双指针
具体思路1:
用双指针pA、pB循环两个链表,链表A循环结束就将其指针指向链表B的头部,链表B循环结束就将其指针指向链表A的头部,这样当pA==pB时,指向的就是相交节点,因为两个指针的移动步数是一样的。
var getIntersectionNode=function(headA,headB){
if(headA==null||headB==null) return null;
let pA=headA,pB=headB;
while(pA!=pB){
pA=pA==null?pB:pA.next;
pB=pB==null?pA:pB.next;
}
return pA;
//一定会相遇,没有相交节点时,在两链表末尾的null相遇
}
具体思路2:
a、求出两个链表的长度及差值,初始化指针pA和pB;
b、将指针cur1和cur2移动到末尾对齐的地方,即将长的链表的指针移动差值个位置;c、两链表的指针后移,直到cur1==cur2。
var getIntersectionNode=function(headA,headB){
let pA=headA,pB=headB;
let c1=c2=1;
while(pA){
c1++;
pA=pA.next;
}
while(pB){
c2++;
pB=pB.next;
}
let s=0;
let cur1=cur2=null;
if(c1>c2){
s=c1-c2;
cur1=headA;
cur2=headB;
}else{
s=c2-c1;
cur1=headB;
cur2=headA;
}
for(let i=0;i<s;i++){
cur1=cur1.next;
}
while(cur1!=cur2){
cur1=cur1.next;
cur2=cur2.next
}
return cur1;
};
147 对链表进行插入排序
问题描述
给定单个链表的头 head ,使用 插入排序 对链表进行排序,并返回 排序后链表的头 。
插入排序 算法的步骤:
- 插入排序是迭代的,每次只移动一个元素,直到所有元素可以形成一个有序的输出列表。
- 每次迭代中,插入排序只从输入数据中移除一个待排序的元素,找到它在序列中适当的位置,并将其插入。
- 重复直到所有输入数据插入完为止。
下面是插入排序算法的一个图形示例。部分排序的列表(黑色)最初只包含列表中的第一个元素。每次迭代时,从输入数据中删除一个元素(红色),并就地插入已排序的列表中。
对链表进行插入排序。
解题思路
双指针
思路一:
a、设置虚拟节点dmy,初始化tail指向当前已排序链表的最后一个节点,pre用于遍历当前已排序的链表;
b、tail.next根据pre.next遍历找到其可以插入的位置;
代码如下:
var insertionSortList = function(head) {
if(head==null||head.next==null) return head;
let dmy=new ListNode(0,head);
let tail=head;
while(tail&&tail.next){
if(tail.next.val>tail.val){
tail=tail.next;
continue;
}
let pre=dmy;
while(pre.next.val<tail.next.val){
pre=pre.next;
}
let nex=tail.next;
tail.next=tail.next.next;
nex.next=pre.next;
pre.next=nex;
}
return dmy.next;
};
思路二:
也是双指针的方法,但是不需要尾节点指向已排序链表的最后一个节点。
var insertionSortList=function(head){
let dmy=new ListNode(-1);
while(head){
let nex=head.next;
let pre=dmy;
while(pre.next&&pre.next.val<head.val){
pre=pre.next;
}
head.next=pre.next;
pre.next=head;
head=nex;
}
return dmy.next;
};
148 排序链表
问题描述
给你链表的头结点 head ,请将其按 升序 排列并返回 排序后的链表 。
解题思路
双指针(插入排序)
这,不是和上个题一样嘛,直接拿过来用就行。
/**
* Definition for singly-linked list.
* function ListNode(val, next) {
* this.val = (val===undefined ? 0 : val)
* this.next = (next===undefined ? null : next)
* }
*/
/**
* @param {ListNode} head
* @return {ListNode}
*/
var sortList = function(head) {
if(head==null||head.next==null) return head;
let dmy=new ListNode(-1,head);
let tail=head;
while(tail&&tail.next){
let temp=tail.next;
if(tail.next.val>tail.val){
tail=tail.next;
continue;
}
let pre=dmy;
while(pre.next&&pre.next.val<tail.next.val) {
pre=pre.next;
}
tail.next=temp.next;
temp.next=pre.next;
pre.next=temp;
}
return dmy.next;
};
归并排序
自顶向下的归并:
-
写出合并两个有序链表的函数merge();
-
确定递归边界(递归终止条件),本题采用左闭右开写法,
*当!start,即该链表中没有元素,直接返回空指针;
*当start.next==end时,代表目前只剩余一个元素,由于end属于右边部分,所以可以直接断开start.next=null后返回start。 -
找到链表中间节点,以中点为界线,将链表拆分为两个子链表。
* 寻找链表的中点可以使用快慢指针的做法,快指针每次移动 2 步,慢指针每次移动 1 步,当快指针到达链表末尾时,慢指针指向的链表节点即为链表的中点。
* 快指针移动时,需要分两次行动,第二次走的时候也要判断是否 fast === end 了,因为不判断,继续往后走就跑出去链表了。 -
左右两部分分别调用mergeList进行递归;
-
递归返回结果通过merge()合并后返回。
var sortList=function(head){
return mergeList(head,null);
};
function mergeList(start,end){
if(!start) return null;
//左闭右开区间
if(start.next==end){
start.next=null;
return start;
}
let slow=fast=start;
while(fast!=end){
slow=slow.next;
fast=fast.next;
if(fast!=end) fast=fast.next;
}
let mid=slow;
return merge(mergeList(start,mid),mergeList(mid,end));
}
function merge(head1,head2){
let newhead=new ListNode(0);
let cur=newhead;
while(head1&&head2){
if(head1.val<head2.val){
cur.next=head1;
head1=head1.next;
}else{
cur.next=head2;
head2=head2.next;
}
cur=cur.next;
}
cur.next=head1==null?head2:head1;
return newhead.next;
}
法二:
var sortList=function(head){
if(!head||!head.next) return head;
let slow=fast=head;
let preslow=null;
while(fast&&fast.next){
preslow=slow;
slow=slow.next;
fast=fast.next.next;
}
preslow.next=null;
const l=sortList(head);
const r=sortList(slow);
return merge(l,r);
};
unction merge(l1, l2) {
const dummy = new ListNode(0);
let prev = dummy;
while (l1 && l2) {
if (l1.val < l2.val) {
prev.next = l1;
l1 = l1.next;
} else {
prev.next = l2;
l2 = l2.next;
}
prev = prev.next;
}
if (l1) prev.next = l1;
if (l2) prev.next = l2;
return dummy.next;