每一年都奔走在自己热爱里

没有人是一座孤岛,总有谁爱着你

反转链表

反转链表

  • 反转链表总体有两种方法,迭代与递归,在迭代过程中需要注意细节,比如反转链表需要同时跟踪两个节点,递归需要注意临界条件与递归函数的意义。

  • 本题中,迭代方法实时跟踪pre与cur节点,并实时更新,比较巧妙的是pre节点初始值的设置

  • 而递归,需要理解递归函数的意义,也就是返回的是反转链表的头节点,以及本节点next的设置

  • 本题相较上面一题控制为链表区间反转,同样有两个思路

  • 迭代:

      • 将curr节点的下一个节点插入到pre节点之后

      • 需要考量的是边界,如果整个链表反转,在原链表之上是无法进行的,需要加入哨兵

      •         // 设置 dummyNode 是这一类问题的一般做法
                ListNode dummyNode = new ListNode(-1);
                dummyNode.next = head;
                ListNode pre = dummyNode;
                for (int i = 0; i < left - 1; i++) {
                    pre = pre.next; //跟踪到指定位置
                }
                ListNode cur = pre.next;
                ListNode next;
                for (int i = 0; i < right - left; i++) { //穿针引线
                    next = cur.next;
                    cur.next = next.next;
                    next.next = pre.next; 
                    pre.next = next;
                }
                return dummyNode.next;
        
    • 哨兵技巧

      • ListNode dummy = new ListNode(0);
        dummy.next = head;
        
        r -= l;
        ListNode hh = dummy;
        while (l-- > 1) hh = hh.next;
        
        ListNode a = hh.next, b = a.next;
        while (r-- > 0) {
            ListNode tmp = b.next;
            b.next = a;
            a = b;
            b = tmp;
        }
        
        hh.next.next = b;
        hh.next = a;
        return dummy.next;
        
    • 迭代方法的一点思考,迭代过程中需要至少跟踪两组信息方便反转,同时在本题中由于是区间反转,还需要记录反转开始,而反转的开始考虑到临界条件,可以通过设置哨兵解决。

  • 递归

    • 反转整个链表是非常轻松的,如果反转链表前面指定长度的一部分呢?

    • 需要记录长度信息:

    • ListNode successor = null; // 后驱节点
      
      // 反转以 head 为起点的 n 个节点,返回新的头结点
      ListNode reverseN(ListNode head, int n) {
          if (n == 1) { 
              // 记录第 n + 1 个节点
              successor = head.next;
              return head;
          }
          // 以 head.next 为起点,需要反转前 n - 1 个节点
          ListNode last = reverseN(head.next, n - 1);
      
          head.next.next = head;
          // 让反转之后的 head 节点和后面的节点连起来
          head.next = successor;
          return last;
      }    
      
    • 现在我们已经知道了反转前N个节点,我们可以找到需要反转的部分链表的头信息,反转指定长度即可

    • ListNode reverseBetween(ListNode head, int m, int n) {
          // base case
          if (m == 1) {
              return reverseN(head, n);
          }
          // 前进到反转的起点触发 base case
          head.next = reverseBetween(head.next, m - 1, n - 1);
          return head;
      }
      
posted @ 2022-07-24 10:19  雨下整夜~  阅读(162)  评论(0)    收藏  举报