LeetCode86:Partition List

来源:互联网 发布:开淘宝怎么拿货 编辑:程序博客网 时间:2024/05/20 20:45

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的元素的左边。并保持元素的相对顺序。
最开始想用快排的思想,发现快排会打乱相对顺序,行不通。
然后想到了冒泡排序的思想,在纸上画了个草图,发现有一定的可行性,但是其中会有大量的判断分支。
基本的思想如下:

  1. 初始定义一个伪指针fakeNode,它的next指针指向head。
  2. 然后定义一个prev指针,这个指针初始和fakeNode一样。因为后面会有指针的删除操作,所以需要保存被删除元素的前一个节点,故这里prev指针指向第一个小于x的前一个元素。
  3. 寻找到第一个小于x的前一个节点,此时prev指针指向它
  4. 如过找到的这个节点不为空并且这个节点不是头节点,那么需要调整指针,使这个节点放置到链表的最前面,并且更新prev的位置指向第一个小于x的元素。
  5. 如过找到的这个节点不为空并且这个节点是头节点,将prev指针移动一个位置,是prev指向第一个小于x的元素。
  6. 如过找到的这个节点为空,表示数组中所有元素都大于x,此时可以直接返回。
  7. 然后就可以用另外一个指针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,速度也是比较快的。只是对于编写代码的人而言工作量会有点大。但是从中也学到了一些技巧。

  1. 链表的删除操作室相对整个算法而言是相当复杂的,因为它需要保存被删除节点的前一个指针,所以可能会有可以避免删除操作的方法。
  2. 伪指针在链表操作中是一种很好的技巧。
  3. 如果真的需要需要删除节点,最好使用两个指针,一个指向当前节点,一个指向它之前的节点。

解法二:
第二种解法相当巧妙,代码也很简单,写起来也不那么容易出错。可以发现优秀的算法实现起来也是很容易的。
它将链表分成了两个链表,一个链表中的元素都小于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;       }};
0 0
原创粉丝点击