1. 程式人生 > 其它 >資料結構篇(2) ts實現單迴圈連結串列

資料結構篇(2) ts實現單迴圈連結串列

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