JS數據結構初識(三)-鏈表

1、定義

鏈表存儲有序的元素集合,但不一樣於數組,鏈表中的元素在內存中並非連續放置的。每一個 元素由一個存儲元素自己的節點和一個指向下一個元素的引用(也稱指針或連接)組成。下面是一個鏈表的基本結構: 數組

例如尋寶遊戲。你有一條線索,這條線索是指向尋找下一條線索的地點的指針。你順着這條連接去下一個地點,獲得另外一條指向再下一處的線索。獲得列表中間的線索的惟一辦法,就是從起點(第一條線索)順着列表尋找。也就是鏈表只有給你頭結點,你就能夠找到剩下的元素。bash

2、鏈表函數骨架

function linkedList() {
        const Node = function (ele) {
            this.ele = ele;
            this.next = null;
        }
        const length = 0;
        const head = null;
        
        // 向列表尾部添加一個新的項。
        this.append = (ele) => {}
        // 向列表的特定位置插入一個新的項。
        this.insert = (position, ele) => {}
        // 從列表的特定位置移除一項。
        this.removeAt = (position) => {}
        // 返回元素在列表中的索引。若是列表中沒有該元素則返回-1。
        this.indexOf = (ele) => {}
        // 從列表中移除一項。
        this.remove = (ele) => {}
        // 若是鏈表中不包含任何元素,返回true,若是鏈表長度大於0則返回false。
        this.isEmpty = () => {}
        // 返回鏈表包含的元素個數。與數組的length屬性相似。
        this.size = () => {}
        // 因爲列表項使用了Node類,就須要重寫繼承自JavaScript對象默認的toString方法,讓其只輸出元素的值。
        this.toString = () => {}
        // 打印全部節點
        this.print = () => {}
    }
複製代碼

逐個實現各個方法app

2.1 向鏈表尾部追加元素

向LinkedList對象尾部添加一個元素時,可能有兩種場景:列表爲空,添加的是第一個元 素,或者列表不爲空,向其追加元素。函數

this.append = (ele) => {
        const newNode = new Node(ele);
        let current; // 當前節點對象

        if (head === null) { // 無頭結點
            head = newNode;
        } else {
            let head = current;
            while(current.next) {
                current = current.next;
            }
            current.next = newNode;
        }
        return ++length;
    } 
複製代碼

2.2 向列表的特定位置插入一個新的項

// 向列表的特定位置插入一個新的項。
    this.insert = (position, ele) => {
        if (position < 0 || position > length) {
            return false;
        }
        const newNode = new Node(ele);
        let current = head;
        let prevNode;
        // let
        if (position === 0) { // 插入的位置是 0 頭節點的位置
            NewNode.next = current;
            head = newNode;
        } else {
            while(index < position) {
                prevNode = current;
                current = current.next;
                index++;
            }
            prevNode.next = newNode;
            newNode.next = current;
        }
        length++;
        return true;
    }
複製代碼

2.3 從列表的特定位置移除一項

// 從列表的特定位置移除一項。
    this.removeAt = (position) => {
        if (position < 0 || position > length) {
            return false;
        }
        const newNode = new Node();
        let current = head;
        let prevNode;
        let index = 0;
        
        if (position === 0) {
            head = newNode;
        } else {
            while (index++ < position) {
                prevNode = current;
                current = current.next;
            }
            prevNode.next = current.next; // 跳過當前current
        }
        length--;
        return true;
    }
複製代碼

2.4 從列表中移除一項。

// 移除指定節點
    this.remove1 = function(ele) {
        const index = this.indexOf(ele);
        return this.removeAt(index);
    }
    
    this.remove = (ele) => {
        let index = 0;
        let current = head;
        let prevNode;
    
        while (current) {
            if (current.ele === ele) {
                break;
            }
            prevNode = current;
            current = current.next;
        }
        if (current.ele !== ele) {
            return false;
        }
        prevNode.next = current.next;
        return true;
    }
複製代碼

3. 整合

function linkedList() {
        const Node = function (ele) {
            this.ele = ele;
            this.next = null;
        }
        const length = 0; // 鏈表的長度
        const head = null; // 頭節點
        
        // 向列表尾部添加一個新的項。向LinkedList對象尾部添加一個元素時,可能有兩種場景:列表爲空,添加的是第一個元 素,或者列表不爲空,向其追加元素。
        this.append = (ele) => {
            const newNode = new Node(ele);
            let current; // 當前節點對象

            if (head === null) { // 無頭節點
                head = newNode;
            } else {
                let head = current;
                while(current.next) {
                    current = current.next;
                }
                current.next = newNode;
            }
            return ++length;
        }
        // 向列表的特定位置插入一個新的項。第一種場景: 須要在列表的起點添加一個元素,也就是第一個 位置。第二種場景: 在列表中間或尾部添加一個元素。
        this.insert = (position, ele) => {
            if (!this.inRange(position)) {
                return false;
            }
            const newNode = new Node(ele);
            let current = head;
            let prevNode;
            // let
            if (position === 0) { // 插入的位置是 0 頭節點的位置
                NewNode.next = current;
                head = newNode;
            } else {
                while(index < position) {
                    prevNode = current;
                    current = current.next;
                    index++;
                }
                prevNode.next = newNode;
                newNode.next = current;
            }
            length++;
            return true;
        }
        // 從列表的特定位置移除一項。狀況1: 咱們要從列表中移除第一個元素(position === 0); 狀況2:移除列表的最後一項或者中間某一項
        this.removeAt = (position) => {
            if (!this.inRange(position)) {
                return false;
            }
            const newNode = new Node();
            let current = head;
            let prevNode;
            let index = 0;
            
            if (position === 0) {
                head = newNode;
            } else {
                while (index++ < position) {
                    prevNode = current;
                    current = current.next;
                }
                prevNode.next = current.next; // 跳過當前current
            }
            length--;
            return true;
        }
        // 返回元素在列表中的索引。若是列表中沒有該元素則返回-1。
        this.indexOf = (ele) => {
            let current = head;
            let index = -1;
            while (current) {
                if (current.ele === ele) {
                    return index;
                }
                current = current.next;
                index++;
            }
            return -1;
        }
        // 從列表中移除一項。
        this.remove = (ele) => {
            let index = 0;
            let current = head;
            let prevNode;

            while (current) {
                if (current.ele === ele) {
                    break;
                }
                prevNode = current;
                current = current.next;
            }

            if (current.ele !== ele) {
                return false;
            }
            prevNode.next = current.next;
            return true;
        }

        this.remove1 = function(ele) {
            const index = this.indexOf(ele);
            return this.removeAt(index);
        }
        
        // 若是鏈表中不包含任何元素,返回true,若是鏈表長度大於0則返回false。
        this.isEmpty = () => length === 0;
        // 返回鏈表包含的元素個數。與數組的length屬性相似。
        this.size = () => length;
        // 因爲列表項使用了Node類,就須要重寫繼承自JavaScript對象默認的toString方法,讓其只輸出元素的值。
        this.toString = () => {
            let current = head;
            let resultStr = '';
            while (current) {
                resultStr += current;
            }
            return resultStr;
        }
        // 打印全部節點
        this.print = () => {
            let current = head;
            let results = [];
            while (current) {
                results.push(current.ele);
            }
            console.log(results);
            return results;
        }
        this.inRange = (position) => {
            return position >= 0 && position <=length;
        }
    }
複製代碼

3、雙向鏈表

雙向鏈表和普通鏈表的區別在於,在鏈表中, 一個節點只有鏈向下一個節點的連接,而在雙向鏈表中,連接是雙向的:一個鏈向下一個元素, 另外一個鏈向前一個元素;普通鏈表只有一個next指針指向後一個元素, 而雙向鏈表則後面的節點有一個prev指向前面節點的指針ui

function linkedList() { const Node = function (ele) { this.ele = ele; this.next = null; this.prev = null; } const length = 0; // 鏈表的長度 const head = null; // 頭節點 this.tail = null; }

4、循環鏈表

循環鏈表能夠像鏈表同樣只有單向引用,也能夠像雙向鏈表同樣有雙向引用。循環鏈表和鏈表之間惟一的區別在於,最後一個元素指向下一個元素的指針(tail.next)不是引用null, 而是指向第一個元素(head),以下圖所示。this

4.1 單向循環鏈表

4.2 雙向循環鏈表

相關文章
相關標籤/搜索