JavaScript_SA 链表

要存储多个元素 数组可能是最常用的数据结构 但是 这种数据结构有一个缺点 在大多数语言中 数组的大小是固定的 在数组中进行插入删除的成本很高 因为需要移动元素
链表存储有序的元素集合 但不同于数组 链表中的元素在内存中并不是连续放置的 每个元素由一个存储元素本身的节点和指向下一个元素的引用(也称指针或链接)组成
链表在添加或删除元素的时候不需要移动其它元素 然而 链表需要使用指针 数组的另一个细节是可以直接访问任何位置的任何元素 而要想访问链表中间的一个元素 需要从起点(表头)开始迭代列表直到找到所需的元素 也就是查找元素链表的时间复杂度大于数组 而删除或添加元素的时候链表不需要移动其它元素 所以链表在插入删除时的时间复杂度小于数组
在这里插入图片描述
1、创建链表

function LinkedList() {
    let Node = function (element) { /* Node类表示要加入列表的项。
    它包含一个element属性,即要添加到列表的值,以及一个next属性,
    即指向列表中下一个节点项的指针。*/
        this.element = element;
        this.next = null;
    };
    let length = 0; // 存储列表项的数量的length属性 
    let head = null; /*我们还需要存储第一个节点的引用。
    为此,可以把这个引用存储在一个称为head的变量中*/
    //在链表尾部添加项
    this.append = function (element) { };
    //特定位置添加 
    this.insert = function (position, element) { };
    //特定位置删除
    this.removeAt = function (position) { };
    //删除一项 
    this.remove = function (element) { };
    //返回元素在链表中的索引 不存在返回-1 
    this.indexOf = function (element) { };
    //链表是否为空 
    this.isEmpty = function () { };
    //链表元素个数 
    this.size = function () { };
    this.getHead = function () { };
    /*由于列表项使用了Node类,就需要重写继承自JavaScript
    对象默认的toString方法,让其只输出元素的值。*/
    this.toString = function () { };
    this.print = function () { };
}

2、具体方法:
①append 链表尾部追加元素
两种场景 一 链表为空 添加的是第一个元素 二 链表不为空 追加元素

this.append = function (element) {
    let node = new Node(element), //把element作为值传入,创建Node项 
        current; /*需要循环访问列表,直到找到最后一项。
        为此,我们需要一个指向列表中current项的变量 */
    if (head === null) { //列表中第一个节点 为空时
        head = node;
    } else {  //不为空时
        current = head; /*要向列表的尾部添加一个元素,首先需要
        找到最后一个元素。记住,我们只有第一个元素的引用*/
        //循环列表,直到找到最后一项
        while (current.next) {
            current = current.next;
        }
        //找到最后一项,将其next赋为node,建立链接
        current.next = node; /*循环访问列表时,当current.next元素为null时,
        我们就知道已经到达列表尾部了。然后要做的就是让当前(也就是最后一个)
        元素的next指针指向想要添加到列表的节点*/
    }
    length++; //更新列表的长度 
};
//调用
let list = new LinkedList(); 
list.append(15); 
list.append(10);

②移除元素 remove从链表中移除一项 removeAt从特定位置移除
在这里插入图片描述

this.removeAt = function (position) {
    //检查越界值
    if (position > -1 && position < length) { 
    /*该方法需要得到移除元素的位置 就需要验证这个位置是否是有效的
    从0(包括0)到列表的长度都是有效的位置*/
        let current = head, //current变量对第一个元素的引用 
            previous, // {3} 
            index = 0; // {4} 
        //移除第一项
        if (position === 0) { /*如果位置为0 即移除第一个元素
        我们要做的就是让head指向第二个元素 我们将用current变量
        创建一个对链表中第一个元素的引用 上面已经引用 我们把
        head赋为current.next 就会移除第一个元素*//**/
            head = current.next;
        } else {
            while (index++ < position) { /*假设我们要移除列表的最
        后一项或者中间某一项。为此,需要依靠一个细节来迭代列表,直到
        到达目标位置——我们会使用一个用于内部控制和递增的index变量*/
                previous = current; //对当前元素的前一个元素的引用
                current = current.next; /*current变量总是为对所循
                环列表的当前元素的引用 */ 
            }
            //将previous与current的下一项链接起来:跳过current,从而移除它
            previous.next = current.next; /*要从列表中移除当前元素,要做
            的就是将previous.next和current.next链接起来*/
        }
        length--; 
        return current.element;
    } else {
        return null; //越界return null 
    }
};

在这里插入图片描述
③插入元素 insert

this.insert = function (position, element) {
    //检查越界值
    if (position >= 0 && position <= length) { //{1} 
        let node = new Node(element),
            current = head,
            previous,
            index = 0;
        if (position === 0) { //在第一个位置添加
            node.next = current; /*current变量是对
    列表中第一个元素的引用。我们需要做的是把node.next
    的值设为current(列表中第一个元素)。现在head和
    node.next都指向了current。接下来要做的就是把head
    的引用改为node,这样列表中就有了一个新元素。*/
            head = node;
        } else {
            while (index++ < position) { /*现在来处理
        第二种场景:在列表中间或尾部添加一个元素。首先,
        我们需要循环访问列表,找到目标位置*/
                previous = current;
                current = current.next;
            }
            node.next = current; /*当跳出循环时,current
        变量将是对想要插入新元素的位置之后一个元素的引用,而
        previous将是对想要插入新元素的位置之前一个元素的引用。
        在这种情况下,我们要在previous和current之间添加新项。
        因此,首先需要把新项(node)和当前项链接起来*/
            previous.next = node; /*然后需要改变previous
        和current之间的链接。我们还需要让previous.next指向
        node*/
        }
        length++; //更新列表的长度
        return true;
    } else {
        return false; 
    }
};

在这里插入图片描述如果我们试图向最后一个位置添加一个新元素,previous将是对列表最后一项的引用,而
current将是null。在这种情况下,node.next将指向current,而previous.next将指向
node,这样列表中就有了一个新的项。
在这里插入图片描述
何向列表中间添加一个新元素:
在这种情况下,我们试图将新的项(node)插入到previous和current元素之间。首先,
我们需要把node.next的值指向current。然后把previous.next的值设为node。这样列表中
就有了一个新的项。
④把链表转换为字符串

toString方法会把LinkedList对象转换成一个字符串。下面是toString方法的实现:
this.toString = function(){ 
   let current = head, //把current变量当作索引 控制循环访问列表
   string = ''; //初始化用于拼接元素值的变量 
   while (current) { //循环访问列表中的每个元素 
   string +=current.element +(current.next ? 'n' : '');/*我们要用
current来检查元素是否存在(如果列表为空,或是到达列表中最后一个元素的
下一位(null),while循环中的代码就不会执行)。然后我们就得到了元素的
内容,将其拼接到字符串中*/ 
   current = current.next; //继续迭代下一个元素
   } 
   return string; //返回列表内容的字符串 
};

⑤indexOf 接收一个元素的值 如果在列表中找到它 就返回元素的位置 否则返回-1

this.indexOf = function(element){ 
   let current = head, /*我们需要一个变量来帮助我们循环访问列表,
这个变量是current,它的初始值是head(列表的第一个元素——我们还需要
一个index变量来计算位置数*/ 
   index = -1; 
   while (current) { /*然后循环访问元素*/ 
      if (element === current.element) { 
         return index; /*检查当前元素是否是我们要找的。
如果是,就返回它的位置*/
      } 
   index++; //如果不是,就继续计数 
   current = current.next; //检查列表中下一个节点 
   } 
   return -1; /*如果列表为空,或是到达列表的尾部
(current = current.next将是null),循环就不会执行。
如果没有找到值,就返回-1。*/
};

⑥isEmpty size getHead

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

3、双向链表
双向链表和普通链表的区别在于,在链表中,一个节点只有链向下一个节点的链接,而在双向链表中,链接是双向的:一个链向下一个元素,另一个链向前一个元素,如图:
在这里插入图片描述

发布了75 篇原创文章 · 获赞 0 · 访问量 3385

猜你喜欢

转载自blog.csdn.net/E_ISOLATE/article/details/101710985