数组是存放在连续内存空间上的相同类型数据的集合。
vector:动态数组,是我们最常使用的数据结构之一,用于 O(1) 的随机读取。因为大部分算法的时间复杂度都会大于 O(n),因此我们经常新建 vector 来存储各种数据或中间变量。因为在尾部增删的复杂度是 O(1),我们也可以把它当作 stack 来用。
array:固定大小的数组,一般在刷题时我们不使用。
数组的元素是不能删的,只能覆盖。
二分查找的前提是数组一定是有序的,详细可以参考之前写的 C/C++二分查找 。
题目描述
给定一个 n 个元素有序的(升序)整型数组 nums 和一个目标值 target ,写一个函数搜索 nums 中的 target,如果目标值存在返回下标,否则返回 -1。
输入输出样例
输入: nums = [-1,0,3,5,9,12], target = 9
输出: 4
解释: 9 出现在 nums 中并且下标为 4
题解
二分查找是较为简单的算法,在算法导论以及算法图解等书籍中都是拿来作为入门引导的算法。但是虽然简单,但是许多人都是一看就会,一写就乱。主要原因还是对二分查找的边界条件比较模糊。
写二分法,区间的定义一般为两种,左闭右闭即[left, right],或者左闭右开即[left, right)。下面通过这个简单的题对这两种算法进行书写。
代码
第一种写法,我们定义 target 是在一个在左闭右闭的区间里,也就是[left, right] 。
class Solution {
public:
int search(vector<int>& nums, int target) {
int left=0,right=nums.size()-1;
while(left<=right){
int mid=left+(right-left)/2;
if(nums[mid]==target) return mid;
else if(nums[mid]>target) right=mid-1;
else left=mid+1;
}
return -1;
}
};
如果说定义 target 是在一个在左闭右开的区间里,也就是[left, right) ,那么二分法的边界处理方式则截然不同。
class Solution {
public:
int search(vector<int>& nums, int target) {
int left=0,right=nums.size();
while(left<right){
int mid=left+(right-left)/2;
if(nums[mid]==target) return mid;
else if(nums[mid]>target) right=mid;
else left=mid+1;
}
return -1;
}
};
题目描述
编写一个高效的算法来搜索 m x n 矩阵 matrix 中的一个目标值 target 。该矩阵具有以下特性:
输入输出样例
输入: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
代码
class Solution {
public:
bool searchMatrix(vector<vector<int>>& matrix, int target) {
int m=matrix.size(),n=matrix[0].size();
int i=m-1,j=0;
while(i>=0&&j<n){
if(matrix[i][j]==target) return true;
else if(matrix[i][j]>target) i--;
else j++;
}
return false;
}
};
题目描述
给你一个数组 nums 和一个值 val,你需要 原地 移除所有数值等于 val 的元素,并返回移除后数组的新长度。
不要使用额外的数组空间,你必须仅使用 O(1) 额外空间并 原地 修改输入数组。
元素的顺序可以改变。你不需要考虑数组中超出新长度后面的元素。
输入输出样例
输入:nums = [3,2,2,3], val = 3
输出:2, nums = [2,2]
解释:函数应该返回新的长度 2, 并且 nums 中的前两个元素均为 2。你不需要考虑数组中超出新长度后面的元素。例如,函数返回的新长度为 2 ,而 nums = [2,2,3,3] 或 nums = [2,2,0,0],也会被视作正确答案。
代码
暴力解法
两层for循环,一个for循环遍历数组元素 ,第二个for循环更新数组。
class Solution {
public:
int removeElement(vector<int>& nums, int val) {
int size=nums.size();
for(int i=0;i<size;i++){
if(nums[i]==val){
for(int j=i+1;j<size;j++){
nums[j-1]=nums[j];
}
i--;
size--;
}
}
return size;
}
};
双指针法(快慢指针法)
通过一个快指针和慢指针在一个for循环下完成两个for循环的工作。
class Solution {
public:
int removeElement(vector<int>& nums, int val) {
int slow=0;
for(int fast=0;fast<nums.size();fast++){
if(nums[fast]!=val){
nums[slow++]=nums[fast];
}
}
return slow;
}
};
题目描述
给定一个含有 n 个正整数的数组和一个正整数 target 。
找出该数组中满足其和 ≥ target 的长度最小的 连续子数组 [numsl, numsl+1, …, numsr-1, numsr] ,并返回其长度。如果不存在符合条件的子数组,返回 0 。
输入输出样例
输入:target = 7, nums = [2,3,1,2,4,3]
输出:2
解释:子数组 [4,3] 是该条件下的长度最小的子数组。
代码
//滑动窗口
class Solution {
public:
int minSubArrayLen(int target, vector<int>& nums) {
if(accumulate(nums.begin(),nums.end(),0)<target) return 0;
int left=0,right=0;
int min_num=INT_MAX;
int sum_num=0;
for(right=0;right<nums.size();right++){
sum_num+=nums[right];
while(sum_num>=target){
min_num=min(min_num,right-left+1);
sum_num-=nums[left];
left++;
}
}
return min_num;
}
};
题目描述
给你一个正整数 n ,生成一个包含 1 到 n2 所有元素,且元素按顺时针顺序螺旋排列的 n x n 正方形矩阵 matrix 。
输入:n = 3
输出:[[1,2,3],[8,9,4],[7,6,5]]
代码
class Solution {
public:
vector<vector<int>> generateMatrix(int n) {
int num=1;
vector<vector<int>> res(n,vector<int>(n,0));
int row=0,col=0;
while(num<=n*n){
for(int i=col;i<n-col;i++){
res[row][i]=num;
num++;
}
for(int i=row+1;i<n-row;i++){
res[i][n-col-1]=num;
num++;
}
for(int i=n-col-2;i>=col;i--){
res[n-row-1][i]=num;
num++;
}
for(int i=n-row-2;i>=row+1;i--){
res[i][col]=num;
num++;
}
row++;
col++;
}
return res;
}
};
题目描述
给定一个 n × n 的二维矩阵 matrix 表示一个图像。请你将图像顺时针旋转 90 度。
你必须在 原地 旋转图像,这意味着你需要直接修改输入的二维矩阵。请不要 使用另一个矩阵来旋转图像。
输入输出样例
输入:matrix = [[1,2,3],[4,5,6],[7,8,9]]
输出:[[7,4,1],[8,5,2],[9,6,3]]
代码
class Solution {
public:
void rotate(vector<vector<int>>& matrix) {
int temp = 0, n = matrix.size()-1;
for (int i = 0; i <= n / 2; ++i) {
for (int j = i; j < n - i; ++j) {
temp = matrix[j][n-i];
matrix[j][n-i] = matrix[i][j];
matrix[i][j] = matrix[n-j][i];
matrix[n-j][i] = matrix[n-i][n-j];
matrix[n-i][n-j] = temp;
}
}
}
};
题目描述
数组arr是[0, 1, …, arr.length - 1]的一种排列,我们将这个数组分割成几个“块”,并将这些块分别进行排序。之后再连接起来,使得连接的结果和按升序排序后的原数组相同。
我们最多能将数组分成多少块?
输入输出样例
输入: arr = [4,3,2,1,0]
输出: 1
解释:
将数组分成2块或者更多块,都无法得到所需的结果。
例如,分成 [4, 3], [2, 1, 0] 的结果是 [3, 4, 0, 1, 2],这不是有序的数组。
题解
从左往右遍历,同时记录当前的最大值,每当当前最大值等于数组位置时,我们可以多一次分割。
为什么可以通过这个算法解决问题呢?如果当前最大值大于数组位置,则说明右边一定有小于数组位置的数字,需要把它也加入待排序的子数组;又因为数组只包含不重复的 0 到 n,所以当前最大值一定不会小于数组位置。所以每当当前最大值等于数组位置时,假设为 p,我们可以成功完成一次分割,并且其与上一次分割位置 q 之间的值一定是 q + 1 到 p 的所有数字。
代码
class Solution {
public:
int maxChunksToSorted(vector<int>& arr) {
int chunks = 0, cur_max = 0;
for (int i = 0; i < arr.size(); ++i) {
cur_max = max(cur_max, arr[i]);
if (cur_max == i) {
++chunks;
}
}
return chunks;
}
};
参考资料:代码随想录