携手创作,共同成长!这是我参与「掘金日新计划 · 8 月更文挑战」的第16天,点击查看活动详情
题目描述
给你一个链表的头节点 head 和一个特定值 x ,请你对链表进行分隔,使得所有 小于 x 的节点都出现在 大于或等于 x 的节点之前。
你应当 保留 两个分区中每个节点的初始相对位置。
示例 1:
输入:head = [1,4,3,2,5,2], x = 3 输出:[1,2,2,4,3,5] 示例 2:
输入:head = [2,1], x = 2 输出:[1,2]
提示:
- 链表中节点的数目在范围 [0, 200] 内
- -100 <= Node.val <= 100
- -200 <= x <= 200
题目元素
给定一个链表和正数x,需要将链表的值进行移动,使所有小于x的值放在大于x的值的前面。
即以一个值为界限,界限左边的值整体大于界限右边的值,但是每个界限内部的值不一定存在大小顺序.
解题思路
链表的处理实际上都是三个处理方式:哨兵节点、虚拟节点、链表移动
- 哨兵节点:哨兵节点也叫哑元节点,可以通过这种方式来理解。针对同一组都是链表的数组,对它们进行删除、插入的操作。其实就是对当前需要删除或插入的节点的前驱节点的next进行操作。但是第一个元素如果想要做删除或者插入的操作时因为它没有前驱节点,所以需要额外花经历去单独处理。所以可以化繁为简,把所有的元素都变成一样的,即在原本的链表的头部加一个空节点,这个空节点就是哨兵节点**,将真实的链表设置为哨兵节点的next**,这样链表内的所有元素都可以通过同样的方式处理。
- 虚拟节点:通过创建一个节点一直指向当前操作节点。
- 链表移动:通过移动节点next来达到移动链表的目的,链表的移动实际上是next指针的移动。
代码实现
/**
* @param head
* @param x
* @return
*/
public static ListNode partition(ListNode head, int x) {
// 因为本题是需要将链表分成比x大的一边和比x小的一边,所以需要两个哑元节点
ListNode smallDummyNode = new ListNode(0);
ListNode largeDummyNode = new ListNode(0);
// 创建两个节点,分别代表大链表当前操作的节点和小链表当前操作的节点
// 初始值为哑元节点
ListNode smallNode = smallDummyNode;
ListNode largeNode = largeDummyNode;
// 遍历head
while (head != null) {
//当链表中还有值
ListNode currentNode = head;
if (currentNode.val >= x) {
largeNode.next = currentNode;
// 链表移动,将currentNode赋值给大链表
largeNode = currentNode;
}else {
smallNode.next = currentNode;
smallNode = currentNode;
}
// 头部链表移动
head = head.next;
}
largeNode.next = null;
// 两个链表进行拼接
smallNode.next = largeDummyNode.next;
return smallDummyNode.next;
}