資料結構篇(2) ts實現單迴圈連結串列
阿新 • • 發佈:2022-03-29
JS的class可以通過extends關鍵字實現類似其他語言的繼承效果,比起使用一個extends關鍵字,在es5中實現繼承要複雜一些,可以通過修改原型鏈的方法實現繼承,讓一個原型物件等於另一個型別的例項等等,雖然也能夠實現,但是不夠直觀。
constructor()方法中的super()表示呼叫父類的建構函式,在這裡就是呼叫SIngleList類裡面的建構函式。
接下來就可以使用SingleList類中已經實現的函數了,但是由於單向迴圈連結串列的某些操作還是不同於單鏈表的,所以對SingleList類中的一些方法,需要在CirSingleList類中重寫。
//繼承單鏈表的屬性和方法,有一些方法需要重寫 class CirListNode extends ListNode { constructor() { super() } // 在單迴圈連結串列中尋找最後一個節點 /** 使用count進行計數,如果與當前連結串列的長度相同就返回子節點,這樣就可以避免陷入無限迴圈*/ findLast(): NodeItem { let count = 0; let currNode = this.head; while (currNode.next) { currNode = currNode.next; count++; if (count === this.size) { return currNode; } } return this.head; } // 在單迴圈連結串列中尋找資料 /** 如果對應元素不存在會導致無限迴圈, 所以需要重寫搜尋函式,如果當前節點等於最後一個子節點就結束迴圈返回null*/ find(item: any): NodeItem { let lastNode = this.findLast(); let currNode = this.head; while (currNode.data != item) { if (currNode == lastNode) { currNode = null; break; } currNode = currNode.next; } return currNode; } // 在資料為item的節點後面插入資料為element元素的節點 /** * 1. 插入的是頭節點位置,如果當前連結串列為空,則將新節點插入到head節點後面,並指向自己,形成迴圈 * 2,插入的是頭節點位置,當前連結串列不為空,則將新節點指向頭節點的下一個節點,然後再將頭節點指向新節點,再將尾節點指向新節點,形成一個新的迴圈 * 3, 插入的是中間位置,就正常插入即可 */ insert(item: any, element: any): void { let newNode = new NodeItem(element); let itemNode = this.find(item); let lastNode = this.findLast(); // 插入的位置處於頭結點之後,第一個節點之前 if (item === 'head') { if (this.size === 0) { this.head.next = newNode; newNode.next = newNode; } else { newNode.next = this.head.next; this.head.next = newNode; lastNode.next = newNode; } this.size++; return; } // 處於連結串列中後位置時 newNode.next = itemNode.next; itemNode.next = newNode; this.size++; } /** * 1. 當前刪除的節點是第一個節點時,如果此時單向迴圈連結串列只有一個節點,直接將此單向連結串列置空即可 * 2. 當前刪除的節點是第一個節點時, 且此時單向迴圈連結串列不僅只有一個節點時,此時將頭節點的next指標指向要刪除節點的下一個節點,並將最後一個節點指向要刪除節點的下一個節點 * 3. 除了前面的兩種情況之外,只要將刪除節點的前一個節點next指標指向要刪除節點的後一個系欸但即可 */ remove(item: any): void { let lastNode = this.findLast(); let itemNode = this.find(item); let preCurNode = this.head; while (preCurNode.next !== itemNode) { preCurNode = preCurNode.next; } if (itemNode === this.head.next) { if (this.size === 1) { this.head.next = null; } else { this.head.next = itemNode.next; lastNode.next = itemNode.next; } } else { preCurNode.next = itemNode.next; } this.size--; } /** * 根據count計數來輸出連結串列內容,防止陷入無限迴圈 */ display(): void { let count = 0; let currNode = this.head; let str = ''; while (count !== this.size) { currNode = currNode.next; str += currNode.data + '=>'; count++; } console.log(str) } /** * 在尾部插入資料 * 用寫好的findLast()方法,找到最後一個節點,然後將最後一個節點next指標指向新的節點,再將新的節點指向此連結串列的第一個節點即可。 */ append(element: any): void { let lastNode = this.findLast(); let newNode = new NodeItem(element); lastNode.next = newNode; newNode.next = this.head.next; this.size++; } } // n個人圍成一圈,殺死第m個人,直到剩下s個人為止 // 輸出存活的人的序號 let myList = new CirListNode(); function killPerson(n: any, m: any, s: any) { for (let i = 1; i <= n; i++) { myList.append(i); } let currNode = undefined; let toKill = null; while(myList.size>s) { toKill = myList.advance(m, currNode); // 從currNode開始,前進m個節點 currNode = toKill; // 儲存要刪除的節點作為下一次迴圈的引數 myList.remove(toKill.data); // 刪除第m個節點 } myList.display(); } killPerson(41, 3, 2); // head->16->31 // killPerson(5, 4, 1); // head->1