Given a linked list and a value x, partition it such that all nodes less than x come before nodes greater than or equal to x.
You should preserve the original relative order of the nodes in each of the two partitions.
For example,
Given 1->4->3->2->5->2 and x = 3,
return 1->2->2->4->3->5.
链表的分割,将链表中的元素分成两个部分,小于x的元素都在大于或等于x的元素的左边。并保持元素的相对顺序。
最开始想用快排的思想,发现快排会打乱相对顺序,行不通。
然后想到了冒泡排序的思想,在纸上画了个草图,发现有一定的可行性,但是其中会有大量的判断分支。
基本的思想如下:
- 初始定义一个伪指针fakeNode,它的next指针指向head。
- 然后定义一个prev指针,这个指针初始和fakeNode一样。因为后面会有指针的删除操作,所以需要保存被删除元素的前一个节点,故这里prev指针指向第一个小于x的前一个元素。
- 寻找到第一个小于x的前一个节点,此时prev指针指向它
- 如过找到的这个节点不为空并且这个节点不是头节点,那么需要调整指针,使这个节点放置到链表的最前面,并且更新prev的位置指向第一个小于x的元素。
- 如过找到的这个节点不为空并且这个节点是头节点,将prev指针移动一个位置,是prev指向第一个小于x的元素。
- 如过找到的这个节点为空,表示数组中所有元素都大于x,此时可以直接返回。
然后就可以用另外一个指针iter遍历链表,当这个元素小于x时,将它插入prev后面。
runtime:8ms
/**
1. Definition for singly-linked list.
2. struct ListNode {
3. int val;
4. ListNode *next;
5. ListNode(int x) : val(x), next(NULL) {}
6. };
*/
class Solution {
public:
ListNode* partition(ListNode* head, int x) {
if(head==NULL||head->next==NULL) return head;
ListNode * fakeNode=new ListNode(0);
ListNode * prev=fakeNode;
fakeNode->next=head;
ListNode * iter=head;
//找到第一个下一个元素大于等于x的节点
while(prev->next!=NULL&&prev->next->val>=x)
prev=prev->next;
if(prev->next!=NULL&&prev->next!=head)
{
ListNode * tmp=prev->next;
prev->next=tmp->next;
tmp->next=head;
fakeNode->next=tmp;
iter=prev;
prev=tmp;
}
else if(prev->next!=NULL&&prev->next==head)
{
prev=prev->next;
}
else{
return fakeNode->next;
}
while(iter->next!=NULL)
{
if(iter->next->val<x)
{
if(iter==prev)
{
iter=iter->next;
prev=prev->next;
}
else
{
ListNode * tmp=iter->next;
iter->next=iter->next->next;
tmp->next=prev->next;
prev->next=tmp;
prev=prev->next;
}
}
else
iter=iter->next;
}
return fakeNode->next;
}
};
上面的代码估计除了第一次见到时会这样编码,知道技巧了后就再也不会这么写了。但是上面也是一种正确的解法,在leetcode中也测试通过了,runtime为8ms,速度也是比较快的。只是对于编写代码的人而言工作量会有点大。但是从中也学到了一些技巧。
- 链表的删除操作室相对整个算法而言是相当复杂的,因为它需要保存被删除节点的前一个指针,所以可能会有可以避免删除操作的方法。
- 伪指针在链表操作中是一种很好的技巧。
- 如果真的需要需要删除节点,最好使用两个指针,一个指向当前节点,一个指向它之前的节点。
解法二:
第二种解法相当巧妙,代码也很简单,写起来也不那么容易出错。可以发现优秀的算法实现起来也是很容易的。
它将链表分成了两个链表,一个链表中的元素都小于x,另外一个链表中的元素都大于等于x。这样就能保证相对顺序了。
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* ListNode *next;
* ListNode(int x) : val(x), next(NULL) {}
* };
*/
class Solution {
public:
//解法二:分离成两个链表,一个链表中的元素都比x小,另外一个都大即可
//也需要使用伪节点
ListNode* partition(ListNode* head, int x) {
ListNode * small=new ListNode(0),*small_tail=small;
ListNode * big=new ListNode(0),*big_tail=big;
ListNode * cur=head;
while(cur!=NULL)
{
if(cur->val<x)
{
small_tail->next=cur;
small_tail=small_tail->next;
cur=cur->next;
small_tail->next=NULL;//这一步需要将加到small_tail尾端的next指针置为NULL,否则返回值中可能会出现循环
}
else
{
big_tail->next=cur;
big_tail=big_tail->next;
cur=cur->next;
big_tail->next=NULL;
}
}
small_tail->next=big->next;
return small->next;
}
};