LeetCode86: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.

链表的分割,将链表中的元素分成两个部分,小于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
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值