25. K 个一组翻转链表(最详细注释,分成三个步骤进行处理,简单易懂)

算法描述:

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

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

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

在这里插入图片描述

算法思路:

将这道题可以分解成几个部分
在这里插入图片描述
以中间情况进行举例

  1. 先将自身4-3链表反转
  2. 和上一个链表的结尾进行连接
  3. 和下一个链表的开始进行连接

然后开始分别解决。

完整代码可直接看文章结尾

先写好循环

while(head!=null){
	// 处理反转
}

1. 将自身反转

使用链表头插法进行自身反转,不同的是这里反转了k个节点就停止。
首先找到这个子链表的开头和结尾,然后略微修改一下头插法即可。
开头不用找,找一下结尾。
定义一个方法getEnd专门用于查结尾。

找结尾节点

// 找到最后一个节点 return null 说明最后已经不够k个了
private ListNode getEnd(ListNode head, int k){
        int index = 1;
        while(head != null){
            head = head.next;
            if(++index == k){
                return head;
            }  
        }
        return head;
    }

这里有个细节,如果找的是最后一段子链表,且不足k个节点,那就保持原有顺序。所以调用该方法返回值为null即保持原有顺序。

// 获取最后一个节点,用于反转
      ListNode end = getEnd(head,k);
      // 如果到了最后不够了,保持原有顺序,直接返回即可
      if(end == null){
          break;
      }

也就是在此时就可以直接终结循环了。

将自身子链表反转

其实这里就很简单了,结尾节点已知,简单推理一下即可。
定义一个方法reverseList专门用于反转子链表

// 反转head-end之间的链表
     private ListNode reverseList(ListNode head,ListNode end) {
        if(head == null || head.next == null){
            return head;
        }
       
        ListNode next = head.next;
        head.next=null;

        ListNode p;
       
        while(next != null && head != end ){
            p = next.next;
            next.next = head;
            head = next;
            next = p;
        } 
        return head; 
    }

在循环中直接调用即可

// 反转本身,head到end反转
       reverseList(head,end);

需要注意的时候,在调用该方法后,传进来的head,返回的时候已经变成了该子链表的结尾。
传进来的end,返回的时候已经变成了该子链表的开始。

2. 与上一个链表进行连接

那就在记录一下上一个链表的结尾 last 即可,也就是每次处理一个子链表后,将该子链表的结尾,赋给last,用于下一个链表处理。
思考第一个链表的时候,其实last是没有的。那就创建一个保护节点protect,当作最先的last,同时该算法最后返回的时候,直接返回protect.next 即是最终的头节点。意外之喜!

循环前定义last

// 定义一个保护节点
        ListNode protect = new ListNode(0,null);
        ListNode last = protect;

每次处理子链表后,再赋值

// 子链表的开头(现在的end)和上一个链表结尾结合
        last.next = end;

下一次循环时,last就是该子链表的结尾

// 当前结尾下一个循环时已成上一个链表的结尾(即目前的head)
      last = head;

3. 与下一个链表进行连接

要记录下一个链表的开头nextGroupStart,但是在处理完子链表后,肯定已经找不到它了,所以在处理前就要先记录。

循环前,定义一下

ListNode nextGroupStart = null;

在循环内部,在处理子链表前赋值

// 记录后一个链表的开始
       nextGroupStart = end.next;

然后在处理完子链表后,再进行连接

// 子链表的结尾(现在的head)和下一个链表开始结合
       head.next = nextGroupStart;

让下一个子链表开始处理

// 让下一个子链表开始处理吧
       head = nextGroupStart;

最后,考虑到一些异常情况,比如空指针,k=1或者0的时候其实根本不用处理等等

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

最后的最后,直接上完整代码

/**
 * Definition for singly-linked list.
 * 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(k==1 || k== 0|| head == null || head.next == null){
            return head;
        }
        // 定义一个保护节点
        ListNode protect = new ListNode(0,null);
        ListNode last = protect;
        ListNode nextGroupStart = null;

        // 反转一串链表, 前一个链表的结尾 + 反转本身 + 后一个链表的开始
        while(head!=null){
            // 获取最后一个节点,用于反转
            ListNode end = getEnd(head,k);
            // 如果到了最后不够了,保持原有顺序,直接返回即可
            if(end == null){
                break;
            }
            // 记录后一个链表的开始
            nextGroupStart = end.next;

            // 反转本身,head到end反转
            reverseList(head,end);
            // 子链表的开头(现在的end)和上一个链表结尾结合
            last.next = end;
            // 子链表的结尾(现在的head)和下一个链表开始结合
            head.next = nextGroupStart;

            // 当前结尾下一个循环时已成上一个链表的结尾(现在的head)
            last = head;
            // 让下一个子链表开始处理吧
            head = nextGroupStart;
        }
        return protect.next;


    }
    // 找到最后一个节点 return null 说明最后已经不够k个了
    private ListNode getEnd(ListNode head, int k){
        int index = 1;
        while(head != null){
            head = head.next;
            if(++index == k){
                return head;
            }  
        }
        return head;
    }


    // 反转head-end之间的链表
     private ListNode reverseList(ListNode head,ListNode end) {
        if(head == null || head.next == null){
            return head;
        }
       
        ListNode next = head.next;
        head.next=null;

        ListNode p;
       
        while(next != null && head != end ){
            p = next.next;
            next.next = head;
            head = next;
            next = p;
        } 
        return head; 
    }
}

在这里插入图片描述

在JavaScript中,如果你想要将链表每k个节点进行一次翻转,你可以使用迭代的方式实现。以下是解决这个问题的一个示例函数: ```javascript function reverseKGroup(head, k) { if (head === null || head.next === null || k <= 1) return head; // 如果链表为空、只有一个元素或k值不合理,则直接返回 let dummy = new ListNode(0); // 创建虚拟头节点 dummy.next = head; let current = head; let prev = dummy; let count = 0; // 计数器,记录当前组的节点数量 while (current !== null) { // 翻转前k个节点 for (let i = 0; i < k && current !== null; i++) { const nextTemp = current.next; // 保存下一个节点 current.next = current.next ? current.next.next : null; // 将当前节点的next指向前一个节点的next nextTemp.prev = current; // 更新前一个节点,使其指向已翻转后的节点 current = nextTemp; // 移动到下一个节点 } // 指针更新 if (count == k) { // 当完成了一组翻转 prev.next = current; // 新的一组开始连接到旧的尾巴 current = prev.next; // 更新当前指针 prev = current.prev; // 更新前一个节点指针,跳过已经翻转的部分 count = 0; // 重置计数器 } else { count++; // 继续累计节点,直到达到k } } return dummy.next; // 返回新的头部节点 } // 示例: // let head = [1,2,3,4,5,6,7]; // 假设这是一个链表,每个元素是一个ListNode实例 // let k = 3; // reverseKGroup(head, k); ``` 这个函数首先创建了一个虚拟头节点`dummy`,然后遍历链表,每当遇到k个连续的节点,就进行翻转,并移动指针以便进入下一组。当遍历结束时,返回翻转后的链表的头部。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

范大

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值