0
点赞
收藏
分享

微信扫一扫

LeetCode链表逆序:翻转策略面试最简代码综合运用


目录

LeetCode025K 个一组翻转链表

解题思路

解法一

解法二

代码实现

解法一

解法二

LeetCode逆序相关题目 

链表反转

排序奇升偶降链表 

LeetCode025K 个一组翻转链表

给你一个链表,每 k 个节点一组进行翻转,请你返回翻转后的链表。

k 是一个正整数,它的值小于或等于链表的长度。

如果节点总数不是 k 的整数倍,那么请将最后剩余的节点保持原有顺序。

示例:

给你这个链表:1->2->3->4->5

当 k = 2 时,应当返回: 2->1->4->3->5

当 k = 3 时,应当返回: 3->2->1->4->5

说明:

你的算法只能使用常数的额外空间。
你不能只是单纯的改变节点内部的值,而是需要实际进行节点交换

解题思路

解法一

1、 先反转以 head 开头的 k 个元素。

2、 将第 k + 1 个元素作为 head 递归调⽤ reverseKGroup 函数。

3、如果最后的元素不⾜ k 个, 就保持不变

LeetCode链表逆序:翻转策略面试最简代码综合运用_链表

解法二

看图写代码

1.为了代码的可读性,定义一个虚拟指针dummy作为head的前驱指针

2.初始需要两个变量 pre 和 endpre 代表待翻转链表的前驱,end 代表待翻转链表的末尾

3.start和next是while循环内的局部变量分别对应 pre 和 end的后继指针

LeetCode链表逆序:翻转策略面试最简代码综合运用_链表_02

代码实现

解法一

先要理解下单链表的逆序算法:漫画:如何将一个链表“逆序”? - 知乎,然后理解如下代码

package com.lzhsite.leetcode.algoritom.practise.linked;

import com.lzhsite.leetcode.algoritom.dataStruct.DLNode;

public class LeetCode025K个一组翻转链表 {

	/** 反转区间 [a, b) 的元素, 注意是左闭右开 */
	DLNode reverse(DLNode a, DLNode b) {
		ListNode p1, p2, p3;
		p1 = a;
		p2 = a.next;
		p3 = null;
		// while 终⽌的条件改⼀下就⾏了
		while (p2 != b) {
			p3 = p2.next;
			p2.next = p1;
			p1 = p2;
			p2 = p3;
		}
		// 返回反转后的头结点
		return p1;
	}

	DLNode reverseKGroup(DLNode head, int k) {
		if (head == null)
			return null;
		// 区间 [a, b) 包含 k 个待反转元素
		DLNode a, b;
		a = b = head;
		for (int i = 0; i < k; i++) {
			// 不⾜ k 个, 不需要反转, base case
			if (b == null)
				return head;
			b = b.next;
		}
		// 如何k个⼀组反转链表
		// 反转前 k 个元素
		DLNode newHead = reverse(a, b);
		// 递归反转后续链表并连接起来
		a.next = reverseKGroup(b, k);
		return newHead;
	}
}

解法二

public ListNode reverseKGroup(ListNode head, int k) {
    if (head == null || k == 1) {
        return head;
    }

    ListNode curr = head;
    int count = 0;
    while (curr != null && count != k) { // 找到第 k+1 个节点
        curr = curr.next;
        count++;
    }

    if (count == k) { // 如果有 k 个节点,则翻转
        ListNode pre = reverseKGroup(curr, k); // 递归连接后续翻转的链表,返回值pre是翻转后链表的头结点
        while (count-- > 0) { // 翻转当前 k 个节点
            ListNode tmp = head.next; // 临时节点,用于暂存下一个节点
            head.next = pre; // 把当前节点指向上一个节点
            pre = head; // 更新 curr,用于记录上一个节点
            head = tmp; // 把备份还给head相当于移动到下一个节点,后续继续执行逆序操作
        }
        head = pre; // 这一行在完成 k 个节点的反转后更head始终指向头结点
    }
    return head;
}

LeetCode逆序相关题目 

链表反转

思考: 比较下单链表逆序的递归写法,思考reverseKGroup逆序时为什么不需要断开原来的指向

因为reverseKGroup操作的是当前节点实现逆序已经覆盖了原来的节点。reverseList操作的是后续节点,原来的节点的下一个没被覆盖。

class Solution {
    public ListNode reverseList(ListNode head) {
     

         if(head==null ||head.next==null){
             return head;
         }

         ListNode newHead =  reverseList(head.next);
         head.next.next = head;
         //断开原来的指向防止形成循环链表,新的指向在上层递归会指定
         head.next = null;

         return newHead;
    }
}

LeetCode92. 反转链表 II 

给你单链表的头指针 head 和两个整数 left 和 right ,其中 left <= right 。请你反转从位置 left 到位置 right 的链表节点,返回 反转后的链表 。

示例 :

LeetCode链表逆序:翻转策略面试最简代码综合运用_逆序_03


输入:head = [1,2,3,4,5], left = 2, right = 4 输出:[1,4,3,2,5]


class Solution {
    public ListNode reverseBetween(ListNode head, int left, int right) {
        if (head == null || left == right) {
            return head;
        }

        ListNode dummy = new ListNode(-1);
        dummy.next = head;

        ListNode prev = dummy;
        for (int i = 0; i < left - 1; i++) {
            prev = prev.next;
        }

            
        // 保存 rightNode 的下一个节点
        ListNode rightNode = prev;
        for (int i = 0; i < right - left + 1; i++) {
            //从 prev 节点开始,向前移动 right - left + 1 步来定位到 right 位置的节点(
            rightNode = rightNode.next;
        }

        //rightNode 的下一个节点
        ListNode tailNext = rightNode.next;

        // 反转部分链表,并获取反转部分的新头节点
        ListNode[] reversed = reverseRecursively(prev.next, right - left + 1);

        // 重新连接反转后的链表
        prev.next = reversed[0];
        reversed[1].next = tailNext;
        return dummy.next;
    }

排序奇升偶降链表 

  • 题目

给定一个奇数位升序,偶数位降序的链表,将其重新排序。


输入: 1->8->3->6->5->4->7->2->NULL 输出: 1->2->3->4->5->6->7->8->NULL


  • 思路

1. 按奇偶位置拆分链表,得1->3->5->7->NULL和8->6->4->2->NULL

2. 反转偶链表,得1->3->5->7->NULL和2->4->6->8->NULL

3. 合并两个有序链表,得1->2->3->4->5->6->7->8->NULL

第2步和第3步分别对应的力扣206. 反转链表和21. 合并两个有序链表,而第1步的解法与328. 奇偶链表差不多。如果搞懂这3道leetcode,那么本篇文章的这道题肯定不在话下了。

  • 代码

class Solution {
    public ListNode oddEvenList(ListNode head) {
        if (head == null) return null;
        
        ListNode odd = head, even = head.next, evenHead = even;
        while (even != null && even.next != null) {
            odd.next = even.next;
            odd = odd.next;
            even.next = odd.next;
            even = even.next;
        }
        odd.next = null; // cut off the odd list
        return mergeTwoLists(head, reverse(evenHead));
    }

    private ListNode reverse(ListNode head) {
        ListNode prev = null;
        while (head != null) {
            ListNode nextNode = head.next;
            head.next = prev;
            prev = head;
            head = nextNode;
        }
        return prev;
    }

    private ListNode mergeTwoLists(ListNode l1, ListNode l2) {
        if (l1 == null) return l2;
        if (l2 == null) return l1;

        if (l1.val < l2.val) {
            l1.next = mergeTwoLists(l1.next, l2);
            return l1;
        } else {
            l2.next = mergeTwoLists(l1, l2.next);
            return l2;
        }
    }
}

举报

相关推荐

0 条评论