参考文章:
前言
单向链表的结构如:A({ val: 1, next: B }) -> B({ val: 2, next: C }) -> … -> N({ val: n, next: null })
首先提供快速生成链表的代码:
function createList(len) {var count = 0;var head = { val: count };var node = head;while (count < len) {count++;node.next = { val: count };node = node.next;}// 鉴于JavaScript变量类型的特殊性,最后一个节点的next设置为"none"便于判断if (!node.next) {node.next = 'none';}return head;}
测试验证:
console.log(createList(3));// 打印结果:// {"val":0,"next":{"val":1,"next":{"val":2,"next":{"val":3,"next":"none"}}}}
删除单向链表节点
代码如下:
function deleteNode(head, val) {// 特殊情况处理,删除的节点是头结点时,比较特别if (head.val == val) return head.next;// 设置两个指针,一个指针指向当前的节点var pre = head;// 一个指针指向当前节点的下一节点var cur = head.next;// 当 cur 为空 或 cur 节点值等于 val 时跳出跳出循环while (cur === 'none' && cur.val != val) {// 两个指针不断的向前移动// pre 来到 cur 的位置pre = cur;// cur 来到下一个节点位置cur = cur.next;}// 相当于覆盖掉了 cur 的节点值pre.next = cur.next;// 最后返回链表头结点就行return head;}
测试验证:
var head = createList(4);console.log('before', JSON.stringify(head));head = deleteNode(head, 1);console.log('after', JSON.stringify(head));// 打印结果:// before {"val":0,"next":{"val":1,"next":{"val":2,"next":{"val":3,"next":{"val":4,"next":"none"}}}}}// after {"val":0,"next":{"val":2,"next":{"val":3,"next":{"val":4,"next":"none"}}}}
全局反转链表
代码如下:
function reverseList(head) {// 寻找递归终止条件// 1、head 指向的结点为 none// 2、head 指向的结点的下一个结点为 none// 在这两种情况下,反转之后的结果还是它自己本身if( head == 'none' || head.next == 'none') return head;// 不断的通过递归调用,直到无法递归下去,递归的最小粒度是在最后一个节点// 因为到最后一个节点的时候,由于当前节点 head 的 next 节点是空,所以会直接返回 headvar cur = reverseList(head.next);// 比如原链表为 1 --> 2 --> 3 --> 4 --> 5// 第一次执行下面代码的时候,head 为 4,那么 head.next = 5// 那么 head.next.next 就是 5.next ,意思就是去设置 5 的下一个节点// 等号右侧为 head,意思就是设置 5 的下一个节点是 4// 这里出现了两个 next// 第一个 next 是「获取」 head 的下一节点// 第二个 next 是「设置」 当前节点的下一节点为等号右侧的值head.next.next = head;// head 原来的下一节点指向自己,所以 head 自己本身就不能再指向原来的下一节点了// 否则会发生无限循环head.next = 'none';// 我们把每次反转后的结果传递给上一层return cur;}
测试验证:
var head = createList(4);console.log('before', JSON.stringify(head));head = reverseList(head);console.log('after', JSON.stringify(head));// 打印结果:// before {"val":0,"next":{"val":1,"next":{"val":2,"next":{"val":3,"next":{"val":4,"next":"none"}}}}}// after {"val":4,"next":{"val":3,"next":{"val":2,"next":{"val":1,"next":{"val":0,"next":"none"}}}}}
局部反转链表
代码如下:
function reverseBetween(head, left, right) {// 一开始设置一个虚拟节点,它的值为 -1,它的值可以设置为任何的数,因为我们根本不需要使用它的值// 设置虚拟节点的目的是为了让原链表中所有节点就都可以按照统一的方式进行翻转// 比如如果翻转的区间包含了原链表中的第一个位置,那么如果不设置dummy// 在翻转的过程中需要设置其它的临时变量来保持第一位置节点的指针var dummy = { val: -1 };// 让虚拟节点指向原链表的头部dummy.next = head;// 设置一个指针,指向以虚拟头节点为链表的头部位置var pre = dummy;// 设置一个指针,指向原链表的头部位置var cur = head;// 从虚拟头节点出发,pre 走 left 步找到需要翻转的左区间// for 循环结束后,pre 的右节点是需要翻转的节点// for 循环结束后,cur 指向的就是需要翻转的节点for (var i = 0; i < left; i++) {// pre 不断的向右移动,直到走到翻转的左区间为止pre = pre.next;// cur 不断的向右移动,找到了需要翻转的第一个节点cur = cur.next;}// 开始翻转这些节点// 初始: 0 --> 1 --> 2 --> 3 --> 4,cur.next = 2// 第一次循环,结果: 0 --> 2 --> 1 --> 3 --> 4,cur.next = 3// 第二次循环,结果: 0 --> 2 --> 3 --> 1 --> 4,cur.next = 4for (var i = 0; i < right - left; i++) {// 设置临时变量,保存当前需要翻转节点的后面的节点var temp = cur.next;cur.next = cur.next.next;temp.next = pre.next;pre.next = temp;}// 最后返回虚拟头节点的下一个节点,因为虚拟节点不在链表中return dummy.next;}
测试验证:
var head = createList(5);console.log('before', JSON.stringify(head));head = reverseBetween(head, 1, 3);console.log('after', JSON.stringify(head));// 打印结果:// before {"val":0,"next":{"val":1,"next":{"val":2,"next":{"val":3,"next":{"val":4,"next":{"val":5,"next":"none"}}}}}}// after {"val":0,"next":{"val":3,"next":{"val":2,"next":{"val":1,"next":{"val":4,"next":{"val":5,"next":"none"}}}}}}
