js封装数据结构_05_双向链表

本文详细介绍了双向链表的特性,包括增、删、改、查等操作的实现,并分享了在实现过程中遇到的问题及解决方案,如忽略长度为0的情况、代码冗余等。此外,还探讨了插入和删除操作的复杂情况,如在链表头、尾及中间进行操作。源代码展示了完整的双向链表数据结构实现。
摘要由CSDN通过智能技术生成

 一、双向链表的特点:

双向链表不同于单向链表,它既可以从头遍历到尾,又可以从尾遍历到头;

链表一般有head、tail、length等属性,head指向头节点,length表示链表节点的个数,tail指向尾部节点;

链表无法通过下标直接访问元素,需要从head指向的头节点或tail指向的尾节点开始一个一个的访问,直到找到对应的元素;

二、这里我实现了:

增:

  • append,向链尾添加节点
  • insert,向特定位置插入新节点

删:

  • removeAt,指定位置删除节点,成功返回删除节点的数据,失败返回null
  • remove,从列表中移除指定节点,成功返回删除节点的数据,失败返回null

改:

  • update,修改某个位置的节点

查:

  • get,获取对应位置的元素
  • indexOf,返回队应元素的索引,没有则返回-1,有多个以数组形式返回

其他:

  • isEmpty,是否为空
  • size,包含元素个数
  • backwardString,从前向后遍历链表,以字符串形式返回

  • forwardString,从后向前遍历链表,以字符串形式返回

  • toString,重写tosTring()方法,以数组形式返回

三、问题或不足

  • 我一般是自己先实现一下各种方法,然后再去对比b站大学里的视频;
  • 这次就犯了好几个错,在这里记录一下,大伙也可以参考参考:
  • 第一个就是写inser和removeAtt时忽略了”length==0“这种情况;
  • 第二个就是代码看起来杂乱,if...else if..else 用的有点多,重复逻辑没抽取;
  • 第三个就是有多余代码,如:
if (!position) {
        this.head = current.next;
        this.head.previous = null;
        // 最后一行代码可不写,Gc会清除它的
        current.next = null;
      }

这里最后一行可以不用写,GC会清除它的,因为没有对象指向它

四、难的点就说一下insert和removeAt吧:

这里得考虑四种情况,最好可以自己画个图:

当length==0时;

当length>0,删除的位置是head时;

当length>0,删除的位置是tail时;

当length>0,删除的位置是中间时,这里可以做判断,当position < this.length / 2用正向遍历,反之反向遍历;

五、源码

function DoublyLinkedList() {
  // 内部类
  function Node(data) {
    this.data = data;
    this.next = null;
    this.previous = null;
  }

  // 属性
  this.head = null;
  this.tail = null;
  this.length = 0;

  // 方法
  // 方法1:append
  DoublyLinkedList.prototype.append = function (data) {
    let newNode = new Node(data);
    // 判断是否是添加的第一个节点
    if (!this.length) {
      this.head = newNode;
      this.tail = newNode;
    } else {
      newNode.previous = this.tail;
      this.tail.next = newNode;
      this.tail = newNode;
    }
    this.length++;
  };

  // 方法2:insert
  DoublyLinkedList.prototype.insert = function (position, element) {
    if (position < 0 || position > this.length) return false;

    let newNode = new Node(element);

    // length==0?
    if (this.length === 0) {
      this.head = newNode;
      this.tail = newNode;
    } else {
      // head?
      if (position === 0) {
        this.head.previous = newNode;
        newNode.next = this.head;
        this.head = newNode;
      } else {
        //tail?
        if (position === this.length) {
          newNode.previous = this.tail;
          this.tail.next = newNode;
          this.tail = newNode;
        } else {
          // middle?
          let currentEl = this.head;
          let index = 0;
          while (index++ < position) {
            currentEl = currentEl.next;
          }
          currentEl.previous.next = newNode;
          newNode.previous = currentEl.previous;
          newNode.next = currentEl;
          currentEl.previous = newNode;
        }
      }
    }
    this.length++;
    return true;
  };

  // 方法3:removeAt
  DoublyLinkedList.prototype.removeAt = function (position) {
    if (position < 0 || position >= this.length) return null;
    let current = this.head;
    if (this.length === 1) {
      this.head = null;
      this.tail = null;
    } else {
      //   head? 我没考略只有一种的情况
      if (!position) {
        this.head = current.next;
        this.head.previous = null;
        // 最后一行代码可不写,Gc会清除它的
        current.next = null;
      }
      //   tail?
      else if (position === this.length - 1) {
        current = this.tail;
        this.tail = current.previous;
        this.tail.next = null;
        current.previous = null;
      }
      //   middle?
      else if (position < this.length / 2) {
        let index = 0;
        while (index++ < position) {
          current = current.next;
        }
        current.previous.next = current.next;
        current.next.previous = current.previous;
        current.next = null;
        current.previous = null;
      } else {
        current = this.tail;
        let index = 0;
        while (index++ < this.length - 1 - position) {
          current = current.previous;
        }
        current.previous.next = current.next;
        current.next.previous = current.previous;
        current.next = null;
        current.previous = null;
      }
    }
    this.length--;
    return current.data;
  };
  // 方法4:remove
  DoublyLinkedList.prototype.remove = function (element) {
    let result = this.indexOf(element);
    if (result == null) return null;
    if (!Array.isArray(result)) {
      this.length--;
      return this.removeAt(result);
    } else {
      for (const key in result) {
        this.length--;
      }
      return this.removeAt(key);
    }
  };

  // 方法5:update
  DoublyLinkedList.prototype.update = function (position, element) {
    if (position < 0 || position >= this.length) return false;
    let current = null;
    if (position < this.length / 2) {
      current = this.head;
      let index = 0;
      while (index++ < position) {
        current = current.next;
      }
      current.data = element;
      return true;
    } else {
      current = this.tail;
      let index = 0;
      while (index++ < this.length - 1 - position) {
        current = current.previous;
      }
      current.data = element;
      return true;
    }
  };

  // 方法6:get
  DoublyLinkedList.prototype.get = function (position) {
    if (position < 0 || position >= this.length) return null;
    let current = null;
    if (position < this.length / 2) {
      current = this.head;
      let index = 0;
      while (index++ < position) {
        current = current.next;
      }
      return current.data;
    } else {
      current = this.tail;
      let index = 0;
      while (index++ < this.length - 1 - position) {
        current = current.previous;
      }
      return current.data;
    }
  };
  // 方法7:indexOf
  DoublyLinkedList.prototype.indexOf = function (element) {
    //    我有一个想法,从两端同时排查
    let current = this.tail;
    let index = this.length - 1;
    let indexArr = [];
    while (current) {
      if (current.data == element) {
        indexArr.push(index);
      }
      current = current.previous;
      index--;
    }
    if (indexArr.length === 0) {
      return -1;
    }
    if (indexArr.length === 1) {
      return indexArr[0];
    }
    return indexArr;
  };

  // 方法8:toString
  DoublyLinkedList.prototype.toString = function () {
    let dataArr = [];
    let current = this.head;
    while (current) {
      dataArr.push(current.data);
      current = current.next;
    }
    return dataArr;
  };
  // 方法9:forwardString
  DoublyLinkedList.prototype.forwardString = function () {
    let current = this.tail;
    let resultString = "";
    while (current) {
      resultString += current.data + " ";
      current = current.previous;
    }
    return resultString;
  };
  // 方法10:backwardString
  DoublyLinkedList.prototype.backwardString = function () {
    let current = this.head;
    let resultString = "";
    // 这里要区分current 和 current.next;这里只能是current,另一个最后一个dada无法拿到
    while (current) {
      resultString += current.data + " ";
      current = current.next;
    }
    return resultString;
  };
  // 方法11:isEmpty
  DoublyLinkedList.prototype.isEmpty = function () {
    if (!this.length) return true;
    return false;
  };
  // 方法12:size
  DoublyLinkedList.prototype.size = function () {
    return this.length;
  };
}

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值