JavaScript實現雙向連結串列過程解析
目錄
- 一、什麼是雙向連結串列
- 二、雙向連結串列的封裝
- 三、雙向連結串列的常用操作
- 1、append(element)方法-----向列表尾部新增一個項
- 2、將連結串列轉化為字串形式
- 3、insert(position,element):向列表的特定位置插入一個項
- 4、get(position):獲取對應位置的元素
- 5、indexOf(element):返回元素在列表中的索引
- 6、 update(position,ele):修改某個位置的元素
- 7、removeAt(position):從列表的指定位置移除一項
- 8、remove(element):從列表中移除一項
- 9、isEmpty():判斷連結串列是否為空
- 10、size():返回連結串列包含的元素個數
一、什麼是雙向連結串列
我們知道單鏈表只能從頭遍歷到尾或從尾遍歷到頭(一般從頭遍歷到尾),即連結串列相連的過程是單向的,實現的原理是上一個連結串列中有一個指向下一個的引用。它有一個比較明顯的缺點:
我們可以輕鬆的到達下一個節點,但是回到前一個節點是很困難的,但是,在實際開發中,經常會遇到需要回到上一個節點的情況,所以這裡就需要雙向連結串列。
所謂雙向連結串列就是:既可以從頭遍歷到尾,又可以從尾遍歷到頭的連結串列,但是,雙向連結串列也是有缺點的,比如:每次在插入或刪除某個節點的時候,需要處理四個節點的引用,而不是兩個,並且相對於單鏈表而言,佔用記憶體會更大一些,但是這些缺點和我們使用起來的方便程度相比,是微不足道的。
雙向連結串列的特點:
- 可以使用一個head和一個tail分別指向頭部和尾部的節點
- 每個節點由三部分組成,前一個節點的指標(prev)、儲存的元素(data)、後一個節點的指標(next)
- 雙向連結串列的第一個節點的prev是null
- 雙向連結串列的最後的節點的next是null
我們可以將其抽象為:
知道了雙向連結串列的結構,我們在一起來看看雙向連結串列的封裝。
二、雙向連結串列的封裝
首先,我們封裝一個DoublyLinkedList
類,用於表示連結串列結構,在這個類裡面在封裝一個內部類Node
,用於封裝每一個節點上的資訊(指向前一個節點的引用、資料和指向下一個節點的引用),然後在Node
function DoublyLinkedList(){ this.head = null; this.tail = null; this.length = 0; function Node(data){ this.data = data; this.prev = null; this.next = null; }
三、雙向連結串列的常用操作
然後可以在裡面新增雙向連結串列常用的操作:
append(element
:向列表尾部新增一個項
insert(position,elemeqXZxtuLTIXnt)
:向列表的特定位置插入一個項
get(position)
:獲取對應位置的元素
indexOf(element)
:返回元素在列表中的索引,如果列表中沒有該元素則返回-1
update(position,ele)
:修改某個位置的元素
removeAt(position)
:從列表的指定位置移除一項
remove(element)
:從列表中移除一項
isEmpty()
:如果連結串列中不包含任何元素,返回true,如果連結串列長度大於0返回false
size()
:返回連結串列包含的元素個數,與陣列的length屬性相關
toString()
:由於列表項用到了Node類,需要重寫繼承自物件預設的toString方法,讓其輸出元素的值
forwardString()
:返回正向遍歷的節點字串形式
backwardString()
:返回反向遍歷的節點字串形式
接下來們就來一個一個實現。
1、append(element)方法-----向列表尾部新增一個項
這個方法和單鏈表的方法相似,先建立一個新列表,在判斷連結串列是否為空,如果為空,則直接讓連結串列的頭部指向新建立的連結串列。如果不為空,則讓新節點的前驅指標指向連結串列尾部,連結串列尾部的節點指向新連結串列。
Doubly.prototype.append = function(data){ var newNode = new Node(data); if(this.length == 0){ this.head = newNode; }else{ newNode.prev = this.tail; this.tail.next = newNode; } this.tail = newNode; this.length += 1; }
2、將連結串列轉化為字串形式
1、toString():正向輸出元素的值
這個方法主要是獲取每一個元素,該方法預設獲取連結串列的任何元素都必須從第一個節點開始,所以我們可以從頭結點開始,迴圈遍歷每一個節點,並且取出其中的element,拼接成字串,並將字串返回。具體方法為建立一個臨時節點current
,讓這個臨時節點指向雙向連結串列的頭部,然後通過next
指標依次向後遍歷,將每次遍歷得到的資料進行拼接。
DoublyLinkedList.prototype.tostring = function(){ var current = this.head; var str = ''; while(current){ str += current.data + ' '; current = current.next; } return str; }
2、forwardString():返回正向遍歷的節點字串形式
該方法也是實現雙向連結串列的正向列印並輸出,所以我們這裡可以直接呼叫上一個方法:
DoublyLinkedList.prototype.forwardString = function(){ return this.toString() }
3、backwardString():返回反向遍歷的節點字串形式
這個方法主要是從後往前遍歷獲取每一個元素並列印,所以我們可以從尾結點開始,迴圈遍歷每一個節點,並且取出其中的element,拼接成字串,並將字串返回。具體方法為建立一個臨時節點current
,讓這個臨時節點指向雙向連結串列的尾部,然後通過prev
指標依次向前遍歷,將每次遍歷得到的資料進行拼接。
DoublyLinkedList.prototype.backwardString = function(){ var current = this.tail; var str = ''; //依次向前遍歷,獲取每一個節點 while(current){ str += current.data +' '; current = current.prev; } return str; }
驗證:
例如我們通過append()
方法建立一個含有三個資料的雙向連結串列,然後分別將他們正向拼接和反向拼接:
var list = new DoublyLinkedList();
list.append("a");
list.append("b");
list.append("c");
list.append("d");
console.log('toStr客棧ing()方法:'+list.toString());
console.log('forwardString()方法:'+list.forwardString());
console.log('backwardString()方法:'+list.backwardString());
列印結果為:
驗證成功。
3、insert(position,element):向列表的特定位置插入一個項
這個方法相對來說是比較複雜的,首先,先判斷要插入的位置是否越界,在不越界的情況下,在根據連結串列的情況判斷,如果連結串列為空,則插入節點為第一個元素,直接讓頭結點和尾節點的指標指向新建立的節點;如果連結串列不為空,則插入的位置有三種情況:插入到首位,插入到末尾和插入到中間部位。具體操作如下:
DoublyLinkedList.prototype.insert = function(position,data){ var newNode = new Node(data); //越界判斷,如果不滿足,返回false if(position<0 || position>this.length){ return false; }else{ //再次判斷 //如果連結串列為空,直接插入 if(position==0){ this.head = newNode; this.tail = newNode; }else { //如果連結串列不為空 //如果插入位置為末尾 if(position == this.length){ this.tail.next = newNode; newNode.prev = this.tail; this.tail = newNode; }else if(position == 0){ //如果插入位置為首位 this.head.prev = newNode; newNode.next = this.head; this.head = newNode; }else{ //如果插入位置為中間 var index = 0; var current = this.head; while(index++ <position){ current = current.next; } newNode.next = current; newNode.prev = current.prev; current.prev.next = newNode; current.prev = newNode; } this.length += 1; } } }
驗證:如果在1位置插入xl
,如下所示:
list.insert(1,'xl') console.log(list.toString());
執行結果為:
驗證成功。
4、get(position):獲取對應位置的元素
這個方法首先要判斷位置是否越界,在不越界的情況下,定義一個臨時節點和索引,讓這個臨時節點的指標指向頭結點,遍歷臨時節點,同時索引自加,如果遍歷道德節點的位置等於要獲取元素的位置,則臨時節點對應位置的元素就是要獲得的元素。
DoublyLinkedList.prototype.get = function(position){ if(position<0 || position>=this.length){ return false; }else{ var index = 0; var current = this.head; while(index++ <position){ current = current.next; } return current.data; } }
驗證:查詢position = 2的元素
console.log('list.get(2):'+list.get(2));
結果為:
驗證成功。
但是,這種查詢方式有一個弊端,即只能從前向後查詢,在某些情況下效率就會很低,所以我們就可以兩頭查詢,具體查詢方式為:當我們要查詢的節點的位置小於或等於連結串列長度的一半,我們就可以從前向後查詢,如果要查詢的節點的位置大於長度的一半,我們就從後往前查詢。實現程式碼為:
DoublyLinkedList.prototype.get = function(position){ if(position<0 || position>=this.length){ return false; }else{ var len = Math.floor(this.length/2); if(position<=len){ var index = 0; var current = this.head; while(index++ <position){ current = current.next; } }else{ var index = this.length-1; var current = this.tail; while(index-->position){ current = current.prev; } } return current.data; } }
5、indexOf(element):返回元素在列表中的索引
建立一個臨時節點和索引,讓臨時節點指向頭結點,依次向後遍歷,同時讓索引跟著遞增,如果遍歷的臨時節點所得到的元素等於我們指定的元素,則此時的臨時節點的位置就是我們目標位置,該位置的索引就是目標值的索引。
DoublyLinkedList.prototype.indexOf = function(data){ var current = this.head; var index = 0; while(current){ if(current.data === data){ return index; } current = current.next; index ++; } return -1; }
驗證成功。
6、 update(position,ele):修改某個位置的元素
首先判斷要修改的位置是否越界,在不越界的情況下,定義一個臨時節點和索引,讓臨時節點指向頭結點,索引位置為0,遍歷臨時節點並判斷臨時節點此時的索引和要修改元素的位置是否相等,如果相等的話,此時臨時節點的位置就是要修改元素的位置,可以直接給臨時節點的資料域更改元素。
DoublyLinkedList.prototype.update = function(position,newData){ if(position<0 || position>=this.length){ return false; }else{ var index = 0; var current = this.head; while(index++ <position){ current = curent.next; } current.data = newData; return true; } }
驗證:將0位置的資料換成rc
list.update(0,'rc') console.log("list.update(0,'rc')為:"); console.log(list.toString());
驗證成功。
7、removeAt(position):從列表的指定位置移除一項
這個方法和insert()
方法的思想相似,首先判斷是否越界,在不越界的情況下,如果連結串列只有一個節點,則直接移除該項,讓連結串列的頭結點和尾節點均指向空。如果連結串列有多個節點的情況下,也分為三種情況,操作如下:
DoublyLinkedList.prototype.removeAt = function(position){ if(position<0 || position>=this.length){ return null; }else{ var current = this.head; if(this.length ===1){ //連結串列長度為1 this.head = null; this.tail = null }else{//連結串列長度不為1 if(position === 0){ //刪除首位 this.head.next.prev = null; this.head = this.head.next; }else if(position === this.length-1){ this.tail.prev.next = null; this.tail = this.tail.prev; }else{ var index = 0; while(index++ <position){ current = current.next; } current.prev.next = current.next; current.next.pre = current.prev; http://www.cppcns.com } } this.length -=1; return current.data; } }
驗證:刪除位置3的資料:
list.removeAt(3) console.log("list.removeAt(3)為:"); console.log(list.toString());
結果為:
驗證成功
8、remove(element):從列表中移除一項
可以直接根據indexOf(element)
方法獲取元素在連結串列中的位置,在根據removeAt(position)
方法將其刪除。
DoublyLinkedList.prototype.remove = function(data){ var index = this.indexOf(data); return this.removeAt(index); }
驗證:刪除資料為rc
的節點:
list.remove('rc'); console.log("list.remove('rc')為:"); console.log(list.toString());
9、isEmpty():判斷連結串列是否為空
直接判斷連結串列中元素的個數是否為零,如果為零則為空,返回true,否則不為空,返回false.
DoublyLinkedList.prototype.isEmpty = function(){ return this.length ==0; }
驗證:
console.log("連結串列是否為空:"+list.isEmpty());
10、size():返回連結串列包含的元素個數
連結串列長度就是元素個數。
DoublyLinkedList.prototype.size = function(){ return this.length; }
驗證:
console.log("連結串列的元素個數為:"+list.size());
以上就是Script實現雙向連結串列過程解析的詳細內容,更多關於JavaScript 雙向連結串列的資料請關注我們其它相關文章!