链表在react
的更新中有所使用,在面试中也是比较常见;比较基础的链表格式有两种:单链表
和环形链表
;node
class LinkNode {
constructor(value){
this.value = value;
this.next = null;
}
}
复制代码
function createLink(){
this.head = null;
}
createLink.prototype.add=function(value){
let linknode = new LinkNode(value)
if(!this.head){
this.head = linknode;
}else{
//进行遍历当前链表 添加到最后面
this.insertNode(linknode)
}
}
createLink.prototype.insertNode=function(node){
let curr = this.head;
while(curr){
if(!curr.next){
curr.next = node;
return ;
}
curr = curr.next;
}
}
复制代码
function getLinkLength(link){
if(!link) return 0
let number = 1;
let curr = link;
while(curr){
number++;
curr = curr.next;
}
return number;
}
复制代码
从头至尾开始查找react
/** * 获取倒数第k个元素 * @param {*} link * @param {*} k */
function getKlineNode(link,k){
//获取当前link长度
let linkSize = getLinkLength(link);
if(linkSize <k) return null;
let curr = link,nu =1;
while(curr){
if(k+nu == linkSize){
return curr.value;
}
nu++;
curr = curr.next;
}
return null;
}
复制代码
let num = 0
function dgGetKlineNode(link,k){
num = k;
if(!link) return null;
let curr = dgGetKlineNode(link.next,k)
//当前存在值 返回继续遍历
if(curr){
return curr;
}else{
//不存在值 说明当前一次遍历结束
num --;//递归一次 减去0
if(num ==0){//num为0 说明到了节点
return link.value
}
}
return null
}
复制代码
function dobuleGetKeyLink(link,key){
if(!link) return null
let curr = link,curr2 =link;
//curr2先走k步
for(var i =0;i<key;i++){
curr2 = curr2.next;
}
//curr2到头,此时curr的位置正好是倒数第k的位置
while(curr2){
curr2 = curr2.next;
curr = curr.next;
}
return curr.value
}
复制代码
借助三方空间存储当前已经遍历过的树形结构面试
function printReverseLink(link){
//借助stack存储已经遍历的结构
let stack = [];
let curr = link;
while(curr){
stack.unshift(curr.value)
curr=curr.next;
}
return stack.join("->")
}
let result = printReverseLink(link.head)
console.log(result)
复制代码
let stackdB = []
function dbPrintReverseLink(link){
//当前节点不为空
if(link){
//继续遍历下一个元素
if(link.next!=null){
dbPrintReverseLink(link.next)
}
//而后在进行存储当前节点,保证后面的节点都在当前节点的前面
stackdB.push(link.value)
}
}
dbPrintReverseLink(link.head);
console.log(stackdB.join("->"))
复制代码
链表的反转,即 先后置换,头结点标称尾部markdown
function reverseLink(link){
if(!link) return null;
let pre = null,pReversedHead;
let curr = link;
while(curr){
let needNext = curr.next;//建立一个临时的next
//调换当前的pre 和curr的指向
curr.next = pre;
pre = curr;
curr = needNext;
}
return pre
}
复制代码
function deleteSingleNode(link,node){
if(!link) return null;
let curr = link,pre=null;
while(curr){
//当前存在 则删除
if(curr.value == node){
//不是删除的在最后一个
if(curr.next){
curr.value = curr.next.value;
curr.next = curr.next.next;
break;
}else{
//最后一个元素
pre.next = null;
break;
}
}
pre = curr;
curr = curr.next;
}
//清除掉空的元素
}
复制代码
是否保留一个节点 给定一个有序链表,如有一个以上相同节点,则将该元素值的节点均删除。函数
function deleteMoreNode(link){
if(!link) return null
let curr = link,pre = link;
//第一个特殊
if(curr.next && curr.value == curr.next.value){
var isFirst = true;
}
while(curr){
//比较当前和后面一个是否相等
if(curr.next){
//两个元素相等
if(curr.value == curr.next.value){
//持续遍历 直到不相等
while(curr.next && curr.value == curr.next.value ){
curr = curr.next;
}
curr = curr.next;
pre.next = curr;
}else{
pre = curr;
curr = curr.next;
}
} else{
curr=curr.next;
}
}
//第一元素相等 删除第一个元素
if(isFirst){
if(link.next){
link.value = link.next.value;
link.next = link.next.next || null;
}else{
link.value = null
}
}
}
复制代码
遍历链表,保留在栈中,每次进行比较栈中是否需存在该元素,存在则进行删除,不存在则继续oop
function deleteMoreNodeStack(link){
if(!link) return null;
let stack =[];
stack.push(link.value)
let curr = link.next,pre = link;
while(curr){
if(stack.indexOf(curr.value)>-1){
//存在当前元素 进行删除
while(stack.indexOf(curr.value)>-1){
curr = curr.next;
//此时说明元素已经不存在了
if(!curr){
pre.value = null;
pre.next = null
return ;
}
}
if(curr){
stack.push(curr.value)
pre.value = curr.value;
pre.next = curr.next;
}
}else{
stack.push(curr.value)
pre = curr;
}
curr && (curr = curr.next)
}
}
复制代码
思路:ui
单链表的指针是指向下一个节点的,若是两个单链表的第一个公共节点就说明他们后面的节点都是在一块儿的。相似下图,因为两个链表的长度多是不一致的,因此首先比较两个链表的长度m,n,而后用两个指针分别指向两个链表的头节点,让较长的链表的指针先走|m-n|个长度,若是他们下面的节点是同样的,就说明出现了第一个公共节点。this
const getLinkLength = require('./link-length')
function findSameNode(link1,link2){
if(!link1 || !link2) return null;
//计算两个链表长度
let size1 = getLinkLength(link1);
let size2 = getLinkLength(link2);
let n = size1- size2;
let mH = null,sH = null;//mH存储长链表 sH存储短链表
if(n>0){
mH=link1;
sH=link2
}else{
mH=link2;
sH=link1
}
//大的先走
n =Math.abs(n)
while(n){
mH = mH.next;
n--;
}
//两个链表 mH 和 sH长度都相等
while( (mH && sH) && mH.value !=sH.value){
mH= mH.next;
sH = sH.next;
}
return mH;
}
复制代码
问题描述 两个用链表表明的整数,其中每一个节点包含一个数字。数字存储按照在原来整数中相反的顺序,使得第一个数字位于链表的开头。写出一个函数将两个整数相加,用链表形式返回和。spa
输入
3->1->5->null
5->9->2->null,
输出
8->0->8->null
复制代码
function comSumNode(link1,link2){
if(!link1 || !link2) {
return link1 || link2
}
let curr1 =link1,curr2 = link2;
//t用来进行计算的总会 sum表示是否有进位
let t = new createLink(),sum=0;
while(curr1 && curr2){
let value = curr2.value + curr1.value;
//判断前一位是否有进位
value = sum ? value+1 : value;
sum = 0
//当前的t.value是否大于0 若是是 则表示下一位须要进位
if(value>10){
sum=1;
value =value -10;
}
t.add(value)
curr2 = curr2.next;
curr1 = curr1.next;
}
//查看sum ,curr2和curr1是否存在剩余的元素
while(curr1){
value = sum? curr1.value+sum:curr1.value;
sum =0;
if(value>=10){
sum =1
value = value-10
}
t.add(value)
curr1= curr1.next;
}
while(curr2){
value = sum? curr2.value+sum:curr2.value;
sum =0;
if(value>=10){
sum =1
value = value-10
}
t.add(value)
curr2= curr2.next;
}
//还存在sum
if(sum){
t.add(sum)
}
return t.head;
}
复制代码
题目:将两个有序链表合并为一个新的有序链表并返回。新链表是经过拼接给定的两个链表的全部节点组成的。.net
示例:
输入:
1->2->4,
1->3->4
输出:
1->1->2->3->4->4
复制代码
/** * 合并两个有序链表 常规作法 * @param {*} link1 * @param {*} link2 */
function mergeLink(link1,link2){
if(!link1 && !link2){
return link2 || link1;
}
let newLink = new createLink();
let curr1=link1,curr2=link2;
while(curr1 && curr2){
if(curr1.value > curr2.value){
//若是link2的元素小将2的元素压入到新链表中 则2的指针向后移动
newLink.add(curr2.value);
curr2 =curr2.next;
}else{
newLink.add(curr1.value);
curr1 =curr1.next;
}
}
//查看是否还存在剩余元素
while(curr2){
newLink.add(curr2.value);
curr2 = curr2.next;
}
while(curr1){
newLink.add(curr1.value);
curr1 = curr1.next;
}
return newLink;
}
let result = mergeLink(link2.head,link.head)
result.print()
复制代码
//递归实现
function dgMergeLink(link1,link2){
let newLink = new LinkNode();
//link1 或者link2为空 则返回对方
if(link1 ==null){
return link2;
}else if(link2 ==null){
return link1
}else{
//比较两个的大小
if(link2.value > link1.value){
newLink = link1; //将link1压入 继续遍历link1.next
newLink.next = dgMergeLink(link1.next,link2)
}else{
newLink = link2; //将link2压入 继续遍历link2.next
newLink.next = dgMergeLink(link1,link2.next)
}
return newLink
}
}
let result1 = dgMergeLink(link2.head,link.head)
let r = new createLink();
r.header = result1;
r.print()
复制代码
单链表中的环是指链表末尾的节点的 next 指针不为 NULL ,而是指向了链表中的某个节点,致使链表中出现了环形结构。
对于最后一个节点8 ,并不是指向null,而是next指向了3,造成了环形结构;
遍历链表,访问过的标记为1,若是遍历到一个节点正好是访问过的,则存在环,不然不存在;
/** * 判断链表是否存在 环 * 穷举法 */
function isCommonRing(link){
let curr = link;
while(curr){
//已经访问过了 则存在 表示 有环
if(curr.isVisted){
return true;
}
curr.isVisted = 1;
curr =curr.next
}
return false;
}
复制代码
快慢指针
function isHaveRingFast(link){
let slow = link;
let fast = link;
while(slow && fast.next){
slow = slow.next;//慢指针
fast = fast.next.next;//快指针
if(slow.value == fast.value){
return true;
}
}
return false;
}
复制代码
slow 指针每次前进一个节点,故 slow 与 fast 相遇时,slow 尚未遍历完整个链表。设 slow 走过节点数为 s,fast 走过节点数为 2s。设环入口点距离头节点为 a,slow 与 fast 首次相遇点距离入口点为 b,环的长度为 r。
设环的长度为r
则slow走过的路程 s=a+b
Fast走过的路程 2s = a+b+n *r (n表示走过的圈数)
s = n * r; 意味着slow指针走过的长度为环的长度整数倍。
若链表头节点到环的末尾节点度为 L,slow 与 fast 的相遇节点距离环入口节点为 X。
则有:
a+X = s = n * r = (n - 1) * r + (L - a);
a = (n - 1) * r + (L - a - X);(正好指向入口节点,须要推导o)
从 slow 与 fast 相遇点出发一个指针 p1,前进 (L - a - X) 步,则此指针到达入口节点。同时指针 p2 从头结点出发,前进 a 步。当 p1 与 p2 相遇时,此时 p1 与 p2 均指向入口节点。
(L - a - X) 是相遇点到入口点距离
复制代码
function getMeetNode(link){
let slow = link;
let fast = link;
while(slow && fast.next){
slow = slow.next;//慢指针
fast = fast.next.next;//快指针
if(slow.value == fast.value){
return slow;
}
}
return null;
}
//根据入口节点 找到入口节点
function findEntry(link){
let node = getMeetNode(link);
if(!node) return null;
let p1 = node;
let p2 = link;
while(p1 != p2){
p1=p1.next;
p2=p2.next
}
return p2
}
复制代码
根据找到的相遇点进行计算便可; 在图3中找到了 slow 与 fast 的相遇节点,令 solw 与 fast 指针从相遇节点出发,按照以前的前进规则,当 slow 与fast 再次相遇时,slow 走过的长度正好为环的长度。 找到相遇点: 特地画了流程图
function comRingLength(link){
if(!link) return null;
let fast = link;
let slow = link;
//找到相遇点
while(fast.next && slow){
fast =fast.next.next;
slow = slow.next
if(fast == slow){
break;
}
}
//继续出发
slow =slow.next;
fast = fast.next.next;
let num = 1;
//当它们再次相遇的时候;
while(slow != fast){
fast = fast.next.next;
slow = slow.next;
num ++;
}
return num;
}
let number = comRingLength(link.head)
console.log(number)
复制代码