初学js中的链表

js中的链表操作

首先我们应该知道什么是链表

  • 在我们基本使用中,储存多个元素,数组可能是最常用的数据结构。这种数据结构非常方便,但是有一个缺点:从数组的起点或者中间插入或移除的成本非常高,因为需要移动元素

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

  • 相比于数组来说,链表的好处在于添加或者删除元素的时候不需要移动其他元素。但是操作链表需要指针。数组的一个优点是可以直接访问任何位置的任何元素,但是要是想访问链表中的某一元素,则是必须从起点开始迭代知道找到目标元素

首先先来看一看单向链表

//这是创建链表的方法
function LinkedList(){
  // 初始的头部节点
    this.head=null;
    // 初始长度
    this.length=0;
    //创建节点的方法,或者是链表的一个项
    var Node=function(element){
        this.element=element;//表示添加到链表的值或者删除链表的值
        this.next=null;//表示指向下一个节点项的指针
    }
       ........
       //这里可以定义链表的操作方法等
}

在这里要强调一下,每一个Node被创建时,它的next指针总是NULL

在这里我们可以使用es6中的class语法糖来实现

就一个小例子

class SignleLinkLit{
  constructor(){
    // 初始的头部节点
    this.head = null
    // 初始长度
    this.length = 0;
  }
  class Node{
      constructor(element){
       // 信息域
        this.element = element;
       // 指针域
       this.next = null;
       }
   }
}

接下来我们可以看看它的一些操作方法

1.向单向链表的尾部追加元素

  • 首先判断链表是否为空,如是,添加的是第一个元素。
  • 列表不为空,向其后追加元素。
  • 要循环访问列表中的所有元素,就需要有一个起点,就是head
this.append=function(element){
    var node=new Node(element),//传入值创建Node项
        current;
    if(this.head===null){//如果为空链表
        this.head=node;//设置node为head(head为第一个节点的引用)
    }else{
        current=this.head;//从表头开始
        while(current.next){
            //循环列表,找到最后一项(列表最后一个节点的下一个元素始终是null)
            current=current.next;
        }
        //使当前最后一项的指针指向node
        current.next=node;
    }
    this.length++;//更新链表的长度
}

2.从链表中删除某一个元素
输入位置,从特定位置移除一个元素

this.deleteOne=function(positon){
    if(positon>-1&&position<length){//有效性检测
      var current=this.head,//用current来循环列表
          previous,
          index=0;
      if(position===0){
          this.head=current.next;
          //移除第一个元素,直接把head指向下一个元素
      }else{
          while(index++ < position){
              //循环列表找到满足条件的那个元素
              previous=current;
              current=current.next;
              //把下一个变量赋值给current
          }
          //跳过current,将当前要移除的元素的上一个与下一项直接连接起来
          previous.next=current.next;
      }
        length--;
        return current.element;
    }else{
        return null;
    }
}

3.从链表添加元素
在任意位置插入一个元素

this.insert=function(position,element){

    if(position>=0&&positon<=length){
    
        var node=new Node(element),
            current=this.head;//通过current从head位置开始迭代
            previous,
            index=0;
        
        if(position===0){//第一位置
        
            node.next=current;//此时current=this.head,指向head那么node就成了第一个
            this.head=node;//node指向head
            
        }else{
        
            while(index++ <position){//循环迭代到目标位置
                previous=current;
                current=current.next; 
            }
            node.next=current;//node的下一个为current
            previous.next=node;//node的上一个位置为previous
            
        }
        length++;
        return true;
        
    }else{
    
        return false;
        
    }
}

4.返回元素的位置

this.indexOf=function(element){
    var current=this.head,
        index=0;
    while(current){
        if(element===current.element){
            return index;//找到返回当前位置
        }
        index ++;
        current=current.next;
    }
    return -1;//找不到返回-1
}

5.将链表转换为字符串

this.toDstring=function(){
    var current=this.head,
         string='';
    while(current){//循环访问列表
        string+=currant.element+(current.next?'\n':'');
        current=current.next;
    }
    return string;//返回字符串
}

那么它的完整代码就是

 //这是创建链表的方法
  function LinkedList() {
    // 初始的头部节点
    this.head = null;
    // 初始长度
    this.length = 0;
    //创建节点的方法,或者是链表的一个项
    var Node = function (element) {
      this.element = element;//表示添加到链表的值或者删除链表的值
      this.next = null;//表示指向下一个节点项的指针
    }
    //追加节点
    this.append = function (element) {
      var node = new Node(element),//传入值创建Node项
        current;
      if (this.head === null) {//如果为空链表
        this.head = node;//设置node为head(head为第一个节点的引用)
      } else {
        current = this.head;//从表头开始
        while (current.next) {
          //循环列表,找到最后一项(列表最后一个节点的下一个元素始终是null)
          current = current.next;
        }
        //使当前最后一项的指针指向node
        current.next = node;
      }
      this.length++;//更新链表的长度
    }
    //移除节点
    this.deleteOne = function (positon) {
      if (positon > -1 && position < length) {//有效性检测
        var current = this.head,//用current来循环列表
          previous,
          index = 0;
        if (position === 0) {
          this.head = current.next;
          //移除第一个元素,直接把head指向下一个元素
        } else {
          while (index++ < position) {
            //循环列表找到满足条件的那个元素
            previous = current;
            current = current.next;
            //把下一个变量赋值给current
          }
          //跳过current,将当前要移除的元素的上一个与下一项直接连接起来
          previous.next = current.next;
        }
        length--;
        return current.element;
      } else {
        return null;
      }
    }
    //插入节点
    this.insert = function (position, element) {
      if (position >= 0 && positon <= length) {
        var node = new Node(element),
          current = this.head;//通过current从head位置开始迭代
        previous,
        index = 0;

        if (position === 0) {//第一位置
          node.next = current;//此时current=this.head,指向head那么node就成了第一个
          this.head = node;//node指向head
        } else {
          while (index++ < position) {//循环迭代到目标位置
            previous = current;
            current = current.next;
          }
          node.next = current;//node的下一个为current
          previous.next = node;//node的上一个位置为previous
        }
        length++;
        return true;
      } else {
        return false;
      }
    }
    //返回元素的位置
    this.indexOf = function (element) {
      var current = this.head,
        index = 0;
      while (current) {
        if (element === current.element) {
          return index;//找到返回当前位置
        }
        index++;
        current = current.next;
      }
      return -1;//找不到返回-1
    }
    //转换为字符串
    this.toDstring = function () {
      var current = this.head,
        string = '';
      while (current) {//循环访问列表
        string += currant.element + (current.next ? '\n' : '');
        current = current.next;
      }
      return string;//返回字符串
    }
  }

其中涉及到循环链表
单向循环链表和链表唯一区别在于:最后一个元素指向下一个元素的指针不是引用null而是指向第一个元素

下面试试双向链表

下面我是用的是es6的class语法糖来写

class DoublyLinkedList{
  class Node {
       constructor(element) {
        this.element = element;
        // 前驱指针
        this.prev = null;
        // 后继指针
        this.next = null;
        }
    }  
    
  constructor(){
    // 初始头部节点
    this.head = null;
    // 初始尾部节点
    this.tail = null;
    // 链表的长度
    this.length = 0;
  }

  // 操作
  //获取长度
  size(){
    return this.length;
  }
  // 获取链表
  getList(isInverted = false){
    return isInverted ? this.tail : this.head;
  }
  // 清空链表
  clear(){
    this.head = this.tail = null;
    this.length = 0;
  }
  // 链表是否为空
  isEmpty(){
    return this.length === 0;
  }
  // 插入节点
  insert(position, element);
  // 删除链表节点
  deleteNode(position);
  // 寻找链表节点
  search(element);
}

1.插入节点的方法
在这之前,我们先来看看一张图,看他如何实现插入的
双向链表的插入

insert(position, element){
    if(position < 0 || position > this.length) return null;

         const node = new Node(element);

    if(!this.head){
         this.head = this.tail = node;
     }else if(position === 0){ // 插入节点是0的话,就需要调整head指向
         node.next = this.head;
         this.head.prev = node;
         // head指向新的头节点
         this.head = node;
     }else if(position === this.length){ // 是尾部
         this.tail.next = node;
         node.prev = this.tail;
         // tail重定向
        this.tail = node;
     }else {//任意位置
        let temp = this.head,
        index = 0;

        while(index < position){
            temp = temp.next;
            index++;
         }
        temp.prev.next = node;
        node.prev = temp.prev;
        temp.prev = node;
        node.next = temp;
    }
    this.length++;
}

2.删除节点的方法
照旧 我们也来看一张图,理解理解
双向链表删除节点

deleteNode(position){
    if(!this.length || position < 0 || position > this.length - 1) return null;

    let temp = this.head, index = 0;

    if(this.length === 1){ // 如果仅有一个节点
        this.clear();
    }else if(position === 0){
        this.head.next.prev = null;
        this.head = this.head.next;
    }else if(position === this.length - 1){
        this.tail.prev.next = null;
        this.tail = this.tail.prev;
    }else{
        while(index < position){
              temp = temp.next;
              index ++;
    }
    temp.prev.next = temp.next;
    temp.next.prev = temp.prev;
  }
  this.length--;
  return temp.element;
}

3.搜索节点

search(element){
  let temp = this.head;
  while(temp){
    if(temp.element === element) return true;
    temp = temp.next;
  }
  return false;
}

如何实现两个链表的合并

将两个升序链表合并为一个新的升序链表并返回。新链表是通过拼接给定的两个链表的所有节点组成的。
示例:
输入:1->2->4, 1->3->4
输出:1->1->2->3->4->4

  • 5
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值