javascript实现单向链表的一些常见操作

本篇博文实现的具体操作内容如下:
1. 遍历打印
2. 获取链表的长度
3. 通过下标获取链表中的某个数据
4. 通过下标设置链表中的某个数据
5. 在链表某一个节点之后加入一个新节点
6. 在链表末尾加入一个新节点
7. 删除一个链表节点
8. 链表倒序
0 链表的创建
//链表的数据结构
function Node(value,next){
  this.value = value;
  this.next = next;
}

//构造链表
/**
 * 
 * @param {Array} arr 链表的每个结点的value值组成的数组
 */
function createNodes(arr){
  let temp,root;
  if(arr !== null){
    root = new Node(arr[0],null);
    temp = root;
  }
  if(arr.length > 1){
    for(let i = 1; i < arr.length; i++){
      const node = new Node(arr[i],null);
      temp.next = node;
      temp = node;
    }
  }
  return root;
}
//const root = createNodes([1,2,3,4,5,6,7]);

在这里插入图片描述

1 遍历打印
//遍历打印(穷举法)
/**
 * 
 * @param {Node} root 要打印的链表的头结点 
 */
function print1(root){
  let temp = root;
  while(temp !== null){
    console.log(temp.value);
    temp = temp.next;
  }
}



//遍历打印(递归)
//递归 = 规律 + 出口
/**
 * 
 * @param {Node} root 要打印的链表的头结点 
 */
function print2(root){
  if(root!= null){   
    console.log(root.value);//只要我当前的结点不为空,我就先把自己打印出来
    print2(root.next);   //再让自己的下一个结点去判断自己是否为空,不是就打印出来....依次规律递归
  }else{
    return ;  //出口,说明链表已经遍历完了,结束递归
  }
}



2 获取链表的长度
//获取链表长度(穷取法)
/**
 * @param {Node} root 链表头结点 
 * @return 返回链表的长度
 */
function getLength1(root){
  let count = 0;
  let temp = root;
  while(temp !== null){
    count++;
    temp = temp.next;//指针后移
  }
  return count;
}



//获取链表长度(递归)
/**
 * @param {Node} root 链表头结点 
 * @return 返回链表的长度
 */
function getLength2(root){
  if(root === null){
      return 0;   //递归出口
  }else{
    return 1 + getLength2(root.next);   //如果当前结点不为空,那么就计算当前结点和其之后的结点的个数之和
  }
}

3 通过下标获取链表中的某个数据
//通过下标获取链表中的某个数据(穷区法)
/**
 * @param {Node} root 
 * @param {Number} index
 * @return 如果找得到,就返回该节点,否则返回0 
 */
function findByIndex1(root,index){
  let count = 0;
  let temp = root;
  while(temp !== null){
    if(count === index){
      return temp;
    }
    count++;
    temp = temp.next;
  }
  return 0;
}

//通过下标获取链表中的某个数据(递归)
/**
 * @param {Node} root 
 * @param {Number} index
 * @return 如果找得到,就返回该节点,否则返回0 
 */
function findByIndex2(root,index){
  function _findByIndex2(node,cur){
    if(cur === index){   //判断当前下标是否是需要寻找的下标
      return node;
    }
    if(!node){
      return 0;  //已经超出链表范围
    }
    return _findByIndex2(node.next,cur+1);
  }
  return _findByIndex2(root,0);
}

4 通过下标设置链表中的某个数据
//通过下标设置链表中的某个数据
//思路:找到下标对应的结点,设置其数据
//穷取法
/**
 * 
 * @param {Node} root  
 * @param {Number} index 
 * @param {*} value 
 * @return 返回1表示修改成功,返回0表示修改失败
 */
function setByIndex1(root,index,value){
  let count = 0;
  let temp = root;
  while(temp !== null){
    if(count === index){
      temp.value = value;
      return 1;
    }
    count++;
    temp = temp.next;
  }
  return 0;
}


//通过递归
/**
 * @param {Node} root  
 * @param {Number} index 
 * @param {*} value 
 * @return 返回1表示修改成功,返回0表示修改失败
 */
function setByIndex2(root,index,value){
  function _setByIndex2(node,cur){
    if(node === null){
      return 0;
    }
    if(cur === index){
      node.value = value;
      return 1;
    }
    _setByIndex2(node.next,cur+1);
  }
  return _setByIndex2(root,0);
}


//其实可以直接调用上面的findByIndex1||findByIndex2方法
/**
 * @param {Node} root  
 * @param {Number} index 
 * @param {*} value 
 * @return 返回1表示修改成功,返回0表示修改失败
 */
function setByIndex3(root,index,value){
  const node = findByIndex1(root,index);//通过findByIndex1()或则findByIndex2()皆可
  if(node !== 0){
    node.value = value;
    return 1;
  }
  return 0;
}


5 在链表某一个节点之后加入一个新节点
//在链表中的某一个节点插入一个新节点(穷取法)
/**
 * @param {Node} root  头结点
 * @param {Number} index  插入的下标位置,从0开始
 * @param {*} value 
 * @return 返回1表示插入成功,返回0表示插入失败
 */
function addByIndex1(root,index,value){
  //需要插入一个结点,就需要找到要插入结点的前一个结点,改变指针的指向
  let temp = root;
  let count = 0;
  if(index <= 0 || index >= getLength1(root)){  //该算法不会改变头结点和尾结点
    //这个输入的判断可加可不加,因为在循环里都是找不到的,但加上会好些,提高代码效率
    return 0;
  }
  while(temp !== null){
    if(count === index - 1){  //找到要插入的位置的前一个元素
      const node = new Node(value,null);
      node.next = temp.next;
      temp.next = node;
      return 1;
    }
    count++;
  }
  return 0;
}



//在链表中的某一个节点插入一个新节点(递归)
/**
 * @param {Node} root  头结点
 * @param {Number} index  插入的下标位置,从0开始
 * @param {*} value 
 * @return 返回1表示插入成功,返回0表示插入失败
 */
function addByIndex2(root,index,value){
  function _addByIndex2(node,cur){
    if(node === null){
      return 0;
    }
    if(cur === index - 1){  //找到了要插入的位置的前一个元素
        const newNode = new Node(value,null);
        newNode.next = node.next;
        node.next = newNode;
        return 1;
    }
    _addByIndex2(node.next,cur+1);
  }
  if(index <= 0 || index >= getLength1(root)){  //该算法不会改变头结点和尾结点
    //这个输入的判断可加可不加,因为在循环里都是找不到的,但加上会好些,提高代码效率
    return 0;
  }else{
    return _addByIndex2(root,0);
  }
}

6 在链表末尾加入一个新节点
//在链表末尾加入一个新节点
/**
 * @param {Node} root 头结点 
 * @param {*} value 新结点的value值
 */
//通过循环找到最后一个结点
function insertLast1(root,value){
  let temp = root;
  const node = new Node(value,null);
  while(temp.next !== null){   //让指针指向最后一个结点
    temp = temp.next;
  }
  temp.next = node;
}


//在链表末尾加入一个新节点
/**
 * @param {Node} root 头结点 
 * @param {*} value 新结点的value值
 */
//通过递归找到最后一个结点
function insertLast2(root,value){
  const newNode = new Node(value,null);
  function _insertLast2(node){
    if(node.next === null){  //找到了最后一个结点(递归的出口)
        node.next = newNode;
        return ;
    }
    _insertLast2(node.next);
  }
  _insertLast2(root);
}

7 删除一个链表节点
//通过下标删除一个结点
//重点:需要拿到要删除结点的前面一个结点
function deleteByIndex1(root,index){
  let temp = root;
  let count = 0;
  if(index <= 0){  //不可以删除头结点
    return 0;
  }
  while(temp !== null){
    if(count === index - 1){   //找到了要删除的结点的前一个结点
        temp.next = temp.next.next;
        return 1;
    }
  }
  return 0;
}

//通过递归
function deleteByIndex2(root,index){
  if(index <= 0){
    return 0;
  }
  function _deleteByIndex2(node,cur){
    if(node === null){   //链表找完了(递归出口)
      return 0;
    }
    if(cur === index-1){  //找到了要删除的结点的前一个结点
      node.next = node.next.next;
      return 1;
    }
    _deleteByIndex2(node.next,cur+1);
  }
 return _deleteByIndex2(root,0);
}

8 链表倒序
//链表倒序,利用递归
function reverseNodes(root){
  let newRoot;
  if(!root || !root.next){  //传入的头结点为空或者该链表只有一个结点时,就无需倒序
    return root;
  }
  if(!root.next.next){   //只有两个结点的情况
    newRoot = root.next;  //先将第二个结点设置为新的头结点
    root.next.next = root;  //让第二个结点指向第一个节点
    root.next = null;  //再让第一个节点指向null
    return newRoot;
  }
  else{  //大于两个结点
    newRoot = reverseNodes(root.next);  //让自己的下一个结点自己去倒序
    root.next.next = root;  //再处理自己
    root.next = null;
    return newRoot;
  }
}
总结

以上就是对单向链表的一些常见操作的实现,基本上都通过了穷取法和分治法两种方式来实现。事实上,在设计有关链表的算法时,通过画数据结构图的方式可以更加直观的来反映出需要进行的操作,能够使在编程时思路更加清晰。以上代码如有考虑不当的地方,欢迎指出。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值