和为k的子数组
和为k的子数组
原题链接:https://leetcode.cn/problems/QTMn0o/
给定一个整数数组和一个整数 k ,请找到该数组中和为 k 的连续子数组的个数,
1.静态滑动窗口1遍历滑动窗口大小,每次for循环滑动窗口大小值固定,每次对滑动窗口中的内容进行求和,尝试更新答案,时间超时:
1234567891011121314class Solution {public: int subarraySum(vector<int>& nums, int k) { int ans = 0; for (int len = 1; len < nums.size(); ++len) { for (int i = 0, j = i + len; j <= nums.size(); ++i, ++j) { int sum = 0; for (int n = i; n < j; ++n) sum += nums[n]; if (sum == k) ans++; } } return ans; }};
所有用例成功输出结果,但由于时间复杂度太高,导致程序超时,
2.静态滑动窗口2遍历滑动窗口大小,每次for循环滑动窗口大小值固定,
维护一个sum值 进行加入与减出,每次窗口移动加入与减出边界值,尝试更新答案:
1234567891011121314151617class Solution {public: int subarraySum(vector<int>& nums, int k) { int ans = 0; for (int len = 1; len <= nums.size(); ++len) { int sum = 0; for (int i = 0; i < len; ++i) sum += n ...
和大于等于target的最短子数组
和大于等于target的最短子数组
原题链接:https://leetcode.cn/problems/2VG8Kg/
给定一个含有 n 个正整数的数组和一个正整数 target ,
找出该数组中满足其和 ≥ target 的长度最小的 连续子数组 [numsl, numsl+1, ..., numsr-1, numsr] ,并返回其长度。
如果不存在符合条件的子数组,返回 0 。
1.静态滑动窗口1遍历滑动窗口大小,每次for循环滑动窗口大小值固定,每次对滑动窗口中的内容进行求和,尝试更新答案,时间超时:
1234567891011121314151617181920class Solution {public: int minSubArrayLen(int target, vector<int>& nums) { int flag = 0; int ans = nums.size(); //遍历滑动窗口的大小 for (int len = 1; len <= nums.size(); ++len) { for (int i = 0, j = i + len; j <= nums.size(); ++i, ++j) { int sum = 0; for (int k = i; k < j; ++k) sum += nums[k]; if (sum >= target && len <= ans) { ans = len; flag = 1; } } } if (!flag) return 0; return ans; }};
2.静态滑动窗口2遍历滑动窗口大小,每次for循环滑动窗口大小值固定,
维护一个sum值 进行加入与减出,每次窗口移动加入与减出 ...
乘积小于K的子数组
乘积小于K的子数组
原题链接:https://leetcode.cn/problems/ZVAVXX/
1.静态滑动窗口
维护一个mul乘积 对乘积进行 * / 操作
当是当滑动窗口的长度len 非常大时, 乘积mul可能会超出数据范围 如果多个 0 ~ 1000 范围的num[i]相乘 可能超出 int 数据范围
这种方法非常容易超出int的数据范围 认定为失败的方法
123456789101112131415161718class Solution {public: int numSubarrayProductLessThanK(vector<int>& nums, int k) { if (k == 0) return 0; int ans = 0; //遍历滑动窗口的长度 for (int len = 1; len <= nums.size(); ++len) { int mul = 1; int flag = 1; for (int i = 0; i < len; ++i) mul *= nums[i]; if (mul < k) ans++; for (int i = 1; i + len <= nums.size(); ++i) { mul /= nums[i - 1]; mul *= nums[i + len - 1]; if (mul < k) ans++; } } return ans; }};
2.动态滑动窗口
从前往后处理所有的nums[i],使用变量i、j代表窗口的左右端点(静态窗口弊端完美解决),使用变量mul记录当前窗口的乘积,
当 mul>=k 时,我们考虑将左端点i右移,同时消除左端点元素 nums[i] 对 mul 的贡献,直到mul>=k不再满足
这样就可以得到每个右端点 nums[i] 的最远左端 ...
排序数组中两个数字之和
排序数组中两个数字之和
原题链接:https://leetcode.cn/problems/kLl5u1/
给定一个已按照 升序排列 的整数数组 numbers ,请你从数组中找出两个数满足相加之和等于目标数 target 。
函数应该以长度为 2 的整数数组的形式返回这两个数的下标值。numbers 的下标 从 0 开始计数 ,所以答案数组应当满足 0 <= answer[0] < answer[1] < numbers.length 。
假设数组中存在且只存在一对符合条件的数字,同时一个数字不能使用两次。
1.双指针1234567891011121314151617181920class Solution {public: vector<int> twoSum(vector<int>& numbers, int target) { vector<int> ans; int low = 0, high = numbers.size() - 1; while (low < high) { int sum = numbers[low] + numbers[high]; if (sum == target) { ans.push_back(low); ans.push_back(high); break; } else if (sum < target) { low++; } else if (sum > target) { high--; } } return ans; }};
时间复杂度:$O(n)$
空间复杂度:$O(1)$
2.二分查找12345678910111213141516171819202122class S ...
数组中和为0的三个数
数组中和为0的三个数
原题链接:https://leetcode.cn/problems/1fGaJU/
1.暴力法暴力法虽然测试用例能正确输出,但是程序时间复杂度太高直接超时,
123456789101112131415161718192021222324252627class Solution {public: vector<vector<int>> threeSum(vector<int>& nums) { vector<vector<int>> ans; sort(nums.begin(), nums.end()); for (int i = 0; i < nums.size(); ++i) { if (i > 0 && nums[i] == nums[i - 1]) continue; //使用双指针解决剩余两个数字 for (int j = i + 1; j < nums.size(); ++j) { for (int k = j + 1; k < nums.size(); ++k) { if (nums[i] + nums[j] + nums[k] == 0) { //判定符合要求的答案是否重复 //ans.push_back({nums[i], nums[j], nums[k]}); if (ans.size() == 0) ans.push_back({nums[i], nums[j], nums[k]}); else { int flag = 1; for (in ...
单词长度的最大乘积
单词长度的最大乘积
原题链接:https://leetcode.cn/problems/aseY1I/
给定一个字符串数组 words,请计算当两个字符串 words[i] 和 words[j] 不包含相同字符时,它们长度的乘积的最大值。假设字符串中只包含英语的小写字母。如果没有不包含相同字符的一对字符串,返回 0。
1.暴力法暴力法虽然测试用例能正确输出,但是程序时间复杂度太高直接超时,
1234567891011121314151617181920212223242526class Solution {public: bool have_same_char(string str1, string str2) { for (char c = 'a'; c <= 'z'; ++c) { int flag1 = 0; int flag2 = 0; for (int i = 0; i < str1.size(); ++i) if (str1[i] == c) {flag1 = 1; break;} for (int i = 0; i < str2.size(); ++i) if (str2[i] == c) {flag2 = 1; break;} if (flag1 && flag2) return true; } return false; } int maxProduct(vector<string>& words) { int max = 0; for (int i = 0; i < words.size(); ++i) { for (int j = i; j < words.size(); ++j) { int temp = words[i].size() * words[j].size() ...
只出现一次的数字
只出现一次的数字
原题链接:https://leetcode.cn/problems/WGki4K/description/
给你一个整数数组 nums ,除某个元素仅出现 一次 外,其余每个元素都恰出现 三次 。请你找出并返回那个只出现了一次的元素。
1.暴力法1234567891011121314151617class Solution {public: int singleNumber(vector<int> &nums) { vector<int> ans = nums; sort(ans.begin(), ans.end()); ans.erase(unique(ans.begin(), ans.end()), ans.end()); for (int i = 0; i < ans.size(); ++i) { int count = 0; for (int j = 0; j < nums.size(); ++j) { if (nums[j] == ans[i]) count++; if (count > 1) break; } if (count == 1) return ans[i]; } return -1; }};
2.哈希法哈希法:哈希映射统计数组中每个元素的出现次数,在统计完成后,遍历哈希映射即可找出只出现一次的元素。
12345678910111213141516class Solution {public: int singleNumber(vector<int>& nums) { int ans = 0; unordered_map<int, int> unmap; for (int num : nums) unmap[num]++; pair<i ...
前n个数字二进制中1的个数
前n个数字二进制中1的个数
原题链接:https://leetcode.cn/problems/w3tCBm/
给定一个非负整数 n ,请计算 0 到 n 之间的每个数字的二进制表示中 1 的个数,并输出一个数组。
1.一般思路12345678910111213141516class Solution {public: vector<int> countBits(int n) { vector<int> ans; for (int i = 0; i <= n; ++i) { int x = i; int count = 0; while (x) { if (x % 2 == 1) count++; x /= 2; } ans.push_back(count); } return ans; }};
2.位运算12345678910111213141516class Solution {public: vector<int> countBits(int n) { vector<int> ans; for (int i = 0; i <= n; ++i) { int count = 0; int x = i; while (x != 0) { count++; x = x & (x - 1); } ans.push_back(count); } return ans; }};
3.动态规划对于所有的数字,只有奇数和偶数两种:
奇数:二进制表示中,奇数一定比前面那个偶数多一 ...
二进制加法
二进制加法
原题链接:https://leetcode.cn/problems/JFETK5/
位运算是将数字用二进制形式表示之后,对每位上0或1的运算,二进制及其位运算是现代计算机科学的基石,
位运算种类:非~、与&、或|、异或^、左移<<、右移>>
按位与&:对应二进制位上,a与b必须同时为真
按位或|:对应二进制进位上,a与b有一边为真即可
异或运算^:对应二进制位上,相同则为0,不同则为1(逆运算首先满足交换律)
关于异或运算的补充:
异或运算是逆运算:由 a ^ b = c 可得 c ^ a = b; c ^ b = a;
相同的数异或运算为0:n ^ n = 0 ;
任何数与0异或运算值不变:0 ^ n = n;
问:在文件中有一堆整数,每一次数都出现了两次,其中有一个值只出现了一次,如何快速找到只出现一次的这个数的值?
答:利用异或运算解决,将所有整数互相异或,最后得到的值即为只出现一次的值
左移 m << n :二进制数整 m 左移n 位
右移 m >> n:二进制数整 m 右移 n 位
运算
按位与&
0 & 0 = 0
1 & 0 = 0
0 & 1 = 0
1 & 1 = 1
按位或|
0 | 0 = 0
1 | 0 = 1
0 | 1 = 1
1 | 1 = 1
按位异或^
0 ^ 0 = 0
1 ^ 0 = 1
0 ^ 1 = 1
1 ^ 1 = 0
1.数制转换主要思路:将输入的二进制转换为10进制,使用10进制进行运算,将得到的结果转化回二进制即可。
存在问题:outside the range of representable values of type ‘int’,将int类型改为long long 无法存储,
1234567891011121314151617181920212223242526272829303132333435class Solution {public: string _ ...
整数除法
整数除法
原题链接:https://leetcode.cn/problems/xoh6Oh/
编程语言可能会提供占据不同内存空间的整数类型,每种类型能表示的整数范围不同,
无符号整数无论二进制表示的最高位是0还是1,其都表示一个正数,32位整数的范围为 ~ 。由于整数范围的限制,如果计算结果超出了范围,就会产生溢出。产生溢出时运行不会出错,但是计算结果可能会出乎意料。
1.减法主要思路:为了求得dividend/divisor,可以不断的从dividend里减去divisor,当不能再减去更多的divisor时,循环执行的次数就是商
存在问题:
当除数很大时,减法操作执行的次数就会很多,导致程序超时,如
没有考虑除数与被除数,在32位int数据类型的范围溢出问题
没有考虑除法结果,在32位int数据类型的范围溢出问题
12345678910111213141516class Solution {public: int divide(int dividend, int divisor) { int flag = 0; int count = 0; if (dividend < 0 && divisor > 0 || dividend > 0 && divisor < 0) flag = 1; dividend = abs(dividend); divisor = abs(divisor); while (dividend - divisor >= 0) { dividend -= divisor; count++; } if (flag) count = -count; return count; }};
2.减法的优化主要思路:
当被除数大于除数时,继续比较判断被除数是否大于除数的2倍,如果是则继续判断是否大于除数的4倍、8倍、… 倍,
如果被除数最多大于除数的 倍,则将被除数减去除数的 倍,然后将剩余的被除数重复前面的步骤(由于每次 ...