给定一个整数数组和一个目标值,找出数组中和为目标值的两个数。你可以假设每个输入只对应一种答案,且同样的元素不能被重复利用。(leetcode1)
方法1:暴力法
思路:对数组进行两次for循环,时间复杂度 O(n2) O ( n 2 )
class Solution {
public:
vector<int> twoSum(vector<int>& nums, int target) {
int length = nums.size();
vector<int> result;
if(length == 0)
return result;
for(int i = 0; i < length; i++){
for(int j = i+1; j < length; j++){
if(nums[i] + nums[j] == target){
result.push_back(i);
result.push_back(j);
return result;
}
}
}
return result;
}
};
方法2:使用map
用hash_map查找的时间复杂度可以为O(1),但是hash_map不在STL里,所以姑且使用map。
class Solution {
public:
vector<int> twoSum(vector<int>& nums, int target) {
map<int, int> help;
for(int i=0; i < nums.size(); i++){
if(help.find(target - nums[i]) != help.end()){
vector<int> result;
result.push_back(i);
result.push_back(help[target-nums[i]]);
return result;
}
help[nums[i]] = i;
}
}
};
给定一个已按照升序排列 的有序数组,找到两个数使得它们相加之和等于目标数。函数应该返回这两个下标值 index1 和 index2,其中 index1 必须小于 index2。(leetcode167)
思路:
index1 和 index2 分别指向头和尾。如果加和大于target,则index2左移;如果加和小于target,index1右移。
class Solution {
public:
vector<int> twoSum(vector<int>& numbers, int target) {
vector<int> result(2);
int index1 = 0;
int index2 = numbers.size() - 1;
while(index1 < index2){
if(numbers[index1] + numbers[index2] > target)
index2--;
else if(numbers[index1] + numbers[index2] < target)
index1++;
else{
result[0] = index1 + 1;
result[1] = index2 + 1;
return result;
}
}
}
};
给定一个二叉搜索树和一个目标结果,如果 BST 中存在两个元素且它们的和等于给定的目标结果,则返回 true。(leetcode 653)
思路1:
先序遍历整个树,如果树中有 k - root->val 这个值,返回真。否则按先序遍历继续找。
/**
* Definition for a binary tree node.
* struct TreeNode {
* int val;
* TreeNode *left;
* TreeNode *right;
* TreeNode(int x) : val(x), left(NULL), right(NULL) {}
* };
*/
class Solution {
public:
bool findNum(TreeNode *node, TreeNode *cur_node, int num){
while(node != NULL){
if(num > node->val)
node = node->right;
else if(num < node->val)
node = node->left;
else if(num == node->val && node != cur_node)
return true;
else if(num == node->val && node == cur_node)
return false;
}
return false;
}
void findTarget1(TreeNode *root, TreeNode *cur_node, int k, bool &result){
if(cur_node != NULL){
int need_find = k - cur_node->val;
if(findNum(root, cur_node, need_find))
result = true;
if(result == false)
findTarget1(root, cur_node->left, k, result);
if(result == false)
findTarget1(root, cur_node->right, k, result);
}
}
bool findTarget(TreeNode* root, int k) {
if(root == NULL)
return false;
TreeNode* cur_root = root;
bool result = false;
findTarget1(root, cur_root, k, result);
return result;
}
};
方法2:
使用辅助数组,利用中序遍历的到的数组是一个升序数组,那么之后就按照升序数组的方法继续计算就可以。这个方法比较好,不适用递归,不容易出错。
/**
* Definition for a binary tree node.
* struct TreeNode {
* int val;
* TreeNode *left;
* TreeNode *right;
* TreeNode(int x) : val(x), left(NULL), right(NULL) {}
* };
*/
class Solution {
public:
void inorder(TreeNode* root, vector<int> &help){
if(root){
inorder(root->left, help);
help.push_back(root->val);
inorder(root->right, help);
}
}
bool findTarget(TreeNode* root, int k) {
vector<int> help;
inorder(root, help);
int index1 = 0;
int index2 = help.size()-1;
while(index1 < index2){
if(help[index1] + help[index2] == k)
return true;
else if(help[index1] + help[index2] > k)
index2--;
else if(help[index1] + help[index2] < k)
index1++;
}
return false;
}
};
给定一个包含 n 个整数的数组 nums,判断 nums 中是否存在三个元素 a,b,c ,使得 a + b + c = 0 ?找出所有满足条件且不重复的三元组。注意:答案中不可以包含重复的三元组。(leetcode 15)
思路:
首先将数组排序,然后从左到右遍历这个数组。在当前遍历的值右边找到两个数加和等于当前值的相反数。由于是要三个数加和等于0,那么一定有一个数小于0,所以只需要遍历到小于等于0的区域。
class Solution {
public:
vector<vector<int>> threeSum(vector<int>& nums) {
vector<vector<int>> result;
int length = nums.size();
if(length == 0)
return result;
sort(nums.begin(), nums.end());
int i = 0;
for(int i = 0; i < length; i++){
if(nums[i] > 0)
break;
if(i != 0 && nums[i] == nums[i-1])
continue;
int need_find = 0 - nums[i];
int L = i + 1;
int R = length - 1;
while(L < R){
if(L > i + 1 && nums[L] == nums[L-1]){
L++;
continue;
}
if(R < length - 1 && nums[R] == nums[R+1]){
R--;
continue; }
// cout<< L << " "<< R<< endl;
if(nums[L] + nums[R] > need_find)
R--;
else if(nums[L] + nums[R] < need_find)
L++;
else if(nums[L] + nums[R] == need_find){
vector<int> cur_result(3);
cur_result[0] = nums[i];
cur_result[1] = nums[L];
cur_result[2] = nums[R];
result.push_back(cur_result);
++L;
--R;
}
}
}
return result;
}
};
给定一个包括 n 个整数的数组 nums 和 一个目标值 target。找出 nums 中的三个整数,使得它们的和与 target 最接近。返回这三个数的和。假定每组输入只存在唯一答案。(leetcode 16)
思路:
和上题基本类似
class Solution {
public:
int threeSumClosest(vector<int>& nums, int target) {
int distance = INT_MAX;
int sum;
int length = nums.size();
if(length < 3)
return -1;
sort(nums.begin(), nums.end());
for(int i = 0; i < length-2; i++){
int L = i + 1;
int R = length - 1;
while(L < R){
int cur_sum = nums[i] + nums[L] + nums[R];
int cur_dis = abs(cur_sum - target);
if(cur_dis < distance){
distance = cur_dis;
sum = cur_sum;
}
if(cur_sum < target)
L++;
else if(cur_sum > target)
R--;
else if(cur_sum == target)
return cur_sum;
}
}
return sum;
}
};
给定一个长度为n的数组和一个整数M,在这个数组里选择三个数,三个数的和小于等于M,返回有多少种方法(大疆笔试题)
思路
和三个数相加为0的思路基本一致,区别在于我们要求小于等于M。举例:[1, 3, 4, 5],如果L指向1, R指向5。如果当前nums[L] + nums[R] < = M
, 那么[1, 5], [1, 4], [1, 3]均满足条件,故count + 3。但是我们也要考虑可能出现重复元素的情况,例如[1, 3, 3, 4, 5], L指向1, R指向5。那么如果还是按上述的方法满足条件的有:[1, 3], [1, 3], [1, 4], [1, 5]四种。实际上里面有重复。所有用一个辅助数组记录nums当的每个值前面有多少重复值。count+4之后再减去重复。但是如果[1, 1, 3, 4, 5], L指向1, R指向5。那么实际结果是[1, 1], [1, 3], [1, 4], [1, 5]。如果减去辅助数组里记录的值,那么就会少1,所有如果是nums[L]==nums[L+1]
,那么count+4-1+1.
int cal_count(vector<int> &array, int M){
sort(array.begin(), array.end());
vector<int> dup(array.size(), 0);
for (int j = 1; j < dup.size(); ++j) {
if(array[j] == array[j-1])
dup[j] = dup[j-1] + 1;
else
dup[j] = dup[j-1];
int count = 0;
for (int i = 0; i < array.size()-2; ++i) {
if(i > 0 && array[i] == array[i-1])
continue;
int L = i + 1;
int R = array.size() - 1;
while(L < R){
if(array[L] == array[L-1] && L != i+1){
L++;
continue;
}
int cur_sum = array[i] + array[L] + array[R];
if(cur_sum <= M){
count = count + R - L - (dup[R] - dup[L]);
if(array[L] == array[L+1])
count++;
L++;
cout<< count<< endl;
}
else
R--;
}
}
return count;
}}
int main(){
vector<int> array = {3,2,5,2,1,4,2,3};
int M = 10;
int result = cal_count(array, M);
cout<< result<< endl;
}
给定一个包含 n 个整数的数组 nums 和一个目标值 target,判断 nums 中是否存在四个元素 a,b,c 和 d ,使得 a + b + c + d 的值与 target 相等?找出所有满足条件且不重复的四元组。(leetcode 18)
思路:
和三个数相加等于固定值类似。这次是固定两个数。时间复杂度 O(N3) O ( N 3 ) ,这个题不能用暴力递归,时间复杂度太高,过不去。
class Solution {
public:
vector<vector<int>> fourSum(vector<int>& nums, int target) {
vector<vector<int>> result;
int length = nums.size();
if(length < 4)
return result;
sort(nums.begin(), nums.end());
for(int i = 0; i < length - 3; i++){
if(i != 0 && nums[i] == nums[i-1])
continue;
for(int j = i+1; j < length - 2; j++){
if(j != i+1 && nums[j] == nums[j-1])
continue;
int L = j + 1;
int R = length - 1;
while(L < R){
if(L != j+1 && nums[L] == nums[L-1]){
L++;
continue;
}
if(R != length -1 && nums[R] == nums[R+1]){
R--;
continue;
}
int sum = nums[i] + nums[j] + nums[L] + nums[R];
if(sum == target){
result.push_back({nums[i], nums[j], nums[L], nums[R]});
L++;
R--;
}
else if(sum > target)
R--;
else
L++;
}
}
}
return result;
}
};
给定四个包含整数的数组列表 A , B , C , D ,计算有多少个元组 (i, j, k, l) ,使得 A[i] + B[j] + C[k] + D[l] = 0。为了使问题简单化,所有的 A, B, C, D 具有相同的长度 N,且 0 ≤ N ≤ 500 。所有整数的范围在 -228 到 228 - 1 之间,最终结果不会超过 231 - 1 。(leetcode 454)
思路:
使用unorder_map查找时间可以达到常数。
class Solution {
public:
int fourSumCount(vector<int>& A, vector<int>& B, vector<int>& C, vector<int>& D) {
int length = A.size();
unordered_map<int, int> help;
for(int i = 0; i < length; i++){
for(int j = 0; j < length; j++){
help[A[i] + B[j]]++;
}
}
int count = 0;
for(int i = 0; i < length; i++){
for(int j = 0; j < length; j++){
int sum = C[i] + D[j];
int need_find = -sum;
count += help.count(need_find) ? help[need_find] : 0;
}
}
return count;
}
};