数组在内存中以连续地址存储。
例如一维数组 int nums[]={1, 2, 3, 4, 5}; //使用{}初始化数组。
二维数组 int nums[][]={{1, 2}, {3, 4}};//二维数组实际上存储的是一个指针数组(*ptr)[], //void* (*ptr)[];
问题: 如何给数组增/删元素?
数组元素若想要进行增/删,则需要对元素进行移动。同时要维护一个size成员。
输入: int nums[]={0, 1, 1, 2, 2, 3, 4, 0, 2},待删除元素为2;
输出: nums;
方法1. 可以使用二重遍历nums的所有成员,循环删除元素,删除时要把后面的元素前移。
int removeArray(int &nums, int target){
for(int i=0;i<nums.size();++i){
if(nums[i]==target){
for(int j=i;j<nums.size();++j){nums[i]=nums[j+1];}
--i;nums.size()--; //--i回退一位;维护size成员。
}}}
方法2. 使用双指针遍历数组元素。
int removeArray(vector<int> &nums, int target){
int ps=0;
for(int pf=0;pf<nums.size();++pf){
if(target!=nums[j]){nums[ps]=nums[j];ps++;}
}return ps;
}//使用ps指针记录当前位置。pf指针遍历数组,记录待移动的元素。
区间合并问题。
输入: vector<vector<int>> nums,每一个vector<int> num形如[ai, bi],表示一个闭区间;将nums的区间进行合并。
输出: vector<vector<int>> nums, nums无重复,交叉区间。
例如输入: nums=[[1, 2], [2, 5], [3, 8], [9, 12]];
输出: 合并为nums=[[1, 8], [9, 12]];
bool compare(vector<int> &v1, vector<int> &v2){
return v1[0]<v2[0];
}
vector<vector<int>> merge(vector<vector<int>> &nums){
vector<vector<int>> res;
vector<int> v=*(nums.begin());//初始化已有区间。
//首先对nums根据左区间排序。
sort(nums.begin(), nums.end(), compare);
for(auto it=nums.begin();it!=nums.end();++it){
//排序后的区间在数轴上的排布是按照区间左界的大小分布的。因此只需要判断待并的区间与已有区间的关系: (包,并),(无交叉)3种。
auto p=*it;
if(p[0]>v[1]){res.push_back(v);v=p;}
else{
if(p[1]>v[1]){v[1]=p[1];}
} }
//添加最后一个区间。
res.push_back(v);
return res;
}
技巧: 双指针方法,通过维护两个指针 *ps, *pf,形成一个窗口。可以处理很多需要遍历数组的问题,将两层循环遍历简化为一层循环。这两个指针的作用为: pf指针是遍历数组的指针,用到for()循环中,ps指针控制窗口的左界。
滑动窗口处理连续子数组问题。
问题:输入一个数组nums,请找出和≥s的,最短连续子数组。
案例输入: nums={1, 3, 2, 1, 1, 4, 2, 2, 3, 4}, s=7。
输出: 最短连续子数组的左右下标。
pair<int, int> minsubArray(vector<int> &nums, int s){
int i=0, sum=0, len=nums.size();
pair<int, int> p(0, 0);
for(int j=0;j<nums.size();++j){
sum+=nums[j];
while(sum≥s){
if(len<(j-i+1)){len=(j-i+1); p.first=i; p.second=j; }
sum-=nums[i];++i; }
}return p;
}
二维数组。
首先,二维数组的存储方式其实是存放了每一行的一维数组的头结点的指针,写法是int (*arr)[4]; 也可以写成指向指针的指针。写法是int **arr; 同理,二维数组的存储方式也不是连续的,通过存储四个列的头指针。