C/C++妙用数据结构-数组

文章目录

  • 概述
  • 二分法
    • 704.二分查找
    • 240.搜索二维矩阵Ⅱ
  • 双指针法
    • 27.移除元素
  • 滑动窗口法
    • 209.长度最小的子数组
  • 模拟
    • 59.螺旋矩阵Ⅱ
    • 48.旋转图像
  • 其他
    • 769.最多能完成排序的块


概述

数组是存放在连续内存空间上的相同类型数据的集合。

vector:动态数组,是我们最常使用的数据结构之一,用于 O(1) 的随机读取。因为大部分算法的时间复杂度都会大于 O(n),因此我们经常新建 vector 来存储各种数据或中间变量。因为在尾部增删的复杂度是 O(1),我们也可以把它当作 stack 来用。

array:固定大小的数组,一般在刷题时我们不使用。

数组的元素是不能删的,只能覆盖。

二分法

704.二分查找

二分查找的前提是数组一定是有序的,详细可以参考之前写的 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] 。

  • while (left <= right) 要使用 <= ,因为left == right是有意义的,所以使用 <=。
  • if (nums[middle] > target) right 要赋值为 middle - 1,因为当前这个nums[middle]一定不是target,那么接下来要查找的左区间结束下标位置就是 middle - 1。
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) ,那么二分法的边界处理方式则截然不同。

  • while (left < right),这里使用 < ,因为left == right在区间[left, right)是没有意义的。
  • if (nums[middle] > target) right 更新为middle,因为当前nums[middle]不等于target,去左区间继续寻找,而寻找区间是左闭右开区间,所以right更新为middle,即:下一个查询区间不会去比较nums[middle]。
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;
    }
};

240.搜索二维矩阵Ⅱ

题目描述

编写一个高效的算法来搜索 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;
    }
};

双指针法

27.移除元素

题目描述

给你一个数组 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;
    }
};

滑动窗口法

209.长度最小的子数组

题目描述

给定一个含有 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;
    }
};

模拟

59.螺旋矩阵Ⅱ

题目描述

给你一个正整数 n ,生成一个包含 1 到 n2 所有元素,且元素按顺时针顺序螺旋排列的 n x n 正方形矩阵 matrix 。

输入输出样例
C/C++妙用数据结构-数组_第1张图片

输入: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;
    }
};

48.旋转图像

题目描述

给定一个 n × n 的二维矩阵 matrix 表示一个图像。请你将图像顺时针旋转 90 度。

你必须在 原地 旋转图像,这意味着你需要直接修改输入的二维矩阵。请不要 使用另一个矩阵来旋转图像。

输入输出样例

C/C++妙用数据结构-数组_第2张图片

输入: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;
            }
        }
    }
};

其他

769.最多能完成排序的块

题目描述

数组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;
    }
};

参考资料:代码随想录

你可能感兴趣的:(算法和数据结构,数据结构,c++,算法)