• 1. Two Sum + 15. 3 Sum + 16. 3 Sum Closest + 18. 4Sum + 167. Two Sum II


    ▶ 问题:给定一个数组 nums 及一个目标值 target,求数组中是否存在 n 项的和恰好等于目标值

    ▶ 第 1题,n = 2,要求返回解

    ● 代码,160 ms,穷举法,时间复杂度 O(n2),空间复杂度 O(1)

     1 class Solution
     2 {
     3 public:
     4     vector<int> twoSum(vector<int> nums, int target)
     5     {
     6         int i, j;
     7         for (i = 0; i < nums.size()-1; i++)
     8         {
     9             for (j = i + 1; j < nums.size(); j++)
    10             {
    11                 if (nums[i] + nums[j] == target)
    12                     return vector<int> {i, j};
    13             }
    14         }
    15         return vector<int>{};
    16     }
    17 };

    ● 代码,9 ms,排序后夹逼,需要构造一个结构来保存下标,连着下标一起排序。时间复杂度 O(n log n),空间复杂度 O(1)

     1 class Solution
     2 {
     3 public:
     4     struct Node
     5     {
     6         int val;
     7         int index;
     8         Node(int pval, int pindex) : val(pval), index(pindex) {}
     9         bool operator < (const Node& b) const {return val < b.val;}
    10     };
    11 
    12     vector<int> twoSum(vector<int>& nums, int target)
    13     {
    14         int i, j, temp;
    15         vector<Node> nums2;
    16         for (i = 0; i < nums.size(); i++)
    17             nums2.push_back(Node(nums[i], i)); 
    18         sort(nums2.begin(), nums2.end());
    19         for (i = 0, j = nums2.size() - 1; i < j; (temp > target) ? j-- : i++ )
    20         {
    21             if ((temp = nums2[i].val + nums2[j].val) == target)
    22                 return vector<int>{nums2[i].index, nums2[j].index}; 
    23         }
    24         return vector<int>{};
    25     }
    26 };

    ● 代码,10 ms,使用散列表,遍历数组时将项放入散列表,同时检查当前项和散列表中已经有的项是否构成问题的解,利用散列表查找时间为常数的特点加快检查速度,最快的解法算法与之相同。时间复杂度 O(n),空间复杂度 O(n)

     1 class Solution
     2 {
     3 public:
     4     vector<int> twoSum(vector<int> nums, int target)
     5     {
     6         int i;
     7         unordered_map<int, int> map;
     8         for (i = 0; i < nums.size(); i++)
     9         {
    10             if (map.find(target - nums[i])!=map.end())
    11                 return vector<int>{map[target - nums[i]], i };
    12             map[nums[i]] = i;
    13         }
    14         return vector<int>{};
    15     }
    16 };

    ▶ 第 15 题,n = 3,target = 0

    ● 大佬的代码,110 ms,排序后夹逼,先指定第一个(最小的)元素,剩下的两个元素使用前面二元和的方法进行夹逼,并跳掉一些多余分支,最快的解法算法与之相同,时间复杂度 O(n2),空间复杂度 O(1)

     1 class Solution
     2 {
     3 public:
     4     vector<vector<int> > threeSum(vector<int> &num)
     5     {
     6         int i, left, right;
     7         vector<vector<int>> output;
     8 
     9         sort(num.begin(), num.end());
    10         for (i = 0; i < num.size();)
    11         {
    12             if (num[i] > 0) // 第一个元素已经大于零,则三个元素的和不可能等于零,剩下的界都不用检查了
    13                 break;
    14             for (left = i + 1, right = num.size() - 1; left < right;)   // 搜索二元和,可能有多解,必须全部搜索完
    15             {
    16                 if (num[left] + num[right] + num[i] < 0)
    17                     left++;
    18                 else if (num[left] + num[right] + num[i] > 0)
    19                     right--;
    20                 else
    21                 {
    22                     output.push_back(vector<int>{num[i], num[left], num[right]});
    23                     for (left++; left < right && num[left] == num[left - 1]; left++);       // 跳过第二个数重复的情况
    24                     for (right--; left < right && num[right] == num[right + 1]; right--);   // 跳过第三个数重复分情况
    25                 }
    26             }
    27             for (i++; i < num.size() && num[i] == num[i - 1]; i++); // 跳过第一个数重复分情况,即调整下一次检查的起点
    28         }
    29         return output;
    30     }
    31 };

    ▶ 第 16 题,n = 3,求最近似解,第 259 题要收费 Orz

    ● 代码,6 ms,排序后夹逼,使用 output 来记录当前最优解的信息。最快的解法算法与之相同,时间复杂度 O(n2),空间复杂度 O(1)

     1 class Solution
     2 {
     3 public:
     4     inline int diff(int a, int b) { return (a > b) ? (a - b) : (b - a); }
     5 
     6     int threeSumClosest(vector<int> &num, int target)
     7     {
     8         int i, target2, left, right, sum2, output;
     9 
    10         std::sort(num.begin(), num.end());
    11         for (i = 0, output = INT_MAX>>1; i < num.size();)
    12         {            
    13             for (target2 = target - num[i], left = i + 1, right = num.size() - 1; left < right;)
    14             {
    15                 sum2 = num[left] + num[right];
    16                 if (sum2 < target2)
    17                     left++;
    18                 else if (sum2 > target2)
    19                     right--;
    20                 else
    21                     return target;
    22                 if (diff(sum2, target2) < diff(target,output))
    23                     output = sum2 + num[i];                
    24             }
    25             for (i++; i < num.size() && num[i] == num[i - 1]; i++);
    26         }
    27         return output;
    28     }
    29 };

    ▶ 第18 题,n = 4

    ● 代码,12 ms,排序后夹逼,先指定两个较小的元素,剩下的两个元素使用前面二元和的方法进行夹逼,并跳掉一些多余分支,最快的解法算法与之相同,时间复杂度 O(n3),空间复杂度 O(1)

     1 class Solution4
     2 {
     3 public:
     4     vector<vector<int>> fourSum(vector<int>& nums, int target)
     5     {
     6         int n, i, j, left, right, sum;
     7         vector<vector<int>> output;
     8         n = nums.size();
     9         sort(nums.begin(), nums.end());
    10         for (i = 0; i < n - 3; i++) // 指定第一个元素
    11         {
    12             if (i > 0 && nums[i] == nums[i - 1] || nums[i] + nums[n - 3] + nums[n - 2] + nums[n - 1] < target)
    13                 continue;                                       // 跳过第一个元素重复或过小的情况
    14             if (nums[i] + nums[i + 1] + nums[i + 2] + nums[i + 3] > target)
    15                 break;                                          // 第一个元素过大,结束搜索
    16             for (j = i + 1; j < n - 2; j++)                     // 指定第二个元素
    17             {
    18                 if (j > i + 1 && nums[j] == nums[j - 1] || nums[i] + nums[j] + nums[n - 2] + nums[n - 1] < target)
    19                     continue;                                   // 跳过第二个元素重复过小的情况
    20                 if (nums[i] + nums[j] + nums[j + 1] + nums[j + 2]>target)
    21                     break;                                      // 第二个元素过大,结束搜索
    22                 for(left = j + 1, right = n - 1; left < right;) // 搜索第三和第四个元素 
    23                 {
    24                     sum = nums[left] + nums[right] + nums[i] + nums[j];
    25                     if (sum < target)
    26                         left++;
    27                     else if (sum > target)
    28                         right--;
    29                     else
    30                     {
    31                         output.push_back(vector<int>{nums[i], nums[j], nums[left], nums[right]});
    32                         for (left++; nums[left] == nums[left - 1] && left < right; left++); 
    33                         for (right--; nums[right] == nums[right + 1] && left < right; right--);
    34                     }
    35                 }
    36             }
    37         }
    38         return output;
    39     }
    40 };

    ▶ 第 167 题,n = 2,默认已经排好了序,可以使用第 1 题结果,第170 题要付费 Orz

    ● 夹逼,7 ms,时间复杂度 O(n)

     1 class Solution
     2 {
     3 public:
     4     vector<int> twoSum(vector<int>& numbers, int target)
     5     {
     6         int left, right;
     7         vector<int>output;
     8         for (left = 0, right = numbers.size() - 1; left < right;)
     9         {
    10             if (numbers[left] + numbers[right] == target)
    11                 return vector<int>{left + 1, right + 1};
    12             else if (numbers[left] + numbers[right] > target)
    13                 right--;
    14             else
    15                 left++;
    16         }
    17         return vector<int>();
    18     }
    19 };

    ● 大佬的代码,7 ms,使用二分法加速,时间复杂度 O(log n)

     1 class Solution
     2 {
     3 public:
     4     int largestSmallerOrLastEqual(vector<int>& numbers, int start, int end, int target)
     5     {
     6         int lp, rp, mp;
     7         for (lp = start, rp = end, mp = lp + (rp - lp) / 2; lp <= rp; mp = lp + (rp - lp) / 2)
     8         {
     9             if (numbers[mp] > target)
    10                 rp = mp - 1;
    11             else
    12                 lp = mp + 1;
    13         }
    14         return rp;
    15     }
    16     int smallestLargerOrFirstEqual(vector<int>& numbers, int start, int end, int target)
    17     {
    18         int lp, rp, mp;
    19         for (lp = start, rp = end, mp = lp + (rp - lp) / 2; lp <= rp; mp = lp + (rp - lp) / 2)
    20         {
    21             if (numbers[mp] < target)
    22                 lp = mp + 1;
    23             else
    24                 rp = mp - 1;
    25         }
    26         return lp;
    27     }
    28     vector<int> twoSum(vector<int>& numbers, int target)
    29     {
    30         const int n = numbers.size();
    31         if (numbers.size() == 0)
    32             return vector<int>{};
    33         int start, end;
    34         for (start = 0, end = n - 1; start < end;)
    35         {
    36             if (numbers[start] + numbers[end] == target)
    37                 return vector<int> {start + 1, end + 1};
    38             if (numbers[start] + numbers[end] > target)
    39                 end = largestSmallerOrLastEqual(numbers, start, end, target - numbers[start]);// end 向前移动到满足 numbers[end] <= target - numbers[start] 的最后一个整数
    40             else
    41                 start = smallestLargerOrFirstEqual(numbers, start, end, target - numbers[end]);// start 向后移动到满足 numbers[start] >= target - numbers[end] 的第一个整数           
    42         }
    43         return vector<int>{};
    44     }
    45 };

    ▶ 第 454 题,n = 4,每个加数分别从指定的集合中选出

    ● 自己的方法,1550 ms,这是将第 18 题的算法移植过来得到的,在使用计数器 counta,countb,countc,countd 计算重复解以前结果为超时,说明这种逐步搜索的方法对于独立的集合来说效率不足

     1 class Solution
     2 {
     3 public:
     4     int fourSumCount(vector<int>& A, vector<int>& B, vector<int>& C, vector<int>& D)
     5     {
     6         const int n = A.size();
     7         int a, b, c, d, count, counta, countb, countc, countd;
     8         sort(A.begin(), A.end()), sort(B.begin(), B.end()), sort(C.begin(), C.end()), sort(D.begin(), D.end());
     9 
    10         for (a = count = 0; a < n; a++)
    11         {
    12             if (A[a] + B[n - 1] + C[n - 1] + D[n - 1] < 0)  // A[a] 过小
    13                 continue;
    14             if (A[a] + B[0] + C[0] + D[0] > 0)              // A[a] 过大
    15                 break;
    16             for (counta = 1; a < n - 1 && A[a + 1] == A[a]; a++, counta++);// A[a] 重复计数
    17             for (b = 0; b < n; b++)
    18             {
    19                 if (A[a] + B[b] + C[n - 1] + D[n - 1] < 0)  // B[b] 过小
    20                     continue;
    21                 if (A[a] + B[b] + C[0] + D[0] > 0)          // B[b] 过大
    22                     break;
    23                 for (countb = 1; b < n - 1 && B[b + 1] == B[b]; b++, countb++);// B[b] 重复计数
    24                 for (c = 0, d = n - 1; c < n && d >= 0;)
    25                 {
    26                     if (A[a] + B[b] + C[c] + D[d] < 0)
    27                         c++;
    28                     else if (A[a] + B[b] + C[c] + D[d] > 0)
    29                         d--;
    30                     else
    31                     {
    32                         for (countc = 1; c < n - 1 && C[c + 1] == C[c]; c++, countc++);
    33                         for (countd = 1; d > 0 && D[d - 1] == D[d]; d--, countd++);
    34                         count += counta * countb * countc * countd;
    35                         c++, d--;
    36                     }
    37                 }
    38             }
    39         }
    40         return count;
    41     }
    42 };

    ● 大佬的解法,197 ms,unordered_map 查找

     1 class Solution
     2 {
     3 public:
     4     int fourSumCount(vector<int>& A, vector<int>& B, vector<int>& C, vector<int>& D)
     5     {
     6         unordered_map<int, int>  abSum;
     7         int count = 0;
     8         auto it = abSum.begin();
     9         for (auto a : A)
    10         {
    11             for (auto b : B)
    12                 abSum[a + b]++;
    13         }        
    14         for (auto c : C)
    15         {
    16             for (auto d : D)
    17             {
    18                 if ((it = abSum.find(0 - c - d))!= abSum.end())
    19                     count += it->second;
    20             }
    21         }
    22         return count;
    23     }
    24 };

    ● 大佬的解法,203 ms,二分查找

     1 class Solution
     2 {
     3 public:
     4     int biSearch(vector<int> & nums, int x, bool LEFT)// 在 nums 中搜索值为 x 的元素,由于存在重复元素,使用了两种二分搜索,用 LEFT 区分
     5     {
     6         int lp, rp, mp;
     7         for (lp = 0, rp = nums.size() - 1, mp = (lp + rp) / 2; lp <= rp; mp = (lp + rp) / 2)
     8         {            
     9             if (LEFT)// 搜索最靠左的 x
    10             {
    11                 if (nums[mp] >= x)
    12                     rp = mp - 1;
    13                 else
    14                     lp = mp + 1;
    15             }
    16             else     // 搜索最靠右的 x
    17             {
    18                 if (nums[mp] <= x)
    19                     lp = mp + 1;
    20                 else
    21                     rp = mp - 1;
    22             }
    23         }
    24         return LEFT ? lp : rp;
    25     }
    26     int fourSumCount(vector<int>& A, vector<int>& B, vector<int>& C, vector<int>& D)
    27     {
    28         const int n = A.size();
    29         vector<int> s, m;
    30         int i, j, sum, ans;
    31         for (i = 0; i < n; i++)
    32         {
    33             for (j = 0; j < n; j++)
    34                 s.push_back(A[i] + B[j]), m.push_back(C[i] + D[j]);
    35         }
    36         sort(s.begin(), s.end()), sort(m.begin(), m.end());
    37         for (i = sum = ans = 0; i < s.size(); ++i)
    38         {
    39             if (i != 0 && s[i] == s[i - 1])//  重复计数
    40                 ans += sum;
    41             else
    42             {
    43                 sum = biSearch(m, -s[i], false) - biSearch(m, -s[i], true) + 1;// 第二个重复计数
    44                 ans += sum;
    45             }
    46         }
    47         return ans;
    48     }
    49 };

    ▶ 第 657 题,n = 2,数据结构是中根二叉树

    ● 代码,38 ms,中根序遍历这棵树,把数字都取出来(恰好为升序数列),再用前面的方法计算

     1 class Solution
     2 {
     3 public:
     4     bool findTarget(TreeNode* root, int k)
     5     {
     6         vector<int> nums;
     7         inorder(root, nums);
     8         for (int i = 0, j = nums.size() - 1; i < j;)
     9         {
    10             if (nums[i] + nums[j] == k)
    11                 return true;
    12             (nums[i] + nums[j] < k) ? i++ : j--; 
    13         }
    14         return false;
    15     }
    16     void inorder(TreeNode* root, vector<int>& nums)// 中根序遍历树
    17     {
    18         if (root == NULL)
    19             return;
    20         inorder(root->left, nums);
    21         nums.push_back(root->val);
    22         inorder(root->right, nums);
    23     }
    24 };

    ● 代码,38 ms,从树的中根序两端向中间遍历

     1 class BSTIterator
     2 {
     3 private:
     4     stack<TreeNode*> s;
     5     bool forward;
     6 public:
     7     BSTIterator(TreeNode* root, bool forward)
     8     {
     9         for (this->forward = forward; root != nullptr; root = forward ? root->left : root->right)
    10             s.push(root);
    11     }
    12     TreeNode* next()
    13     {
    14         if (s.empty())
    15             return NULL;
    16         TreeNode *top, *tmp;        
    17         for (top = s.top(), s.pop(), tmp = (forward ? top->right : top->left); tmp != nullptr; s.push(tmp), tmp = (forward ? tmp->left : tmp->right));
    18         return top;
    19     }
    20 };
    21 class Solution
    22 {
    23 public:
    24     bool findTarget(TreeNode* root, int k)
    25     {
    26         int sum;
    27         BSTIterator forward(root, true), backward(root, false);// 获取最前结点和最后结点(最小和最大元素)
    28         TreeNode *fn, *bn;
    29         for (fn = forward.next(), bn = backward.next(); fn != bn;)
    30         {
    31             sum = fn->val + bn->val;
    32             if (sum == k)
    33                 return true;
    34             (sum < k) ? (fn = forward.next()) : (bn = backward.next());
    35         }
    36         return false;
    37     }
    38 };
  • 相关阅读:
    在web.config 的config
    有道理
    showModalDialog和showModelessDialog使用心得
    VBScript的 trim()
    RetroGuard的使用方法(转)
    NHibernate 的语句的问题
    Web控件TreeView展开无闪烁的两个解决方法
    如何在类库项目中添加Web窗体文件
    签名活动链接
    __EVENTTARGET为空或不是对象
  • 原文地址:https://www.cnblogs.com/cuancuancuanhao/p/8186301.html
Copyright © 2020-2023  润新知