学习JavaScript数据结构与算法(四)——链表

来源:互联网 发布:矩阵怎么计算 编辑:程序博客网 时间:2024/06/08 21:59

链表存储有序的元素集合,但不同于数组,链表中的元素在内存中并不是连续放置的。
每个元素由一个存储元素本身的节点和指向下一个元素的引用(也称指针或链接)组成。

链表

相比较与传统的数组,链表的一个好处在于,添加或移除元素的时候不需要移动其他元素。
特别需要注意的是,链表需要使用指针。
对于数组而言,可以直接访问任何位置的任何元素;而要想访问链表中的一个元素,需要从起点(表头)开始迭代列表直到找到所需的元素。

在现实生活中,也有一些链表的例子。
举个例子,一群人手拉手站成一排,每个人是一个元素,手就是链向下一个人的指针。可以向队列中增加人,只需要找到想加入的点,断开连接,插入一个人在重新连接起来。
再比如火车,一列火车是由一节节车厢组成的,每节车厢都相互连接。每节车厢都是列表的元素,车厢间的连接就是指针。
再举个例子,寻宝游戏。你有一条线索,这条线索是指向下一个线索的地点的指针。你顺着这条线索去寻找下一个线索。得到中间的线索的唯一方法就是,从第一条线索开始顺着列表寻找。

创建链表

先建立骨架,如下所示。

function LinkedList () {    /*    Node辅助类,表示要加入列表的项。    包含一个element属性,即要添加到列表的值,以及一个next属性,即指向列表中下一个节点项的指针。     */    var Node = function(element){        this.element = element;        this.next -= null;    };    var length = 0;//存储列表项的数量的length属性    var head = null;//存储第一个节点的引用    /*    append(element)    向列表尾部添加一个新的项     */    this.append = function(element){};    /*    insert(position, element)    向列表的特定位置插入一个新的项     */    this.insert = function(position, element){};    /*    removeAt(position)    从列表的特定位置移除一项     */    this.removeAt = function(position){};    /*    remove(element)    从列表中移除一项     */    this.remove = function(element){};    /*    indexOf(element)    返回元素在列表中的索引    如果列表中没有该元素则返回-1     */    this.indexOf = function(element){};    /*    isEmpty()    如果链表中不包含任何元素,返回true    如果链表长度大于0则返回false     */    this.isEmpty = function(){};    /*    size()    返回链表包含的元素个数    与数组的length属性类似     */    this.size = function(){};    /*    getHead()    获取第一个元素     */    this.getHead = function(){};    /*    toString()    把LinkedList对象转换成一个字符串     */    this.toString = function(){};    this.print = function(){};}

接下来,实现上述代码中的那些方法。

1、向链表尾部追加元素——append()

可能出现两种情况:一是列表为空,添加的是第一个元素;二是列表不为空,向其追加元素。

向链表尾部追加元素,列表为空

向链表尾部追加元素,列表不为空

this.append = function(element){    var node = new Node(element),        current;    //向为空的列表添加一个元素    if (head == null) {        head = node;    }    //向不为空的列表尾部添加元素    else {        current = head;        //循环列表,直到找到最后一项        //列表最后一个节点的下一个元素始终是null        while (current.next) {            current = current.next;        }        //找到最后一项,将其next赋为node,建立链接        current.next = node;    }    length++;//更新列表的长度};

2、根据给定位置从链表中移除元素——removeAt()

可能出现两种情况:一是移除第一个元素,二是移除第一个元素之外的元素。

移除第一个元素

移除最后一个元素

移除中间的元素

this.removeAt = function(position){    //检查越界值,验证这个位置是否有效    //从0(包括0)到列表的长度(size-1)都是有效的位置    if (position > -1 && position < length) {        var current = head,            previous,            index = 0;        //移除第一项        if (position === 0) {            head = current.next;        }        else {            while (index++ < position) {                previous = current;                current = current.next;            }            /*            current是对要移除元素的引用            previous是对要移除元素的前一个元素的引用            要移除current,只需将previous与current的下一项链接起来             */            previous.next = current.next;        }        length--;        return current.element;    }    //不是有效位置,返回null    else {        return null;    }};

3、在任意位置插入一个元素——insert()

在起点插入元素

在尾部添加元素

在中间添加元素

this.insert = function(position, element){    //检查越界值    if (position >= 0 && position <= length) {        var node = new Node(element),            current = head,            previous,            index = 0;        //在列表的起点添加一个元素        if (position === 0) {            node.next = current;            head = node;        }        //在列表中间或尾部添加一个元素        else {            while (index++ < position) {                previous = current;                current = current.next;            }            node.next = current;            previous.next = node;        }        length++;//更新列表长度        return true;    }    //越界就返回false,表示没有添加项到列表中    else {        return false;    }};

4、其他方法的实现

this.toString = function(){    var current = head,        string = '';    while (current) {        string = current.element + (current.next ? ', ' : '');        current = current.next;    }    return string;};this.indexOf = function(element){    var current = head,        index = 0;    while (current) {        if (element === current.element) {            return index;        }        index++;        current = current.next;//检查列表中下一个节点    }    return -1;};this.remove = function(element){    var index = this.indexOf(element);    return this.removeAt(index);};this.isEmpty = function(){    return length === 0;};this.size = function(){    return length;//列表的length是内部控制的,因为LinkedList是从头构建的};this.getHead = function(){    return head;//head为私有变量,不能在LinkedList实例外部被访问和更改,只有通过LinkedList实例才可以};this.print = function(){    console.log(this.toString());};

测试代码如下:

var list = new LinkedList();list.append(15);list.print();  //输出15console.log(list.indexOf(15));  //输出0list.append(10);list.print();  //输出15, 10console.log(list.indexOf(10));  //输出1list.append(13);list.print();  //输出15, 10, 13console.log(list.indexOf(13));  //输出2console.log(list.indexOf(10));  //输出1list.append(11);list.append(12);list.print();  //输出15, 10, 13, 11, 12console.log(list.removeAt(1));  //输出10list.print()  //输出15, 13, 11, 12console.log(list.removeAt(3));  //输出12list.print();  //输出15, 13, 11list.insert(0,16);list.print();  //输出16, 15, 13, 11list.insert(1,17);list.print();  //输出16, 17, 15, 13, 11list.insert(list.size(),18);list.print();  //输出16, 17, 15, 13, 11, 18list.remove(16);list.print();  //输出17, 15, 13, 11, 18list.remove(11);list.print();  //输出17, 15, 13, 18

在控制台显示的结果如下:

使用LinkedList类

阅读全文
0 0
原创粉丝点击