JavaScript鏈表

鏈表

定義

鏈表和數組很類似,不一樣的是,鏈表中的元素在內存中不是連續放置的,鏈表中的元素其實是由一組節點構成的,其中每個節點都是由數據元素和指向下一個數據元素的引用(指針)構成的。要想訪問鏈表中間的一個元素,須要從頭(表頭)開始迭代鏈表,直到找到所需的元素。node

In computer science, a linked list is a data structure consisting of a group of nodes which together represent a sequence. Under the simplest form, each node is composed of data and a reference (in other words, a link) to the next node in the sequence; more complex variants add additional links.算法

鏈表是一種動態的數據結構,能夠從中任意的添加或者移除項,它會按需進行擴容。數組的大小是固定的,從數組的起點或者中間插入或者移除項的成本很高,由於須要移動元素。數組

類別現實生活中的實例就是火車了,每節車箱彼此連接,很容易分離某一節車皮,改變它的位置,添加或者移除一節車皮。每節車皮就是鏈表的元素,車皮間的鏈接就是指針。數據結構

代碼實現

js是這樣實現普通鏈表的:app

function LinkedList() {

    var Node = function(element){

        this.element = element;
        this.next = null;//每個元素的默認下一個元素的索引是null
    };

    var length = 0;
    var head = null;//初始化,當鏈表爲空的時候,head指向null。

    this.append = function(element){

        var node = new Node(element),
            current;

        if (head === null){ //first node on list
            head = node;
        } else {

            current = head;

            //loop the list until find last item
            while(current.next){
                current = current.next;
            }

            //get last item and assign next to added item to make the link
            current.next = node;
        }

        length++; //update size of list,此時已經在鏈表中增長了一個元素
    };

    this.insert = function(position, element){

        //check for out-of-bounds values
        if (position >= 0 && position <= length){

            var node = new Node(element),
                current = head,
                previous,
                index = 0;

            if (position === 0){ //add on first position

                node.next = current;
                head = node;

            } else {
                while (index++ < position){
                    previous = current;
                    current = current.next;
                }
                node.next = current;
                previous.next = node;
            }

            length++; //update size of list

            return true;

        } else {
            return false;
        }
    };

    this.removeAt = function(position){

        //check for out-of-bounds values
        if (position > -1 && position < length){

            var current = head,
                previous,
                index = 0;

            //removing first item
            if (position === 0){
                head = current.next;//移除第一個元素很容易,直接將指針指向current.next便可。
            } else {

                while (index++ < position){

                    previous = current;
                    current = current.next;
                }

                //link previous with current's next - skip it to remove
                previous.next = current.next;
            }

            length--;

            return current.element;

        } else {
            return null;
        }
    };

    this.remove = function(element){

        var index = this.indexOf(element);
        return this.removeAt(index);
    };

    this.indexOf = function(element){

        var current = head,
            index = 0;

        while (current) {
            if (element === current.element) {
                return index;
            }
            index++;
            current = current.next;
        }

        return -1;
    };

    this.isEmpty = function() {
        return length === 0;
    };

    this.size = function() {
        return length;
    };

    this.getHead = function(){
        return head;
    };

    this.toString = function(){

        var current = head,
            string = '';

        while (current) {
            string = current.element;
            current = current.next;
        }
        return string;

    };

    this.print = function(){
        console.log(this.toString());
    };
}

其中尤爲須要注意從鏈表移除元素的removeAt函數。函數

 previous.next = current.next;

current變量是對要移除元素的引用,previous是對要移除元素的前一個元素的引用,那麼要移除current元素所須要作的就是將previous.next和current.next連接起來。oop

插入一個元素時,須要注意跳出循環時,current是對想要插入新元素的位置以後的一個元素的引用。previous則是對想要插入的新元素前一個元素的引用。這時,在previous和current之間添加新項,須要做出以下的變換。優化

 node.next = current;
 previous.next = node;

head變量是LinkedList類的私有變量,若是咱們要在類的外部實現循環訪問鏈表,則須要提供一種獲取類的第一個元素的方法~getHead方法。this

雙向鏈表

定義

鏈表由許多變體,其中一種是雙向鏈表。在雙向鏈表中,連接是雙向的,一個連接鏈向下一個元素,另外一個連接鏈向前一個元素。
spa

In computer science, a doubly linked list is a linked data structure that consists of a set of sequentially linked records called nodes. Each node contains two fields, called links, that are references to the previous and to the next node in the sequence of nodes.

在雙向鏈表中能夠有兩種迭代鏈表的方向。

代碼實現

js是這樣實現雙向鏈表的:

function DoublyLinkedList() {

    var Node = function(element){

        this.element = element;
        this.next = null;
        this.prev = null; //NEW
    };

    var length = 0;
    var head = null;
    var tail = null; //NEW

    this.append = function(element){

        var node = new Node(element),
            current;

        if (head === null){ //first node on list
            head = node;
            tail = node; //NEW
        } else {

            //attach to the tail node //NEW
            tail.next = node;
            node.prev = tail;
            tail = node;
        }

        length++; //update size of list
    };

    this.insert = function(position, element){

        //check for out-of-bounds values
        if (position >= 0 && position <= length){

            var node = new Node(element),
                current = head,
                previous,
                index = 0;

            if (position === 0){ //add on first position

                if (!head){       //NEW
                    head = node;
                    tail = node;
                } else {
                    node.next = current;
                    current.prev = node; //NEW {1}
                    head = node;
                }

            } else  if (position === length) { //last item //NEW

                current = tail;     // {2}
                current.next = node;
                node.prev = current;
                tail = node;

            } else {
                while (index++ < position){ //{3}
                    previous = current;
                    current = current.next;
                }
                node.next = current;
                previous.next = node;

                current.prev = node; //NEW
                node.prev = previous; //NEW
            }

            length++; //update size of list

            return true;

        } else {
            return false;
        }
    };

    this.removeAt = function(position){

        //check for out-of-bounds values
        if (position > -1 && position < length){

            var current = head,
                previous,
                index = 0;

            //removing first item
            if (position === 0){

                head = current.next; // {1}

                //if there is only one item, then we update tail as well //NEW
                if (length === 1){ // {2}
                    tail = null;
                } else {
                    head.prev = null; // {3}
                }

            } else if (position === length-1){ //last item //NEW

                current = tail; // {4}
                tail = current.prev;
                tail.next = null;

            } else {

                while (index++ < position){ // {5}

                    previous = current;
                    current = current.next;
                }

                //link previous with current's next - skip it to remove
                previous.next = current.next; // {6}
                current.next.prev = previous; //NEW
            }

            length--;

            return current.element;

        } else {
            return null;
        }
    };

    this.remove = function(element){

        var index = this.indexOf(element);
        return this.removeAt(index);
    };

    this.indexOf = function(element){

        var current = head,
            index = -1;

        //check first item
        if (element == current.element){
            return 0;
        }

        index++;

        //check in the middle of the list
        while(current.next){

            if (element == current.element){
                return index;
            }

            current = current.next;
            index++;
        }

        //check last item
        if (element == current.element){
            return index;
        }

        return -1;
    };

    this.isEmpty = function() {
        return length === 0;
    };

    this. size = function() {
        return length;
    };

    this.toString = function(){

        var current = head,
            s = current ? current.element : '';

        while(current && current.next){
            current = current.next;
            s += ', ' + current.element;
        }

        return s;
    };

    this.inverseToString = function() {

        var current = tail,
            s = current ? current.element : '';

        while(current && current.prev){
            current = current.prev;
            s += ', ' + current.element;
        }

        return s;
    };

    this.print = function(){
        console.log(this.toString());
    };

    this.printInverse = function(){
        console.log(this.inverseToString());
    };

    this.getHead = function(){
        return head;
    };

    this.getTail = function(){
        return tail;
    }
}

在任意位置插入新的元素,或者移除元素時,能夠對position進行判斷,若是position>length/2,就最好從尾部開始迭代,從而減小迭代次數,優化算法。

循環鏈表

定義

循環鏈表只是在普通鏈表的基礎上作了一點點的變更,即最後一個元素指向下一個元素的指針(tail.next)不是null,而是指向第一個元素(head)。

A circular linked list is a linked list in which the head element's previous pointer points to the tail element and the tail element's next pointer points to the head element. In the special case of a circular list with only one element, the element's previous and next pointers point to itself, and it is both the head and tail of the list.

Programming a circular list presents some challenges, but the code is actually simpler and more concise than for a conventional linked list, because the lack of NULL pointers means that we never need to check for them.

代碼實現

js是這樣實現循環鏈表的:

function CircularLinkedList() {

    var Node = function(element){

        this.element = element;
        this.next = null;
    };

    var length = 0;
    var head = null;

    this.append = function(element){

        var node = new Node(element),
            current;

        if (head === null){ //first node on list
            head = node;
        } else {

            current = head;

            //loop the list until find last item
            while(current.next !== head){ //last element will be head instead of NULL
                current = current.next;
            }

            //get last item and assign next to added item to make the link
            current.next = node;
        }

        //set node.next to head - to have circular list
        node.next = head;

        length++; //update size of list
    };

    this.insert = function(position, element){

        //check for out-of-bounds values
        if (position >= 0 && position <= length){

            var node = new Node(element),
                current = head,
                previous,
                index = 0;

            if (position === 0){ //add on first position

                node.next = current;

                //update last element
                while(current.next !== head){ //last element will be head instead of NULL
                    current = current.next;
                }

                head = node;
                current.next = head;

            } else {
                while (index++ < position){
                    previous = current;
                    current = current.next;
                }
                node.next = current;
                previous.next = node;

                if (node.next === null){ //update in case last element
                    node.next = head;
                }
            }

            length++; //update size of list

            return true;

        } else {
            return false;
        }
    };

    this.removeAt = function(position){

        //check for out-of-bounds values
        if (position > -1 && position < length){

            var current = head,
                previous,
                index = 0;

            //removing first item
            if (position === 0){

                while(current.next !== head){ //needs to update last element first
                    current = current.next;
                }

                head = head.next;
                current.next = head;

            } else { //no need to update last element for circular list

                while (index++ < position){

                    previous = current;
                    current = current.next;
                }

                //link previous with current's next - skip it to remove
                previous.next = current.next;
            }

            length--;

            return current.element;

        } else {
            return null;
        }
    };

    this.remove = function(element){

        var index = this.indexOf(element);
        return this.removeAt(index);
    };

    this.indexOf = function(element){

        var current = head,
            index = -1;

        //check first item
        if (element == current.element){
            return 0;
        }

        index++;

        //check in the middle of the list
        while(current.next !== head){

            if (element == current.element){
                return index;
            }

            current = current.next;
            index++;
        }

        //check last item
        if (element == current.element){
            return index;
        }

        return -1;
    };

    this.isEmpty = function() {
        return length === 0;
    };

    this.size = function() {
        return length;
    };

    this.getHead = function(){
        return head;
    };

    this.toString = function(){

        var current = head,
            s = current.element;

        while(current.next !== head){
            current = current.next;
            s += ', ' + current.element;
        }

        return s.toString();
    };

    this.print = function(){
        console.log(this.toString());
    };
}

雙向循環鏈表

雙向循環鏈表有指向head元素的tail.next和指向tail元素的head.prev從而構成一個環路。

相關文章
相關標籤/搜索