数组
数组是存放在连续空间上的相同类型数据的集合
数组下标都是从0开始的
数组内存空间的地址是连续的
数组的元素是不能删的,只能覆盖
在C++中二维数组是连续分布的(地址为16进制,相差4个字节)
方法
-
二分法
-
双指针法
1.相向
2.同向:滑动窗口
3.背向:归并排序 -
快排法
-
模拟过程
704.二分查找
基础:给定一个 n 个元素有序的(升序)整型数组 nums 和一个目标值 target ,写一个函数搜索 nums 中的 target,如果目标值存在返回下标,否则返回 -1。(无重复元素)
class Solution{
public:
int search(vector<int>& nums, int target){
if (nums.size() == 0)
return - 1;
int start = 0;
int end = nums.size() - 1;
while (start + 1 < end){
int middle = start + ((end - start) / 2);
if (nums[middle] < target){
start = middle;
}
else if(nums[middle]>target){
end = middle;
}else{
return middle;
}
}
return -1;
}
};
升级:给一个升序数组,找到 target 最后一次出现的位置,如果没出现过返回 -1。(有重复元素)
#include <iostream>
#include <vector>
using namespace std;
class Solution{
public:
int search(vector<int>& nums, int target){
if (nums.size() == 0)
return - 1;
int start = 0;
int end = nums.size() - 1;
while (start + 1 < end){
int middle = start + ((end - start) / 2);
if (nums[middle] < target){
start = middle;
}
else{
end = middle;
}
}
if (nums[start] == target){
return start;
}
if (nums[end] == target){
return end;
}
return -1;
}
};
0027.移除元素
给你一个数组 nums 和一个值 val,你需要 原地 移除所有数值等于 val 的元素,并返回移除后数组的新长度。
不要使用额外的数组空间,你必须仅使用 O(1) 额外空间并原地修改输入数组。
元素的顺序可以改变。你不需要考虑数组中超出新长度后面的元素。
//双指针法
//同向双指针法(快慢指针法):通过一个快指针和慢指针在一个for循环下完成两个for循环的工作。
/*
定义快慢指针
快指针:寻找新数组的元素 ,新数组就是不含有目标元素的数组
慢指针:指向更新 新数组下标的位置
时间复杂度O(n)
空间复杂度O(1)
此方法的实现没有改变元素的相对位置
*/
class Solution{
public:
int removeElement(vector<int>& nums,int val){
int slowIndex=0;
for(int fastIndex=0;fastIndex<nums.size();fastIndex++){
if(val!=nums[fastIndex]){
nums[slowIndex++]=nums[fastIndex];
}
return slowIndex;
}
}
//相向双指针方法,基于元素顺序可以改变的题目描述改变了元素相对位置,确保了移动最少元素
//时间复杂度:O(n)
//空间复杂度:O(1)
class Solution{
public:
int removeElement(vector<int>& nums,int val){
int leftIndex=0;
int rightIndex=nums.size()-1;
while(leftIndex<=rightIndex){
while(leftIndex<=rightIndex&&nums[leftIndex]!=val){
leftIndex++;
}
while(leftIndex<=rightIndex&&nums[rightIndex]==val){
rightIndex--;
}
//就右边不等于val的元素覆盖左边等于val的元素
if(leftIndex<rightIndex){
nums[leftIndex++]==nums[rightIndex--];
}
}
//leftIndex一定指向了最终数组末尾的下一个元素
return leftIndex;
}
}
26.删除排序数组中的重复项
给你一个 升序排列 的数组 nums ,请你 原地 删除重复出现的元素,使每个元素 只出现一次 ,返回删除后数组的新长度。元素的 相对顺序 应该保持 一致 。
由于在某些语言中不能改变数组的长度,所以必须将结果放在数组nums的第一部分。更规范地说,如果在删除重复项之后有 k 个元素,那么 nums 的前 k 个元素应该保存最终结果。
将最终结果插入 nums 的前 k 个位置后返回 k 。
不要使用额外的空间,你必须在 原地 修改输入数组 并在使用 O(1) 额外空间的条件下完成。
class Solution {
public:
int removeDuplicates(vector<int>& nums) {
if(nums.size()==0){
return 0;
}
int slowIndex=1;
for(int fastIndex=1;fastIndex<nums.size();fastIndex++){
if(nums[fastIndex-1]!=nums[fastIndex]){
nums[slowIndex++]=nums[fastIndex];
}
}
return slowIndex;
}
};
83.移动零
给定一个数组 nums,编写一个函数将所有 0 移动到数组的末尾,同时保持非零元素的相对顺序。
请注意 ,必须在不复制数组的情况下原地对数组进行操作。
//双指针法
class Solution {
public:
void moveZeroes(vector<int>& nums) {
if(nums.size()==0){
return;
}
int slowIndex=0;
for(int fastIndex=0;fastIndex<nums.size();fastIndex++){
if(nums[fastIndex]!=0){
nums[slowIndex++]=nums[fastIndex];
}
}
for(int fastIndex=slowIndex;fastIndex<nums.size();fastIndex++){
nums[fastIndex]=0;
}
}
};
//快排法
class Solution {
public:
void moveZeroes(vector<int>& nums) {
if(nums.size()==0){
return;
}
int left=0;
for(int right=0;right<nums.size();right++){
if(nums[right]!=0){
swap(nums[left],nums[right]);
left++;
}
}
}
};
844.比较含退格的字符串
给定 s 和 t 两个字符串,当它们分别被输入到空白的文本编辑器后,如果两者相等,返回 true 。# 代表退格字符。
注意:如果对空文本输入退格字符,文本继续为空。
class Solution{
public:
bool backspaceCompare(String S,String T){
int i=S.length()-1,j=T.length()-1;
int skipS=0,skipT=0;
while(i>=0||j>=0){
while(i>=0){
if(S[i]=='#'){
skipS++,i--;
}else if(skipS>0){
skipS--,i--;
}else{
break;
}
}
while(j>=0){
if(T[j]=='#'){
skipT++,j--;
}else if(skipT>0){
skipT--,j--;
}else{
break;
}
}
if(i>=0&&j>=0){
if(S[i]!=T[j]){
return false;
}
}
i--,j--;
}
return true;
}
}
977.有序数组的平方
给你一个按 非递减顺序 排序的整数数组 nums,返回 每个数字的平方 组成的新数组,要求也按 非递减顺序 排序。
//双指针法
//归并排序法
//背向双指针
class Solution{
public:
vector<int> sortedSquares(vector<int>& nums){
int n=nums.size();
int negative=-1;
for(int i=0;i<n;i++){
if(nums[i]<0){
negative=i;
}else{
break;
}
}
vector<int> ans;
int i=negative,j=negative+1;
while(i>=0||j<n){
if(i<0){
ans.push_back(nums[j]*nums[j]);
j++;
}
else if(j==n){
ans.push_back(nums[i]*nums[i]);
i--;
}
else if(nums[i]*nums[i]<nums[j]*nums[j]){
ans.push_back(nums[i]*nums[i]);
i--;
}
else{
ans.push_back(nums[j]*nums[j]);
j++;
}
}
}
};
//相向双指针,逆序放入答案
class Solution{
public:
vector<int> sortedSquares(vector<int>& nums){
int n=nums.size();
vector<int> ans(n);
for(int i=0,j=n-1,pos=n-1;i<=j;){
if(nums[i]*nums[i]>nums[j]*nums[j]){
ans[pos]=nums[i]*nums[i];
i++;
}
else{
ans[pos]=nums[j]*nums[j];
j--;
}
pos--;
}
return ans;
}
}
209长度最小的子数组
给定一个含有 n 个正整数的数组和一个正整数 s ,找出该数组中满足其和 ≥ s 的长度最小的 连续 子数组,并返回其长度。如果不存在符合条件的子数组,返回 0。
//滑动窗口
class Solution{
public:
int minSubArrayLen(int s,vector<int>& nums){
int result=INT32_MAX;
int sum=0;
int i=0;
int subLength=0;
for(int j=0;j<nums.size();j++){
sum+=nums[j];
while(sum>=s){
subLength=(j-i+1);
result=result<subLength:result:subLength;
}
}
retrun result==INT32_MAX?0:result;
}
}
904水果成篮
你正在探访一家农场,农场从左到右种植了一排果树。这些树用一个整数数组 fruits 表示,其中 fruits[i] 是第 i 棵树上的水果 种类 。
你想要尽可能多地收集水果。然而,农场的主人设定了一些严格的规矩,你必须按照要求采摘水果:
你只有 两个 篮子,并且每个篮子只能装 单一类型 的水果。每个篮子能够装的水果总量没有限制。
你可以选择任意一棵树开始采摘,你必须从 每棵 树(包括开始采摘的树)上 恰好摘一个水果 。采摘的水果应当符合篮子中的水果类型。每采摘一次,你将会向右移动到下一棵树,并继续采摘。
一旦你走到某棵树前,但水果不符合篮子的水果类型,那么就必须停止采摘。
给你一个整数数组 fruits ,返回你可以收集的水果的 最大 数目。
class Solution {
public:
int totalFruit(vector<int>& fruits) {
//由题意,转化问题为:求只包含两种元素的最长连续子序列
//滑动窗口法
int i = 0; //滑动窗口左边界
int sub_len = 0; //子序列长度
unordered_map<int, int> basket; //创建篮子
for (int j = 0; j < fruits.size(); j++) {
basket[fruits[j]]++;
//如果篮子中的水果数目超过两种,则需要移动左边界,对应从子序列中删去水果的value要减一
while(basket.size() > 2) {
basket[fruits[i]]--;
//若对应水果key的value变为0,说明篮子里已经没有这种水果了,水果种类要对应变化
if (basket[fruits[i]] == 0) {
basket.erase(fruits[i]);
}
i++;
}
//在第二个for循环结束后,篮子中的水果一定满足题意要求,此时更新子序列长度
sub_len = max(sub_len, j - i + 1);
}
return sub_len;
}
};
76.最小覆盖子串
给你一个字符串 s 、一个字符串 t 。返回 s 中涵盖 t 所有字符的最小子串。如果 s 中不存在涵盖 t 所有字符的子串,则返回空字符串 “” 。
注意:
对于 t 中重复字符,我们寻找的子字符串中该字符数量必须不少于 t 中该字符数量。
如果 s 中存在这样的子串,我们保证它是唯一的答案。
class Solution {
public:
string minWindow(string s, string t) {
unordered_map<char, int> hs, ht;
for (auto c: t) ht[c] ++ ;
string res;
int cnt = 0;
for (int j = 0, i = 0; j < s.size(); j ++ ) {
hs[s[j]] ++ ;
if (hs[s[j]] <= ht[s[j]]) cnt ++ ;
while (hs[s[i]] > ht[s[i]]){
hs[s[i]] --;
i++;
}
if (cnt == t.size()) {
if (res.empty() || j - i + 1 < res.size())
res = s.substr(i, j - i + 1);
}
}
return res;
}
};
0059.螺旋矩阵II
给你一个正整数 n ,生成一个包含 1 到 n^2 所有元素,且元素按顺时针顺序螺旋排列的 n x n 正方形矩阵 matrix 。
class Solution{
public:
vector<vector<int>> generateMatrix(int n){
vector<vector<int>> res(n,vector<int>(n,0));
int startx=0,starty=0;
int loop=n/2;
int mid=n/2;
int count=1;
int offset=1;
int i,j;
while(loop--){
i=startx;
j=starty;
for(j=starty;j<n-offset;j++){
res[startx][j]=count++;
}
for(i=startx;i<n-offset;i++){
res[i][j]=count++;
}
for(;j>starty;j--){
res[i][j]=count++;
}
for(;i>startx;i--){
res[i][j]=count++;
}
startx++;
starty++;
offset+=1;
}
if(n%2){
res[mid][mid]=count;
}
return res;
}
};
54.螺旋矩阵
给你一个 m 行 n 列的矩阵 matrix ,请按照 顺时针螺旋顺序 ,返回矩阵中的所有元素。
class Solution {
public:
vector<int> spiralOrder(vector<vector<int>>& matrix) {
vector <int> ans;
if(matrix.empty()) return ans; //若数组为空,直接返回答案
int u = 0; //赋值上下左右边界
int d = matrix.size() - 1;
int l = 0;
int r = matrix[0].size() - 1;
while(true)
{
for(int i = l; i <= r; ++i) ans.push_back(matrix[u][i]); //向右移动直到最右
if(++ u > d) break; //重新设定上边界,若上边界大于下边界,则遍历遍历完成,下同
for(int i = u; i <= d; ++i) ans.push_back(matrix[i][r]); //向下
if(-- r < l) break; //重新设定有边界
for(int i = r; i >= l; --i) ans.push_back(matrix[d][i]); //向左
if(-- d < u) break; //重新设定下边界
for(int i = d; i >= u; --i) ans.push_back(matrix[i][l]); //向上
if(++ l > r) break; //重新设定左边界
}
return ans;
}
};
剑指 Offer 29. 顺时针打印矩阵
输入一个矩阵,按照从外向里以顺时针的顺序依次打印出每一个数字。
同54.螺旋矩阵