数组理论基础:
注:图片和内容均来自代码随想录,仅作为学习笔记使用
代码随想录链接:代码随想录 (programmercarl.com)
数组是存放在连续地址上的相同数据类型的数据集合。
数组的下标从零开始;
数组内存地址空间连续。
由于数组内存空间地址的连续性,在增删元素时,需要将增(删)索引index后的所有数据后移(前移)一位。
vector和array的区别:vector严格来说是容器,其底层为数组。
二维数组:如上,其在c++中的存储空间也是连续的可以看作是一维数组的一维数组(即按行存储);
二分查找:
本质是boundary(边界),每次寻找中间位置,判断中间位置处是否满足题目要去,以此来取区间。
模板:来自acwing
bool check() //用于判断是否满足条件;
int b_search1(int l, int r){
while(l<r){
mid = (l+r)/2;
if(check(mid)) r = mid;
else l = mid+1;
}
return l;
}
int b_search2(int l,int r){
while(l<r){
mid = (l+r+1)/2;
if(check(mid)) l = mid;
else r = mid -1;
}
return l;
}
力扣例题:
分析:
数组有序,也就是说可以划分边界(boundary)为>=target和<target的两部分,计算nums[mid]的值,若大于target,取左半边,若小于取右半边。
class Solution {
public:
int search(vector<int>& nums, int target) {
int l = 0,r = nums.size() - 1;
while(l < r){
int mid = (l+r)/2;
if(nums[mid]>=target) r=mid;
else l = mid+1;
}
if(nums[l]==target) return l;
else return -1;
}
};
效果如下:
注:
模板是为了快速避免边界问题。
移除元素:
原题链接:. - 力扣(LeetCode)
由于数组在存储空间上是连续的,因此不能够单独地删除元素,而是要使用覆盖。
暴力流:
双重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--; //由于删除了一个元素所以i指针要往前一位
size--; //删除了一个元素,所以size的大小要减一
}
}
return size;
}
};
双指针:
分为两个指针,一个为快指针(指向新数组中存储的元素),一个为慢指针(指向更新数组的下标位置)。
用该方法可以只使用一个for循环语句实现对数组元素的删除操作,是如何实现的呢?
首先当数组元素不等于要删除元素时,快慢指针一起后移,当数组元素等于要删除元素时,快指针继续移动,而慢指针停留在原位置处,直到后继元素不等于要删除元素后继续一起移动。其中慢指针在移动的过程中实际上也实现了赋值功能,具体代码如下。
class Solution {
public:
int removeElement(vector<int>& nums, int val) {
int lowindex = 0;
for(int fastindex =0;fastindex<nums.size();fastindex++){
if(val!=nums[fastindex]){
nums[lowindex++] = nums[fastindex];
// 为什么这么写?为了实现快慢指针在遇到val时的同步移动
}
}
return lowindex;
}
};
有序数组的平方:
原题链接:977. 有序数组的平方 - 力扣(LeetCode)
暴力流:
原数组平方后直接进行排序。
class Solution {
public:
vector<int> sortedSquares(vector<int>& nums) {
for(int i = 0;i<nums.size();i++)
nums[i] = nums[i]*nums[i];
sort(nums.begin(),nums.end());
//注意sort第二个参数为尾后迭代器
return nums;
}
};
双指针流:
该数组本身便是有序的,使用双指针关注第一个和最后一个元素的大小便可(为整数数组,不存在小于1的分数的情况)。
class Solution {
public:
vector<int> sortedSquares(vector<int>& nums) {
vector<int>new_nums = nums;
int lowindex = 0, highindex = nums.size()-1;
for(int k = nums.size()-1;k>=0;k--){
if(nums[lowindex]*nums[lowindex] < nums[highindex]*nums[highindex]){
new_nums[k] = nums[highindex]*nums[highindex];
highindex--;
}
else{
new_nums[k] = nums[lowindex]*nums[lowindex];
lowindex ++;
}
}
return new_nums;
}
};
高精度加法:
#include <iostream>
#include <vector>
#include <string>
using namespace std;
vector <int> AddFunction(vector<int> &A, vector<int> &B){
if(B.size()>A.size()) return AddFunction(B,A);
vector<int> C;
int t = 0; //定义一个辅助进位符,计算为ai+bi+t,前一位无进位为0
for(int i = 0;i<A.size();i++){
t+=A[i];
if(i<B.size())
t+=B[i];
C.push_back(t%10); //不能使用C[i]来进行幅值,因为在这是并未分配空间只能用push_back;
t /= 10;
}
if(t)
C.push_back(1);
return C;
}
int main(){
string a,b;
cin >> a>>b;
vector<int>A,B;
for(int i = a.size()-1;i>=0;i--) A.push_back(a[i]-'0'); //为什么要-'0'因为读取的是字符型,为ASCII码,故减去‘0’的ASCII来还原
for(int i = b.size()-1;i>=0;i--) B.push_back(b[i]-'0');
auto C = AddFunction(A,B);
for(int i =C.size()-1 ;i>=0;i--) cout<<C[i];
cout<<endl;
return 0;
}
// 下面的错误原因,没有考虑到B的位数在小于A时未补0,故在循环内加一个if(i<B.size())便可解决
/*
#include <iostream>
#include <vector>
#include <string>
using namespace std;
vector <int> AddFunction(vector<int> &A, vector<int> &B){
if(B.size()>A.size()) return AddFunction(B,A);
vector<int> C;
int t = 0; //定义一个辅助进位符,计算为ai+bi+t,前一位无进位为0
for(int i = 0;i<A.size();i++){
t+=A[i];
t+=B[i];
C.push_back(t%10);
t /= 10;
}
if(t)
C.push_back(1);
return C;
}
int main(){
string a,b;
cin >> a>>b;
vector<int>A,B;
for(int i = a.size()-1;i>=0;i--) A.push_back(a[i]-'0'); //为什么要-'0'因为读取的是字符型,为ASCII码,故减去‘0’的ASCII来还原
for(int i = b.size()-1;i>=0;i--) B.push_back(b[i]-'0');
auto C = AddFunction(A,B);
for(int i =C.size()-1 ;i>=0;i--) cout<<C[i];
cout<<endl;
return 0;
}
输入:
1000000
1
输出:
14000001
*/
长度最小的子数组:
滑动窗口:
不断地调节子序列的起始位置与终止位置,从而得出我们想要的结果。
在暴力算法中,一个for循环来遍历起始位置,一个for循环来遍历终止位置。
滑动窗口则通过一个for循环来实现这个操作。
但是只用一个for循环是用来表示起始位置还是终止位置呢?
若表示启示位置,依然需要暴力循环来找到终止位置;
所以选择表示终止位置,下面的关键在于初始位置如何去移动呢,可以在循环的外界定义一个sum值,在for循环寻找终止位置时进行一个累加,当sum>=target时,计算此时的长度,然后将起始位置进行++操作,当sum<target时结束。然后对上述部分进行循环操作,不断更新区间的长度。
(可以看做时双指针的一个变种)
·窗口是什么?——本题中的窗口位>=target的区间;
·如何移动窗口的起始位置?——当窗口的值>=target了就需要向前移动了;
·如何移动窗口的终止位置?——for循环中的索引。
题目链接:. - 力扣(LeetCode)
class Solution {
public:
int minSubArrayLen(int target, vector<int>& nums) {
int sum = 0;
int result = 1e9+10; //防止巧合故加10,理论加1便可,习惯。
int i = 0;
int length = 0;
for(int j = 0; j<nums.size(); j++){
sum += nums[j];
while(sum >= target){
length = j-i+1;
result = result < length ?result : length; //判断result的值是否为历史最短的区间
sum -= nums[i++];
}
}
if(result == 1e9+10) return 0;
else return result;
}
};
螺旋矩阵:
要坚持循环不变量:
模拟顺时针画出改矩形的过程:
·从左往右填充上行;
·从上往下填充右行;
·从右往左填充下行;
·从下往上填充左行。
为什么要坚持循环不变量呢,其实就是一个规范化的问题,如果每次的循环遵循的规则时不一致的很容易导致循环出现问题,在本题中坚持左闭右开的循环原则,结果如下图所示:
当n为奇数时,中间需要额外处理。
当n=1时,不需要循环,当n=2时,循环一次,当n=3时,循环一次,当n=4时循环两次,所以循环次数由n/2来决定。
class Solution {
public:
vector<vector<int>> generateMatrix(int n) {
int startx = 0, starty = 0; // 默认循环初始位置为(0,0)
int loop = n/2;
int default_loop = 1; // 默认设置的循环值减数为1
int i,j;
int count = 1;
vector<vector<int>>res(n,vector<int>(n));// 前行后列,村重空间连续,数组的数组
while(loop--){
i = startx;
j = starty;
for(j;j<n-default_loop;j++){
res[i][j] = count++;
}
for(i;i<n-default_loop;i++){
res[i][j] = count++;
}
for(j;j>startx; j--){
res[i][j] = count++;
}
for(i;i>starty; i--){
res[i][j] = count++;
}
startx ++;
starty ++;
default_loop++;
}
if(n%2)
{res[n/2][n/2] = count;}
return res;
}
};
题目链接:59. 螺旋矩阵 II - 力扣(LeetCode)
STL库——Vector简单介绍:
Vector是STL库中的一种容器(方便记忆可以暂且将其记为动态数组)
1、创建一个vector:
// 在使用stl库中的vector时,要包含头文件
#include <vector>
vector<数据类型> 向量名(向量大小)
//比如声明一个int类型的向量:
vector<int>array1;
vector<int>array2(2);
vector<int>array3 {0,1,2};
2、访问vector中的元素:
和普通定义的数组元素一样使用[]访问,不过vector的[]是operator(重载)后的。
3、迭代器:
下介绍几个比较常用的迭代器
1.begin——返回容器中第一个元素的迭代器;
2.end——返回容器末元素后一个位置的迭代器;
可以这么理解,一个vector的范围为[begin,end)即[begin,end-1]。
3.rbegin——返回逆序的容器的第一个元素的迭代器;
4.rend——返回逆序的容器的最后一个元素前一个位置的迭代器;
5.cbegin——c代表const其余不变;
6.cend——c代表const其余不变;
7.crbegin
8.crend
4、容积:capacity(后根据用到的范围不断补充)
1.size——当前容器的实际大小;
2.capacity——总共可容纳的元素个数;
3.resize——改变size的大小。
5、一些操作:
1.push_back(数值):在vector的末尾(最高位)添加一个元素。
2.pop_back():删除最高位的的元素值。(可以用于消除首0)。