c++ 二分查找,数组学习

//  二分查找主要适合有规律的数组,通过不断  移动指向数组中段的指针(不断缩小目标所在区间)  
实现查找目标的目的。
在没有规律的数组中,一般用  双指针  去遍历寻找目标。

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

class Solution:
public:
    int search(vector<int>& nums , int target){
        int left = 0;
        int right = nums.size() - 1;
        while(left <= right){          //这里的left<=right 主要还是与下面的middle计算方式有关
            int middle = left + ((right - left)/2);
            if(nums[middle] > target){
                right = middle - 1;
            }
            else if(nums[middle] < target){
                left = middle + 1;
            }
            else{
                return middle;
            }
        }
            return -1;
    }
};

//题目: 给定一个排序数组和一个目标值,在数组中找到目标值,并返回其索引。
//如果目标值不存在于数组中,返回它将会被按顺序插入的位置。 
                  输入: nums = [1,3,5,6], target = 5
                  输出: 2

class Solution {
public:
    int searchInsert(vector<int>& nums, int target) {
        int n = nums.size();
        int left = 0;
        int right = n - 1; // 定义target在左闭右闭的区间里,[left, right]
        while (left <= right) { // 当left==right,区间[left, right]依然有效
            int middle = left + ((right - left) / 2);// 防止溢出 等同于(left + right)/2
            if (nums[middle] > target) {
                right = middle - 1; // target 在左区间,所以[left, middle - 1]
            } else if (nums[middle] < target) {
                left = middle + 1; // target 在右区间,所以[middle + 1, right]
            } else { // nums[middle] == target
                return middle;
            }
        }
        // 分别处理如下四种情况
        // 目标值在数组所有元素之前  [0, -1]
        // 目标值等于数组中某一个元素  return middle;
        // 目标值插入数组中的位置 [left, right],return  right + 1
        // 目标值在数组所有元素之后的情况 [left, right], return right + 1
        return right + 1;
    }
};
题目:在排序数组中查找目标出现的第一个位置和最后位置。
示例:
    输入:nums = [5,7,7,8,8,10],target = 8
    输出:[3,4]

思路:
    考虑三种情况:
    1、target 不在数组的范围内 返回{-1,-1}
    2、target 在数组范围内,但是数组中没有target,返回{-1,-1}
    3、target 在数组中 返回其在数组中的位置

class Solution{
public:
    vector<int> searchRange(vector<int>& nums,int target){
        int leftBorder = getLeftBorder(nums, target);
        int rightBorder = getRightBorder(nums, target);
        // 情况一
        if (leftBorder == -2 || rightBorder == -2) return {-1, -1};
        // 情况三
        if (rightBorder - leftBorder > 1) return {leftBorder + 1, rightBorder - 1};
        // 情况二
        return {-1, -1};
    }
private:
    int getRightBorder(vector<int>& nums,int target){
        int left = 0;
        int right = nums.size() - 1;
        int rightBorder = -2; //记录rightBorder没有被赋值的情况
        while(left <= right){
            int middle = left + (right-left)/2;
            if(nums[middle] > target){
                right = middle - 1;
            }
            else{
                left = middle + 1;
                rightBorder = left;
            }
        }
        return rightBorder;
    }
    
    int getLeftBorder(vector<int>& nums,int target){
        int left = 0;
        int right = nums.size() - 1;
        int leftBorder = -2;
        while(left <= right){
            int middle = left + (right - left)/2;
            if(nums[middle] >= target){
                right = middle - 1;
                leftBorder = right;
            }
            else{
                left = middle + 1;
            }
        }
            return leftBorder;
    }
};

数组学习总结:

1、数组的元素只能覆盖,不能删除。例如先创建一个n*n的元素全为0 的矩阵,开辟一个空间,然后再对其特定位置进行赋值来覆盖其原本的值。

2、二维数组在内存空间并不是一个连续的地址空间。

// 滑动窗口法:使用双指针
/*

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

示例:
输入:s = 7, nums = [2,3,1,2,4,3] 输出:2 解释:子数组 [4,3] 是该条件下的长度最小的子数组。

*/

//使用滑动窗口法,不断调节子序列的起始位置和终止位置,从而得出我们想要的结果

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];

            // key point
            while(sum >= s){
                subLength = j - i + 1;
                result = result < subLength ? result : subLength;
                sum -= nums[i++];
            }
        }
        return result == INT32_MAX ? 0 : result;
}
};

螺旋矩阵

            //使用vector创建数组方法:适用于每一行的列数相等的二维数组
            /*
                vector<vector<int>> matrix(m, vector<int>(n, -1));
                //以下是拆分理解:

                //创建一维数组matirx,这个数组里有m个元素,元素是int型vector。
                vector<vector<int>> matrix(m);

                //除了定义数组类型及数组大小外,同时给数组中的元素赋值:将元素赋值为大小为n的                    
                  int型vector。
                vector<vector<int>> matrix(m, vector<int>(n));

                //除了定义数组类型、数组大小、列的大小,同时给数组列中的元素(或者说,数组中的所 
                  有元素)赋值为-1。
                vector<vector<int>> matrix(m, vector<int>(n, -1));
            */

 每一条边都只栈n-1,剩余角点的位置留给下一条边,如图:

螺旋矩阵

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

示例:

输入: 3 输出:

            矩阵:
                    1 2 3
                    8 9 4
                    7 6 5
*/

class Solution{
public:
        vector<vector<int>> generateMatrix(int n){
            vector<vector<int>> res(n, vector<int>(n,0));
            //vector定义二维数组
            int startx = 0, starty = 0;     //定义每循环一个圈的起始位置
            int loop = n/2;   //每个圈循环几次,例如n=3,则为3*3的矩阵,只需要循环最外边的一圈
            int mid = n/2;    //矩阵中间的位置,不需要循环直接赋值
            int count = 1;    //用来给矩阵每一个位置赋值
            int offset = 1;  //每一次循环都要控制每一条边遍历的长度
            int i,j;

            while(loop --){
                i = startx;
                j = starty;

                //下面使用for循环模拟转一圈
                
                //模拟填充最上行从左到右(左闭右开)
                for (j = starty; j < starty + n - offset; j++){
                    res[startx[j]] = count++;
                }
                //模拟填充最右列从上到下(左闭右开)
                for(i = startx; i < startx + n - offset; i++){
                    res[i][j] = count++;
                }
                //模拟填充最下行从右到左(左闭右开)
                for(; j < starty; j--){
                    res[i][j] = count++;
                }
                //模拟填充左列从下到上(左闭右开)
                for(; i > startx; i--){
                    res[i][j] = count++;
                }

                //第二圈开始的时候,起始位置变化。例如:(0,0) ->  (1,1)
                startx++;
                starty++;

                //offset 控制每一圈里面的每一条边的遍历长度
                offset += 2;
            }

            //如果n为技术,需要单独给最中间的位置赋值
            if(n % 2){
                res[mid][mid] = count;
            }
            return res;
}
};

以上所有都引自“代码随想录” 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值