目录
例2:leetcode 34.在排序数组中查找元素的第一个和最后一个位置
知识点:
- 数组是存放在连续内存空间上的相同类型数据的集合。
- 数组下标是从0开始的;
- 数组内存空间的地址是连续的,删除元素或者增添元素时要移动其他元素的地址。
- 数组的元素不能删只能覆盖。
- vector 与array区别:vector底层实现是array,vector是容器,不是数组。
算法:
1、二分查找(循环不变量原则)
重点:根据查找区间的定义做边界处理。
二分查找的使用前提条件:有序数组且数组中无重复元素。
区间定义一般有两种:左闭右开,左闭右闭。
例1:leetcode 704二分查找
给定一个 n 个元素有序的(升序)整型数组 nums 和一个目标值 target ,写一个函数搜索 nums 中的 target,如果目标值存在返回下标,否则返回 -1。
#include<iostream>
using namespace std;
#include<vector>
int search(vector<int>&nums, int target) {
int left = 0;
int right = nums.size() - 1;//定义target在[left,right]左闭右闭区间里
while (left <= right) {
int middle = left + (right - left) / 2;//防止溢出,==(right - left) / 2
if (nums[middle] < target) {
left = middle + 1;
}
else if (nums[middle] > target) {
right = middle - 1;
}
else {
return middle;
}
}
return -1;
}
int main() {
cout << "请输入数组的元素个数: ";
int n;
while (cin >> n) {
vector<int> nums(n);
for (int i = 0; i < n; i++) cin >> nums[i];
cout << "请输入目标值(整数): " ;
int target;
cin >> target;
if (search(nums, target) == -1) {
cout << "该数组中没有与目标值相等的元素。" << endl;
}
else {
cout << "该数组中与目标值相等的元素下标为: " ;
cout<<search(nums, target)<<endl;
}
}
system("pause");
return 0;
}
补充:<<1,左移1位相当于乘以2;>>1,右移1位相当于除以2;
例2:leetcode 34.在排序数组中查找元素的第一个和最后一个位置
给定一个按照升序排列的整数数组 nums,和一个目标值 target。找出给定目标值在数组中的开始位置和结束位置。
如果数组中不存在目标值 target,返回 [-1, -1]。
思路:
第一个位置:数组中第一个等于target的位置
最后一个位置:数组中最后一个大于target元素的位置减1;
int search4(vector<int>&nums, int target,bool lf) {//bool lf是用来判断计算的是左指针还是右指针
int left = 0;
int right = nums.size() - 1;//定义target在[left,right)左闭右开的区间里
int result = nums.size();//比如说,数组为[7,7,8,8],目标值为8,其实进入不到if语句里面即result值不会变,所以一开始设置它为数组长度,最后取到的下标值减1
while (left <= right) {
int middle = left + (right - left) / 2;//防止溢出,==(right - left) / 2
if ((nums[middle] >= target && lf) || nums[middle]>target) {
right = middle - 1; //target在右区间,[middle+1, right)
result = middle;
}
else{
left = middle + 1;//target在左区间,[left,middle)
}
}
return result;
}
vector<int> serchFirstAndLast(vector<int>&nums, int target) {
int leftptr = search4(nums, target, true);
int rightptr = search4(nums, target, false)-1;
if (leftptr <= rightptr && rightptr <nums.size() && nums[leftptr]==target && nums[rightptr] == target) {
return vector<int> { leftptr, rightptr };
}
return vector<int> { -1, -1 };
}
例3:leetcode 69. x的平方根,使用二分查找
int mySqrt(int x) {
int l = 1, r = x, result = x;
while (l <= r) {
int mid = l + (r - l) / 2;
if ( mid*mid <= x) {
l = mid + 1;
result = mid;
}
else {
r = mid - 1;
}
}
return result;
}
2、双指针
双指针法:通过一个快指针和慢指针在一个for循环下完成两个for循环的工作。
例1:leetcode 27. 移除元素
给你一个数组 nums 和一个值 val,你需要 原地 移除所有数值等于 val 的元素,并返回移除后数组的新长度。
不要使用额外的数组空间,你必须仅使用 O(1) 额外空间并 原地 修改输入数组。
元素的顺序可以改变。你不需要考虑数组中超出新长度后面的元素。
#include<iostream>
using namespace std;
#include<vector>
int removeElement2(vector<int>& nums, int val) {
int slow = 0;//慢指针
for (int i = 0; i < nums.size(); i++) {
//若与目标值相等,快指针往前一步,若不等,则慢指针与快指针同时移动
if (nums[i] != val) {
nums[slow++] = nums[i];
}
}
return slow;
}
int main() {
cout << "请输入数组的元素个数: ";
int n;
while (cin >> n) {
vector<int> nums(n);
for (int i = 0; i < n; i++) cin >> nums[i];
cout << "请输入目标值(整数): ";
int target;
cin >> target;
//移除元素
int len = removeElement2(nums, target);
cout << "移除元素后的数组长度为:"<< len <<endl;
for (int i = 0; i < len; i++) {
cout<< nums[i] <<" ";
}
system("pause");
return 0;
}
例2:leetcode 26. 删除有序数组中的重复项
核心代码如下:
int removeDuplicates(vector<int>& nums) {
int n = nums.size();
if (n == 0) return 0;
int slow = 0;
for (int i = 0; i < n; i++) {
if (nums[slow] != nums[i]) {
nums[++slow] = nums[i];
}
}
return slow + 1;
}
例3:leetcode 977. 有序数组的平方
给你一个按 非递减顺序 排序的整数数组 nums,返回 每个数字的平方 组成的新数组,要求也按 非递减顺序 排序。
vector<int> sortedSquares(vector<int>& nums) {
int first = 0, last = nums.size() - 1;
vector<int> v(last+1,0);
for(int i=last;i>=0;i--) {
if (nums[first] * nums[first] > nums[last] * nums[last]) {
v[i] = nums[first] * nums[first];
first++;
}
else {
v[i] = nums[last] * nums[last];
last--;
}
}
return v;
}
3、滑动窗口
不断的调节子序列的起始位置和终止位置,从而得出我们想要的结果。
滑动窗口主要应用在数组和字符串上。
例1:leetcode 209. 长度最小的子数组
int minSubArrayLen(int target, vector<int>& nums) {
int n = nums.size();
int first = 0;//滑动窗口起始位置
int subLength = 0;//子数组长度
int sum = 0;//子数组的和
int result = INT32_MAX;//一开始默认为最大值
for (int i = 0; i < n; i++) {
sum += nums[i];
while (sum >= target) {
//当子数组总和大于等于目标值时,滑动窗口的起始位置需往前移
subLength = i - first + 1;
result = result < subLength ? result : subLength;
sum -= nums[first];
first++; //不断变更子序列的起始位置
}
}
// 如果result没有被赋值的话,就返回0,说明没有符合条件的子序列
return result==INT32_MAX ? 0 : result;
}
例2:leetcode 904. 水果成篮
这道题得先要弄清楚它的题目要求:即找出只存在两种类型的最长子数组,并返回其长度
int totalFruit(vector<int>& fruits) {
int first = 0, second = 0;
int len = 0;
int subLen = 0;
int temp = 0;//需要借助一个变量记录第三种元素出现时应该从哪个下标开始。
for (int i = 0; i < fruits.size(); i++) {
if (fruits[first] != fruits[i] && fruits[second] != fruits[i])
{
if (fruits[first] != fruits[second]) {
first = temp;
}
second = i;
}
if (fruits[temp] != fruits[i]) {
temp = i;
}
subLen = i - first + 1;
len = len < subLen ? subLen : len;
}
return len;
}