【JavaScript算法】-链表合辑08:160 相交链表、146 LRU缓存、147 对链表进行插入

一、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;
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值