Idiot-maker

  :: 首页 :: 博问 :: 闪存 :: 新随笔 :: 联系 :: 订阅 订阅 :: 管理 ::

https://oj.leetcode.com/problems/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小的节点(1)可直接跳过不管。遇到>=x的节点,就把他们都[4,3]往后移动,移到下一个比x小(2)的节点,再把这个小于x的节点(2)放到之前大于x区域的第一个位置(4)。等等,这么说好像有点问题,其实是反过来了。为什么?

其实是,遇到>=x的节点不动,但是记录一个位置,就是这个连续都是>=x数字的区域的第一个位置。一旦遇到<x的节点,那么这个区域就清晰了,里面所有元素都>=x。于是,将这个区域整体后移一个,再将当前<x的这个节点放到这个区域的第一个。类似于将区域的最后一个元素放到首元素。这样,原来元素的顺序也不会打乱,是稳定的变化。

同时,把记录这个>=x区域的开始位置后移一个,因为他们都只后移了一位。

然后游标往后,继续遍历整个链表。

/**
 * Definition for singly-linked list.
 * public class ListNode {
 *     int val;
 *     ListNode next;
 *     ListNode(int x) {
 *         val = x;
 *         next = null;
 *     }
 * }
 */
public class Solution {
    public ListNode partition(ListNode head, int x) {
        ListNode swapNode = head;
        ListNode traverseNode = head;
        ListNode returnNode = head;
        
        while(traverseNode != null){
            if(traverseNode.val >= x){
                //do nothing
            }
            //考虑例子1->4->3->2->5->2,traverse到2,swapNode在4
            if(traverseNode.val < x){
                //留着一个备份,因为下面swapNode要往后移动,一直到traverseNode
                ListNode tempNode = swapNode;
                
                //给traverseNode的值留一个备份,后面和swapNode后节点交换
                int temp = traverseNode.val;
                
                //从[swapNode, traverseNode)每个节点往后移一个
                //即变成1->4->4->3->5->2
                int pre = swapNode.val;
                while(swapNode != traverseNode){
                    int tempValue = swapNode.next.val;
                    swapNode.next.val = pre;
                    pre = tempValue;
                    swapNode = swapNode.next;
                }
                //将前面存的最后一个值,2,赋予第一个节点4
                //即变成1->2->4->3->5->2
                tempNode.val = temp;
                //swapNode从原先的位置往后移动一个
                swapNode = tempNode.next;
            }
            // if(swapNode != null && swapNode.val < x){
            //     swapNode = swapNode.next;
            // }
            traverseNode = traverseNode.next;
        }
        return returnNode;
    }
}

这个逻辑应该来讲还是比较清晰的,但是在整个区域后移一位的时候,由于循环内需要两个临时变量,和单纯的变量交换还是有很大不同,在这个子问题上,我考虑了很久,基本功弱的缺点毕现。

再想想,一个区域内,所有元素按顺序都后移一位,难道不是用链表操作更简便吗?于是开始写代码。

/**
 * Definition for singly-linked list.
 * public class ListNode {
 *     int val;
 *     ListNode next;
 *     ListNode(int x) {
 *         val = x;
 *         next = null;
 *     }
 * }
 */
public class Solution {
    public ListNode partition(ListNode head, int x) {
        ListNode traverseNode = head;
        ListNode returnNode = head;
        ListNode preTraverseNode = new ListNode(0);
        preTraverseNode.next = head;
        ListNode swapNode = preTraverseNode;
        
        while(traverseNode != null && preTraverseNode != null){
            if(traverseNode.val >= x){
                //do nothing
            }
            
            // 必须判断traverseNode != swapNode,否则引起环,死循环
            if(traverseNode.val < x && traverseNode != swapNode){
                // ListNode next = traverseNode.next;
                //返回的node不再是原head,而是被甩到前面去较小的那个
                if(swapNode.next == returnNode){
                    returnNode = traverseNode;
                }
                preTraverseNode.next = traverseNode.next;
                traverseNode.next = swapNode.next;  
                swapNode.next = traverseNode;  
                // traverseNode = next;
                traverseNode = preTraverseNode.next;
                swapNode = swapNode.next;
                continue;
                //traverseNode与pre已经到位,无需再往后移动
            }
            if(traverseNode != null && preTraverseNode != null){
                preTraverseNode = preTraverseNode.next;
                traverseNode = traverseNode.next;
            }
        }
        return returnNode;
    }
}

在实际操作中发现,需要同时取得swapNode和traverseNode的前驱节点。链表操作有个特点,快了,你就再也回不去了,因为它是单向的,如果慢了,你可以一直node.next.next...往下取值。

这里我将swapNode的初始值设置在了一个dummy的节点上,用它指向head,所有swapNode就始终比上题都往前一位,即便这样,还是很容易出错,上面正确的代码我又花了半天时间才写出,错了大概十几个版本吧。

事实上,traverseNode也可以放在这个dummy上,这样就不需要那个preTraverseNode了,原来的preTraverseNode就是现在的traverseNode,原来的traverseNode就是现在的traverseNode.next。中间只需要一个获取preTraverseNode的下一个节点保存preTranverseNode的下一个节点即可,代码如下。

/**
 * Definition for singly-linked list.
 * public class ListNode {
 *     int val;
 *     ListNode next;
 *     ListNode(int x) {
 *         val = x;
 *         next = null;
 *     }
 * }
 */
public class Solution {
    public ListNode partition(ListNode head, int x) {
        // ListNode traverseNode = head;
        ListNode returnNode = head;
        ListNode preTraverseNode = new ListNode(0);
        preTraverseNode.next = head;
        ListNode swapNode = preTraverseNode;
        
        while(preTraverseNode.next != null){
            if(preTraverseNode.next.val >= x){
                //do nothing
            }
            
            // 必须判断preTraverseNode.next != swapNode,否则引起环,死循环
            if(preTraverseNode.next.val < x && preTraverseNode.next != swapNode){
                //返回的node不再是原head,而是被甩到前面去较小的那个
                if(swapNode.next == returnNode){
                    returnNode = preTraverseNode.next;
                }
                //获取preTraverseNode的下一个节点,不因为下面就要改变
                ListNode nextNode = preTraverseNode.next;
                preTraverseNode.next = preTraverseNode.next.next;
                nextNode.next = swapNode.next;  
                swapNode.next = nextNode;  
                swapNode = swapNode.next;
                continue;
                //preTraverseNode.next已经到位,无需再往后移动
            }
            if(preTraverseNode.next != null){
                preTraverseNode = preTraverseNode.next;
                // preTraverseNode.next = preTraverseNode.next.next;
            }
        }
        return returnNode;
    }
}

 以上均为in place的解法,还有一种解法,遍历一次链表,将<x和>=x的值分别加入一个队列,再重新建立一个新的链表,也可以,不过不是in place。

update 2015/05/19:

二刷

/**
 * Definition for singly-linked list.
 * public class ListNode {
 *     int val;
 *     ListNode next;
 *     ListNode(int x) { val = x; }
 * }
 */
public class Solution {
    public ListNode partition(ListNode head, int x) {
        if(head == null || head.next == null) {
            return head;
        }
        ListNode dummy = new ListNode(0);
        dummy.next = head;
        ListNode cur = dummy;
        ListNode slow = dummy;
        while(cur.next != null) {
            if(cur.next.val < x) {
                ListNode next = cur.next;
                cur.next = cur.next.next;
                ListNode next1 = slow.next;
                slow.next = next;
                next.next = next1;
                slow = slow.next;
                cur = next;
            } else {
                cur = cur.next;
            }
        }
        return dummy.next;
    }
}

 

posted on 2015-02-04 22:03  NickyYe  阅读(189)  评论(0)    收藏  举报