前端数据结构之【链表】-- javascript实现

链表相对于数组来说,要复杂的多,首先,链表不需要连续的内存空间,它是由一组零散的内存块透过指针连接而成,所以,每一个块中必须包含当前节点内容以及后继指针。最常见的链表类型有单链表、双链表以及循环链表。

单链表:

只有一个方向,next依次往后
在这里插入图片描述

//初始化一个链表
function List() {
      //节点,包含自身和next
      let Node = function (element) {
        this.element = element;
        this.next = null;
      }
      // 初始头节点为 null
      let head = null
      // 链表长度
      let length = 0
      // 查找节点
      this.getList = function () { return head }
      // 链表是否为空
      this.isEmpty = function () {
        if(!head) return true
        return false
      }
      // 获取链表的size
      this.size = function () {
        return length
      }
 }

1.添加节点:
解题思路:需要添加在链表末尾,所以要找到next为null的节点上去添加,时间复杂度为O(n)
在这里插入图片描述

     // 需要添加在链表末尾,所以要找到next为null的节点上去添加,时间复杂度为O(n)
      this.append = function (element) {
        let node = new Node(element), p = head;
        if(!head) { //如果head不存在
          head = node;
        } else {
          while(p.next) {
            p = p.next;
          }
          p.next = node ;         
        }
        length += 1;
        console.log(head)
      }

2.查找节点:
解题思路: 按照单链表依次往下找,找到该元素的值与需要查找的元素相等。

      // 查找节点,可见查找速度的时间复杂度由元素在链表那个位置决定,最好是O(1),最坏是O(n)
      this.search = function (element) {
        let p = head;
        if(!p) return null
        while(p) {
          if(p.element === element) return true
          p = p.next
        }
        return false
      }

3.插入节点:
解题思路:和添加节点类似,只不过,需要知道插入的位置,然后将节点插入到该位置上的元素的next,再将后面的元素添加到插入的元素上的next。
在这里插入图片描述

      //最好是在O(1),最坏是O(n)。
      this.insert = function (position, element) {
        let node = new Node(element)
        if(position >= 0 && position<= length) {
          if(position === 0) {
            node.next = head;
            head = node
          } else {
            let index = 0;
            let prev = head, cur =  head;
            while(index < position) {
              prev = cur;
              cur = cur.next
              index++
            }
            prev.next = node;
            node.next = cur
          }
          length += 1
          return
        } else {
          return null
        }
      }

4.删除节点:
解题思路:查找到需要删除的节点之后,将节点的next赋值给该节点上一个节点的next。
在这里插入图片描述

      // 查找到需要删除的节点,最好是删除头结点,时间复杂度O(1), 最坏是删除尾节点,O(n)
      this.remove = function (element) {
        let p = head, prev = head;
        if(!p) return
        while(p) {
          if(p.element === element){
            p = p.next
            prev.next = p;
            length -= 1;
          } else {
            prev = p;
            p = p.next
          }
        } 
      }

测试一下

    var node = new List()
    node.size() //0
    node.append(4) //
    node.append(5)
    node.append(7)
    node.insert(0,3)
    console.log(node.size()) // 4
    node.insert(3,6)

双链表

单链表只有一个next往后,那么双链表,则是有一个prev和一个next.
在这里插入图片描述

function doubleList(){
    let Node = function(element) {
      this.element = element;
      this.prev = null;
      this.next = null;
    }
    // 初始头节点为 null
    let head = null;
    // 新增尾结点
    let tail = null;
    // 链表长度
    let length = 0
    // 获取链表
    this.getList = function () { return head }
}

1.插入节点,包含了添加节点。
解题思路:和单链表类似,在某个链表里有的位置进行插入的时候,需要对prev和next做处理。
在这里插入图片描述

this.insert = function(position, element) {
      let node = new Node(element);
      if(position >=0 && position<= length) {
        let prev = head,
            curr = head,
            index = 0;
        if(position === 0) {
          if(!head) { //与单链表不同,首尾节点都要赋值
            head = node;
            tail = node;
          } else {
            //双向
            node.next = head;
            head.prev = node;
            //节点变化,node成为新的头结点
            head = node;
          }
        } else if(position === length) { //在尾结点插入,即添加节点的做法
          tail.next = node;
          node.prev = taile;
          //节点变化,node成为新的尾结点
          tail = node;
        } else {
          while(index < position) {
            prev = curr;
            curr = curr.next;
            index++;
          }
          //插入到prev后,curr前
          prev.next = node;
          node.next = curr;
          curr.prev = node;
          node.prev = prev;
        }
        length += 1;
        return true
      } else {
        return false
      }
    }

2.删除节点
解题思路:删除某个位置的节点,就是依次遍历,将删除位置的节点.next.prev纸箱该位置的前一个节点的next
在这里插入图片描述

    this.remove = function(position) {
      if(position >=0 && position< length && length > 0) {
        let prev = head,
            curr = head,
            index = 0;
        if(position === 0) {// 移除头节点
          if(length === 1) { // 仅有一个节点
            head = null
            tail = null
          } else {
            head = head.next;
            head.prev = null;
          }
        } else if(position === length-1) {// 移除尾结点,将尾结点前移
          curr = tail;
          tail = curr.prev;
          tail.next = null;
        } else {
          while(index < position) {
            prev = curr;
            curr = curr.next;
            index++;
          }
          console.log('当前prev', prev)
          console.log('当前curr', curr)
          // 移除curr
          prev.next = curr.next
          curr.next.prev = prev
        }
        length -= 1
        return curr.element
      } 
    }
  }

循环单链表与双链表

顾名思义:当形成一个环形链表时,即尾结点最后指向头结点,形成环。
在这里插入图片描述

function circleList(){
  let Node = function(element) {
    this.element = element;
    this.next = null;
  }
  let head = null;
  let length = 0;
  
  //和单链表类似,唯一不同的是:循环单链表的循环结束条件为 index++ < length。
  //同理删除也是
  this.search = function(element){ //与单链表不同的是需要一个结束条件,因为没有指向null
    let p = head , index = 0;
    if(!p) return null
    while(index++ < length) { //当index小于length,否则就查找完了
      if(p.element === element) return true
      p = p.next
      //index++;
    }
    return false
  }
}

1.插入节点
解题思路:与单链表插入类似,只是当从链表头(尾)插入的时候,需要改变尾结点的指向到头结点。

this.insert = function(position, element) {
    let node = new Node(element)
    if(position>=0 && position<=length) {
      let prev = head,
      curr = head,
      index = 0;
      if(position === 0) { //与单链表插入不同
        while(index < length) { //目的是找到最后一个节点
          prev = curr;
          curr = curr.next;
          index++;
        }
        prev.next = node; //prev为最后一个节点,最后一个节点指向头结点,此时头结点为插入的node。
        node.next = curr; //curr为之前的头结点
        head = node; //最后把head变为头结点
      } else {
        while(index < position) {
          prev = curr;
          curr = curr.next;
          index++;
        }
        prev.next = node //prev为position位置处的前一个节点
        node.next = node.next = curr ? head: curr;
      }
      length++;
    } else {
      return null
    }
  };

2.查询单链表
解题思路:和单链表类似,唯一不同就是结束条件为从头遍历的节点数小于链表长度

  this.search = function(element){ //与单链表不同的是需要一个结束条件,因为没有指向null
    let p = head , index = 0;
    if(!p) return null
    while(index++ < length) { //当index小于length,否则就查找完了
      if(p.element === element) return true
      p = p.next
      //index++;
    }
    return false
  }

循环双向链表就不说了,本质也是双向链表,也是尾结点的next指向了头结点,头结点的pre指向了尾结点。

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

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值