链表编程专题--分组逆序链表

1.题目

给定单向链表,以k个为一组,组内链表逆序,最后输出链表首节点。例如有单链表A->B->C->D->E->F->G->H,当k=3时,A->B->C逆序为C->B->A,同理D->E->F逆序为F->E->D,G->H逆序为H->G,最后链表就是C->B->A->F->E->D->H->G。

2.解法

因为要按k个一组,因此要有一个变量记录已经遍历了多少个,遍历到K个或者链表结束的时候,就要开始新的一组遍历。因每组的第一个结点逆序后都会成为该组的最后结点,也就是下一组第一个结点的前一个结点,因此要记录上一组的最初第一个结点是什么(暂且叫lastFirstNode),以便本组逆序结束后,能链接在lastFirstNode后,还要考虑每组第一个节点的next值要先赋空,不然逆序后会和第二个节点成环等问题,具体看代码及注释:

/**
 * Definition for singly-linked list.
 * public class ListNode {
 *     int val;
 *     ListNode next;
 *     ListNode(int x) { val = x; }
 * }
 */
public static ListNode reverse(ListNode head, int k) {
    if (head == null) {
        return head;
    }       
    ListNode firstNode = head;// 本组第一个结点
    ListNode lastFirstNode = null;// 上组逆序前第一个结点
    ListNode nowNode = head.next;// 正在遍历到的结点
    ListNode finnalHead = head;// 最终输出的链表头
    // 只有一个结点时,直接输出该节点即可
    if (nowNode == null) {
        return head;
    }
    ListNode nextNode = nowNode.next;// 正在遍历到的结点的下一个节点,可能为null
    ListNode priousNode = head; // 正在遍历到的结点的前一个节点
    // 因最终链表的头结点必然为第一组链表逆序后的首节点,因此设置一标志位以判定是否正在处理的是第一组链表
    boolean firstK = true; 
    // 因最初正在遍历的结点我们设置的是第二个节点,因此无需遍历k次,而是k-1次。
    // 此处需判断nowNode != null的原因是
    for (int i = 0; i < k - 1 && nowNode != null; i++) {
        nextNode = nowNode.next;
        // 上组逆序结束后,会使firstNode和nowNode指向本组首节点,此时将nowNode指向后移,并做是否为空判断,
        // 若为空,则说明本组只有一个结点,直接赋到上组逆序后的最后一个结点(也就是逆序前第一个节点)后即可
        if (firstNode.equals(nowNode)) {
            nowNode = nowNode.next;
            if (nowNode == null) {
                lastFirstNode.next = priousNode;
                break;
            }
            nextNode = nowNode.next;
        }
        // 在遍历本组链表的开始时就需要将首节点的next值设为null,否则会出现和第二个节点成环的现象
        if (i == 0) {
            firstNode.next = null;
        }
        // 两个节点逆序
        nowNode.next = priousNode;
        // 将正在遍历的结点后移,这里就体现出保持下一个节点引用的作用了:
        // 若不保持,则在逆序后,无法找到原本的下一个节点了。
        priousNode = nowNode;
        nowNode = nextNode;
        // 如果遍历到本组最后的结点
        if (i == k - 2 || nowNode == null) {
            if (firstK == true) {
                // 记录最终链表的首节点
                finnalHead = priousNode;
                firstK = false;
            }
            // 因在本次for循环结束后,i会自动加1,因此这里赋为-1,以便本次for循环结束后,i变为0
            i = -1;
            // 除第一组链表外,都需链接到上一组逆序后的链表末尾,因此这里对是否第一组做判断
            if (lastFirstNode != null) {
                lastFirstNode.next = priousNode;
            }
            // 将本组链表链接到上一组末尾后,将上组未逆序时首节点赋值为本组未逆序时首节点,
            // 准备开始下一组的遍历逆序工作:此时nextNode指向的是下一组链表的第一个节点,
            // firstNode和priousNode也都赋值为下一组第一个节点,
            lastFirstNode = firstNode;
            firstNode = nowNode;
            priousNode = nowNode;
        }
    }
    return finnalHead;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值