前端資料結構(3)之連結串列及其應用

zhunny發表於2019-03-27

基礎

  連結串列是一種在物理儲存單元中非連續,非順序的資料結構,它的邏輯順序是通過指標連結次序實現的。一個單連結串列的節點在Javascript中可以表示為:

function Node(data){
    this.data = data;
    this.next = null;
}
複製程式碼

  單連結串列的結構示意圖如下:

前端資料結構(3)之連結串列及其應用

連結串列的實現

  連結串列需要實現的幾個基本的方法是:append(向連結串列尾部追加節點),print(列印整個連結串列的元素),insert(向某個index位置插入新節點),remove(刪除某個位置index的節點),get(獲取某個位置index的節點),indexOf(給定某資料,查詢它在連結串列的哪個位置)。

function LinkList() {
    let Node = function(data) {
        this.data = data;
        this.next = null;
    }

    let length = 0;
    let head = null;
    let tail = null;

    this.append = function(data) {
        let node = new Node(data)
        if (head == null) {
            head = node;
            tail = node;
        } else {
            tail.next = node;
            tail = node;
        }
        length = length + 1;
        return true;
    };

    this.print = function() {
        let curr_node = head;
        while (curr_node) {
            console.log(curr_node.data);
            curr_node = curr_node.next;
        }
    };

    this.insert = function(index, data) {
        if (index < 0 || index > length) {
            return false;
        } else if (index == length) {
            return this.append(data);
        } else {
            let node = new Node(data);
            if (index == 0) {
                node.next = head;
                head = node;
            } else {
                let insert_index = 1;
                let curr_node = head;
                while (insert_index < index) {
                    insert_index += 1;
                    curr_node = curr_node.next;
                }
                let next_node = curr_node.next;
                node.next = next_node;
                curr_node.next = node;
            }
            length = length + 1;
            return true;
        }
    };

    this.remove = function(index) {
        if (index < 0 || index >= length) {
            return false;
        } else {
            let del_node = null;
            if (index == 0) {
                del_node = head;
                head = head.next;
            } else {
                let del_index = 1;
                let curr_node = head;
                while (del_index < index) {
                    del_index += 1;
                    curr_node = curr_node.next;
                }
                del_node = curr_node.next;
                let next_node = curr_node.next.next;
                curr_node.next = next_node;

                if (del_node.next == null) {
                    tail = curr_node;
                }
            }
            length = length - 1;
            return del_node.data;
        }
    };
    
    this.get = function(index) {
        if (index < 0 || index >= length) {
            return null;
        } else {
            if (index == 0) {
                return head;
            } else {
                let curr_node = head;
                let curr = 0;
                while (curr < index) {
                    curr += 1;
                    curr_node = curr_node.next;
                }
                return curr_node.data;
            }
        }
    }
    
    this.indexOf = function(data) {
        let index = -1;
        let curr_node = head;
        while (curr_node) {
            index += 1
            if (curr_node.data == data) {
                return index;
            } else {
                curr_node = curr_node.next;
            }
        }
        return -1;
    }
}

let link = new LinkList();
link.append(2);
link.append(4);
link.append(8);
link.append(7);
link.print()
複製程式碼

連結串列的應用題

  1. 反轉單連結串列
      比較常考的一道題,有兩種方式來解決,一種是迭代法,一種是遞迴法。迭代法就是需要定義三個節點指標,一個指向當前節點,一個指向前面一個節點,一個指向後面一個節點,反轉就是說,當前節點的next指標指向前面一個節點。
      遞迴方法就是你不會反轉當前連結串列,讓遞迴方法先幫你反轉當前節點的下一個節點開始的單連結串列,把反轉後的頭節點返回。你再將當前頭節點連線到返回頭節點的尾部。
class Node {
    constructor(data) {
        this.data = data;
        this.next = null;
    }
}
let node1 = new Node(1);
let node2 = new Node(2);
let node3 = new Node(3);
let node4 = new Node(4);
node1.next = node2;
node2.next = node3;
node3.next = node4;
node4.next = null;

function print(node) {
    let str = "";
    while (node) {
        str += node.data.toString() + "->";
        node = node.next;
    }
    str += "null"
    console.log(str);
}

print(node1);

function reverse(node) {
    if (!node) {
        return null;
    }
    let curr_node = node;
    let pre_node = null;
    while (curr_node) {
        let next_node = curr_node.next;
        curr_node.next = pre_node;
        pre_node = curr_node;
        curr_node = next_node;
    }
    return pre_node;
}
let node = reverse(node1);
print(node);

function reverse_digui(node) {
    if (!node) {
        return null;
    }
    if(node.next == null){
        return node;
    }
    let new_head = reverse_digui(node.next);
    node.next.next = node;
    node.next = null;
    return new_head;
}
複製程式碼
  1. 從尾到頭列印單連結串列
      還是用遞迴方法,不知道如何反向列印,就先讓下一個節點所在的單連結串列反向列印,等全部列印完,再把自己列印出來。
function reverse_print(node){
    if(!node){
        return null;
    }
    
    if(node == null){
        return;
    }else{
        reverse_print(node.next);
        console.log(node.data);
    }
}
複製程式碼

相關文章