K 个一组翻转链表

K 个一组翻转链表

1、题目

  • 题目说明

    • 给你一个链表,每 k 个节点一组进行翻转,请你返回翻转后的链表。
    • k 是一个正整数,它的值小于或等于链表的长度。
    • 如果节点总数不是 k 的整数倍,那么请将最后剩余的节点保持原有顺序。
  • 示例:

    • 给你这个链表:1->2->3->4->5
    • 当 k = 2 时,应当返回: 2->1->4->3->5
    • 当 k = 3 时,应当返回: 3->2->1->4->5
  • 说明:

    • 你的算法只能使用常数的额外空间。看这图应该就清楚了,In-place 就是常数内存,Out-place 就是额外内存,说白了让你用 In-place 方式的算法来解决问题。

    image-20200722204342414

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

2、代码

2.1、代码思路

  • 以两个为一组讲解思路,其他情况类似

    • curNode 指向当前的节点
    • nextNode 为 curNode 的下一个节点
    • reverseHead 指向每一段子链表的首节点
    • reverseTail 指向每一段子链表的尾节点
  • 下面举例说明一下程序的大致流程,以 K = 2 为例讲解

    • 原链表如下

    image-20200723084658007

    • 初始操作:
      • reverseHead 为子链表的链表头,可以看作是子链表的头结点,初始化时是自己 new 出来的
      • reverseTail 每次初始化时,指向子链表的首节点,随着翻转的进行,链表首节点自然就变成了尾节点

    image-20200723085246958

    • 执行第一次翻转后:将 curNode 指向的节点放在 reversehead 之后 ,curNode、nextNode 指针后移

    image-20200723084737792

    • 执行第二次翻转:
      • 将 curNode 指向的节点放在 reversehead 之后 ,curNode、nextNode 指针后移
      • 此时赶紧将原链表的头结点 head 指向 reverseHead ,因为这时候 reverseHead 指向的节点是新的首节点,不珍惜这次,之后就没机会啦

    image-20200723084923216

    image-20200723085638646

    • 此时已经执行了两次翻转,需要重新初始化子链表的头结点和尾节点
      • reverseHead 指向之前的 reverseTail ,作为子链表新的头结点
      • reverseTail 指向 curNode ,随着翻转的进行,reverseTail 会成为子链表的尾节点

    image-20200723085625380

    • 注意:如果链表长度不是 K 的整数倍,那么退出循环后需要将 reverseHead 指向 curNode ,否则后面的子链表将丢失。。。

    image-20200722232942683

image-20200723090035239

2.2、链表节点的定义

// 节点
class Node {
	public Integer data;
	public Node next;

	public Node(Integer data) {
		this.data = data;
	}

	public String toString() {
		return data.toString();
	}
}

2.3、K 个一组翻转链表

// 链表类
class SingleLinkedList {

	private Node head = new Node(0);

	public void add(Node node) {
		// 首节点指针不能移动哦,需要定义辅助指针
		Node preNode = head;
		while (preNode.next != null) {
			preNode = preNode.next;
		}
		preNode.next = node;
	}

	public void show() {
		// 首节点指针不能移动哦,需要定义辅助指针
		Node curNode = head.next;
		while (curNode != null) {
			System.out.print(curNode.data + "-->");
			curNode = curNode.next;
		}
		System.out.println();
	}

	public int length() {
		// 首节点指针不能移动哦,需要定义辅助指针
		Node curNode = head.next;
		// 链表长度
		int len = 0;
		while (curNode != null) {
			len++;
			curNode = curNode.next;
		}
		return len;
	}

	public void reverse(int gap) {
		int len = length();

		// gap 过大
		if (len < gap) {
			return;
		}

		// 链表中没有元素或者只有一个元素
		if (head.next == null || head.next.next == null) {
			return;
		}

		// 新的头结点和尾节点
		Node reverseHead = new Node(0);
		Node reverseTail = new Node(0);

		// 当前正在遍历的节点
		Node curNode = head.next;
		// 当前节点的下个节点
		Node nextNode = curNode.next;
		// reverseTail 是每个子链表的第一个元素
		reverseTail = curNode;

		// 翻转计数器,记录当前一共翻转了多少个节点
		int reverseCount = 0;

		while (curNode != null) {
			// 将当前正在遍历的节点挂在 reverseHead 之后
			nextNode = curNode.next;
			curNode.next = reverseHead.next;
			reverseHead.next = curNode;
			// 指针后移,计数器加 1
			curNode = nextNode;
			reverseCount++;

			// 到达指定个数,则重新需要为 reverseHead 和 reverseTail 重新赋值
			if (reverseCount % gap == 0) {
				// 如果是第一次到达指定个数,则将头指针指向首节点,否则后面就没机会了。。。
				if (reverseCount / gap == 1) {
					head.next = reverseHead.next;
				}
				// 头指针指向左边链表尾节点
				reverseHead = reverseTail;
				// 尾指针指向右边链表的首节点
				reverseTail = curNode;
				// 如果不足 gap 个,则推出,不再继续翻转
				if (reverseCount + gap > len) {
					break;
				}
			}
		}
		// 如果不足 gap 个,不翻转,直接连接
		if (reverseCount != len) {
			reverseHead.next = curNode;
		}
	}
}

2.4、代码测试

  • 代码
public static void main(String[] args) {

    SingleLinkedList singleLinkedList = new SingleLinkedList();

    singleLinkedList.add(new Node(1));
    singleLinkedList.add(new Node(2));
    singleLinkedList.add(new Node(3));
    singleLinkedList.add(new Node(4));
    singleLinkedList.add(new Node(5));

    System.out.println("翻转前~~~");
    singleLinkedList.show();

    System.out.println("翻转后~~~");
    singleLinkedList.reverse(2);
    singleLinkedList.show();

}
  • 程序运行结果
翻转前~~~
1-->2-->3-->4-->5-->
翻转后~~~
2-->1-->4-->3-->5-->

2.5、全部代码

public class ReverseListWithK {
	public static void main(String[] args) {

		SingleLinkedList singleLinkedList = new SingleLinkedList();

		singleLinkedList.add(new Node(1));
		singleLinkedList.add(new Node(2));
		singleLinkedList.add(new Node(3));
		singleLinkedList.add(new Node(4));
		singleLinkedList.add(new Node(5));

		System.out.println("翻转前~~~");
		singleLinkedList.show();

		System.out.println("翻转后~~~");
		singleLinkedList.reverse(2);
		singleLinkedList.show();

	}
}

// 链表类
class SingleLinkedList {

	private Node head = new Node(0);

	public void add(Node node) {
		// 首节点指针不能移动哦,需要定义辅助指针
		Node preNode = head;
		while (preNode.next != null) {
			preNode = preNode.next;
		}
		preNode.next = node;
	}

	public void show() {
		// 首节点指针不能移动哦,需要定义辅助指针
		Node curNode = head.next;
		while (curNode != null) {
			System.out.print(curNode.data + "-->");
			curNode = curNode.next;
		}
		System.out.println();
	}

	public int length() {
		// 首节点指针不能移动哦,需要定义辅助指针
		Node curNode = head.next;
		// 链表长度
		int len = 0;
		while (curNode != null) {
			len++;
			curNode = curNode.next;
		}
		return len;
	}

	public void reverse(int gap) {
		int len = length();

		// gap 过大
		if (len < gap) {
			return;
		}

		// 链表中没有元素或者只有一个元素
		if (head.next == null || head.next.next == null) {
			return;
		}

		// 新的头结点和尾节点
		Node reverseHead = new Node(0);
		Node reverseTail = new Node(0);

		// 当前正在遍历的节点
		Node curNode = head.next;
		// 当前节点的下个节点
		Node nextNode = curNode.next;
		// reverseTail 是每个子链表的第一个元素
		reverseTail = curNode;

		// 翻转计数器,记录当前一共翻转了多少个节点
		int reverseCount = 0;

		while (curNode != null) {
			// 将当前正在遍历的节点挂在 reverseHead 之后
			nextNode = curNode.next;
			curNode.next = reverseHead.next;
			reverseHead.next = curNode;
			// 指针后移,计数器加 1
			curNode = nextNode;
			reverseCount++;

			// 到达指定个数,则重新需要为 reverseHead 和 reverseTail 重新赋值
			if (reverseCount % gap == 0) {
				// 如果是第一次到达指定个数,则将头指针指向首节点,否则后面就没机会了。。。
				if (reverseCount / gap == 1) {
					head.next = reverseHead.next;
				}
				if (reverseCount + gap > len) {
					break;
				}
				reverseHead = reverseTail;
				reverseTail = curNode;
			}
		}
		if (reverseCount == len) {
			reverseTail.next = null;
		} else {
			reverseTail.next = curNode;
		}
	}
}

// 节点
class Node {
	public Integer data;
	public Node next;

	public Node(Integer data) {
		this.data = data;
	}

	public String toString() {
		return data.toString();
	}
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值