C++算法学习一.数组

 1.数组的理论基础

数组是存放在连续内存空间上的相同类型数据的集合。数组可以方便的通过下标索引的方式获取到下标下对应的数据。

注:数组下标从0开始,内存空间地址连续。

删除添加元素需要移动其他元素地址。数组元素不能删除,只能覆盖。

二维数组内存空间地址也是连续的。

 2.二分查找(704题)

 根据代码随想录,记录学习一些算法经验,

题目描述:给定一个 n 个元素有序的(升序)整型数组 nums 和一个目标值 target  ,写一个函数搜索 nums 中的 target,如果目标值存在返回下标,否则返回 -1。

这里面关键词有序,数组,无重复元素,满足两个条件可以考虑二分法。

注:边界条件设定,中间取值到底是-1,还是+1

 一般边界设置左闭右闭,或者左闭右开,

采用的是左闭右闭写法,这样写记得左+1,右-1的边界条件即可,在一个while(left<=right)条件下一直循环,下面是伪代码:

class solution{
     int search(vector&nums, int target){
          int left = 0;//左端边界
          int right = nums.size()-1;//右端边界
          while(left<=right){//边界使用左闭右闭条件
              int mid = left + ((right - left) / 2);//中间值
              if(nums[mid]< target){
                 left = mid+1;//左端+1;
              }else if(nums[mid] > target){
                 right = mid -1;//右端-1;
              }else{
                 return mid;//找到下标值,返回
              }
          }
          return -1; //未找到返回-1;   
     }
};

 下面介绍左闭右开写法

class Solution {
public:
    int search(vector& nums, int target) {
        int left = 0,right = nums.size() ;
        while(leftnums[mid]){
                left=mid+1;
            }else{
                return mid;
            }
        }
        return -1;
    }
};

区间的定义就是不变量,那么在循环中坚持根据查找区间的定义来做边界处理,就是循环不变量规则 。

总结:right值是否减一与设置有关,左必右闭->nums.size()-1->mid-1,左闭右开->nums.size()->mid;二分法的时间复杂度是O(logn)。

3.移除元素(27题)

题目描述:

给你一个数组 nums 和一个值 val,你需要 原地 移除所有数值等于 val 的元素,并返回移除后数组的新长度

不要使用额外的数组空间,你必须仅使用 O(1) 额外空间并原地修改输入数组。

元素的顺序可以改变。你不需要考虑数组中超出新长度后面的元素。

数组元素在内存地址上连续的,删除不了元素,只能覆盖。

暴力解法:不建议采用双循环做法,两个循环,第一个循环遍历数组,第二个循环更新数组。

class Solution {
public:
    int removeElement(vector& nums, int val) {
        int size = nums.size();
        for(int i = 0 ;i

 暴力解法的时间复杂度o(n2)空间复杂度o(1)

双指针解法:定义一个快指针和一个慢指针在一个循环下完成任务,代码如下:

class Solution {
public:
    int removeElement(vector& nums, int val) {
        int slow = 0;//定义慢指针,
        for(int fast = 0;fast

双指针时间复杂度是:o(n),空间复杂度o(1);

总结数组链表大多数题目都可以使用双指针来解决。

4.有序数组的平方(977题)

题目描述:给你一个按 非递减顺序 排序的整数数组 nums,返回 每个数字的平方 组成的新数组,要求也按 非递减顺序 排序。

暴力解法:先平方在排序,代码如下:

class Solution {
public:
    vector sortedSquares(vector& nums) {
        vectorresult;
        for(int i = 0; i

 时间复杂度是 O(n + nlogn)

双指针方法:数组其实是有序的, 只不过负数平方之后可能成为最大数了。那么数组平方的最大值就在数组的两端,不是最左边就是最右边,不可能是中间

class Solution {
public:
    vector sortedSquares(vector& nums) {
        int k = nums.size()-1;//准备定义新数组的大小
        vectorresult(nums.size(),0);//定义全为0数组
        for(int i = 0, j=nums.size()-1;i<=j;)//定义两个指针从首段和尾端开始
        {
            if(nums[i]*nums[i]

 时间复杂度为O(n)总结:体现用空间换时间的操作。

5.长度最小的子数组(209题)

题目描述:给定一个含有 n 个正整数的数组和一个正整数 s ,找出该数组中满足其和 ≥ s 的长度最小的 连续 子数组,并返回其长度。如果不存在符合条件的子数组,返回 0。

暴力解法:双循环,找出符合条件数组,

class Solution {
public:
    int minSubArrayLen(int target, vector& nums) {
        int result = INT32_MAX;
        int sum = 0;
        int sublength = 0;
        for(int i = 0;i= target){
                    sublength = j - i + 1;//取子数组长度
                    result = result < sublength ? result : sublength;//返回结果
                    break;//跳出这次循环
                }
            }
        }
        return result == INT32_MAX ? 0 : result;
    }
};
  • 时间复杂度:O(n^2)空间复杂度:O(1)

 滑动窗口方法:所谓滑动窗口,就是不断的调节子序列的起始位置和终止位置,从而得出我们要想的结果。滑动窗口也可以理解为双指针法的一种。

实现滑动窗口,主要确定如下三点:

  • 窗口内是什么?
  • 如何移动窗口的起始位置?
  • 如何移动窗口的结束位置
class Solution {
public:
    int minSubArrayLen(int target, vector& nums) {
        int result = INT32_MAX;
        int i = 0;// 滑动窗口起始位置
        int sum = 0;// 滑动窗口数值之和
        int sublength = 0;// 滑动窗口的长度
        for(int j = 0;j < nums.size();j++){
            sum+=nums[j];
            // 注意这里使用while,每次更新 i(起始位置),并不断比较子序列是否符合条件
            while(sum >= target){
                sublength = j - i + 1;//取子数组长度
                result = result < sublength ? result : sublength;//返回结果
                sum -=nums[i++];// 这里体现出滑动窗口的精髓之处,不断变更i(子序列的起始位置)
            }
            }
        }
        return result == INT32_MAX ? 0 : result;
    }
};

窗口就是 满足其和 ≥ s 的长度最小的 连续 子数组。

窗口的起始位置如何移动:如果当前窗口的值大于s了,窗口就要向前移动了(也就是该缩小了)。

窗口的结束位置如何移动:窗口的结束位置就是遍历数组的指针,也就是for循环里的索引。

  • 时间复杂度:O(n)
  • 空间复杂度:O(1)

6.螺旋矩阵II (59题)

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

示例:输入: 3 输出: [ [ 1, 2, 3 ], [ 8, 9, 4 ], [ 7, 6, 5 ] ]

模拟:螺旋打印过程,遵循循环不变量原则

模拟顺时针画矩阵的过程:

  • 填充上行从左到右
  • 填充右列从上到下
  • 填充下行从右到左
  • 填充左列从下到上
class Solution {
public:
    vector> generateMatrix(int n) {
        vector>res(n,vector(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;jstarty;j--){
                res[i][j]=count++;
            }
            for(;i>startx;i--){
                res[i][j]=count++;
            }
            //第二圈
            startx++;
            starty++;
            offset += 2;
        }
        if(n%2){
            res[mid][mid]=count;
        }
        return res;
    }
};
  • 时间复杂度 O(n^2): 模拟遍历二维矩阵的时间
  • 空间复杂度 O(1)

6.(数组)总结: 

数组理论基础:数组是存放在连续内存空间上的相同类型数据的集合。通过下标进行索引,查找很方便,删除增加元素麻烦,内存空间地址连续。

二分查找:注意边界问题,左右边界定义,记得数组有序,无重复元素,左闭右开和左闭右闭的写法和边界处理问题,right值是否减一与设置有关,左必右闭->nums.size()-1->mid-1,左闭右开->nums.size()->mid,遵循循环不变量原则。

移除元素:因为数组连续地址空间,删除某元素只能通过覆盖,所以采用双指针操作来完成,

有序数组平方:有序,平方之后也有一定的规律,同样可以采用双指针的形式来完成操作,两侧的值较大,中间小,对比可以从结果集后向前进行插入,放较大值。

长度最小的子数组:滑动窗口方法,要知道窗口是什么,起始位置和终止位置,如何移动,此题指针移动就是窗口的末尾位置,改变前指针就是初始位置,判断条件就是给出的条件。

螺旋矩阵:循环打印过程遵循循环不变量原则,模拟的过程,要知道需要转多少圈,以及中心点变化,循环打印的边界。

值得思考的问题是滑动窗口的长度最小的子数组!!!!

你可能感兴趣的:(算法,c++,学习)