JavaScript链表

左心房为你撑大大i 提交于 2020-03-02 18:45:59

链表

定义

链表和数组很相似,不同的是,链表中的元素在内存中不是连续放置的,链表中的元素实际上是由一组节点构成的,其中每一个节点都是由数据元素和指向下一个数据元素的引用(指针)构成的。要想访问链表中间的一个元素,需要从头(表头)开始迭代链表,直到找到所需的元素。

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是这样实现普通链表的:

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链接起来。

插入一个元素时,需要注意跳出循环时,current是对想要插入新元素的位置之后的一个元素的引用。previous则是对想要插入的新元素前一个元素的引用。这时,在previous和current之间添加新项,需要作出如下的变换。

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

head变量是LinkedList类的私有变量,如果我们要在类的外部实现循环访问链表,则需要提供一种获取类的第一个元素的方法~getHead方法。

双向链表

定义

链表由许多变体,其中一种是双向链表。在双向链表中,链接是双向的,一个链接链向下一个元素,另一个链接链向前一个元素。

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从而构成一个环路。


易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!