数组001

这篇博客探讨了数组这种数据结构的基础知识,包括数组的特点、内存分布以及数组元素的管理。文章详细介绍了数组的方法,如二分查找、双指针法(相向、同向和背向)以及快速排序。此外,还讨论了多个编程题目,涉及数组操作,如移除元素、删除重复项、移动零、比较含退格的字符串、有序数组的平方、长度最小的子数组、水果成篮问题以及最小覆盖子串。最后,博主分享了螺旋矩阵的实现方法。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

数组

数组是存放在连续空间上的相同类型数据的集合
数组下标都是从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.螺旋矩阵
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值