Partition List
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
.
分析:《Introductin to Alogrithims》讲解quick sort是给出了一种partition array的方法,但是这种方法用在这道题上不好,原因有二。第一,partition array的方法不能preserve the original relative order。第二,partition array中是两个元素不断互换,对于链表来说我们可以采用更高效的方法。在这里,我们定义几个指针,index指向大于等于x的结点段(这个结点段指第一个大于等于x的结点到p之前的结点),p指向当前遍历到的结点,pre_p指向p前一个结点。在partition的时候如果p->val小于x我们只要将p移动到index后面即可(这里的移动包含几个操作,比如处理pre_p->next, p->next)。具体实现时,有两个方面要注意,一是如果p移动到index后面后,遍历的指针应该指向p移动前的next;二是如果index后面是p,即index = pre_p时,操作跟index!=pre_p不同。算法时间复杂度是O(n),空间复杂度是O(1)。代码如下:
1 class Solution { 2 public: 3 ListNode *partition(ListNode *head, int x) { 4 ListNode * dummy = new ListNode(-1); 5 dummy->next = head; 6 ListNode * index = dummy; 7 ListNode * p = head, * pre_p = dummy; 8 while(p){ 9 if(p->val < x){ 10 if(pre_p != index){ 11 pre_p->next = p->next; 12 p->next = index->next; 13 index->next = p; 14 }else pre_p = p; 15 index = index->next; 16 p = pre_p->next; 17 }else{ 18 pre_p = p; 19 p = p->next; 20 } 21 } 22 return dummy->next; 23 } 24 };
出上面的方法外,还有一种更好理解的方法,操作也更简便,我们可以设两个虚拟结点,然后一个虚拟结点后是小于x的结点,另一个虚拟结点后是大于等于x的结点。时间复杂度和空间复杂度亦为O(n)和O(1)。
1 class Solution { 2 public: 3 ListNode* partition(ListNode* head, int x) { 4 ListNode left_dummy(-1); // 头结点 5 ListNode right_dummy(-1); // 头结点 6 auto left_cur = &left_dummy; 7 auto right_cur = &right_dummy; 8 for (ListNode *cur = head; cur; cur = cur->next) { 9 if (cur->val < x) { 10 left_cur->next = cur; 11 left_cur = cur; 12 } else { 13 right_cur->next = cur; 14 right_cur = cur; 15 } 16 } 17 left_cur->next = right_dummy.next; 18 right_cur->next = nullptr; 19 return left_dummy.next; 20 } 21 };
Note:在链表头部加一个dummy结点很多时候可以方便操作简化代码。