leetcode25.K个一组翻转链表

Posted by serrini on March 16, 2020

leetcode25.K个一组翻转链表

Question

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

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

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

 

示例:

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

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

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

 

说明:

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

来源:力扣(LeetCode) 链接:https://leetcode-cn.com/problems/reverse-nodes-in-k-group 著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。

Answer

解法一

public class Solution {
	public ListNode reverseKGroup(ListNode head, int k) {
		ListNode dummyNode = new ListNode(0);
		dummyNode.next = head;
		
		//开始时pre和end都在dummyNode处
		ListNode pre = dummyNode; //待翻转链表的前驱
		ListNode end = dummyNode; //待翻转链表的末尾
		
		while(end.next != null) {
			for(int i = 0; i < k && end != null; i++) {
				end = end.next; //end指针后移,看是否足够翻转
			}
			if(end == null)	break; //不够k个就跳出
			
			ListNode start = pre.next;
			ListNode next = end.next; //next记录待翻转的结点
			end.next = null; //从end往后断开,准备翻转【start,end】区间
			
			pre.next = reverse(start); //更新pre
			start.next = next; //将翻转后的部分与后面未翻转部分连起来
			
			pre = start; //重置pre指针
			end = pre; //重置end指针
		}
		return dummyNode.next;
    }

	private ListNode reverse(ListNode head) {
	//单链表的翻转操作,遍历实现
		ListNode pre = null;
		ListNode curr = head;
		ListNode temp = null;
		while(curr != null) {	
			temp = curr.next;
			curr.next = pre;
			
			pre = curr;
			curr = temp;
		}
		return pre;
	}
}

解法二

import java.util.ArrayDeque;
import java.util.Deque;

public class Solution {
	public ListNode reverseKGroup(ListNode head, int k) {
		Deque<ListNode> stack = new ArrayDeque<ListNode>();
		ListNode dummyNode = new ListNode(0);
		ListNode p = dummyNode; //p用于生成新的链表,dummyNode记住头结点
		while(true) {
			int count = 0;
			ListNode tmp = head; //临时结点tmp
			while(tmp != null && count < k) {
				stack.add(tmp); //往栈里头加
				tmp = tmp.next;
				count++;
			}
			if(count != k) {
				//不够k个不翻转
				p.next = head;
				break;
			}
			while(!stack.isEmpty()) {
				p.next = stack.pollLast(); //取出来
				p = p.next;
			}
			p.next = tmp;
			head = tmp;
		}
		return dummyNode.next;
	}
}

Attention

解法一(常数空间):

题解转载自王小二

  • 链表分区为已翻转部分+待翻转部分+未翻转部分
  • 每次翻转前,要确定翻转链表的范围,这个必须通过 k 次循环来确定
  • 需记录翻转链表前驱和后继,方便翻转完成后把已翻转部分和未翻转部分连接起来
  • 初始需要两个变量 pre 和 endpre 代表待翻转链表的前驱,end 代表待翻转链表的末尾
  • 经过k此循环,end 到达末尾,记录待翻转链表的后继 next = end.next
  • 翻转链表,然后将三部分链表连接起来,然后重置 pre 和 end 指针,然后进入下一次循环
  • 特殊情况,当翻转部分长度不足 k 时,在定位 end 完成后,end==null,已经到达末尾,说明题目已完成,直接返回即可
  • 时间复杂度为 O(n*K) ,最好的情况为 O(n) ,最差的情况未 O(n^2)
  • 空间复杂度为 O(1) ,除了几个必须的节点指针外,我们并没有占用其他空间