K 个一组翻转链表

题目要求

编写一个函数,实现对链表的分组翻转功能。具体来说,给定一个链表的头节点 head,应该将链表中的每 k 个节点看作一组进行翻转。如果链表中的节点数不是 k 的整数倍,那么最后剩余的节点应保持原有的顺序。在翻转过程中,不能仅仅改变节点的值,而是需要通过改变节点之间的链接关系来实现翻转。

解题思路

解决这个问题可以分为以下几个步骤:

  1. 遍历链表:首先需要遍历整个链表,确定链表的总长度,以便知道最后一组是否有足够的 k 个节点进行翻转。

  2. 分组翻转:从头节点开始,对链表中的每 k 个节点进行翻转。翻转的基本方法是,对于每一组内的节点,逐个将其摘下并插入到当前组的起始位置,直到该组内的节点全部翻转完毕。

  3. 处理剩余节点:如果链表的长度不是 k 的整数倍,那么最后剩余的节点不需要翻转,保持原样即可。

  4. 链接各组:在翻转每一组节点的同时,需要注意维护好相邻两组之间的链接关系,确保链表的连续性。

  5. 返回结果:最后返回新链表的头节点,这个头节点是第一次翻转后的头节点。

具体实现时,可以使用几个辅助指针来帮助标记和链接:

  • prev:指向当前组翻转前的第一个节点的前一个节点,初始时为 null
  • current:当前遍历到的节点。
  • next:指向 current 节点的下一个节点,用于在翻转节点时不丢失链表的其余部分。
  • tail:当前组翻转前的第一个节点,翻转后将成为该组的尾节点,需要链接到下一组的头节点。

在实现时,需要注意边界条件的处理,如链表长度小于 k,链表为空等情况。

Golang 版本

package main

import (
	"fmt"
)

// ListNode is a definition for singly-linked list.
type ListNode struct {
	Val  int
	Next *ListNode
}

func reverseKGroup(head *ListNode, k int) *ListNode {
	if head == nil || k == 1 {
		return head
	}

	// 创建一个虚拟头节点,便于处理头部翻转的情况
	dummy := &ListNode{Next: head}
	prevGroupEnd := dummy

	// 遍历链表,确定链表长度
	length := 0
	for current := head; current != nil; current = current.Next {
		length++
	}

	// 对每k个节点进行翻转
	for length >= k {
		current := prevGroupEnd.Next
		next := current.Next
		for i := 1; i < k; i++ {
			current.Next = next.Next
			next.Next = prevGroupEnd.Next
			prevGroupEnd.Next = next
			next = current.Next
		}
		prevGroupEnd = current
		length -= k
	}

	return dummy.Next
}

func printList(head *ListNode) {
	for head != nil {
		fmt.Printf("%d ", head.Val)
		head = head.Next
	}
	fmt.Println()
}

func main() {
	// 创建一个示例链表 1->2->3->4->5
	nodes := []int{1, 2, 3, 4, 5}
	dummy := &ListNode{}
	current := dummy
	for _, val := range nodes {
		current.Next = &ListNode{Val: val}
		current = current.Next
	}

	// 应用翻转函数
	k := 2
	result := reverseKGroup(dummy.Next, k)

	// 打印翻转后的链表
	printList(result)
}

这段代码定义了一个链表节点 ListNode,实现了一个 reverseKGroup 函数来解决题目中的问题。main 函数创建了一个示例链表,并调用 reverseKGroup 函数进行测试,最后打印出翻转后的链表。

C++版本

#include <iostream>

// Definition for singly-linked list.
struct ListNode {
    int val;
    ListNode *next;
    ListNode() : val(0), next(nullptr) {}
    ListNode(int x) : val(x), next(nullptr) {}
    ListNode(int x, ListNode *next) : val(x), next(next) {}
};

class Solution {
public:
    ListNode* reverseKGroup(ListNode* head, int k) {
        if (head == nullptr || k == 1) {
            return head;
        }

        ListNode dummy(0);
        dummy.next = head;
        ListNode *prevGroupEnd = &dummy;

        while (head != nullptr) {
            // Check if there are k nodes left in the list
            ListNode *tail = head;
            int i;
            for (i = 0; i < k; ++i) {
                if (tail == nullptr) break;
                tail = tail->next;
            }
            if (i != k) break; // Less than k nodes, no need to reverse

            // Reverse k nodes
            ListNode *prev = tail, *curr = head, *next = nullptr;
            while (curr != tail) {
                next = curr->next;
                curr->next = prev;
                prev = curr;
                curr = next;
            }

            // Connect with previous part
            prevGroupEnd->next = prev;
            prevGroupEnd = head;
            head = tail;
        }

        return dummy.next;
    }
};

void printList(ListNode* head) {
    while (head != nullptr) {
        std::cout << head->val << " ";
        head = head->next;
    }
    std::cout << std::endl;
}

int main() {
    // Create a sample linked list 1->2->3->4->5
    ListNode *list = new ListNode(1, new ListNode(2, new ListNode(3, new ListNode(4, new ListNode(5)))));

    Solution solution;
    int k = 2;
    ListNode *result = solution.reverseKGroup(list, k);

    // Print the reversed list
    printList(result);

    // Clean up memory
    while (result != nullptr) {
        ListNode *temp = result;
        result = result->next;
        delete temp;
    }

    return 0;
}

这段 C++代码定义了一个链表节点 ListNode 和一个解决问题的类 SolutionSolution 类中的 reverseKGroup 方法实现了题目要求的链表分组翻转功能。main 函数创建了一个示例链表,并调用 reverseKGroup 方法进行测试,最后打印出翻转后的链表,并且在最后进行了内存清理。

Python 版本

class ListNode:
    def __init__(self, val=0, next=None):
        self.val = val
        self.next = next

class Solution:
    def reverseKGroup(self, head: ListNode, k: int) -> ListNode:
        if head is None or k == 1:
            return head

        # Dummy node initialization
        dummy = ListNode(0)
        dummy.next = head
        prev_group_end = dummy

        # Count the number of nodes in the list
        count = 0
        current = head
        while current:
            count += 1
            current = current.next

        # Reverse nodes in k groups
        while count >= k:
            current = prev_group_end.next
            next_node = current.next
            for i in range(1, k):
                current.next = next_node.next
                next_node.next = prev_group_end.next
                prev_group_end.next = next_node
                next_node = current.next
            prev_group_end = current
            count -= k

        return dummy.next

# Helper function to print the list
def print_list(node):
    while node:
        print(node.val, end=" ")
        node = node.next
    print()

# Example usage
if __name__ == "__main__":
    # Create a sample linked list 1->2->3->4->5
    nodes = [ListNode(i) for i in range(1, 6)]
    for i in range(len(nodes) - 1):
        nodes[i].next = nodes[i + 1]
    head = nodes[0]

    # Apply the reverse function
    k = 2
    solution = Solution()
    reversed_list_head = solution.reverseKGroup(head, k)

    # Print the reversed list
    print_list(reversed_list_head)

这段 Python 代码定义了一个链表节点 ListNode 和一个解决问题的类 SolutionSolution 类中的 reverseKGroup 方法实现了题目要求的链表分组翻转功能。在 if __name__ == "__main__": 部分,代码创建了一个示例链表,并调用 reverseKGroup 方法进行测试,最后打印出翻转后的链表。

Java 版本

public class ListNode {
    int val;
    ListNode next;
    ListNode() {}
    ListNode(int val) { this.val = val; }
    ListNode(int val, ListNode next) { this.val = val; this.next = next; }
}

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

        ListNode dummy = new ListNode(0);
        dummy.next = head;
        ListNode prevGroupEnd = dummy;
        ListNode current = head;

        // Count the total number of nodes in the list
        int count = 0;
        while (current != null) {
            count++;
            current = current.next;
        }

        // Reverse nodes in k groups
        while (count >= k) {
            current = prevGroupEnd.next;
            ListNode next = current.next;
            for (int i = 1; i < k; i++) {
                current.next = next.next;
                next.next = prevGroupEnd.next;
                prevGroupEnd.next = next;
                next = current.next;
            }
            prevGroupEnd = current;
            count -= k;
        }

        return dummy.next;
    }
}

public class Main {
    public static void printList(ListNode node) {
        while (node != null) {
            System.out.print(node.val + " ");
            node = node.next;
        }
        System.out.println();
    }

    public static void main(String[] args) {
        // Create a sample linked list 1->2->3->4->5
        ListNode head = new ListNode(1, new ListNode(2, new ListNode(3, new ListNode(4, new ListNode(5)))));

        Solution solution = new Solution();
        int k = 2;
        ListNode result = solution.reverseKGroup(head, k);

        // Print the reversed list
        printList(result);
    }
}

这段 Java 代码定义了一个链表节点 ListNode 和一个解决问题的类 SolutionSolution 类中的 reverseKGroup 方法实现了题目要求的链表分组翻转功能。Main 类中的 main 方法创建了一个示例链表,并调用 reverseKGroup 方法进行测试,最后打印出翻转后的链表。

Rust 版本

use std::rc::Rc;
use std::cell::RefCell;

// Definition for singly-linked list.
#[derive(PartialEq, Eq, Clone, Debug)]
pub struct ListNode {
    pub val: i32,
    pub next: Option<Rc<RefCell<ListNode>>>,
}

impl ListNode {
    #[inline]
    fn new(val: i32) -> Self {
        ListNode {
            val,
            next: None,
        }
    }
}

pub struct Solution;

impl Solution {
    pub fn reverse_k_group(head: Option<Rc<RefCell<ListNode>>>, k: i32) -> Option<Rc<RefCell<ListNode>>> {
        let mut head = head;
        let mut stack = Vec::new();
        let mut current = head.clone();

        // Push nodes to stack and pop to reverse in groups of k
        while let Some(node) = current {
            stack.push(node.clone());
            if stack.len() == k as usize {
                while let Some(top) = stack.pop() {
                    if stack.is_empty() {
                        // Connect the last node of the reversed group to the next group
                        top.borrow_mut().next = Self::reverse_k_group(top.borrow().next.clone(), k);
                        return Some(top);
                    }
                    // Reverse the link
                    top.borrow_mut().next = stack.last().cloned();
                }
            }
            current = node.borrow().next.clone();
        }

        // If the number of nodes is not a multiple of k, return the remaining nodes as is
        head
    }
}

fn main() {
    // Helper function to create a list from a Vec of i32
    fn to_list(vec: Vec<i32>) -> Option<Rc<RefCell<ListNode>>> {
        let mut current = None;
        for &val in vec.iter().rev() {
            let mut node = ListNode::new(val);
            node.next = current;
            current = Some(Rc::new(RefCell::new(node)));
        }
        current
    }

    // Helper function to collect values from a list into a Vec<i32>
    fn collect_values(head: Option<Rc<RefCell<ListNode>>>) -> Vec<i32> {
        let mut values = Vec::new();
        let mut current = head;
        while let Some(node) = current {
            values.push(node.borrow().val);
            current = node.borrow().next.clone();
        }
        values
    }

    // Example usage
    let list = to_list(vec![1, 2, 3, 4, 5]);
    let k = 2;
    let reversed_list = Solution::reverse_k_group(list, k);
    println!("{:?}", collect_values(reversed_list));
}

这段 Rust 代码定义了一个链表节点 ListNode 和一个解决问题的结构体 SolutionSolution 结构体中的 reverse_k_group 函数实现了题目要求的链表分组翻转功能。main 函数中创建了一个示例链表,并调用 reverse_k_group 函数进行测试,最后打印出翻转后的链表的值。

总结

上面给出的解法是针对链表分组翻转问题的不同编程语言版本的实现。无论是使用 Golang、C++、Python、Java 还是 Rust,解决问题的思路都是一致的:

  1. 首先,需要遍历链表,确定链表的长度,以便后续处理。
  2. 然后,对链表中的每 k 个节点一组进行翻转。在翻转过程中,需要维护好相邻两组之间的链接关系,确保链表的连续性。
  3. 如果节点总数不是 k 的整数倍,剩余的节点保持原有顺序。

这些解法都采用了类似的逻辑,但在具体实现上有一些语言特定的差异。例如,不同语言对于链表的表示和操作方式有所不同,因此在代码中会有相应的语言特定的语法和数据结构的使用。