鏈表在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)
複製代碼