Showing posts with label sort. Show all posts
Showing posts with label sort. Show all posts

Wednesday, November 26, 2014

[LeetCode] Merge Intervals

Given a collection of intervals, merge all overlapping intervals.
For example,
Given [1,3],[2,6],[8,10],[15,18],
return [1,6],[8,10],[15,18].

思路:

从Insert Interval那题的解法,我们知道了如何判断两个interval是否重合,如果不重合,如何判断先后顺序。那么这题就很简单了,首先按照start的大小来给所有interval排序,start小的在前。然后扫描逐个插入结果。如果发现当前interval a和结果中最后一个插入的interval b不重合,则插入a到b的后面;反之如果重合,则将a合并到b中。注意要给object排序需要定义一个compare structure作为sort函数的额外参数。


 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
class Solution {
public:
    struct compInterval {
        bool operator()(const Interval &a, const Interval &b) const {
            return a.start<b.start;
        }
    };

    vector<Interval> merge(vector<Interval> &intervals) {
        sort(intervals.begin(),intervals.end(),compInterval());
        vector<Interval> ret;
        for(int i=0; i<intervals.size(); i++) {
            if(ret.empty() || ret.back().end < intervals[i].start)  // no overlap
                ret.push_back(intervals[i]);
            else   // overlap
                ret.back().end = max(ret.back().end, intervals[i].end);
        }
        return ret;
    }
};

Wednesday, November 19, 2014

[LeetCode] Insertion Sort List

Sort a linked list using insertion sort.

思路:
拿出原list的头节点,用一个指针扫描新list直到找到插入位置,并插入。注意点:
1. 用dummy head来简化新list的头节点操作。
2. 由于插入节点需要依赖插入位置的前一节点。所以用指针p来查找新list节点时,始终用p->next来和要插入的节点比较,而不是用p来比较。
3. 注意当节点需要插入新list尾部的情况。


 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
class Solution {
public:
    ListNode *insertionSortList(ListNode *head) {
        ListNode *newHead = new ListNode(INT_MIN);
        while(head) {
            ListNode *cur = head;
            ListNode *p = newHead;
            head = head->next;
            while(p->next && p->next->val<=cur->val) 
                p = p->next;
            cur->next = p->next;
            p->next = cur;
        }
      
        head = newHead->next;
        delete newHead;
        return head;
    }
};

Saturday, November 15, 2014

[LeetCode] Sort Colors

Given an array with n objects colored red, white or blue, sort them so that objects of the same color are adjacent, with the colors in the order red, white and blue.
Here, we will use the integers 0, 1, and 2 to represent the color red, white, and blue respectively.
Note:
You are not suppose to use the library's sort function for this problem.

思路:

这里要求one pass完成排序,需要利用只有数组元素只有3个数的特性,否则无法完成。排序完成后一定是0...01...12....2,所以可以扫描数组,当遇到0时,交换到前部,当遇到1时,交换到后部。用双指针left, right来记录当前已经就位的0序列和2序列的边界位置。

假设已经完成到如下所示的状态:

0......0   1......1  x1 x2 .... xm   2.....2
              |           |               |
            left        cur          right

(1) A[cur] = 1:已经就位,cur++即可
(2) A[cur] = 0:交换A[cur]和A[left]。由于A[left]=1或left=cur,所以交换以后A[cur]已经就位,cur++,left++
(3) A[cur] = 2:交换A[cur]和A[right],right--。由于xm的值未知,cur不能增加,继续判断xm。
cur > right扫描结束。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
class Solution {
public:
    void sortColors(int A[], int n) {
        int left=0, right=n-1;
        int i = 0;
        while(i<=right) {
            if(A[i]==0) 
                swap(A[i++],A[left++]);
            else if(A[i]==1) 
                i++;
            else if(A[i]==2) 
                swap(A[i],A[right--]);
        }
    }
};

[LeetCode] 4Sum

Given an array S of n integers, are there elements abc, and d in S such that a + b + c + d = target? Find all unique quadruplets in the array which gives the sum of target.
Note:
  • Elements in a quadruplet (a,b,c,d) must be in non-descending order. (ie, a ≤ b ≤ c ≤ d)
  • The solution set must not contain duplicate quadruplets.
    For example, given array S = {1 0 -1 0 -2 2}, and target = 0.

    A solution set is:
    (-1,  0, 0, 1)
    (-2, -1, 1, 2)
    (-2,  0, 0, 2)



思路:

既然已经做了2sum, 3sum, 3sum closest。自然能推广到4sum。但这里希望能推广到更普遍的k-sum问题。这里使用递归的思路:

1. k-sum问题可以转化为(k-1)-sum问题:对于数组中每个数A[i],在A[i+1:n-1]中寻找target-A[i]的(k-1)-sum问题。
2. 直到k=2时,用2sum的双指针扫描来完成。

去重复解的技巧和3Sum问题一模一样。


 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
class Solution {
public:
    vector<vector<int> > fourSum(vector<int> &num, int target) {
        vector<vector<int>> allSol;
        vector<int> sol;
        sort(num.begin(),num.end());
        kSum(num, 0, num.size()-1, target, 4, sol, allSol);
        return allSol;
    }
    
    void kSum(vector<int> &num, int start, int end, int target, int k, vector<int> &sol, vector<vector<int>> &allSol) {
        if(k<=0) return;
        if(k==1) {
            for(int i=start; i<=end; i++) {
                if(num[i]==target) {
                    sol.push_back(target);
                    allSol.push_back(sol);
                    sol.pop_back();
                    return;
                }
            }
        } 
        
        if(k==2) {
            twoSum(num, start, end, target, sol, allSol);
            return;
        }
    
        for(int i=start; i<=end-k+1; i++) {
            if(i>start && num[i]==num[i-1]) continue;
            sol.push_back(num[i]);
            kSum(num, i+1, end, target-num[i], k-1, sol, allSol);
            sol.pop_back();
        }
    }
    
    void twoSum(vector<int> &num, int start, int end, int target, vector<int> &sol, vector<vector<int>> &allSol) {
        while(start<end) {
            int sum = num[start]+num[end];
            if(sum==target) {
                sol.push_back(num[start]);
                sol.push_back(num[end]);
                allSol.push_back(sol);
                sol.pop_back();
                sol.pop_back();
                start++;
                end--;
                while(num[start]==num[start-1]) start++;
                while(num[end]==num[end+1]) end--;
            }
            else if(sum<target)
                start++;
            else
                end--;
        }
    }
};

[LeetCode] 3Sum Closest

Given an array S of n integers, find three integers in S such that the sum is closest to a given number, target. Return the sum of the three integers. You may assume that each input would have exactly one solution.
    For example, given array S = {-1 2 1 -4}, and target = 1.

    The sum that is closest to the target is 2. (-1 + 2 + 1 = 2).



思路:

3sum问题的变种。一样的遍历每个数,对剩余数组进行双指针扫描。区别仅仅在于当:
sum = A[left] + A[right]
(1) sum = target时直接返回
(2) sum != target时,在相应移动left/right指针之前,先计算abs(sum-target)的值,并更新结果。


 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
class Solution {
public:
    int threeSumClosest(vector<int> &num, int target) {
        if(num.size()<3) return INT_MAX;
        sort(num.begin(),num.end());
        int minDiff = INT_MAX;
        for(int i=0; i<num.size()-2; i++) {
            int left=i+1, right = num.size()-1;
            while(left<right) {
                int diff = num[i]+num[left]+num[right]-target;
                if(abs(diff)<abs(minDiff)) minDiff = diff;
                if(diff==0) 
                    break;
                else if(diff<0)
                    left++;
                else
                    right--;
            }
        } 
        return target+minDiff;
    }
};

[LeetCode] 3Sum

Given an array S of n integers, are there elements abc in S such that a + b + c = 0? Find all unique triplets in the array which gives the sum of zero.
Note:
  • Elements in a triplet (a,b,c) must be in non-descending order. (ie, a ≤ b ≤ c)
  • The solution set must not contain duplicate triplets.
    For example, given array S = {-1 0 1 2 -1 -4},

    A solution set is:
    (-1, 0, 1)
    (-1, -1, 2)


思路:

题目要求的是a+b+c=0,问题可以推广到a+b+c=target。3sum问题可以转化为2sum问题:对于任意一个A[i],在数组中的其他数中解2sum问题,目标为target-A[i]。与2sum那题不同,这题要求返回的不是index而是数字本身,并且解不唯一。同时要求解排序并去重。

对排序来说,2sum中的双指针法更为方便,因为算法本身就用到排序。双指针排序法本身会去除一些重复的可能性:

(1, 2, 3, 4), target = 6
在扫描1时,解(2, 3, 4)的2sum = 5问题,找到一个解(1, 2, 3)。
在扫描2时,应当只对后面的数解2sum问题,即对(3, 4)解2sum = 4问题。这样避免再次重复找到解(1, 2, 3)。

但当存在重复数字时,光靠排序仍然无法去重:

(1, 2, 2, 2, 3, 4), target = 9
扫描第一个2时,解(2, 2, 3, 4)中的2sum=7问题,得到解(2, 3, 4)
扫描第二个2时,解(2, 3, 4)中的2sum=7问题,仍然会得到(2, 3, 4)

去除因重复数字而造成重复解有两个办法,一是将结果存到一个hash table中。由于STL的hash table (unordered_set, unordered_map)并不能为vector类型,除非自己提供一个hash function,颇为不便,也增加额外存储空间。而另一种方法就是在扫描数组时跳过重复的数字。上例中,只扫描1, 2, 3, 4来求相应的2sum问题。进一步简化,可以只扫描1, 2。因为3已经是倒数第二个数字,不可能有以它为最小数字的解。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
class Solution {
public:
    vector<vector<int> > threeSum(vector<int> &num) {
        return threeSumGen(num, 0);
    }
    
    vector<vector<int> > threeSumGen(vector<int> &num, int target) {
        vector<vector<int>> allSol;
        if(num.size()<3) return allSol;
        sort(num.begin(),num.end());
        for(int i=0; i<num.size()-2; i++) {
            if(i>0 && num[i]==num[i-1]) continue;
            int left=i+1, right=num.size()-1;
            while(left<right) {
                int curSum = num[left]+num[right];
                int curTarget = target-num[i];
                if(curSum==curTarget) {
                    vector<int> sol;
                    sol.push_back(num[i]);
                    sol.push_back(num[left]);
                    sol.push_back(num[right]);
                    allSol.push_back(sol);
                    left++;
                    right--;
                    while(num[left]==num[left-1]) left++;
                    while(num[right]==num[right+1]) right--;
                }
                else if(curSum<curTarget)
                    left++;
                else
                    right--;
            }
        }
        return allSol;
    }
};

总结:

这题原理上并不难,但要正确做好去重,还是要注意不少细节。红色部分的代码即为去重部分。ln 12为扫描时候的去重,ln 23-26为2sum时的去重。

[LeetCode] Two Sum

Given an array of integers, find two numbers such that they add up to a specific target number.
The function twoSum should return indices of the two numbers such that they add up to the target, where index1 must be less than index2. Please note that your returned answers (both index1 and index2) are not zero-based.
You may assume that each input would have exactly one solution.
Input: numbers={2, 7, 11, 15}, target=9
Output: index1=1, index2=2


思路1:hash table

对于数组中每个数来A[i]来说,需要在数组的其他元素中寻找target - A[i]。所以问题转化为数组查找元素问题,当给定一个值时,需要能快速判断是否存在于数组中,如果存在,index是多少。

可以将所有元素插入一个hash table <key = A[i], val = i>,然后重新遍历每个元素,计算target - A[i],然后在hash table中查找。这里仍需解决2个问题:

(1) 重复元素的问题:比如{2 2 3}, target = 4。解决方法为hash table的val变为一个vector<int>来记录所有j:A[j] = A[i]。
(2) index 1 < index 2:假设A[i] + A[j] = target并且i<j。那么在遍历查找时从左向右,一定在扫描i的时候就找到个solution。返回按照这个顺序即可。

时间和额外空间复杂度均为O(n)

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
class Solution {
public:
    vector<int> twoSum(vector<int> &numbers, int target) {
        vector<int> res;
        int first = -1, second = -1;
        unordered_map<int, vector<int> > ht;
        for(int i=0; i<numbers.size(); i++) 
            ht[numbers[i]].push_back(i);
        
        for(int i=0; i<numbers.size(); i++) {
            int val = target - numbers[i];
            if(ht.count(val)) {
                if(numbers[i]!=val) {
                    first = i+1;
                    second = ht[val][0]+1;
                    break;
                }
                else if(ht[val].size()>1) {
                    first = ht[val][0]+1;
                    second = ht[val][1]+1;
                    break;
                }
            }
        }
        
        res.push_back(first);
        res.push_back(second);
        return res;
    }
};

思路2:two pointers

将array排序,双指针left/right分别指向头尾。然后两个指针分别向中间移动寻找目标。
(1) A[left] + A[right] = target:直接返回(left+1, right+1)。
(2) A[left] + A[right] > target:说明A[right]不可能是解,right--
(3) A[left] + A[right] < target:说明A[left]不可能是解,left++
中止条件:left >= right

但排序会打乱原来数组index的顺序。我们可以建立一个class/struct/pair来存储val/index,并overload operator < 来以val值排序。这样我们可以track排序后每个数的原有index。

重复元素这里无须特殊处理。index 1和index 2分别取找到的两个index的min/max即可。时间复杂度由于排序的关系为O(n log n),额外空间复杂度O(n)。


 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
class Solution {
    class elem {
    public:    
        int val;
        int index;
        elem(int v, int i):val(v),index(i) {}
        bool operator<(const elem &e) const {
            return val<e.val;
        }
    };
    
public:
    vector<int> twoSum(vector<int> &numbers, int target) {
        vector<int> res(2,-1);
        vector<elem> arr;
        for(int i=0; i<numbers.size(); i++) 
            arr.push_back(elem(numbers[i],i));

        sort(arr.begin(),arr.end());
        int left = 0, right = arr.size()-1;
        while(left<right) {
            if(arr[left].val+arr[right].val==target) {
                res[0] = min(arr[left].index,arr[right].index)+1;
                res[1] = max(arr[left].index,arr[right].index)+1;
                break;
            }
            else if(arr[left].val+arr[right].val<target) 
                left++;
            else
                right--;
        }
        return res;
    }
};