zoukankan      html  css  js  c++  java
  • Leetcode——链表和数组(2)

    两数之和

    给定一个整数数组 nums 和一个目标值 target,请你在该数组中找出和为目标值的那 两个 整数,并返回他们的数组下标。

    你可以假设每种输入只会对应一个答案。但是,数组中同一个元素不能使用两遍。

    示例:

    给定 nums = [2, 7, 11, 15], target = 9
    
    因为 nums[0] + nums[1] = 2 + 7 = 9
    所以返回 [0, 1]
    

    哈希

    遍历一个数字,把另一个数字事先将其存储起来,

    使用一个 HashMap,来建立数字和其坐标位置之间的映射,

    由于 HashMap 是常数级的查找效率,这样在遍历数组的时候,用 target 减去遍历到的数字,就是另一个需要的数字了,

    直接在 HashMap 中查找其是否存在即可,注意要判断查找到的数字不是第一个数字,

    比如 target 是4,遍历到了一个2,那么另外一个2不能是之前那个2,整个实现步骤为:

    先遍历一遍数组,建立 HashMap 映射,然后再遍历一遍,开始查找,找到则记录 index。

    C++

    class Solution {
    public:
        vector<int> twoSum(vector<int>& nums, int target) {
            unordered_map<int, int> m;
            vector<int> res;
            for (int i = 0; i < nums.size(); ++i) {
                m[nums[i]] = i;
            }
            for (int i = 0; i < nums.size(); ++i) {
                int t = target - nums[i];
                if (m.count(t) && m[t] != i) {
                    res.push_back(i);
                    res.push_back(m[t]);
                    break;
                }
            }
            return res;
        }
    };
    

    Java

    public class Solution {
        public int[] twoSum(int[] nums, int target) {
            HashMap<Integer, Integer> m = new HashMap<Integer, Integer>();
            int[] res = new int[2];
            for (int i = 0; i < nums.length; ++i) {
                m.put(nums[i], i);
            }
            for (int i = 0; i < nums.length; ++i) {
                int t = target - nums[i];
                if (m.containsKey(t) && m.get(t) != i) {
                    res[0] = i;
                    res[1] = m.get(t);
                    break;
                }
            }
            return res;
        }
    } 
    

    优化

    C++

    class Solution {
    public:
        vector<int> twoSum(vector<int>& nums, int target) {
            unordered_map<int, int> m;
            for (int i = 0; i < nums.size(); ++i) {
                if (m.count(target - nums[i])) {
                    return {i, m[target - nums[i]]};
                }
                m[nums[i]] = i;
            }
            return {};
        }
    };
    

    Java

    public class Solution {
        public int[] twoSum(int[] nums, int target) {
            HashMap<Integer, Integer> m = new HashMap<Integer, Integer>();
            int[] res = new int[2];
            for (int i = 0; i < nums.length; ++i) {
                if (m.containsKey(target - nums[i])) {
                    res[0] = i;
                    res[1] = m.get(target - nums[i]);
                    break;
                }
                m.put(nums[i], i);
            }
            return res;
        }
    }
    

    三数之和

    给你一个包含 n 个整数的数组 nums,判断 nums 中是否存在三个元素 a,b,c ,使得 a + b + c = 0 ?请你找出所有满足条件且不重复的三元组。

    注意:答案中不可以包含重复的三元组。

    示例:

    给定数组 nums = [-1, 0, 1, 2, -1, -4],
    
    满足要求的三元组集合为:
    [
      [-1, 0, 1],
      [-1, -1, 2]
    ]
    

    双指针

    fix 一个数,然后去找另外两个数,只要找到两个数且和为第一个 fix 数的相反数就行了

    对原数组进行排序,然后开始遍历排序后的数组,这里注意不是遍历到最后一个停止,而是到倒数第三个就可以了。

    这里可以先做个剪枝优化,就是当遍历到正数的时候就 break,为啥呢,因为数组现在是有序的了,如果第一个要 fix 的数就是正数了,则后面的数字就都是正数,就永远不会出现和为0的情况了。

    然后还要加上重复就跳过的处理,处理方法是从第二个数开始,如果和前面的数字相等,就跳过,因为不想把相同的数字fix两次。

    对于遍历到的数,用0减去这个 fix 的数得到一个 target,然后只需要再之后找到两个数之和等于 target 即可。

    用两个指针分别指向 fix 数字之后开始的数组首尾两个数,如果两个数和正好为 target,则将这两个数和 fix 的数一起存入结果中。然后就是跳过重复数字的步骤了,两个指针都需要检测重复数字。

    如果两数之和小于 target,则将左边那个指针i右移一位,使得指向的数字增大一些。同理,如果两数之和大于 target,则将右边那个指针j左移一位,使得指向的数字减小一些

    class Solution {
    public:
        vector<vector<int>> threeSum(vector<int>& nums) {
            vector<vector<int>> res;
            sort(nums.begin(), nums.end());
            if (nums.empty() || nums.back() < 0 || nums.front() > 0) return {};
            for (int k = 0; k < (int)nums.size() - 2; ++k) {
                if (nums[k] > 0) break;
                if (k > 0 && nums[k] == nums[k - 1]) continue;
                int target = 0 - nums[k], i = k + 1, j = (int)nums.size() - 1;
                while (i < j) {
                    if (nums[i] + nums[j] == target) {
                        res.push_back({nums[k], nums[i], nums[j]});
                        while (i < j && nums[i] == nums[i + 1]) ++i;
                        while (i < j && nums[j] == nums[j - 1]) --j;
                        ++i; --j;
                    } else if (nums[i] + nums[j] < target) ++i;
                    else --j;
                }
            }
            return res;
        }
    };
    

    最接近的三数之和

    给定一个包括 n 个整数的数组 nums 和 一个目标值 target。找出 nums 中的三个整数,使得它们的和与 target 最接近。返回这三个数的和。假定每组输入只存在唯一答案。

    例如,给定数组 nums = [-1,2,1,-4], 和 target = 1.
    
    与 target 最接近的三个数的和为 2. (-1 + 2 + 1 = 2).
    

    双指针

    定义一个变量 diff 用来记录差的绝对值,然后还是要先将数组排个序,然后开始遍历数组

    先确定一个数,

    然后用两个指针 leftright 来滑动寻找另外两个数,每确定两个数,求出此三数之和,

    然后算和给定值的差的绝对值存在 newDiff 中,然后和 diff 比较并更新 diff 和结果 closest 即可

    class Solution {
    public:
        int threeSumClosest(vector<int>& nums, int target) {
            int closest = nums[0] + nums[1] + nums[2];
            int diff = abs(closest - target);
            sort(nums.begin(), nums.end());
            for (int i = 0; i < nums.size() - 2; ++i) {
                int left = i + 1, right = nums.size() - 1;
                while (left < right) {
                    int sum = nums[i] + nums[left] + nums[right];
                    int newDiff = abs(sum - target);
                    if (diff > newDiff) {
                        diff = newDiff;
                        closest = sum;
                    }
                    if (sum < target) ++left;
                    else --right;
                }
            }
            return closest;
        }
    };
    

    优化

    每次判断一下,当 nums[i]*3 > target 的时候,就可以直接比较 closestnums[i] + nums[i+1] + nums[i+2] 的值,返回较小的那个,因为数组已经排过序了,后面的数字只会越来越大,就不必再往后比较了

    class Solution {
    public:
        int threeSumClosest(vector<int>& nums, int target) {
            int closest = nums[0] + nums[1] + nums[2];
            int diff = abs(closest - target);
            sort(nums.begin(), nums.end());
            for (int i = 0; i < nums.size() - 2; ++i) {
                if (nums[i] * 3 > target) return min(closest, nums[i] + nums[i + 1] + nums[i + 2]);
                int left = i + 1, right = nums.size() - 1;
                while (left < right) {
                    int sum = nums[i] + nums[left] + nums[right];
                    int newDiff = abs(sum - target);
                    if (diff > newDiff) {
                        diff = newDiff;
                        closest = sum;
                    }
                    if (sum < target) ++left;
                    else --right;
                }
            }
            return closest;
        }
    };
    

    较小的三数之和

    给定一个长度为 n 的整数数组和一个目标值 target,寻找能够使条件 nums[i] + nums[j] + nums[k] < target 成立的三元组 i, j, k 个数(0 <= i < j < k < n)。

    示例:
    
    输入: nums = [-2,0,1,3], target = 2
    输出: 2 
    解释: 因为一共有两个三元组满足累加和小于 2:
         [-2,0,1]
         [-2,0,3]
    

    进阶:是否能在 O(n2) 的时间复杂度内解决?

    暴力

    将所有的可能的三个数字的组合都遍历一遍,比较三数之和跟目标值之间的大小,小于的话则结果自增1

    class Solution {
    public:
        int threeSumSmaller(vector<int>& nums, int target) {
            int res = 0;
            sort(nums.begin(), nums.end());
            for (int i = 0; i < int(nums.size() - 2); ++i) {
                int left = i + 1, right = nums.size() - 1, sum = target - nums[i];
                for (int j = left; j <= right; ++j) {
                    for (int k = j + 1; k <= right; ++k) {
                        if (nums[j] + nums[k] < sum) ++res;
                    }
                }
            }
            return res;
        }
    };
    

    双指针

    判断三个数之和小于目标值时,此时结果应该加上 right-left,因为数组排序了以后,

    如果加上 num[right] 小于目标值的话,那么加上一个更小的数必定也会小于目标值,然后将左指针右移一位,否则将右指针左移一位

    class Solution {
    public:
        int threeSumSmaller(vector<int>& nums, int target) {
            if (nums.size() < 3) return 0;
            int res = 0, n = nums.size();
            sort(nums.begin(), nums.end());
            for (int i = 0; i < n - 2; ++i) {
                int left = i + 1, right = n - 1;
                while (left < right) {
                    if (nums[i] + nums[left] + nums[right] < target) {
                        res += right - left;
                        ++left;
                    } else {
                        --right;
                    }
                }
            }
            return res;
        }
    };
    

    四数之和

    给定一个包含 n 个整数的数组 nums 和一个目标值 target,判断 nums 中是否存在四个元素 a,b,c 和 d ,使得 a + b + c + d 的值与 target 相等?找出所有满足条件且不重复的四元组。

    注意:

    答案中不可以包含重复的四元组。

    示例:

    给定数组 nums = [1, 0, -1, 0, -2, 2],和 target = 0。
    
    满足要求的四元组集合为:
    [
      [-1,  0, 0, 1],
      [-2, -1, 1, 2],
      [-2,  0, 0, 2]
    ]
    

    进行去重复处理。主要可以进行的有三个地方,

    首先在两个 for 循环下可以各放一个,因为一旦当前的数字跟上面处理过的数字相同了,那么找下来肯定还是重复的。

    之后就是当 sum 等于 target 的时候了,在将四个数字加入结果 res 之后,leftright 都需要去重复处理,分别像各自的方面遍历即可

    class Solution {
    public:
        vector<vector<int>> fourSum(vector<int> &nums, int target) {
            vector<vector<int>> res;
            int n = nums.size();
            sort(nums.begin(), nums.end());
            for (int i = 0; i < n - 3; ++i) {
                if (i > 0 && nums[i] == nums[i - 1]) continue;
                for (int j = i + 1; j < n - 2; ++j) {
                    if (j > i + 1 && nums[j] == nums[j - 1]) continue;
                    int left = j + 1, right = n - 1;
                    while (left < right) {
                        int sum = nums[i] + nums[j] + nums[left] + nums[right];
                        if (sum == target) {
                            vector<int> out{nums[i], nums[j], nums[left], nums[right]};
                            res.push_back(out);
                            while (left < right && nums[left] == nums[left + 1]) ++left;
                            while (left < right && nums[right] == nums[right - 1]) --right;
                            ++left; --right;
                        } else if (sum < target) ++left;
                        else --right;
                    }
                }
            }
            return res;
        }
    };
    

    四数相加 II

    给定四个包含整数的数组列表 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 。

    例如:

    输入:
    A = [ 1, 2]
    B = [-2,-1]
    C = [-1, 2]
    D = [ 0, 2]
    
    输出:
    2
    
    解释:
    两个元组如下:
    1. (0, 0, 0, 1) -> A[0] + B[0] + C[0] + D[1] = 1 + (-2) + (-1) + 2 = 0
    2. (1, 1, 0, 0) -> A[1] + B[1] + C[0] + D[0] = 2 + (-1) + (-1) + 0 = 0
    

    哈希

    如果把A和B的两两之和都求出来,在 HashMap 中建立两数之和跟其出现次数之间的映射,

    那么再遍历C和D中任意两个数之和,只要看哈希表存不存在这两数之和的相反数就行了

    class Solution {
    public:
        int fourSumCount(vector<int>& A, vector<int>& B, vector<int>& C, vector<int>& D) {
            int res = 0;
            unordered_map<int, int> m;
            for (int i = 0; i < A.size(); ++i) {
                for (int j = 0; j < B.size(); ++j) {
                    ++m[A[i] + B[j]];
                }
            }
            for (int i = 0; i < C.size(); ++i) {
                for (int j = 0; j < D.size(); ++j) {
                    int target = -1 * (C[i] + D[j]);
                    res += m[target];
                }
            }
            return res;
        }
    };
    

    两哈希

    用了两个 HashMap 分别记录 AB 和 CB 的两两之和出现次数,

    然后遍历其中一个 HashMap,并在另一个 HashMap 中找和的相反数出现的次数

    class Solution {
    public:
        int fourSumCount(vector<int>& A, vector<int>& B, vector<int>& C, vector<int>& D) {
            int res = 0, n = A.size();
            unordered_map<int, int> m1, m2;
            for (int i = 0; i < n; ++i) {
                for (int j = 0; j < n; ++j) {
                    ++m1[A[i] + B[j]];
                    ++m2[C[i] + D[j]];
                }
            }
            for (auto a : m1) res += a.second * m2[-a.first];
            return res;
        }
    };
    
  • 相关阅读:
    【python35小工具】b站弹幕保存
    w7 python35 输出中文乱码解决
    【python】版本35 正则-非库-爬虫-读写xlw文件
    【python小工具】我在bilibili个人资料里控制家里的电脑
    【python小工具】linux 低权限密码记录 提权小套路
    [学习交流]博客园 cnblog 添加github链接和自定义美化学习
    cp2102 驱动 win7x64 -2018
    基于Vue实现可以拖拽的树形表格(原创)
    不起眼的 z-index 却能牵扯出这么大的学问
    彻底搞懂CSS伪类选择器:is、not
  • 原文地址:https://www.cnblogs.com/wwj99/p/13024839.html
Copyright © 2011-2022 走看看