遍历查找题目汇总
- 剑指 Offer 03. 数组中重复的数字
- 剑指 Offer 04. 二维数组中的查找(双指针)
- 剑指 Offer 11. 旋转数组的最小数字
- 剑指 Offer 50. 第一个只出现一次的字符
- 剑指 Offer 53 - I. 在排序数组中查找数字 I
- 剑指 Offer 53 - II. 0~n-1中缺失的数字
- 剑指 Offer II 006. 排序数组中两个数字之和(双指针)
- 剑指 Offer II 007. 数组中和为 0 的三个数(双指针)
- 剑指 Offer II 008. 和大于等于 target 的最短子数组(前缀和)
- 剑指 Offer II 009. 乘积小于 K 的子数组(双指针,滑动窗口)
- 剑指 Offer II 010. 和为 k 的子数组
剑指 Offer 03. 数组中重复的数字
题目
找出数组中重复的数字。
在一个长度为 n 的数组 nums 里的所有数字都在 0~n-1 的范围内。数组中某些数字是重复的,但不知道有几个数字重复了,也不知道每个数字重复了几次。请找出数组中任意一个重复的数字。
示例 1:
输入:
[2, 3, 1, 0, 2, 5, 3]
输出:2 或 3
限制:
2 <= n <= 100000
来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/shu-zu-zhong-zhong-fu-de-shu-zi-lcof
代码
这个题目非常简单,将每一个数加到集合当中,如果数在集合中就直接返回即可
class Solution {
public:
int findRepeatNumber(vector<int>& nums) {
set<int> s;
for(int i=0; i < nums.size(); i++){
if(s.count(nums[i]))
return nums[i];
else{
s.insert(nums[i]);
}
}
return -1;
}
};
剑指 Offer 04. 二维数组中的查找(双指针)
题目
在一个 n * m 的二维数组中,每一行都按照从左到右递增的顺序排序,每一列都按照从上到下递增的顺序排序。请完成一个高效的函数,输入这样的一个二维数组和一个整数,判断数组中是否含有该整数。
示例:
现有矩阵 matrix 如下:
[
[1, 4, 7, 11, 15],
[2, 5, 8, 12, 19],
[3, 6, 9, 16, 22],
[10, 13, 14, 17, 24],
[18, 21, 23, 26, 30]
]
给定 target = 5,返回 true。
给定 target = 20,返回 false。
限制:
0 <= n <= 1000
0 <= m <= 1000
来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/er-wei-shu-zu-zhong-de-cha-zhao-lcof
分析
由于从左到右,从上到下都是递增的,所以我们可以选择左下角或右上角作为起始点。如果以左下角为起点,如果比target大则向右查找,如果比target小则向上查找。如果以右上角为起点,如果比target大则向下查找,如果比target小向左查找。
代码
class Solution {
public:
bool findNumberIn2DArray(vector<vector<int>>& matrix, int target) {
int i = matrix.size() - 1, j = 0;
while(i >= 0 && j < matrix[0].size())
{
if(matrix[i][j] > target) i--;
else if(matrix[i][j] < target) j++;
else return true;
}
return false;
}
};
剑指 Offer 11. 旋转数组的最小数字
题目
把一个数组最开始的若干个元素搬到数组的末尾,我们称之为数组的旋转。
给你一个可能存在 重复 元素值的数组 numbers ,它原来是一个升序排列的数组,并按上述情形进行了一次旋转。请返回旋转数组的最小元素。例如,数组 [3,4,5,1,2] 为 [1,2,3,4,5] 的一次旋转,该数组的最小值为1。
示例 1:
输入:[3,4,5,1,2]
输出:1
示例 2:
输入:[2,2,2,0,1]
输出:0
来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/xuan-zhuan-shu-zu-de-zui-xiao-shu-zi-lcof
分析
方法1:从左向右依次遍历,找出比左侧元素小的元素
方法2:二分法,如果mid的值小于right的值说明待查找的数在mid左侧,如果mid的值大于right说明待查找的数在mid右侧,如果mid的值等于right的值说明待查找的值可能在mid的左边,也有可能mid就是待查找的值
代码
class Solution {
public:
int minArray(vector<int>& numbers) {
int left = 0 , mid, right = numbers.size() - 1;
while(left < right){
mid = left + (right - left) / 2;
if(numbers[mid] < numbers[right]){
right = mid;
}else if(numbers[mid] > numbers[right]){
left = mid + 1;
}else{
right--;
}
}
return numbers[left];
}
};
剑指 Offer 50. 第一个只出现一次的字符
题目
在字符串 s 中找出第一个只出现一次的字符。如果没有,返回一个单空格。 s 只包含小写字母。
示例 1:
输入:s = “abaccdeff”
输出:‘b’
示例 2:
输入:s = “”
输出:’ ’
限制:
0 <= s 的长度 <= 50000
来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/di-yi-ge-zhi-chu-xian-yi-ci-de-zi-fu-lcof
代码
class Solution {
public:
char firstUniqChar(string s) {
if(s == ""){
return ' ';
}
map<char, int> m;
for(int i=0; i < s.size(); i++){
m[s[i]]++;
}
for(int i=0; i < s.size(); i++){
if(m[s[i]] == 1)
return s[i];
}
return ' ';
}
};
剑指 Offer 53 - I. 在排序数组中查找数字 I
题目
统计一个数字在排序数组中出现的次数。
示例 1:
输入: nums = [5,7,7,8,8,10], target = 8
输出: 2
示例 2:
输入: nums = [5,7,7,8,8,10], target = 6
输出: 0
提示:
0 <= nums.length <= 105
-109 <= nums[i] <= 109
nums 是一个非递减数组
-109 <= target <= 109
来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/zai-pai-xu-shu-zu-zhong-cha-zhao-shu-zi-lcof
代码
class Solution {
public:
int search(vector<int>& nums, int target) {
int cnt=0;
for(int i=0; i < nums.size(); i++){
if(nums[i]==target){
cnt++;
}
}
return cnt;
}
};
剑指 Offer 53 - II. 0~n-1中缺失的数字
题目
一个长度为n-1的递增排序数组中的所有数字都是唯一的,并且每个数字都在范围0~n-1之内。在范围0~n-1内的n个数字中有且只有一个数字不在该数组中,请找出这个数字。
示例 1:
输入: [0,1,3]
输出: 2
示例 2:
输入: [0,1,2,3,4,5,6,7,9]
输出: 8
输入: [0]
输出: 1
来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/que-shi-de-shu-zi-lcof
代码
class Solution {
public:
int missingNumber(vector<int>& nums) {
int cur=0;
for(int i=0; i < nums.size(); i++){
if(nums[i] == cur){
cur++;
}else{
return cur;
}
}
return nums[nums.size()-1]+1;
}
};
剑指 Offer II 006. 排序数组中两个数字之和(双指针)
给定一个已按照 升序排列 的整数数组 numbers ,请你从数组中找出两个数满足相加之和等于目标数 target 。
函数应该以长度为 2 的整数数组的形式返回这两个数的下标值。numbers 的下标 从 0 开始计数 ,所以答案数组应当满足 0 <= answer[0] < answer[1] < numbers.length 。
假设数组中存在且只存在一对符合条件的数字,同时一个数字不能使用两次。
示例 1:
输入:numbers = [1,2,4,6,10], target = 8
输出:[1,3]
解释:2 与 6 之和等于目标数 8 。因此 index1 = 1, index2 = 3 。
示例 2:
输入:numbers = [2,3,4], target = 6
输出:[0,2]
示例 3:
输入:numbers = [-1,0], target = -1
输出:[0,1]
提示:
2 <= numbers.length <= 3 * 104
-1000 <= numbers[i] <= 1000
numbers 按 递增顺序 排列
-1000 <= target <= 1000
仅存在一个有效答案
来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/kLl5u1
分析
双指针和target进行比较,如果和比target大右指针左移,如果小左指针右移,如果相等直接返回
代码
class Solution {
public:
vector<int> twoSum(vector<int>& numbers, int target) {
int start=0, end=numbers.size()-1;
while(start <= end){
if(numbers[start] + numbers[end] == target){
return vector<int> {start, end};
}else if(numbers[start] + numbers[end] > target){
end--;
}else if(numbers[start] + numbers[end] < target){
start++;
}
}
return vector<int> {-1, -1};
}
};
剑指 Offer II 007. 数组中和为 0 的三个数(双指针)
给定一个包含 n 个整数的数组 nums,判断 nums 中是否存在三个元素 a ,b ,c ,使得 a + b + c = 0 ?请找出所有和为 0 且 不重复 的三元组。
示例 1:
输入:nums = [-1,0,1,2,-1,-4]
输出:[[-1,-1,2],[-1,0,1]]
示例 2:
输入:nums = []
输出:[]
示例 3:
输入:nums = [0]
输出:[]
提示:
0 <= nums.length <= 3000
-105 <= nums[i] <= 105
来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/1fGaJU
分析
首先进行排序,排序后任取一个数,然后取反。在这个数之后取双指针,如果左指针和右指针的和小于取反的数左指针右移,否则右指针左移。
代码
class Solution {
public:
vector<vector<int>> threeSum(vector<int>& nums) {
if(nums.size()<3){
return {};
}
sort(nums.begin(), nums.end());
vector<vector<int>> ans;
for(int i=0; i < nums.size()-2; i++){
if(i > 0 && nums[i]==nums[i-1])
continue;
int target = -nums[i];
int left=i+1;
int right=nums.size()-1;
while(left < right){
if(nums[left]+nums[right] == target){
ans.push_back({nums[i], nums[left], nums[right]});
while(left < right && nums[left]==nums[++left]);
while(left < right && nums[right]==nums[--right]);
}else if(nums[left]+nums[right] > target){
right--;
}else{
left++;
}
}
}
return ans;
}
};
剑指 Offer II 008. 和大于等于 target 的最短子数组(前缀和)
题目
给定一个含有 n 个正整数的数组和一个正整数 target 。
找出该数组中满足其和 ≥ target 的长度最小的 连续子数组 [numsl, numsl+1, …, numsr-1, numsr] ,并返回其长度。如果不存在符合条件的子数组,返回 0 。
示例 1:
输入:target = 7, nums = [2,3,1,2,4,3]
输出:2
解释:子数组 [4,3] 是该条件下的长度最小的子数组。
示例 2:
输入:target = 4, nums = [1,4,4]
输出:1
示例 3:
输入:target = 11, nums = [1,1,1,1,1,1,1,1]
输出:0
提示:
1 <= target <= 109
1 <= nums.length <= 105
1 <= nums[i] <= 105
进阶:
如果你已经实现 O(n) 时间复杂度的解法, 请尝试设计一个 O(n log(n)) 时间复杂度的解法。
来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/2VG8Kg
分析
打表遍历得到前缀和sums,那么i到j的数的和为sums[j]-sums[i]
,那么问题就变成了sums[j]-sums[i] > target
但是这样我们不方便遍历,我们把问题转换成target + sums[i] < sums[j]
,遍历得到i
然后求j
是否存在即可。
代码
class Solution {
public:
int minSubArrayLen(int s, vector<int>& nums) {
int n = nums.size();
if (n == 0) {
return 0;
}
int ans = INT_MAX;
vector<int> sums(n + 1, 0);
for (int i = 1; i <= n; i++) {
sums[i] = sums[i - 1] + nums[i - 1];
}
for (int i = 1; i <= n; i++) {
int target = s + sums[i - 1];
auto bound = lower_bound(sums.begin(), sums.end(), target);
if (bound != sums.end()) {
ans = min(ans, static_cast<int>((bound - sums.begin()) - (i - 1)));
}
}
return ans == INT_MAX ? 0 : ans;
}
};
剑指 Offer II 009. 乘积小于 K 的子数组(双指针,滑动窗口)
题目
给定一个正整数数组 nums和整数 k ,请找出该数组内乘积小于 k 的连续的子数组的个数。
示例 1:
输入: nums = [10,5,2,6], k = 100
输出: 8
解释: 8 个乘积小于 100 的子数组分别为: [10], [5], [2], [6], [10,5], [5,2], [2,6], [5,2,6]。
需要注意的是 [10,5,2] 并不是乘积小于100的子数组。
示例 2:
输入: nums = [1,2,3], k = 0
输出: 0
提示:
1 <= nums.length <= 3 * 104
1 <= nums[i] <= 1000
0 <= k <= 106
来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/ZVAVXX
分析
双指针,设置左指针和右指针,如果满足条件右指针左移,统计答案, 如果不满足条件移动左指针。
以示例1[10,5,2,6]
为例,统计答案的方式如下:
left | right | 乘积 | 区间 | 满足的子数组 | 满足子数组数 |
---|---|---|---|---|---|
0 | 0 | 10 | [10] | [10] | 1 |
0 | 1 | 50 | [10,5] | [10],[5],[10, 5] | 3 |
0 | 2 | 100 | - | - | 0 |
1 | 1 | 5 | [5] | [5] | 1 |
1 | 2 | 10 | [5,2] | [5], [2],[5,2] | 3 |
1 | 3 | 60 | [5,2, 6] | [5], [2],[5,2],[6],[2,6],[5,2,6] | 6 |
从部分情况的表格可以看出每次right满足条件右移一位,答案应该增加right-left+1
代码
class Solution {
public:
int numSubarrayProductLessThanK(vector<int>& nums, int k) {
if(k <= 1){
return 0;
}
int ans=0;
int left=0, right=0, mult=1;
while(right < nums.size()){
mult *= nums[right];
while(mult >= k){
mult /= nums[left];
left++;
}
ans += right - left + 1;
right++;
}
return ans;
}
};
剑指 Offer II 010. 和为 k 的子数组
给定一个整数数组和一个整数 k ,请找到该数组中和为 k 的连续子数组的个数。
示例 1 :
输入:nums = [1,1,1], k = 2
输出: 2
解释: 此题 [1,1] 与 [1,1] 为两种不同的情况
示例 2 :
输入:nums = [1,2,3], k = 3
输出: 2
提示:
1 <= nums.length <= 2 * 104
-1000 <= nums[i] <= 1000
-107 <= k <= 107
来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/QTMn0o
分析
假设我们求得了nums的前缀和,那么任意连续子数组都可以用pre[j]-pre[i],j>i
表示,满足情况pre[j]-pre[i] = k
,即pre[j]-k=pre[i]
, 所以我们在遍历时只遍历j
即可,由于pre[i]
一定比pre[j]
早出现,所以我们不需要保存数组,用一个整数存储即可。
代码
class Solution {
public:
int subarraySum(vector<int>& nums, int k) {
int pre = 0;
map<int, int> m;
m[0]=1;
int ans=0;
for(int i=0; i < nums.size(); i++){
pre += nums[i];
int target = pre - k;
ans += m[target];
m[pre]++;
}
return ans;
}
};