1.数组理论基础
1.1 数组定义
数组定义:一组相关变量能够一个接一个地存储在计算机存储器的一块连续区间内。我们将这样的表示法称为数组 array
2.LeetCode 相关题目
2.3_977有序数组的平方
2.3.1算法描述
这个题本身就是顺序排下来的,只不过存在负数的时候,如果将其平方后它的值会变大,也就是说负数存在的情况下,如果对数组取平方,那么越靠左的值可能越大,而中间的值反而最小,就比如说 0 是一个中间数,在取平方后反而最小了
我们很难找到数组最小值在哪一块,但是我们可以确定在平方之后数组的最大值有可能就在数组两边
①定义两个指针 i,j ,分别指向数组的左右两端,先存储大的数据
②定义一个 result 用于存放计算好的平方值,从后向前存储,因为步骤 ① 是先处理平方值大的数据
2.3.2 C++ 代码实现
class Solution {
public:
vector<int> sortedSquares(vector<int>& nums) {
int l = 0;
int r = nums.size()-1;
vector<int>res(nums.size(),0);
for(int i =nums.size()-1;i>=0;i--){
if(nums[l]*nums[l]>nums[r]*nums[r]){
res[i] = nums[l]*nums[l];
l++;
}
else{
res[i] = nums[r]*nums[r];
r--;
}
}
return res;
}
};
2.3.3 时空复杂度
时间复杂度:O(logn)
空间复杂度:O(N)
2.4.3 时空复杂度
时间复杂度:O(N)
空间复杂度:O(1)
2.5_59螺旋矩阵②(media)
2.5.1 算法描述
螺旋矩阵的路程:
- 填充上行从左到右
- 填充右列从上到下
- 填充下行从右到左
- 填充左列从下到上
- 。。。。。。
**在定义变量时是左闭右闭,在循环时使用 = 。有点像二分法,因为填充的时候当前格的 index 是可以填充的状态,所以在 for 循环遍历的时候可以使用 = **

这里在最外层循环时使用 while(1) ,通过变量 left,right ,top,down 的关系判断是否发到了临界值
第一重循环控制 n~n² 的累增,嵌套的循环放置累增的数值,每判断完一行或者一列就要对相应的方位变量进行 ++ ,指向下一次要判断哪
Top 和 Down 在 if 判断时为什么不用等于:
因为这是先 – 再判断的,等于的时候是有被处理掉的
2.5.2 C++ 代码实现
class Solution {
public:
vector<vector<int>> generateMatrix(int n) {
vector<vector<int>> ans(n, vector<int>(n));
int top = 0, right = n - 1, left = 0, x = 1, bottom = n - 1;
while (true) {
for (int i = left; i <= right; ++i) ans[top][i] = x++;
if (++top > bottom) break;
for (int i = top; i <= bottom; ++i) ans[i][right] = x++;
if (--right < left) break;
for (int i = right; i >= left; --i) ans[bottom][i] = x++;
if (--bottom < top) break;
for (int i = bottom; i >= top; --i) ans[i][left] = x++;
if (++left > right) break;
}
return ans;
}
};
2.5.3时空复杂度
时间复杂度:O(N²)
空间复杂度:O(N²)
3.其他题目
数组内部的增删改查:
两个指针:i,j 。
i 指向 j 下一次要赋值的 index ,只有 j 遍历的元素满足某个要求时才会将值赋值给 i
j 是不断在数组中走动的元素,所以对 j 进行 for 循环判断是否满足题目条件。
1.i,j 初始化
i,j 最开始是同步的,如果 j 满足要求了直接将其赋值给 i
2.for 循环判断 j
(1)nums[j] == 条件 TODO
(2)nums[j] != 条件 {
nums[i++] = nums[j];
}
3.6_41 缺失的第一个正数(hard)
index 和 x 的映射+原地哈希
3.6.1 算法描述
这个题使用的是:原地哈希的方法
因为数组长度是 N ,其中没有出现的最小正整数只能在 [1,N+1] 中选
步骤:
1.先判断 nums 中是否有 1 ,因为不考虑负数,所以将所有不再范围内的数都替换为 1
[3,4,-1,-2,1,5,16,0,2,0] -->[3,4,1,1,1,5,1,1,2,1]
2.遍历 nums 中的每一个数 x 给 index = x-1 的值添加负号
第一个数字是 3 ,给 index = 2 的位置添加负号标记,表示 3 已经出现过了
[3,4,1,1,1,5,1,1,2,1]–>[3,4,-1,1,1,5,1,1,2,1]
当遍历到 -1 时代表 1 出现了所以给 index = 0 的位置添加负号
[3,4,-1,1,1,5,1,1,2,1]–>[-3,4,-1,1,1,5,1,1,2,1]
但是数组中可能与多个 x=1 的值,那这样 index=0 的数一会正一会负,就不知道这个 index 对应的值是否出现过了。所以在添加负号时先将其变为正,假装之前没出现过然后再对其添加负号
。。。。。
[-3,-4,-1,-1,-1,5,1,1,2,1]
3.所有出现过的 x 它所对应的 index 都变为了负数
我们只需要找到那个最小的没有出现的 x 就好,那么再遍历一遍数组,看哪个 index 上的数为正数代表这个 index 对应的 x 没有出现过,从上面看是 index = 5 ,也就是 x=6 没有出现过,那么最终答案就是 6
在代码中将 step1 变为了替换成 n+1
3.6.2 C++ 代码实现
class Solution {
public:
int firstMissingPositive(vector<int>& nums) {
int n = nums.size();
// 将所有负数替换成 n+1 (这里不是替换成 1 )
for(int& num:nums){
if(num<=0){
num = n+1;
}
}
// 映射
for(int i =0;i<n;i++){
int num = abs(nums[i]); // 将其转为正数
if(num<=n){ // 需要判断的那 [1,N] 的值
nums[num-1] = -abs(nums[num-1]);
}
}
// 遍历哪个 index 是第一个出现的正数
for(int i =0;i<n;i++){
if(nums[i]>0) return i+1;
}
return n+1;
}
};
3.6.3 时空复杂度
时间复杂度:O(N)
空间复杂度:O(1)
3.7_4寻找两个正序数组的中位数 (hard)
3.7.1算法描述
方案1 :双指针
可以使用双指针法,我们知道中位数出现在哪个位置,判断指针移动的个数
方案2:第 k 个数
这里使用的思路是最小第 k 个正数,求中位数的话无非这个 k 就是中间的那个数
如果一个数组是奇数个,第 k 个数为 (n/2)+1
如果一个数组是偶数个,第 k 个数为 ((n/2)+(n/2)+1)/2.0
这里使用的方法是折半删除法:取目标值的一半进行删除
现在有下面两个数组,一共有 17 个数组成,找到中位数,也就是第 9 个数

S1:找到前 8 位数
因为 k = 9,每个数组分割出一个可删除的子数组出来
9/2 -1 = 3,即从当前 index 的位置出发,向后取 3 位,也就是前 4 个数

S2:比较最后一个值的大小
4<=12 ,也就是说 [1,2,3,4] 这个子数组是可以完全删除的,因为中位数有可能在 [5,6,7,8] 中,也有可能在第二个数组中
S3:再进行一次折半删除
通过 S1,前 4 个小的数已经找到,所以 k 变为了在剩下的数组中找到第 k-4 小元素
这时 k-= windows +1 个数
那么在这一轮 window 的个数也发生改变,并且每一次都是 k/2-1
A 组子数组位置变化,B 组子数组大小变,位置不变

S4:不断的折半删除
退出条件:
如果 nums1 用尽了,则返回 nums2 中的第 k 小的元素。
如果 nums2 用尽了,则返回 nums1 中的第 k 小的元素。
如果 k 为 1,则返回当前两个数组中第一个元素的最小值。
3.7.2 代码实现
class Solution {
public:
// 寻找第 k 个值
int findK(vector<int>& nums1, vector<int>& nums2,int k){
int m = nums1.size();
int n = nums2.size();
int index1 = 0;
int index2 = 0;
while(true){
// 判断边界条件
// 1. 其中一个数组中没有值了,返回另一个数组的第 k 个值
if(index1 >= m) return nums2[index2+k-1];
if(index2 >= n) return nums1[index1+k-1];
// 2. k == 1
if(k==1) return min(nums1[index1],nums2[index2]);
// 折半删除:判断两个数组子数组最后一个值,删除整个子数组
int window = k/2-1; // 设置窗口大小
int index1_end = min(index1+window,m-1);
int index2_end = min(index2+window,n-1);
int val1 = nums1[index1_end];
int val2 = nums2[index2_end];
if(val1<=val2){
k -= index1_end-index1+1;
index1 = index1_end+1;
}else {
k -= index2_end-index2+1;
index2 = index2_end+1;
}
}
}
double findMedianSortedArrays(vector<int>& nums1, vector<int>& nums2) {
int m = nums1.size();
int n = nums2.size();
// 奇数
if((m+n)%2!=0){
return findK(nums1,nums2,(m+n)/2+1);
} else return (findK(nums1,nums2,(m+n)/2)+findK(nums1,nums2,(m+n)/2+1))/2.0;
}
};
3.7.3 时空复杂度

1616

被折叠的 条评论
为什么被折叠?



