ch7 - 双指针Two Pointers

双指针的题目分为以下几类:

  • 同向双指针
  • 相向双指针
  • Two Sum - 几乎所有Two Sum的变种
  • Partition - Quick Select (★★★★★)
    - 分成两部分
    - 分成三部分
  • 一些你没听过的(但是面试会考的)排序算法

目录:

  • 1.同向双指针 - Windows Sum(604 in lintcode)
  • 2.同向双指针 - Move Zeroes (539 in lintcode)
  • 3.同向双指针 - remove-duplicate-numbers-in-array (521 in lintcode
  • 4.相向双指针 - valid-palindrome 有效回文串 (415 in lintcode)
  • 5.相向双指针 - rotate-string 旋转字符串 (8 in lintcode)
  • 6.相向双指针 - recover-rotated-sorted-array 恢复旋转排序数组 (39 in lintcode)
  • 7.Two-Sum - A. two-sum-input-array-is-sorted、(★★★★★)
    • B. two-sum 两数之和 ( 56 in lintcode)、
    • C. two-sum-data-structure-design ( 607 in lintcode )
    • D. two-sum-unique-pairs ( 587 in lintcode ) (★★★★★)
    • E. two-sum-less-than-or-equal-to-target 统计所有和<=target的配对数 ( 609 in lintcode )
    • F. two-sum-greater-than-target ( 443 in lintcode )
  • 8.3-Sum - 3sum (57 in lintcode )(★★★★★)
  • 9.3-Sum - triangle-count (382 in lintcode )
  • 10.two-sum-closest-to-target (533 in lintcode )
  • 11.3Sum-closest ( 59 in lintcode )
  • 12.4sum (58 in lintcode )
  • 13.two-sum-difference-equals-to-target (610 in lintcode ) ( 同向双指针 )
  • 14.partition-array 分而不治 (31 in lintcode )(★★★★★)
  • 15.sort-colors 颜色分类(148 in lintcode ) - follow up(partition-array-ii 将数组分为三部分)
  • 16.sort-colors-ii 彩虹排序(143 in lintcode )
  • 17.其他有趣的排序

心得:

1). 对于求2个变量如何组合的问题,可以循环其中一个变量,然后研究另外一个变量如何变化。对于3个变量如何组合的问题,可以循环其中一个变量,看其他2个怎么变化,或者循环其中两个的和,看剩下的一个怎么变化。
2).求k-sum,除了two pointers,还可以使用hash表来做。

如4-sum,a+b+c+d = 0,可以将a+b这样的pairs放入hash表中,key是两个数的和,value是所有和为该值的pair对。
最坏时间复杂度为O(n^(k-1))

3).k-sum的最坏时间复杂度:O(n^(k-1)),无论用哪种方式。
4). 基于比较的排序最快就是O(nlogn)

同向双指针

1. 同向双指针 - Windows Sum(604 in lintcode)

1.1 题目

http://www.lintcode.com/zh-cn/problem/window-sum/
http://www.jiuzhang.com/solution/window-sum/

给你一个大小为n的整型数组和一个大小为k的滑动窗口,将滑动窗口从头移到尾,输出从开始到结束每一个时刻滑动窗口内的数的和。

1.2 思路

两个指针分别指向窗口的开始和结束位置。依次后移,加窗口末尾数,减去窗口起始数,然后右移一格。

1.3 代码

我的代码 - 记录窗口的末尾位置

class Solution {
public:
    /*
     * @param nums: a list of integers.
     * @param k: length of window.
     * @return: the sum of the element inside the window at each moving.
     */
    vector<int> winSum(vector<int> &nums, int k) {
        // write your code here
        vector<int> res;
        if(nums.size() < k || k<=0){ //临界条件
            return res;
        }

        int sum = 0;
        for(int i=0;i<k;++i){
            sum += nums[i];
        }
        res.push_back(sum);

        for(int i = k;i<nums.size();++i){
            sum += nums[i];
            sum -= nums[i-k];
            res.push_back(sum);
        }

        return res;
    }
};

参考答案 - 记录窗口的开始位置

class Solution {
    public:
    vector<int> winSum(vector<int> &nums, int k) {
          // write your code here
          if (nums.size() < k || k <= 0)
               return vector<int>();

          int n = nums.size();
          vector<int> sums(n - k + 1, 0);
          for (int i = 0; i < k; i++)
               sums[0] += nums[i];
          for (int i = 1; i < n - k + 1; i++) {
               sums[i] = sums[i-1] - nums[i-1] + nums[i + k-1];
          }

          return sums;
     }
};

1.4 follow up

 求窗口的最大值.

2. 同向双指针 - Move Zeroes (539 in lintcode)

2.1 题目

http://www.lintcode.com/zh-cn/problem/move-zeroes/
http://www.jiuzhang.com/solution/move-zeroes/

给一个数组 nums 写一个函数将 0 移动到数组的最后面,非零元素保持原数组的顺序
给出 nums = [0, 1, 0, 3, 12], 调用函数之后, nums = [1, 3, 12, 0, 0].

2.2 思路

两个指针left、right,一快一慢,快指针不停往后走,遇到0不处理,慢指针始终指向最左边的0。

left指向无0区域的后面一个位置,right扫一遍数组。看到任何非0数,往前换。

快排中的partition不能保证排序的稳定性,这道题目要求排序的稳定性,如[1,1’ , 1’’]

2.3 代码

class Solution {
public:
    /*
     * @param nums: an integer array
     * @return: 
     */
    void moveZeroes(vector<int>& nums) {
        int left=0, right=0;
        while(right < nums.size()){
            if(nums[right] != 0){
                int tmp = nums[left];
                nums[left] = nums[right];
                nums[right] = tmp;
                left++;
            }
            right++;
        }
    }
};

3. 同向双指针 - remove-duplicate-numbers-in-array (521 in lintcode)

3.1 题目

http://www.lintcode.com/zh-cn/problem/remove-duplicate-numbers-in-array/
http://www.jiuzhang.com/solution/remove-duplicate-numbers-in-array/

给一个整数数组,去除重复的元素。
你应该做这些事
1.在原数组上操作
2.将去除重复之后的元素放在数组的开头
3.返回去除重复元素之后的元素个数

样例:
给出 nums = [1,3,1,4,4,2],你需要做以下操作
1.将重复元素扔在最后面 => nums = [1,3,4,2,?,?].
2.返回个数 4
实际上我们并不在意?是什么

3.2 思路 & 代码

1) 方法1:使用hash表,时间复杂度为O(n)
class Solution {
public:
    /*
     * @param nums: an array of integers
     * @return: the number of unique integers
     */
    int deduplication(vector<int> &nums) {
        if(nums.size() == 0){
            return 0;
        }

        unordered_map<int,bool> m;
        for(int i=0;i<nums.size();++i){
            if(m.find(nums[i]) == m.end()){
                m[nums[i]] = true;
            }
        }

        int k=0;
        for(unordered_map<int,bool>::iterator it = m.begin();it!=m.end();++it){
            nums[k++] = it->first;
        }

        return m.size();
    }
};
2) 方法2:不使用额外空间,时间复杂度为O(nlogn) - sort

index指向无重复区域的最后一个元素。

class Solution {
public:
    /*
     * @param nums: an array of integers
     * @return: the number of unique integers
     */
    int deduplication(vector<int> &nums) {
        if(nums.size() == 0){
            return 0;
        }

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

        int index = 0; //无重复区域的最后一个数
        for(int i = 0;i<nums.size();++i){
            if(nums[i] != nums[index]){
                nums[++index] = nums[i];
            }
        }

        return index+1;
    }
};

第二部分:相向双指针

4. 相向双指针 - valid-palindrome 有效回文串 (415 in lintcode)

4.1 题目

http://www.lintcode.com/zh-cn/problem/valid-palindrome/
http://www.jiuzhang.com/solution/valid-palindrome/

给定一个字符串,判断其是否为一个回文串。只包含字母和数字,忽略大小写。

“A man, a plan, a canal: Panama” 是一个回文。
“race a car” 不是一个回文。

4.2 求解

1). 左右两个指针一次比较,并向中间移动。这道题目需要注意对非字母数字的处理,以及大小写字母的处理。
2). isalnum等函数
defined in header <cctype>
int isalnum( int ch );
3).STL算法 transform函数

该算法用于实行容器元素的变换操作。有如下两个使用原型,一个将迭代器区间[first,last)中元素,执行一元函数对象op操作,交换后的结果放在[result,result+(last-first))区间中。另一个将迭代器区间[first1,last1)的元素i,依次与[first2,first2+(last-first))的元素j,执行二元函数操作binary_op(*i,*j),交换结果放在[result,result+(last1-first1))。

1. template < class InputIterator, class OutputIterator, class UnaryOperator >  
2.   OutputIterator transform ( InputIterator first1, InputIterator last1,  
3.                              OutputIterator result, UnaryOperator op );  
4.   
5. template < class InputIterator1, class InputIterator2,  
6.            class OutputIterator, class BinaryOperator >  
7.   OutputIterator transform ( InputIterator1 first1, InputIterator1 last1,  
8.                              InputIterator2 first2, OutputIterator result,  
9.                              BinaryOperator binary_op ); 
class Solution {
public:
    /*
     * @param s: A string
     * @return: Whether the string is a valid palindrome
     */
    bool isPalindrome(string &s) {
        // write your code here
        transform(s.begin(),s.end(),s.begin(),::tolower);

        auto left = s.begin(), right = prev(s.end());
        while(left < right){
            if(!::isalnum(*left)){
                ++left;   
            }
            else if(!::isalnum(*right)){
                --right;
            }
            else if(*left != *right){
                return false;
            }
            else{
                left++;
                right--;
            }
        }

        return true;
    }
};

5. 相向双指针 - rotate-string 旋转字符串 (8 in lintcode)

5.1 题目

http://www.lintcode.com/zh-cn/problem/recover-rotated-sorted-array/
http://www.jiuzhang.com/solution/rotate-string/

给定一个字符串和一个偏移量,根据偏移量旋转字符串(从左向右旋转)

对于字符串 “abcdefg”.

 offset=0 => "abcdefg"
 offset=1 => "gabcdef"
 offset=2 => "fgabcde"
 offset=3 => "efgabcd"

5.2 思路 & 解法

 三次翻转法:1). 整体翻转;
           2). 翻转前offset位;
           3). 翻转剩下的位数;
class Solution {
public:
    /*
     * @param str: An array of char
     * @param offset: An integer
     * @return: nothing
     */
    void rotateString(string &str, int offset) {
        // write your code here
        if(str.size()==0){
            return;
        }

        offset = offset%str.size();  //!!! 
        reverse(str, 0, str.size()-1);
        reverse(str, 0, offset-1);
        reverse(str, offset, str.size()-1);
    }

    void reverse(string &str, int start, int end){
        for(int i=start, j=end;i<j;i++,j--){
            char ch = str[i];
            str[i] = str[j];
            str[j] = ch;
        }
    }
};

6. 相向双指针 - recover-rotated-sorted-array 恢复旋转排序数组 (39 in lintcode)

6.1 题目

http://www.lintcode.com/zh-cn/problem/recover-rotated-sorted-array/
http://www.jiuzhang.com/solution/recover-rotated-sorted-array/

给定一个旋转排序数组,在原地恢复其排序。
[4, 5, 1, 2, 3] -> [1, 2, 3, 4, 5]

6.2 思路 & 解法

找到排序数组的开头,然后用上题中的方法进行旋转右移。

class Solution {
public:
    /*
     * @param nums: An integer array
     * @return: nothing
     */
    void recoverRotatedSortedArray(vector<int> &nums) {
        //write your code here
        for(int i=0;i<nums.size()-1;++i){
            if(nums[i] > nums[i+1]){
                int offset = nums.size()-(i+1);
                reverse(nums, 0, nums.size()-1);
                reverse(nums, 0, offset-1);
                reverse(nums, offset, nums.size()-1);
            }
        }
    }

    void reverse(vector<int> &nums, int start, int end){
        for(int i=start, j=end;i<j;i++,j--){
            int t = nums[i];
            nums[i] = nums[j];
            nums[j] = t;
        }
    }
};

7.第三部分 Two-Sum -

A. Two-Sum - two-sum-input-array-is-sorted ( 608 in lintcode ) (★★★★★)

当数组已经排好序时:
http://www.lintcode.com/zh-cn/problem/two-sum-input-array-is-sorted/

class Solution {
public:
    /*
     * @param nums: an array of Integer
     * @param target: target = nums[index1] + nums[index2]
     * @return: [index1 + 1, index2 + 1] (index1 < index2)
     */
    vector<int> twoSum(vector<int> &nums, int target) {
        // write your code here
        vector<int> res;

        int i = 0, j = nums.size()-1;
        while(i<j){
            if(nums[i] + nums[j] > target){
                j--;
            }
            else if(nums[i] + nums[j] < target){
                ++i;
            }
            else{
                res.push_back(i+1);
                res.push_back(j+1);
                return res;
            }
        }
        return res;
    }
};

B.Two-Sum - two-sum 两数之和 ( 56 in lintcode )

7.1 题目

http://www.lintcode.com/zh-cn/problem/two-sum/
http://www.jiuzhang.com/solution/two-sum/

给一个整数数组,找到两个数使得他们的和等于一个给定的数 target。
你需要实现的函数twoSum需要返回这两个数的下标, 并且第一个下标小于第二个下标。注意这里下标的范围是 0 到 n-1。

给出 numbers = [2, 7, 11, 15], target = 9, 返回 [0, 1].

7.2 思路 & 代码

               time          space
 HashSet       O(n)           O(n)
 binarySearch O(nlogn)        O(1)  //先排序之后再二分法查找。但是没第三个方法好
 Sort + 2P    O(nlogn)        O(1)
方法1:HashSet

每次将一个数x放入HashSet之前,查找target-x是否在hash表中。

class Solution {
public:
    /*
     * @param numbers: An array of Integer
     * @param target: target = numbers[index1] + numbers[index2]
     * @return: [index1 + 1, index2 + 1] (index1 < index2)
     */
    vector<int> twoSum(vector<int> &numbers, int target) {
        // write your code here
        vector<int> res;
        unordered_map<int,int> m;
        for(int i=0;i<numbers.size();++i){
            if(m.find(target - numbers[i]) == m.end()){
                m[numbers[i]] = i;
            }
            else{
                res.push_back(m[target - numbers[i]]);
                res.push_back(i);
                return res;
            }
        }
        return res;
    }
};
方法2:Sort + 2P

1). 排序之后,一前一后两个指针向中间走。
在这里插入图片描述
不能排序后,再使用2P,因为排序后下标顺序改变。下述方法的复杂度是O(n^2)。

class Solution {
public:
    /*
     * @param numbers: An array of Integer
     * @param target: target = numbers[index1] + numbers[index2]
     * @return: [index1 + 1, index2 + 1] (index1 < index2)
     */
    vector<int> twoSum(vector<int> &numbers, int target) {
        // write your code here
        int l=numbers.size();
        int i,j;
        int flag=0;//flag作为找到答案后跳出的一个标记用变量
        for(i=0;i<l;++i)
        {
            for(j=i+1;j<l;++j)
            {
                if(numbers[i]+numbers[j]==target)
                {
                    flag=1;
                    break;
                }
            }
            if(flag)
                break;
        }
        vector<int> ans;
        ans.push_back(i);
        ans.push_back(j);
        return ans;
    }
};
方法3:BS
 for(i=0;i<n-1;++i){
      binarySearch( i, n-1, target-num[i] ); //从 i 开始 二分查找target-num[i].
 }

time: O(log1 + log 2 + log 3+ … + logn) = O(nlogn)

C.Two-Sum - two-sum-data-structure-design ( 607 in lintcode ) - 数据流

设计b并实现一个 TwoSum 类。他需要支持以下操作:add 和 find。
add -把这个数添加到内部的数据结构。
find -是否存在任意一对数字之和等于这个值

1). 局部维护一个数组,每次find的时候先sort,然后再2 Pointers
 此时add是O(1), find是O(nlogn)
2). add的时候就binary search插入,花费O(logn)的时间,find的时候2P,O(n)
3). 维护一个hashset或hashmap<num,count>,这样add是O(1), find是O(n);√

java:

public class TwoSum {

    private List<Integer> list = null;
    private Map<Integer, Integer> map = null; //hash_map的find时间是O(1)
    public TwoSum() {
        list = new ArrayList<Integer>();
        map = new HashMap<Integer, Integer>();
    }

    // Add the number to an internal data structure.
    public void add(int number) {
        // Write your code here
        if (map.containsKey(number)) {
            map.put(number, map.get(number) + 1);
        } else {
            map.put(number, 1);
            list.add(number);
        }
    }

    // Find if there exists any pair of numbers which sum is equal to the value.
    public boolean find(int value) {
        // Write your code here
        for (int i = 0; i < list.size(); i++) {
            int num1 = list.get(i), num2 = value - num1;
            if ((num1 == num2 && map.get(num1) > 1) ||
                (num1 != num2 && map.containsKey(num2)))
                return true;
        }
        return false;
    }
}

C++:

public class TwoSum {

    unordered_multiset<int> nums; // unordered库的底层是hash实现,multiset保证set中可以插入相同元素

    // Add the number to an internal data structure.
    public void add(int number) {
        nums.insert(number);
    }

    // Find if there exists any pair of numbers which sum is equal to the value.
    public boolean find(int value) {
        for(int i:nums){
            int count = i == value-i ? 2:1;
            if(nums.count(value-i) >= count){
                return true;
            }
        }

        return false;
    }
}

D.Two-Sum - two-sum-unique-pairs ( 587 in lintcode ) -(★★★★★)

1) 题目

http://www.lintcode.com/zh-cn/problem/two-sum-unique-pairs/
http://www.jiuzhang.com/solution/two-sum-unique-pairs/

给一整数数组, 找到数组中有多少组 不同的元素对 有相同的和, 且和为给出的 target 值, 返回对数.

2)思路

找方案数的问题,不是先找到所有答案再去重,而是搜的过程中避免找重复的。

在之前2 pointers的基础上,每次找到等于target的pair后,left++,right–,而不是直接返回。

如何去重? - [1,1,…, 18,18] 当挪动到某个数时,一直挪到与当前不一样的数为止。

3) 代码

class Solution {
public:
    /*
     * @param nums: an array of integer
     * @param target: An integer
     * @return: An integer
     */
    int twoSum6(vector<int> &nums, int target) {
        // write your code here
        if(nums.size()==0){
            return 0;
        }

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

        int cnt = 0;
        int left = 0, right = nums.size()-1;
        while(left < right){
            if(nums[left] + nums[right] == target){
                cnt++;
                left++;
                right--;
                while(left<right && nums[left] == nums[left-1]){
                    left++;
                }
                while(left<right && nums[right] == nums[right+1]){
                    right--;   
                }
            }
            else if(nums[left] + nums[right] > target){
                right--;
            }
            else{
                left++;
            }
        }
        return cnt;
    }
};

E. two-sum-less-than-or-equal-to-target 统计所有和<=target的配对数 ( 609 in lintcode )

1.题目

http://www.lintcode.com/zh-cn/problem/two-sum-less-than-or-equal-to-target/
http://www.jiuzhang.com/solution/two-sum-less-than-or-equal-to-target/

给定一个整数数组,找出这个数组中有多少对的和是小于或等于目标值。返回对数。

样例
给定数组为 [2,7,11,15],目标值为 24
返回 5。
2+7<24
2+11<24
2+15<24
7+11<24
7+15<24

2.代码

class Solution {
public:
    /*
     * @param nums: an array of integer
     * @param target: an integer
     * @return: an integer
     */
     //不去重
    int twoSum5(vector<int> &nums, int target) {
        // write your code here
        if(nums.size()==0){
            return 0;
        }

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

        int cnt = 0;
        int left = 0, right = nums.size()-1;
        while(left < right){
            if(nums[left] + nums[right] <= target){
                cnt += right-left;
                left++;
            }
            else{
                right--;
            }
        }
        return cnt;
    }
};

F. two-sum-greater-than-target ( 443 in lintcode )

class Solution {
public:
    /*
     * @param nums: an array of integer
     * @param target: An integer
     * @return: an integer
     */
    int twoSum2(vector<int> &nums, int target) {
        // write your code here
        if(nums.size()==0){
            return 0;
        }

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

        int cnt = 0;
        int left = 0, right = nums.size()-1;
        while(left < right){
            if(nums[left] + nums[right] > target){
                cnt += right-left;
                right--;
            }
            else{
                left++;
            }
        }
        return cnt;

    }
};

8. 3-Sum - 3sum (57 in lintcode)(★★★★★)

1.题目

http://www.lintcode.com/zh-cn/problem/3sum/
http://www.jiuzhang.com/solution/3sum/

给出一个有n个整数的数组S,在S中找到三个整数a, b, c,找到所有使得a + b + c = 0的三元组。

样例
如S = {-1 0 1 2 -1 -4}, 你需要返回的三元组集合的是:
(-1, 0, 1)
(-1, -1, 2)

2.思路 & 代码

1). 比two-sum更严格的要求用two Pointers。
2) hash:O(n^2)时间 + O(n)空间。 将数都放在hash表中,然后两重循环,分别是num1,num2,查找target-num1-num2是否在hash表中。
3)Two pointers:O(n^2) time + O(1) extra space.

排序 -> for循环最小的一个数xi,则需要在xi+1~xn之间去找Two Sum

class Solution {
public:
    /*
     * @param numbers: Give an array numbers of n integer
     * @return: Find all unique triplets in the array which gives the sum of zero.
     */
    vector<vector<int>> threeSum(vector<int> &numbers) {
        // write your code here
        vector<vector<int> > res;
        if(numbers.size() < 3){
            return res;
        }

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

        for(int i=0;i<numbers.size()-2;++i){
            if(i>0 && numbers[i] == numbers[i-1]){
                continue;
            }

            int left = i+1, right = numbers.size()-1;
            int target = -numbers[i];
            TwoSum(numbers, left, right, target, res);
        }

        return res;
    }

    void TwoSum(vector<int> &nums, int left, int right, int target, vector<vector<int>> &res){
        while(left < right){
            if(nums[left] + nums[right] == target){
                vector<int> pair;
                pair.push_back(-target);
                pair.push_back(nums[left]);
                pair.push_back(nums[right]);
                res.push_back(pair);

                left++;
                right--;

                while(left<right && nums[left] == nums[left-1]){
                    left++;
                }

                while(left<right && nums[right] == nums[right+1]){
                    right--;   
                }
            }
            else if(nums[left] + nums[right] > target){
                right--;
            }
            else{
                left++;
            }
        }
    }
};

9. 3-Sum - triangle-count (382 in lintcode )

9.1 题目

http://www.lintcode.com/zh-cn/problem/triangle-count/
http://www.jiuzhang.com/solution/triangle-count/

给定一个整数数组,在该数组中,寻找三个数,分别代表三角形三条边的长度,问,可以寻找到多少组这样的三个数来组成三角形?

样例
例如,给定数组 S = {3,4,6,7},返回 3
其中我们可以找到的三个三角形为:
{3,4,6}
{3,6,7}
{4,6,7}

给定数组 S = {4,4,4,4}, 返回 4
其中我们可以找到的三个三角形为:
{4(1),4(2),4(3)}
{4(1),4(2),4(4)}
{4(1),4(3),4(4)}
{4(2),4(3),4(4)}

9.2 思路 & 代码

1). 三角形的充要条件是 《=》 两条最小的边之和大于第三边
2). 思路:枚举最大边的所有可能性c,即整个数组扫一遍;然后再0~c-1之间,找left、right之和大于A[c]的
3). 代码:
class Solution {
public:
    /*
     * @param S: A list of integers
     * @return: An integer
     */
    int triangleCount(vector<int> &S) {
        // write your code here
        int cnt = 0;

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

        for(int i=0;i<S.size();++i){
            int left = 0, right = i-1;
            while(left < right){
                if(S[left]+S[right] > S[i]){
                    cnt += right-left;
                    right--;
                }
                else{
                    left++;
                }
            }
        }
        return cnt;
    }
};

10. two-sum-closest-to-target (533 in lintcode )

10.1 题目

http://www.lintcode.com/zh-cn/problem/two-sum-closest-to-target/
http://www.jiuzhang.com/solution/two-sum-closest-to-target/

找到两个数字使得他们和最接近target。

样例
nums = [-1, 2, 1, -4],target = 4.
最接近值为 1.

10.2 思路 & 代码

在这里插入图片描述

class Solution {
public:
    /*
     * @param nums: an integer array
     * @param target: An integer
     * @return: the difference between the sum and the target
     */
    int twoSumClosest(vector<int> &nums, int target) {
        // write your code here
        if(nums.size()==0){
            return -1;
        }

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

        int left = 0, right = nums.size()-1;
        int best = INT_MAX;
        while(left < right){
            int diff = abs(nums[left] + nums[right] - target);
            best = min(best, diff);
            if(nums[left] + nums[right] > target){
                right--;
            }
            else{
                left++;
            }
        }
        return best;
    }
};

11. 3Sum-closest ( 59 in lintcode )

11.1 题目

http://www.lintcode.com/zh-cn/problem/3sum-closest/
http://www.jiuzhang.com/solution/3sum-closest/

给一个包含 n 个整数的数组 S, 找到和与给定整数 target 最接近的三元组,返回这三个数的和。

样例
例如 S = [-1, 2, 1, -4] and target = 1. 和最接近 1 的三元组是 -1 + 2 + 1 = 2.

11.2 思路 & 代码

class Solution {
public:
    /*
     * @param numbers: Give an array numbers of n integer
     * @param target: An integer
     * @return: return the sum of the three integers, the sum closest target.
     */
    int threeSumClosest(vector<int> &numbers, int target) {
        // write your code here
        if(numbers.size() < 3){
            return -1;
        }

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

        int best = numbers[0] + numbers[1] + numbers[2];
        //for循环最小的那个数,如果是4sum-cloest,则再多一层循环。
        for(int i=0;i<numbers.size()-2;++i){
            int left = i + 1, right = numbers.size()-1;
            while(left < right){
                int sum = numbers[i] + numbers[left] + numbers[right];
                if(abs(sum - target) < abs(best - target)){
                    best  = sum;
                }
                if(sum < target){
                    left ++ ;
                }
                else{
                    right--;
                }
            }
        }
        return best;
    }
};

12. 4sum (58 in lintcode )

12.1 题目

http://www.lintcode.com/zh-cn/problem/4sum/
http://www.jiuzhang.com/solution/4sum/

给一个包含n个数的整数数组S,在S中找到所有使得和为给定整数target的四元组(a, b, c, d)。

样例
例如,对于给定的整数数组S=[1, 0, -1, 0, -2, 2] 和 target=0. 满足要求的四元组集合为:
(-1, 0, 0, 1)
(-2, -1, 1, 2)
(-2, 0, 0, 2)

12.2 思路 & 代码

!!!每多一个sum,就是多一层循环,和3sum很类似。

class Solution {
public:
    /*
     * @param numbers: Give an array
     * @param target: An integer
     * @return: Find all unique quadruplets in the array which gives the sum of zero
     */
    vector<vector<int>> fourSum(vector<int> numbers, int target) {
        // write your code here
        vector<vector<int>> res;
        if(numbers.size() < 4){
            return res;
        }

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

        for(int i=0;i<numbers.size()-3;++i){
            if(i>0 && numbers[i] == numbers[i-1]){
                continue;
            }
            for(int j=i+1;j<numbers.size()-2;++j){
                if(j>i+1 && numbers[j] == numbers[j-1]){
                    continue;
                }
                int left = j+1, right = numbers.size()-1;
                while(left < right){
                    int sum = numbers[i] + numbers[j] + numbers[left] + numbers[right];
                    if(sum == target){
                        vector<int> pair(4,0);
                        pair[0] = numbers[i];
                        pair[1] = numbers[j];
                        pair[2] = numbers[left];
                        pair[3] = numbers[right];
                        res.push_back(pair);
                        left++;
                        right--;

                        while(left < right && numbers[right] == numbers[right+1]){
                            right--;
                        }
                        while(left < right && numbers[left] == numbers[left-1]){
                            left++;
                        }
                    }
                    else if(sum > target){
                        right--;
                    }
                    else{
                        left++;
                    }
                }
            }
        }

        return res;
    }
};

13. two-sum-difference-equals-to-target (610 in lintcode )(同向双指针 )

13.1 题目

http://www.lintcode.com/zh-cn/problem/two-sum-difference-equals-to-target/
http://www.jiuzhang.com/solution/two-sum-difference-equals-to-target/

给定一个整数数组,找到两个数的 差 等于目标值。index1必须小于index2。注意返回的index1和index2不是 0-based。

样例
给定的数组为 [2,7,15,24],目标值为 5,返回 [1,2] (7 - 2 = 5)

13.2 思路 & 代码

1). 对于无序数组,需要进行排序,如果最终返回下标,则在排序之前需要记录每个数的原始下标
2). 代码
class Solution {
public:
    /*
     * @param nums: an array of Integer
     * @param target: an integer
     * @return: [index1 + 1, index2 + 1] (index1 < index2)
     */

    class pair{
    public:
        int val;
        int pos;
        pair(int _val, int _pos):val(_val),pos(_pos){};
        bool operator<(const pair &p)const{
            return (val < p.val)||(val == p.val && pos<p.pos);
        }
    };
    vector<int> twoSum7(vector<int> &nums, int target) {
        // write your code here
        vector<int> res;
        if(nums.size()<2){
            return res;
        }

        vector<pair> valpos;
        for(int i=0;i<nums.size();++i){
            pair p(nums[i],i);
            valpos.push_back(p);
        }

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

        target = abs(target);

        int j=0;
        for(int i=0;i<nums.size();++i){
            if(i==j){
                j++;
            }
            while(j<nums.size() && valpos[j].val - valpos[i].val < target){
                j++;
            }
            if(j<nums.size() && valpos[j].val - valpos[i].val == target){
                int index1 = valpos[i].pos;
                int index2 = valpos[j].pos;
                if(index1 > index2){
                    int t = index1;
                    index1 = index2;
                    index2 = t;
                }
                res.push_back(index1+1);
                res.push_back(index2+1);
                return res;
            }
        }

        return res;
    }
};

第四部分:partition array

14. partition-array (31 in lintcode )

14.1 题目

http://www.lintcode.com/zh-cn/problem/partition-array/
http://www.jiuzhang.com/solution/partition-array/

给出一个整数数组 nums 和一个整数 k。划分数组(即移动数组 nums中的元素),使得:

  • 所有小于k的元素移到左边
  • 所有大于等于k的元素移到右边
    返回数组划分的位置,即数组中第一个位置 i,满足 nums[i] 大于等于 k。

给出数组 nums = [3,2,2,1] 和 k = 2,返回 1.

14.2 思路 & 代码

1).与quick sort的partition的不同:

本题中partition-array,小于k的都去左边,大于等于k的都去右边,有明显的划分。而quick sort中,等于k的时候左右两边都可以去,是为了平衡。如[0,0,0,…,0,0],

2). 代码错误
class Solution {
public:
    /*
     * @param nums: The integer array you should partition
     * @param k: An integer
     * @return: The index after partition
     */
    int partitionArray(vector<int> &nums, int k) {
        if(nums.size()==0){
            return 0;
        }

        int left = 0, right = nums.size()-1;
        while(left < right){
            while(left<right && nums[left] < k){
                left++;
            }
            while(left<right && nums[right] >= k){
                right--;
            }

            if(left < right){
                int t = nums[left];
                nums[left] = nums[right];
                nums[right] = t;
                left++;
                right--;
            }
        }

        if(nums[left]<k){ //注意此处:因为上面出循环的条件是:left<right,所以可能没有判断left就跳出了循环,所以此处需要判断
            return left+1;
        }

        return left;
    }
};

14.3 类似题目

A. 将奇偶数分开 - Partition Array by Odd and Even

http://www.lintcode.com/problem/partition-array-by-odd-and-even/
http://www.jiuzhang.com/solutions/partition-array-by-odd-and-even/

B. 将正负数分开 - Interleaving Positive and Negative Numbers

http://www.lintcode.com/problem/interleaving-positive-and-negative-numbers/
http://www.jiuzhang.com/solutions/interleaving-positive-and-negative-integers/

C.将大小写字母分开 - Sort Letters by Case

http://www.lintcode.com/problem/sort-letters-by-case/
http://www.jiuzhang.com/solutions/sort-letters-by-case/

15. sort-colors 颜色分类(148 in lintcode )

15.1 题目

http://www.lintcode.com/zh-cn/problem/sort-colors/
http://www.jiuzhang.com/solution/sort-colors/

给定一个包含红,白,蓝且长度为 n 的数组,将数组元素进行分类使相同颜色的元素相邻,并按照红、白、蓝的顺序进行排序。
我们可以使用整数 0,1 和 2 分别代表红,白,蓝。

样例
给你数组 [1, 0, 1, 2], 需要将该数组原地排序为 [0, 1, 1, 2]。

15.2 思路 & 代码

1). 做两次partition,第一次将0和1,2分开,第二次将1和2分开。

2). 改进:走一次得到结果

L指向0所在区域的下一个,R指向2所在区域的前一个,然后有一个指针i扫一遍数组,碰到0,和L换,碰到2,和R换,碰到1,不处理。
在这里插入图片描述
在这里插入图片描述

class Solution {
public:
    /*
     * @param nums: A list of integer which is 0, 1 or 2 
     * @return: nothing
     */
    void sortColors(vector<int> &nums) {
        // write your code here
        int l = 0, r = nums.size()-1;
        int i=0;
        while(i <= r){
            if(nums[i] == 0){
                swap(nums, l, i);
                l++;
                i++;
            }
            else if(nums[i] == 1){
                i++;
            }
            else{
                swap(nums,r,i);
                r--;
            }
        }
    }

    void swap(vector<int> &nums, int i, int j){
        int t = nums[i];
        nums[i] = nums[j];
        nums[j] = t;
    }
};

15.2 follow up

http://www.lintcode.com/zh-cn/problem/partition-array-ii/
http://www.jiuzhang.com/solution/partition-array-ii/

将一个没有经过排序的整数数组划分为 3 部分:
1.第一部分中所有的值都 < low
2.第二部分中所有的值都 >= low并且 <= high
3.第三部分中所有的值都 > high
返回任意一种可能的情况。

class Solution {
public:
    /*
     * @param nums: an integer array
     * @param low: An integer
     * @param high: An integer
     * @return: 
     */
    void partition2(vector<int> &nums, int low, int high) {
        // write your code here
        int l = 0, r=nums.size()-1;
        int i=0;
        while(i<=r){
            if(nums[i]<low){
                swap(nums,i,l);
                l++;
                i++;
            }
            else if(nums[i]>high){
                swap(nums, i, r);
                r--;
            }
            else{
                i++;
            }
        }
    }
    void swap(vector<int> &nums, int i, int j){
        int t = nums[i];
        nums[i] = nums[j];
        nums[j] = t;
    }
};

16. sort-colors-ii 彩虹排序(143 in lintcode )

16.1题目

http://www.lintcode.com/zh-cn/problem/sort-colors-ii/
http://www.jiuzhang.com/solution/sort-colors-ii/

给定一个有n个对象(包括k种不同的颜色,并按照1到k进行编号)的数组,将对象进行分类使相同颜色的对象相邻,并按照1,2,…k的顺序进行排序。

样例
给出colors=[3, 2, 2, 1, 4],k=4, 你的代码应该在原地操作使得数组变成[1, 2, 2, 3, 4]

16.2 思路 & 代码

1). n个数,k种颜色
2).最简单的思路:计数排序count sort。

time:O(n)
缺点:要求每个数是一定范围内的整数。本题要求不使用count sort

3). 基于比较的排序最快就是O(nlogn)
4). time:
 k=1 => O(1)
 k=2 => O(n)
 k=3 => O(n)
 ...
 k=n => O(nlogn) 直接用quick sort/merge sort等

∴ time: O(nlogk) => T(k) = 2T(k/2) + O(n)
在这里插入图片描述

5)4种颜色,0,1分一堆,2,3分一堆

在这里插入图片描述

6).代码
class Solution {
public:
    /*
     * @param colors: A list of integer
     * @param k: An integer
     * @return: nothing
     */
    void sortColors2(vector<int> &colors, int k) {
        // write your code here
        if(colors.size() ==0 ){
            return;
        }
        rainbowSort(colors, 0, colors.size()-1, 1, k);
    }

    void rainbowSort(vector<int> &colors, int left, int right, int colorfrom, int colorto){

        if(colorfrom == colorto){
            return;
        }

        if(left >= right){
            return;
        }

        int mid = (colorfrom+colorto)/2;
        int l = left,r = right;
        while(l <= r){
            while(l<=r && colors[l] <= mid){
                l++;
            }
            while(l<=r && colors[r] > mid){
                r--;
            }
            if(l<=r){
                int t = colors[l];
                colors[l] = colors[r];
                colors[r] = t;
                r--;
                l++;
            }
        }

        rainbowSort(colors, left, r, colorfrom, mid);
        rainbowSort(colors, l, right, mid+1, colorto);
    }
};

17. 其他有趣的排序

烙饼排序 Pancake Sort(有可能会考哦)
https://en.wikipedia.org/wiki/Pancake_sorting
http://www.geeksforgeeks.org/pancake-sorting/

睡眠排序 Sleep Sort
https://rosettacode.org/wiki/Sorting_algorithms/Sleep_sort

面条排序 Spaghetti Sort
https://en.wikipedia.org/wiki/Spaghetti_sort

猴子排序 Bogo Sort
https://en.wikipedia.org/wiki/Bogosort

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值