双指针的题目分为以下几类:
- 同向双指针
- 相向双指针
- 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