RominYue’s Blog

A lifeway of coders

Leetcode Problem 16-20 K-sum专题

| Comments

源代码push到了github上,戳这里

Problem 16: Letter Combinations of a Phone Number

题目描述: 手机上的虚拟键盘数字2-9都有对应的字母.现在给一个全是数字的字符串,让你把数字对应到相应的字母,写出所有的答案.

Input: Digit string “23”
Output: [“ad”, “ae”, “af”, “bd”, “be”, “bf”, “cd”, “ce”, “cf”]
解法:很简单的 dfs 或者 bfs

Problem 17: Valid Parentheses

题目描述:给定括号序列,判断其合法性 解法: 用栈模拟,经典题.

K-sum 专题

1 sum

题目描述:在给定序列里寻找一个数,使之等于target,返回该数.
解法:将数组排序,二分查找, 时间复杂度O(logn)

Binary Search
1
2
3
4
5
6
7
8
9
10
11
12
int binary_search(int a[],int n,int target)
{
  int l = 0, r = n - 1, mid;
    while(l <= r)
    {
      mid = (l + r) >> 1;
        if(a[mid] == target) return a[mid];
        else if(a[mid] > target) r = mid - 1;
        else l = mid + 1;
    }
    return -1;
}

解法二: 当然,如果找到一个比较好的hash函数或者hashtable, O(1)的复杂度就可以找到. 下述所有的涉及二分查找的方法都可以用hash法代替,故不讨论.

2 sum

题目描述: 在给定序列中寻找所有满足且不重复的元组(x,y),x <= y,使得 x + y == target
解法一: 参照 1 sum 的思路,先排序,固定住a,此时只要在数组中二分寻找存不存在数(target - x)即可

解法一
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
vector<vector<int> > two_sum_one(int a[],int n,int target)
{
  vector<vector<int> > ans;
  //fix x
    for(int i = 0; i < n; i++)
    {
      int tmp = binary_search(a,n,target - a[i]);
        if(tmp == -1 || tmp == i)continue;
        else
        {
          //find a solution
            vector<int> tmpans;
            tmpans.push_back(x);
            tmpans.push_back(y);
            ans.push_back(tmpans);
        }
    }
    //remove the duplicate answer in vector ans
    //we can sort vector ans and do the remove action
    return ans;
}

解法二: 对于一个有序数组 a <= b <= c <= d, 我们可以发现以下的不等式成立:

a + d <= b + d  
a + d >= a + c  

设想如果有两个指针start,end, start指向a, end指向d,如果此时start + end < target,那么依据上面的不等式,只需要将start指针迁移,后面的和一定比前面的和大.这样在O(n)的时间内就可以搞定. 排序的时间复杂度是O(nlogn),那么总的复杂度还是O(nlogn).这种思想在后面会用到.

解法二
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
vector<vector<int> > two_sum_two(int a[],int n,int target)
{
  vector<vector<int> > ans;
    for(int i = 0, k = n - 1; i < k;)
    {
      //this will remove the duplicate answer, think a bit
      if(i > 0 && a[i] == a[i-1])continue;

      if(a[i] + a[k] == target)
        {
          //find a solution
            vector<int> tmpans;
            tmpans.push_back(a[i]);
            tmpans.push_back(a[k]);
            ans.push_back(tmpans);
            i++;
            continue;
        }
        else if(a[i] + a[k] < target) i++;
        else k --;
    }
    return ans;
}

3 sum

题目描述: 在给定序列中寻找所有满足且不重复的元组(x,y,z),x <= y <= z,使得 x + y + z == target
经过上面的推导,显然此题也有两种解法.对于解法一,固定住x,y,二分查找z,时间复杂度是O(n2logn).对于解法二,固定住x,问题就便成了2 sum的问题.时间复杂度为O(n2).

4 sum

对于此问题而言,可以延续上面的解法,但是偶数-sum问题可以做一下化简.将数组两两之和求出来,形成新的数组,在新的数组里面只要找出两个数之和等于target即可.即4 sum问题转化为了 2 sum + 2 sum的问题.上述两种方法随便挑.时间复杂度都是O(n2).

K sum

显然,可以将K sum问题拆分成 K - i sum 和 i sum 两个问题求解,酌情考虑.

Problem 18: 3Sum

如上面所讨论的,我的实现是采用了第两个指针的方法.

problem 19: 4Sum

如上面讨论,我的实现是转化成了 3 sum 问题,时间复杂度是O(n3).

Problem 20: 3Sum Closest

题目描述: 和3 sum不同的唯一一点就是,他要在数组中找3个数,使得和最接近target.输出这个和.
解法:大体思想不变.我用的是两个指针的方法,那么每次只要取 abs(target - sum)的最小值,并在更新的时候记录这个差是正的还是负的(和比target大还是小).

ThreeSumClosest
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
int threeSumClosest(vector<int> &num, int target) {

    int ans = 1e9 +7,flag = 1;

    sort(num.begin(),num.end());

    int len = num.size();
    for(int i = 0; i < len - 2; i++)
    {
        for(int j = i + 1, k = len - 1; j < k;)
        {
            int tmp = num[j] + num[k] + num[i];
            if(tmp < target)
            {
                if(target - tmp < ans)
                {
                    ans = target - tmp;
                    flag = -1;
                }
                j++;
            }
            else
            {
                if(tmp - target < ans)
                {
                    ans = tmp - target;
                    flag = 1;
                }
                k--;
            }
        }
    }
    return target + flag*ans;
}

Comments