摘要:在上一篇文章中,我們了解了隊列和棧的描述,現(xiàn)在讓我們來了解一下單鏈表和雙向鏈表的實現(xiàn)。單鏈表和雙向鏈表具有以下特點可動態(tài)分配空間,但不能隨機訪問。
在上一篇文章中,我們了解了隊列和棧的JavaScript描述,現(xiàn)在讓我們來了解一下 單鏈表 和雙向鏈表 的實現(xiàn)。本文的代碼并非所有都由本人所寫,只是出于學(xué)習(xí)目的,在此分享出來,并加上一定的解釋,便于大家學(xué)習(xí)。
本系列文章的代碼可在https://github.com/HolyZheng/...找到。
我們直入話題:
單鏈表單鏈表 是存儲結(jié)構(gòu)的一種,它具有以下特點:
單鏈表的特點:單鏈表不可隨機訪問
單鏈表不需要占連續(xù)的存儲空間,可動態(tài)分配
插入與刪除操作不需要移動多個元素
每個節(jié)點既存儲數(shù)據(jù),又同時存儲指向下一節(jié)點的地址
現(xiàn)在我們創(chuàng)建一個單鏈表,并給它添加add、searchNode、remove 三個方法。
創(chuàng)建一個單鏈表:
//單鏈表 function SinglyList () { this._length = 0; this.head = null; }
這個單鏈表暫時又兩個屬性: _length 鏈表的長度,head 鏈表頭節(jié)點。
每一個節(jié)點需要存儲數(shù)據(jù),還要指向下一節(jié)點,所以為每個節(jié)點創(chuàng)建一個node類:
//結(jié)點 function Node (data) { this.data = data; this.next = null; }
node類 具有兩個屬性,data 存儲數(shù)據(jù),next 指向下一節(jié)點。
現(xiàn)在我們?yōu)樗砑訋讉€基本的方法:add、searchNode 和 remove 函數(shù)。我們先實現(xiàn) add 方法,給單鏈表添加節(jié)點。在 add 方法中我們需要考慮兩中情況,分別為:單鏈表為空和單鏈表不為空。
//add方法 SinglyList.prototype.add = function (value) { var node = new Node(value), currentNode = this.head; //1st:當(dāng)單鏈表為空時 if (!currentNode) { this.head = node; this._length++; return node; } //2nd:單鏈表不為空 while (currentNode.next) { currentNode = currentNode.next; } currentNode.next = node; this._length++; return node; }
可以看到在代碼中,我們先定義了一個 currentNode 變量,指向 this.head ,然后判斷如果當(dāng)前鏈表為空,直接將新節(jié)點賦值給 this.head ,如果不為空,先將currentNode指向最后的節(jié)點,然后再執(zhí)行 currentNode.next = node將新節(jié)點添加到鏈表的末尾。
再來實現(xiàn) searchNode 方法:
searchNode方法的作用是搜索特定位置的節(jié)點
//searchNode方法 SinglyList.prototype.searchNode = function (position) { var currentNode = this.head, length = this._length, message = {failure: "Failure: non-existent node in this list"}; //1st:位置position非法 if (length === 0 || position < 1 || position > length) { throw new Error(message.failure); } //2nd:位置position合法 for (var i = 1; i < position; i++) { currentNode = currentNode.next; } return currentNode; }
我們很簡單就實現(xiàn)了這個方法,先檢測鏈表是否為空和查詢的位置是否合法,不為空且位置合法的話,利用一個循環(huán),將currentNode指向特定的position,然后就可以訪問到需要的節(jié)點了。
我們現(xiàn)在來看一下最后的一個方法: remove。 remove方法比起前兩個方法的話,要復(fù)雜一點,因為要考慮刪減了一個元素之后,還要保持整個鏈表的連續(xù)性。
//remove方法 SinglyList.prototype.remove = function (position) { var currentNode = this.head, length = this._length, message = {failure: "Failure: non-existent node in this list"}, beforeNodeToDelete = null, nodeToDelete = null; //1st 位置position非法 if (position < 0 || position > length) { throw new Error(message.failure); } //2nd 位置position為 1 if (position === 1) { this.head = currentNode.next; nodeToDelete = currentNode; currentNode = null; this._length--; return nodeToDelete; } //3rd position為其他位子 for(var i = 1; i < position; i++) { beforeNodeToDelete = currentNode; nodeToDelete = currentNode.next; currentNode = currentNode.next; } beforeNodeToDelete.next = nodeToDelete.next; currentNode = null; this._length--; return nodeToDelete; }
首先檢查 position 的值是否合法,然后看position的值是否為 1,如果為 1 那就好辦了,將 this.head 指向原this.head.next,然后長度減 1 即可。如果position為其他位置,那就要先拿到 要刪除節(jié)點的前一節(jié)點 <和 要刪除的節(jié)點 然后將前一節(jié)點的next指向要刪除節(jié)點的next,以保持刪除節(jié)點后,鏈表的連續(xù)。理解了這點,那就基本可以理解代碼了。
雙向鏈表雙向鏈表就是在單鏈表的基礎(chǔ)上,添加了一個指向當(dāng)前結(jié)點的前驅(qū)的變量,這樣就可以方便的由后繼來找到其前驅(qū),就可以雙向的訪問鏈表。
同樣我們先來創(chuàng)建一個 結(jié)點類 :
//節(jié)點類 function Node (value) { this.data = value; this.previous = null; this.next = null; }
可以看到這里多了一個 this.previous ,作用就是指向它的前驅(qū)。
然后再來看一下雙向鏈表這個類:
function DoublyList () { this._length = 0; this.head = null; this.tail = null; }
比起 單鏈表, 雙向鏈表 多了一個指向尾結(jié)點的 this.tail。
同樣,在這里我們實現(xiàn) add、searchNode 和 remove 三個方法。先來看 add 方法:
//add方法 DoublyList.prototype.add = function (value) { var node = new Node(value); if(this._length) { this.tail.next = node; node.previous = this.tail; this.tail = node; } else { this.head = node; this.tail = node; } this._length++; return node; }
在插入新結(jié)點的時候我們一如既往的需要檢查鏈表是否為空,如果鏈表為空,就將 this.head 和 this.tail 都指向新結(jié)點,如果不為空,那就將新結(jié)點添加到鏈表末尾,并將新結(jié)點的 previous 指向原 this.tail 。這樣就完成了 add 方法。
searchNode方法:
//searchNode DoublyList.prototype.searchNode = function (position) { var currentNode = this.head, length = this._length, message = {failure: "Failure: non-existent node in this list"}; if(length === 0 || position < 1 || position > length) { throw new Error(message.failure); } for (var i = 1; i < position; i++) { currentNode = currentNode.next; } return currentNode; }
雙向鏈表的searchNode方法和單鏈表的差不多,都是借助循環(huán)直接拿到要訪問的結(jié)點。
最后是最復(fù)雜的remove方法
//remove方法 DoublyList.prototype.remove = function (position) { var currentNode = this.head, length = this._length, message = {failure: "Failure: non-existent node in this list"}, beforeNodeToDelete = null, nodeToDelete = null, afterNodeToDelete = null; //1st: 位置position非法 if (length === 0 || position < 1 || position > length) { throw new Error(message.failure); } //2nd 位置為第一個節(jié)點 if (position === 1) { nodeToDelete = this.head; this.head = currentNode.next; if (this.head) { this.head.previous = null; } else { this.tail = null; } //3rd 位置為最后一個節(jié)點 } else if (position === this._length) { this.tail = this.tail.previous; this.tail.next = null; //4th 位置為其他節(jié)點 } else { for (var i = 1; i < position; i++) { currentNode = currentNode.next; } beforeNodeToDelete = currentNode.previous; nodeToDelete = currentNode; afterNodeToDelete = currentNode.next; beforeNodeToDelete.next = afterNodeToDelete; afterNodeToDelete.previous = beforeNodeToDelete; } this._length--; return nodeToDelete; }
remove方法要對傳進來的 position 進行判斷,分成 4 種情況,
position非法,拋出錯誤。
position為 1,將this.head 指向下一個結(jié)點,然后將this.head.previous = null,這時要判斷一下 this.head 是否為空,如果為空就表明這個雙向鏈表原本只有一個結(jié)點,所以 remove 后 需要把 this.tail = null 。
當(dāng) position 為最后一個結(jié)點時,我們把 this.tail 前移this.tail = this.tail.previous,此時 this.tail 指向倒數(shù)第二個結(jié)點,再執(zhí)行this.tail.next = null,就把最后一個結(jié)點remove掉了
最復(fù)雜的情況,position 為其他位置,我們先定位到要remove掉的結(jié)點,然后將要刪除結(jié)點的前一結(jié)點與要刪除結(jié)點的后一結(jié)點鏈接起來,就把要刪除的結(jié)點remove掉了,既beforeNodeToDelete.next = afterNodeToDelete ; afterNodeToDelete.previous = beforeNodeToDelete
總結(jié)單鏈表和雙向鏈表,為存儲結(jié)構(gòu)的一種。
單鏈表和雙向鏈表具有以下特點:
可動態(tài)分配空間,但不能隨機訪問。
插入和刪除操作不需要移動多個元素
每個節(jié)點既存儲數(shù)據(jù),又同時存儲指向下一節(jié)點的地址
雙向鏈表為在單鏈表的基礎(chǔ)上,添加了一個指向當(dāng)前結(jié)點的前驅(qū)的變量,這樣就可以方便的由后繼來找到其前驅(qū),就可以雙向的訪問鏈表。
文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請注明本文地址:http://m.hztianpu.com/yun/90371.html
摘要:重建二叉樹輸入某二叉樹的前序遍歷和中序遍歷的結(jié)果,請重建出該二叉樹。所以先通過前序遍歷找出根節(jié)點,然后將中序遍歷分為左右子樹兩組,最后對于每個子樹依次遞歸調(diào)用。 重建二叉樹 輸入某二叉樹的前序遍歷和中序遍歷的結(jié)果,請重建出該二叉樹。假設(shè)輸入的前序遍歷和中序遍歷的結(jié)果中都不含重復(fù)的數(shù)字。例如輸入前序遍歷序列{1,2,4,7,3,5,6,8}和中序遍歷序列{4,7,2,1,5,3,8,6}...
摘要:有關(guān)算法,數(shù)據(jù)結(jié)構(gòu)的代碼已上傳至算法與數(shù)據(jù)結(jié)構(gòu)。構(gòu)造函數(shù)深度優(yōu)先遍歷廣度優(yōu)先遍歷插入中序遍歷前序遍歷后序遍歷聲明一棵樹聲明一個節(jié)點相關(guān)算法深度優(yōu)先遍歷深度優(yōu)先遍歷,先查看左孩子是否存在,若存在,傳入遞歸,否則,再查看右孩子。 這次來了解一下二叉樹,以及相應(yīng)的算法。以下代碼并非所有都由本人所寫,只是在此分享出來,以便大家學(xué)習(xí)。 有關(guān)javascript算法,數(shù)據(jù)結(jié)構(gòu)的代碼已上傳至 jav...
摘要:用于檢查傳入的對象是否是當(dāng)前對象的原型。返回對象的字符串?dāng)?shù)值或布爾值表示。類型表示獨一無二的值。是一種基本數(shù)據(jù)類型。一個值能作為對象屬性的標識符這是該數(shù)據(jù)類型僅有的目的。圍繞原始數(shù)據(jù)類型創(chuàng)建一個顯式包裝器對象從開始不再被支持。 Object 類型 ECMAScript中的對象就是一組數(shù)據(jù)和功能的集合。 創(chuàng)建對象 const o = new Object(); const o = new...
摘要:用于檢查傳入的對象是否是當(dāng)前對象的原型。返回對象的字符串?dāng)?shù)值或布爾值表示。類型表示獨一無二的值。是一種基本數(shù)據(jù)類型。一個值能作為對象屬性的標識符這是該數(shù)據(jù)類型僅有的目的。圍繞原始數(shù)據(jù)類型創(chuàng)建一個顯式包裝器對象從開始不再被支持。 Object 類型 ECMAScript中的對象就是一組數(shù)據(jù)和功能的集合。 創(chuàng)建對象 const o = new Object(); const o = new...
閱讀 2481·2021-11-19 09:40
閱讀 3680·2021-10-12 10:12
閱讀 1944·2021-09-22 15:04
閱讀 2963·2021-09-02 09:53
閱讀 853·2019-08-29 11:03
閱讀 1180·2019-08-28 18:11
閱讀 1785·2019-08-23 15:28
閱讀 3649·2019-08-23 15:05