目录
1.题目
给你一个链表的头节点
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
- 代码模版
/** * Definition for singly-linked list. * struct ListNode { * int val; * struct ListNode *next; * }; */ struct ListNode* partition(struct ListNode* head, int x) { }
★注:示例1画的图其实是错的,最后的结果应该是1->2->2->3->4->5
2.自解
算法:开辟两个带哨兵位的新链表进行尾插,比x小的放到一个链表,比x大的放到另外一个链表,最后将这两个链表与x合并为一个链表,返回头节点的地址.为了避免尾插时找尾,为两个链表定义各自的尾指针
本题使用带哨兵位的头节点,防止没有一个数字比x大或没有一个数字比x小出现,导致某个链表为空
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* struct ListNode *next;
* };
*/
struct ListNode* partition(struct ListNode* head, int x)
{
struct ListNode* cur=head;
struct ListNode* p1guard=(struct ListNode*)malloc(sizeof(struct ListNode));
struct ListNode* p2guard=(struct ListNode*)malloc(sizeof(struct ListNode));
struct ListNode* p1tail=p1guard;
struct ListNode* p2tail=p2guard;
while(cur)
{
if (cur->val<x)
{
struct ListNode* newnode=(struct ListNode*)malloc(sizeof(struct ListNode));
newnode->val=cur->val;
p1tail->next=newnode;
p1tail=newnode;
}
else
{
struct ListNode* newnode=(struct ListNode*)malloc(sizeof(struct ListNode));
newnode->val=cur->val;
p2tail->next=newnode;
p2tail=newnode;
}
cur=cur->next;
}
p1tail->next=p2tail->next=NULL;//不置空会导致野指针行为,这个非常关键
if (p1tail==p1guard)//一个小于x的都没有
return p2guard->next;
if (p2tail==p2guard)//一个大于等于x的都没有
return p1guard->next;
p1tail->next=p2guard->next;
return p1guard->next;
}
可以不用判断节点是否开辟成功,最后也不用free
提交结果
3.其他解法
方法1:开辟一个新链表
算法:只开辟一个带哨兵位的新链表,小于x的头插,大于等于x的尾插
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* struct ListNode *next;
* };
*/
struct ListNode* partition(struct ListNode* head, int x)
{
struct ListNode* cur=head;
struct ListNode* p1guard=(struct ListNode*)malloc(sizeof(struct ListNode));
p1guard->next=NULL;
struct ListNode* p1tail=p1guard;
while(cur)
{
if (cur->val<x)
{
if (p1tail==p1guard)//只有哨兵位,尾插
{
struct ListNode* newnode=(struct ListNode*)malloc(sizeof(struct ListNode));
newnode->val=cur->val;
p1guard->next=newnode;
p1tail=newnode;
}
else
{
struct ListNode* newnode=(struct ListNode*)malloc(sizeof(struct ListNode));
newnode->val=cur->val;
newnode->next=p1guard->next;
p1guard->next=newnode;
}
}
else
{
struct ListNode* newnode=(struct ListNode*)malloc(sizeof(struct ListNode));
newnode->val=cur->val;
p1tail->next=newnode;
p1tail=newnode;
}
cur=cur->next;
}
p1tail->next=NULL;//不置空会导致野指针行为,这个非常关键
return p1guard->next;
}
可以不用判断节点是否开辟成功,最后也不用free
提交结果
方法2:不开辟额外的空间
线索:"小于 x
的节点都出现在 大于或等于 x
的节点之前"显然是数据分块问题,可以在原空间的基础上使用双指针
算法:设双指针p1和p2都从head出发,由于要遵循:"小于 x
的节点都出现在 大于或等于 x
的节点之前",因此就要通过交换两个指针指向的值来让大于或等于x的值翻到后面去
设p1找大值,p2找小值,注意:一开始要让p1和p2都要指向同一个节点,且节点的值大于或等于x,这样后面才容易换,之后p2先出发找小,与p1交换才能让大于或等于x的值翻到后面去,小于x的值翻到前面去(注意循环时p1和p2都不能为空!)
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* struct ListNode *next;
* };
*/
struct ListNode* partition(struct ListNode* head, int x)
{
if(head == NULL)
return head;
struct ListNode* p1 = head;
struct ListNode* p2 = head;
while(p1 != NULL && p1->val < x)
{
p1 = p1->next;
}
while(p1 != NULL)
{
while(p2 != NULL && p2->val < x)
{
p2 = p2->next;
}
while(p1 != NULL && p1->val >= x)
{
p1 = p1->next;
}
if(p2 != NULL && p1 != NULL)
{
int tmp=p2->val;
p2->val=p1->val;
p1->val=tmp;
}
}
return head;
}