栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 软件开发 > 后端开发 > C/C++/C#

代码随想录刷题1 「数组」

C/C++/C# 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

代码随想录刷题1 「数组」

代码随想录刷题1 「数组」 1. 二分查找
  1. 区间定义有两种:左闭右闭[left, right],左闭右开[left, right)。

  2. while () 内的条件确定:是否有意义?左闭右闭时:left == right 时有意义,故循环条件确定为 left <= right

  3. 迭代时,left和right的移动,遵循循环不变量原则。

  4. 考虑到可能发生的整型溢出,求mid时提倡以下写法:

	int mid = left + (right - left) / 2;
	int mid = left + ((right - left) >> 1);
  1. 经典题目:Leetcode704, Leetcode35
//Leetcode704
	//经典二分查找:nums有序,查找target
	//时间复杂度:O(logn),其中 nn 是数组的长度。
	//空间复杂度:O(1)。
	
	//左闭右闭
    public int search(int[] nums, int target) {
        int left = 0, right = nums.length - 1;
        while (left <= right) {//左闭右闭时,left == right时有意义
            int mid = left + ((right - left) >> 1);
            if(target == nums[mid]){
                return mid;
            }else if(target < nums[mid]){
                right = mid - 1;
            }else{
                left = mid + 1;
            }
        }
        return -1;
    }
    //左闭右开
    public int search(int[] nums, int target){
        int left = 0, right = nums.length;
        while(left < right){
            int mid = left + ((right - left) >> 1);
            if(target == nums[mid]){
                return mid;
            }else if(target < nums[mid]){
                right = mid;//右开,遵循循环不变量原则
            }else{
                left = mid + 1;
            }
        }
        return -1;
    }
  1. 二分查找拓展:寻找左右边界,题目Leetcode34。分开考虑左边界与右边界。需要注意:

    1. 寻找右边界的方法findRightBorder 返回值为实际右边界 + 1,即最小的大于target的数的下标。寻找左边界的方法findLeftBorder 返回值为实际左边界 - 1,即最大的小于target的数的下标。

    2. 寻找右边界时,向右迭代的条件含有等号,寻找左边界时,向左迭代的条件含有等号。

    3. 寻找右边界时,向右迭代时更新右边界为left,寻找左边界时,向左迭代时更新左边界为right。在最后一次迭代后,left = right + 1。寻找右边界时,此时right指向最后一个target,即实际右边界,left指向实际右边界 + 1。寻找左边界时,此时left指向最前一个target,即实际左边界,right指向实际左边界 - 1。

    4. 共有三种情况:1)target存在于数组中;2)target不存在于数组中,且target < nums[0] || target > nums[n-1];3)target不存在于数组中,且targrt > nums[0] && target < nums[n-1]。

//Leetcode34
	//时间复杂度:O(logn) 两次二分查找。
	//空间复杂度:O(1)。
	//nums有序,返回target的左右边界下标
    public int[] searchRange(int[] nums, int target) {
            int rightBorder = findRightBorder(nums, target);
            int leftBorder = findLeftBorder(nums, target);
            //情况2:target < nums[0] 或 target > nums[n-1]
            //情况3:target不满足情况2,但是在数组中不存在
            if(rightBorder == -2 || leftBorder == -2 || rightBorder - leftBorder <= 1){
                return new int[]{-1, -1};
            }
            //情况1:target存在在数组中
            return new int[]{leftBorder + 1, rightBorder - 1};
        }

        //返回右边界:大于target的第一个数的下标
        public int findRightBorder(int[] nums, int target){
            int rightBorder = -2;
            int left = 0, right = nums.length - 1;
            while(left <= right){
                int mid = left + ((right - left) >> 1);
                if(target >= nums[mid]){//注意:寻找右边界时,向右迭代的条件含有等号
                    left = mid + 1;
                    rightBorder = left;//寻找右边界时,向右迭代时更新右边界为left
                }else{
                    right = mid - 1;
                }
            }
            return rightBorder; 
        }

        //返回左边界:小于target的第一个数的下标
        public int findLeftBorder(int[] nums, int target){
            int leftBorder = -2;
            int left = 0, right = nums.length - 1;
            while(left <= right){
                int mid = left + ((right - left) >> 1);
                if(target <= nums[mid]){//注意:寻找左边界时,向左迭代的条件含有等号
                    right = mid - 1;
                    leftBorder = right;//寻找左边界时,向左迭代时更新左边界为right
                }else{
                    left = mid + 1;
                }
            }
            return leftBorder;
        }
2. 双指针
  1. 移除元素 Leetcode27 ,可采用双指针法,两种解法。1)前后指针 2)快慢指针。

    1. 前后双指针:前指针从左向右遍历,查找到target时,将其值替换为后指针位置的值(需要判断后指针位置的值是否是target,如果是,后指针需要自减,再重新进入循环判断),直至前指针超过后指针,退出循环。期间需要维护count用来记录新数组的长度,初始值为0,每当 i ++ 时,count ++ 。

      //Leetcode27
      	//前后双指针
      	//时间复杂度:O(n)。
      	//空间复杂度:O(1)。
      	public int removeElement(int[] nums, int val) {
                  int i = 0, j = nums.length - 1;
                  int count = 0;
                  while(i <= j){
                      if(nums[i] == val){
                          if(nums[j] != val){
                              nums[i] = nums[j];
                              i++;
                              j--;
                              count++;
                          }else{
                              j--;
                          }
      
                      }else{
                          i++;
                          count++;
                      }
                  }
                  return count;
              }
      
    2. 快慢双指针:快指针和慢指针初始位于0下标位置,判断快指针的值是否是val,如果不是就其赋给将慢指针位置的值,慢指针后移,如果是则不赋值。每次循环快指针必须后移。当快指针等于nums的长度时退出循环(刚好越界),此时慢指针所处的下标位置即新数组长度(刚好越界)。

      	//快慢双指针
      	//时间复杂度:O(n)。
      	//空间复杂度:O(1)。
          public int removeElement(int[] nums, int val) {
                  int fast = 0, slow = 0;
                  while(fast < nums.length){
                      if(nums[fast] != val){
                          nums[slow] = nums[fast];
                          slow++;
                      }
                      fast++;
                  }
                  return slow;
              }
      
  2. 有序数组的平方 Leetcode977,采用双指针法。由于nums为非递减序列,且包含负数,则可以采用前后双指针,循环判断两指针位置处的值谁平方(绝对值)更大,填入新数组。注意:填入新数组时需要从后向前填入,以确保新数组为非递减序列。

    //Leetcode977
    	//双指针法
    	//时间复杂度:O(n)。
    	//空间复杂度:O(1)。???
        public int[] sortedSquares(int[] nums) {
                int i = 0, j = nums.length - 1;
                int[] res = new int[nums.length];
                for(int k = nums.length - 1; k >= 0; k--){
                    if(Math.abs(nums[i]) > Math.abs(nums[j])){
                        res[k] = nums[i] * nums[i];
                        i++;
                    }else{
                        res[k] = nums[j] * nums[j];
                        j--;
                    }
                }
                return res;
            }
    
    
3. 滑动窗口
  1. 长度最小的子数组 Leetcode209,采用滑动窗口法,sum记为窗口内数值之和。如果sum < target,说明需要扩展窗口,right右移。如果sum >= target,说明可能需要压缩窗口,left左移。期间维护res为满足要求(sum >= target)窗口的最小长度。

    //Leetcode209
    	//滑动窗口法
    	//时间复杂度:O(n)。
    	//空间复杂度:O(1)。
        public int minSubArrayLen(int target, int[] nums) {
                //int n = nums.length;
                int left = 0;
                int res = Integer.MAX_VALUE;
                int sum = 0;
                for(int right = 0; right < nums.length; right++){
                    sum += nums[right];
                    while(sum >= target){
                        res = Math.min(res, right - left + 1);
                        sum -= nums[left];
                        left++;
                    }
                }
                return res == Integer.MAX_VALUE ? 0 : res;
            }	
    
4. 螺旋矩阵
  1. 经典题目:螺旋矩阵 Leetcode54,螺旋矩阵 II Leetcode59。前者为顺时针螺旋返回m × n矩阵的元素,后者为顺时针螺旋填入n × n方阵的元素,可采用设定边界的模拟法或前闭后开的循环不变量原则法。

  2. 设定边界l, r, t, b 分别表示未填入(未返回)区域的左、右、上、下边界。

  3. 在从左向右的遍历后,t++ 缩小上边界;从上向下遍历后,r-- 缩小右边界;从右向左的遍历后,b-- 缩小下边界;从下向上遍历后,l++缩小左边界。

  4. 循环条件可以从两方面入手:一是用count记录遍历过的元素个数,需要count <= maxVal。二是用l <= r && t <= b。(需要多加考虑该处,特别是最后就剩一个元素或一行(一列)元素时,何时退出循环)

    //Leetcode54:
    //时间复杂度:O(mn)。
    //空间复杂度:O(1)。???
    	public List spiralOrder(int[][] matrix) {
                int m = matrix.length;
                int n = matrix[0].length;
                List list = new ArrayList<>();
                int l = 0, r = n - 1, t = 0, b = m - 1;
                int count = 0;
                int maxVal = n * m;
                while(l <= r && t <= b){
                    for(int i = l; i <= r && count++ < maxVal; i++){
                        list.add(matrix[t][i]);
                    }
                    t++;
                    for(int i = t; i <= b && count++ < maxVal; i++){
                        list.add(matrix[i][r]);
                    }
                    r--;
                    for(int i = r; i >= l && count++ < maxVal; i--){
                        list.add(matrix[b][i]);
                    }
                    b--;
                     for(int i = b; i >= t && count++ < maxVal; i--){
                        list.add(matrix[i][l]);
                    }
                    l++;
                }
                return list;
            }
    
    //Leetcode59:
    //时间复杂度:O(mn)。
    //空间复杂度:O(1)。???
    	public int[][] generateMatrix(int n) {
            int[][] matrix = new int[n][n];
            int maxVal = n * n;
            int count = 1;
            int l = 0, r = n - 1, t = 0, b = n - 1;
            while(count <= maxVal){
                for(int i = l; i <= r; i++){
                    matrix[t][i] = count++;
                }
                t++;
                for(int i = t; i <= b; i++){
                    matrix[i][r] = count++;
                }
                r--;
                for(int i = r; i >= l; i--){
                    matrix[b][i] = count++;
                }
                b--;
                for(int i = b; i >= t; i--){
                    matrix[i][l] = count++;
                }
                l++;
            }
            return matrix;
        }
    

转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/989600.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号