链表翻转——JS实现链表中部分连续节点的翻转

251 阅读2分钟
  • LeetCode题目: reverse-nodes-in-k-group

  • 题目描述: 给定一个链表head,以及一个数值k,开始大于0且小于等于head长度一个整数,将k范围内的链表翻转,最终返回修改后的链表!eg1:

list: 1->2->3->4->5;
当k = 2 , output: 2->1->4->3->5
当k = 3 , output: 3->2->1->4->5
  • 题目分析: 单链表的特点是当前节点保存了当前节点的值和指向后续节点的值,链表的长度需要遍历得到,因此有两种思路,1.遍历链表获得链表长度,根据k值做分段,在段内改变节点的指向;2.不关心链表的长度,通过数组记录不同的节点,当数组长度 === k之后进行交换,超过nk的节点则顺序添加到新节点。

  • 解法一: 通过遍历链表长度,然后从头截取k,2k,3k,对获取的链接做翻转操作。代码如下:

    链表遍历函数:

    function findLinkListLength(list) {
         let count = 0;    
         let tmp = list;
         while(tmp) {
             tmp = tmp.next
             count++
         } 
         return count;
     }
    

    链表节点交换函数:

    function reverse(node, k) {
         let previous = null;
         let current = node;
         let following;
         let tail = node;        
         for(let i = 0; i < k; i++) {
             following = current.next; //保存当前节点指向信息
             current.next = previous; //交换节点指向
             previous = current;
             current = following;
         }        
         return [previous, tail, following];    
     }
    

    实现链表的翻转:

     const reverseKGroup = function(head, k) {
         let linkListLength = findLinkListLength(head)
         let remainingReverses = Math.floor(linkListLength / k);
         let newHead;
         let previousTail;
         while(remainingReverses > 0)  {
             let [front, tail, following] = reverse(head, k);
             
             if (remainingReverses === Math.floor(linkListLength / k)) {
                 newHead = front;
                 previousTail = tail;
             } else {
                 previousTail.next = front; 
                 previousTail = tail;
             } 
         tail.next = following 
         
         head = following 
             
         remainingReverses--
         }
         return newHead 
     };  
    
  • 解法二: 不关心链表的长短,对链表进行遍历,将节点值保存到数组中并生成节点,当数组长度大于等于k,将数组生成链表;

    链表节点生成函数:

    const ListNode = (val, next)=>{
        return {
            val: val,
            next: next === undefined ? null : next
        }
    }
    

    完整解法:

    const reverseKGroup = function(head, k) {
        const list = [];
        const pointer = [];
        while (head) {
            const node = ListNode(head.val, null); //生成新节点
            pointer.unshift(node);
            if (pointer.length >= k) { 
                while (pointer.length > 0) {
                    list.push(pointer.shift());
                }
            }
            head = head.next;
        }
        while (pointer.length > 0) {
            list.push(pointer.pop());
        }
        for (let i = 0; i < list.length; i++) { //生成链表
            if (i !== list.length - 1) {
                list[i].next = list[i + 1]
            } else {
                list[i].next = null;
            }
        }
        return list[0]
    };
    
  • 两种解法分析: 第一种解法使用了更多的内存空间,但是通过遍历数组,截取nk范围内的链表节点,直接交换节点指向,在执行时间上更优。整个交换的写法也更符合链表的处理方法;第二种解法则更容易理解,更多的采用了数组的处理方法,使用的内存更少,但是相对的时间复杂度更高。综合来说第一种解法是更符合链表特性,在整体执行效率上更优秀。