LEETCODE(力扣) 25. K 个一组翻转链表

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

k 是一个正整数,它的值小于或等于链表的长度。如果节点总数不是 k 的整数倍,那么请将最后剩余的节点保持原有顺序。

你不能只是单纯的改变节点内部的值,而是需要实际进行节点交换。

示例 1:

输入:head = [1,2,3,4,5], k = 2
输出:[2,1,4,3,5]
示例 2:

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

提示:
链表中的节点数目为 n
1 <= k <= n <= 5000
0 <= Node.val <= 1000

进阶:你可以设计一个只用 O(1) 额外内存空间的算法解决此问题吗?

自解

image

因为每次只对部分节点进行处理,因此需要记录处理段前、后的节点,而头节点因无更前面的节点,需要单独写处理逻辑,因此可以用一个dummy节点插入头节点前面,减少代码量。
因为要与dummy节点的处理逻辑保持一致,我们每次reverse传入的参数要传入处理段的前一个节点。
如下,每次处理传入节点后的k个节点,处理完毕后将处理段首节点连接到已处理段尾节点上,本次处理段的尾节点连在未处理段的首结点上,并返回本次处理段的尾节点
主函数while循环到全部处理完毕或剩余节点不足k时(reverse都返回NULL)退出。

/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     struct ListNode *next;
 * };
 */
struct ListNode *reverse(struct ListNode *head,int k)
{
    /*last、next0用于反转链表时的中间变量,next1记录处理段的后一个节点,head1记录处理段的前一个节点*/
    struct ListNode *last=head,*next0=NULL,*next1=NULL,*head1=head;
    int i=k;
    head=head->next;//从处理段开始反转
    next1=head;
    while(next1&&i)//遍历到处理段的后一个节点,同时检查节点数量是否足够
    {
        i--;
        next1=next1->next;
    }
    if(i)return NULL;//节点数量不够时返回
    while(head&&k)//开始对节点进行反转,反转k个
    {
        next0=head->next;
        head->next=last;
        last=head;
        head=next0;
        k--;
    }
    head1->next=last;//last是该次反转后的处理段首结点,连接到已处理段的尾节点上
    while(last->next!=head1)last=last->next;//将last遍历到尾节点上
    last->next=next0;//该次反转后的处理段尾节点连接到未处理段上
    return last;//该次处理段的尾节点
}

struct ListNode* reverseKGroup(struct ListNode* head, int k) {
    struct ListNode dummy,*temp=NULL,*last=NULL,*head1=NULL;
    dummy.next=head;
    head=&dummy;
        while(head->next&&(head=reverse(head,k)));
        return dummy.next;
}

力扣解

自解的反转处理与该解法相同,但该解法代码更加简洁,for循环的判断也值得深究

struct ListNode* reverseKGroup(struct ListNode* head, int k) {
    // 统计节点个数
    int n = 0;
    for (struct ListNode* cur = head; cur; cur = cur->next) {
        n++;
    }

    struct ListNode dummy = {0, head};
    struct ListNode* p0 = &dummy;
    struct ListNode* pre = NULL;
    struct ListNode* cur = head;

    // k 个一组处理
    for (; n >= k; n -= k) {
        for (int i = 0; i < k; i++) { // 同 92 题
            struct ListNode* nxt = cur->next;
            cur->next = pre; // 每次循环只修改一个 next,方便大家理解
            pre = cur;
            cur = nxt;
        }

        // 见视频
        struct ListNode* nxt = p0->next;//--p0->next此时指向该次处理段的尾节点
        p0->next->next = cur;//--p0->next->next是未处理段的首结点
        p0->next = pre;//--pre是该次处理段的尾节点
        p0 = nxt;//--p0指向该次处理段的尾节点
                //--这段处理就是为了使cur指向已处理段的最后一个节点,原因就是我在自解说的,减少代码处理量,不必单独对首结点进行逻辑处理(ps:--注释仅为个人理解,与灵佬无关)
    }
    return dummy.next;
}

作者:灵茶山艾府
链接:https://leetcode.cn/problems/reverse-nodes-in-k-group/solutions/1992228/you-xie-cuo-liao-yi-ge-shi-pin-jiang-tou-plfs/
来源:力扣(LeetCode)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

仿解

个人认为灵佬的代码更简洁也更优美,进行了仿写练习

/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     struct ListNode *next;
 * };
 */
struct ListNode* reverseKGroup(struct ListNode* head, int k) {
    int i=0;
    struct ListNode dummy={0,head};
    struct ListNode *p=&dummy;
    struct ListNode *cur=head,*nxt=head,*last=NULL;

    while(nxt)
    {
        nxt=nxt->next;
        i++;
    }
    for(;i>=k;i-=k)
    {
        for(int j=0;j<k;j++)
        {
            nxt=cur->next;
            cur->next=last;
            last=cur;
            cur=nxt;
        }
        nxt=p->next;//nxt存放处理段尾节点
        p->next=last;//连接已处理段尾节点与处理段首结点
        nxt->next=cur;//处理段尾节点连接未处理段首结点
        p=nxt;
        
    }
    return dummy.next;

}
posted @ 2025-04-29 14:51  Osen  阅读(51)  评论(0)    收藏  举报