单链表-js

本文详细介绍了单链表的特性,包括元素的逻辑和物理位置关系、存储结构以及链表的操作,如插入、删除、获取节点和遍历。通过JavaScript实现了头插法、尾插法、指定位置插入、删除指定位置和数据域节点的功能,并提供了测试用例展示其正确性。
摘要由CSDN通过智能技术生成

单链表

特点

  • 元素在逻辑关系上相邻,在物理位置上不相邻,不像线性表的元素在逻辑和物理上均相邻,链表在执行插入和删除操作时不需要移动大量元素,但也失去了顺序表可随机存取的优点
  • 链表的存储单元不连续,因此为了表示每个元素与其直接后继元素的关系,还需存储一个指示其直接后继的信息
  • 单链表的结点包括两个域:数据域和指针域。数据域存储元素信息,指针域存储其直接后继的存储位置
  • 单链表的存取只能从头指针开始
  • 链表中数据的逻辑关系由结点中的指针指示,指针是数据元素之间逻辑关系的映像
  • 在归并两个链表为一个链表时,不需要建立额外的新表的空间结点,只需要将原来两个链表中结点之间的关系解除,按归并规则将所有结点链接成一个链表即可。

功能需求

  • 插入节点:头插法、尾插法、给定插入的位置
  • 删除节点:删除指定位置的节点、删除指定数据域的节点
  • 获得节点:获得第一个节点、获得最后一个节点、获得指定位置的节点、获得指定数据域的节点
  • 获得链表的节点数
  • 链表的遍历

设计实现

使用es5

  • 用工厂模式创建节点对象

    //采用工厂模式创建节点对象
    function createNode(data){
        var node=new Object();
        node.data=data;//es的变量是松散类型的,可以保存任何类型的数据
        node.nextNode=null;
        return node;
    }
    
  • 创建链表,返回一个头节点head,以head作为链表的引用,head节点的数据域不保存数据,不计入链表的节点,以head.nextNode作为链表的第一个节点,在链表中的位置是0

    //创建链表
    function createLink(){
        var head=createNode(null);
        return head;
    }
    
  • 插入节点

    • 头插法

      //插入节点(头插法)
      function insertNodeTop(head,data){
          //头插法实际是在特定位置插入节点,可以通过调用insertNodeIndex()函数实现,实现代码的复用
          //insertNodeIndex(head,data,0);
          var node=createNode(data);
          node.nextNode=head.nextNode;
          head.nextNode=node;
          //return head;
          //js参数是按值传递的,对象是引用类型按引用访问变量,函数内部的head与外部的head按引用访问的是同一个对象,因此这里可以不用设置 return 
      }
      
    • 尾插法

      //插入节点(尾插法)
      function insertNodeLast(head,data){
          //尾插法同头插法是在指定位置插入节点,可以通过调用insertNodeIndex()函数实现,区别在于要获得链表的长度
          //insertNodeIndex(head,data,getLinkLength(head));
      
          var p=head;
          while(p.nextNode!==null){//用全等判定,因为 undefined==null 为 true
              p=p.nextNode;
          }
          p.nextNode=createNode(data);
      }
      
    • 插入指定的位置

      //插入指定位置,头节点head不计入节点数,节点位置从0开始
      function insertNodeIndex(head,data,index){
          //index允许传入字符串或数字,如 "1" 或 1
          if((typeof index)=="string"||(typeof index)=="number"){
              if((typeof index)=="string"){
                  index=Number(index);//String转Number,后面涉及到与index有关的“+”,避免出错
              }
              var length=getLinkLength(head);
              //index=0表示在表头插入,index=length表示插入最后一个节点的后面,当index>=length时表示在末端插入
              //当index<0时,在length+index处插入,如果length+index<0 则报错,前面已经进行过index的string转number,不然要注意“+”的自动类型转换
              if(index<0){
                  index=length+index;
                  if(index<0){
                      //length+index仍然小于 0 ,报错
                  }
              }
              //q是p的前置节点,设置q的原因是当退出循环p===null时,只设置一个p会失去对链表的链接
              var q=head;
              var p=q.nextNode;
              var pos=0;//记录p所指节点的位置,从 0 开始,第一个节点在链表中的位置是 0
              while(p!==null&&pos<index){
                  q=q.nextNode;
                  p=p.nextNode;
                  pos++;
              }
              var node=createNode(data);
              q.nextNode=node;
              node.nextNode=p;
          }
          else{
              //index既不是字符串也不是数字,报错
          }
      }
      
  • 删除节点

    • 删除指定位置的节点

      //删除指定位置的节点
      function deleteNodeIndex(head,index){
          if(index>=getLinkLength(head)||index<0){//可能涉及到自动类型转换,index可以为字符串
              //删除的指定位置的节点不存在,报错
          }
          else{
              var q=head;
              var p=q.nextNode;
              var pos=0;
              while(p!==null&&pos<index){
                  q=q.nextNode;
                  p=p.nextNode;
                  pos++;
              }
              q.nextNode=p.nextNode;
              p=null;//解除对对象节点的引用,垃圾自动回收
          }
      }
      
    • 删除指定数据域的节点

      //删除指定数据的节点
      function deleteNodeData(head,data){
          var q=head;
          var p=q.nextNode;
          while(p!==null){
              if(p.data===data){//data可能是基本数据类型也可能是引用类型
      
                  //删除找到的第一个符合与给定数据相等的节点
                  q.nextNode=p.nextNode;
                  p=null;
                  return;
      
                  //删除链表中所有的与给定数据相等的节点
                  //q.nextNode=p.nextNode;
                  //p=q.nextNode;
              }
              else{
                  q=q.nextNode;
                  p=p.nextNode;
              }
          }
      }
      
  • 获得节点

    • 获得链表中的第一个节点

      //获得链表的第一个节点
      function getNodeTop(head){
          return head.nextNode;//若链表之中还没有节点则返回null
      }
      
    • 获得链表的最后一个节点

      //获得链表最末尾的节点
      function getNodeLast(head){
          var p=head;
          while(p.nextNode!==null){
              p=p.nextNode;
          }
          //若链表之中还没有节点则返回null
          if(p===head){
              return null;
          }
          else{
              return p;
          }
      }
      
    • 获得指定位置的节点

      //获得链表指定位置的节点
      function getNodeIndex(head,index){//返回找到的节点的引用,没找到则返回null
          if(index>=getLinkLength(head)||index<0){//可能涉及到自动类型转换,index可以为字符串
              //要获得的指定位置的节点不存在,报错
          }
          else{
              var p=head;
              var pos=-1;
              while(p.nextNode!==null&&pos<index){
                  p=p.nextNode;
                  pos++;
              }
              if(p===head){
                  return null;//若链表之中还没有节点则返回null
              }
              return p;
          }
      }
      
    • 获得指定数据域的节点

      //获得指定数据域的节点
      function getNodeData(head,data){//返回找到的节点的引用,没找到则返回null
          var p=head.nextNode;
          while(p!==null){
              if(p.data===data){//data可能是基本数据类型也可能是引用类型
                  //返回找到的第一个符合与给定数据相等的节点的引用
                  return p;
              }
              else{
                  p=p.nextNode;
              }
          }
          return null;
      }
      
  • 获得链表的节点数

    //获得链表的节点数
    function getLinkLength(head){
        var length=0;
        var p=head.nextNode;
        while(p!==null){
            length++;
            p=p.nextNode;
        }
        return length;
    }
    
  • 链表的遍历

    //链表遍历
    function  traverse(head){
        var p=head.nextNode;
        while(p!==null){
            console.log(p.data);
            p=p.nextNode;
        }
    }
    

测试

//测试
function test(){
    var strs=[1,2,3,4,5,6,7,8,9];
    var head=createLink();
    strs.forEach(function(value,index,array){
        insertNodeTop(head,value);//头插法
    })
    strs.forEach(function(value,index,array){
        insertNodeLast(head,value);//尾插法
    })
    traverse(head);
    console.log("--------");

    insertNodeIndex(head,-1,"0");//在头部插入,index为字符串
    traverse(head);
    console.log("----------");
    insertNodeIndex(head,-2,getLinkLength(head));//在尾部插入
    traverse(head);
    console.log("----------");
    insertNodeIndex(head,-3,50);//插入位置超出链表的长度,在尾部插入
    traverse(head);
    console.log("-----------");
    insertNodeIndex(head,-4,4);//在中部插入
    traverse(head);
    console.log("-----------");
    insertNodeIndex(head,-5,-1);//插入位置为负数
    traverse(head);
    console.log("-----------");

    deleteNodeIndex(head,0);//删除第一个节点
    traverse(head);
    console.log("-----------");
    deleteNodeIndex(head,getLinkLength(head)-1);//删除尾部节点
    traverse(head);
    console.log("-----------");
    deleteNodeIndex(head,1);//删除第二个节点
    traverse(head);
    console.log("-----------");

    console.log(getNodeTop(head).data);//获得第一个节点
    console.log(getNodeIndex(head,0).data);
    console.log("-----------");
    console.log(getNodeLast(head).data);//获得最后一个节点
    console.log(getNodeIndex(head,getLinkLength(head)-1).data);
    console.log("-----------");
    console.log(getNodeIndex(head,5).data);//获得第二个节点
}

测试结果:符合预期

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值