【代码随想录】第一章-数组

【代码随想录】第一章-数组

  • 第一章-数组
    • 1 二分查找
      • 704.二分查找
        • Tip:左右边界
      • 35.搜索插入位置
      • 34.在排序数组中查找元素的第一个和最后一个位置
      • 69. x的平方根
      • 367.有效的完全平方数
    • 2 移除元素
      • 27.移除元素
      • 26.删除有序数组中的重复项
      • 283.移动零
      • 844.比较含退格的字符串
        • Method1:退格操作-快慢双指针
        • Method2:栈
    • 3 有序数组的平方
      • 977.有序数组的平方
    • 4 长度最小的子数组(滑动窗口)
      • 209.长度最小的子数组
      • 904.水果成篮(哈希表结合滑动窗口)
      • 76.最小覆盖子串
    • 5 螺旋矩阵(边界收缩)
      • 59.螺旋矩阵II
      • 54.螺旋矩阵
      • LCR 146. 螺旋遍历二维数组
    • 6 区间和(前缀数组)
      • Kama.58.区间和
      • 53.最大子数组和
    • 7 开发商购买土地(二维前缀数组)
      • Kama.44.开发商购买土地


第一章-数组

1 二分查找

704.二分查找

给定一个 n 个元素有序的(升序)整型数组 nums 和一个目标值 target ,写一个函数搜索 nums 中的 target,如果目标值存在返回下标,否则返回 -1。
输入: nums = [-1,0,3,5,9,12], target = 9 输出: 4

思路:
正常的二分查找即可,这个代码就不展示了。这里着重说一下判断左边界和右边界

Tip:左右边界

找左边界就是找第一个等于target的,找右边界也可以转换为找等于target+1的。

int n = nums.length;
int low = 0;
int high = n;
while (low < high) {
    int mid = (low + high) >> 1;
    if (nums[mid] >= target) {
        high = mid;
    } else {
        low = mid + 1;
    }
}

找右边界,重点是nums[mid] > target


35.搜索插入位置

给定一个排序数组和一个目标值,在数组中找到目标值,并返回其索引。如果目标值不存在于数组中,返回它将会被按顺序插入的位置。你可以假设数组中无重复元素。
输入: [1,3,5,6], 5 输出: 2

思路:
正常的二分查找即可,这个代码就不展示了。


34.在排序数组中查找元素的第一个和最后一个位置

给定一个按照升序排列的整数数组 nums,和一个目标值 target。找出给定目标值在数组中的开始位置和结束位置。如果数组中不存在目标值 target,返回 [-1, -1]。
输入:nums = [5,7,7,8,8,10], target = 8 输出:[3,4]

思路:
等于寻找左边界和右边界

class Solution {
    public int[] searchRange(int[] nums, int target) {
        int n = nums.length;
        int low = 0;
        int high = n;
        while (low < high) {
            int mid = (low + high) >> 1;
            if (nums[mid] >= target) {
                high = mid;
            } else {
                low = mid + 1;
            }
        }
        if (low == n || nums[low] != target) {
            return new int[] { -1, -1 };
        }
        int[] arr = new int[2];
        arr[0] = low;
        low = 0;
        high = n;
        while (low < high) {
            int mid = (low + high) >> 1;
            if (nums[mid] > target) {
                high = mid;
            } else {
                low = mid + 1;
            }
        }
        arr[1] = low - 1;
        return arr;
    }
}

69. x的平方根

给你一个非负整数x,计算并返回x的算术平方根。由于返回类型是整数,结果只保留整数部分,小数部分将被舍去。注意:不允许使用任何内置指数函数和算符,例如pow(x,0.5)或者x**0.5。
输入:x = 4 输出:2

思路:
利用二分查找进行快速搜索

class Solution {
    public int mySqrt(int x) {
        if (x == 0) return 0;  
        long left = 1, right = x;  // 使用 long 类型避免溢出
        while (left <= right) {
            long mid = left + (right - left) / 2;  
            if (mid * mid == x) {
                return (int) mid;  
            } else if (mid * mid < x) {
                left = mid + 1; 
            } else {
                right = mid - 1;
            }
        }
        return (int) right;
    }
}

367.有效的完全平方数

给你一个正整数num。如果num是一个完全平方数,则返回true,否则返回false。完全平方数是一个可以写成某个整数的平方的整数。换句话说,它可以写成某个整数和自身的乘积。
输入:num = 16 输出:true

思路:
一样的思路就不展示代码了。


2 移除元素

27.移除元素

给你一个数组nums和一个值val,你需要原地移除所有数值等于val的元素,并返回移除后数组的新长度。不要使用额外的数组空间,你必须仅使用O(1)额外空间并原地修改输入数组。元素的顺序可以改变。你不需要考虑数组中超出新长度后面的元素。
输入:nums=[3,2,2,3],val=3 输出:2,nums=[2,2,,]

思路:
正常遍历,记录好下标即可。

class Solution {
    public int removeElement(int[] nums, int val) {
        int n = nums.length;
        if (n == 0) {
            return 0;
        }
        int index = 0;
        for (int i = 0; i < n; i++) {
            if (nums[i] != val) {
                nums[index] = nums[i];
                index++;
            }
        }
        return index;
    }
}

26.删除有序数组中的重复项

给给你一个非严格递增排列的数组nums,请你原地删除重复出现的元素,使每个元素只出现一次,返回删除后数组的新长度。元素的相对顺序应该保持一致。然后返回nums中唯一元素的个数。考虑nums的唯一元素的数量为k,你需要做以下事情确保你的题解可以被通过:更改数组nums,使nums的前k个元素包含唯一元素,并按照它们最初在nums中出现的顺序排列。nums的其余元素与nums的大小不重要。返回k。
输入:nums=[1,1,2] 输出:2,nums=[1,2,_]

思路:
正常遍历,记录好下标即可。


283.移动零

给定一个数组nums,编写一个函数将所有0移动到数组的末尾,同时保持非零元素的相对顺序。请注意,必须在不复制数组的情况下原地对数组进行操作。
输入: nums = [0,1,0,3,12] 输出: [1,3,12,0,0]

思路:
记录一下非0的个数,碰到0就跳过,碰到非0和之前的index进行交换,之后补0即可。


844.比较含退格的字符串

给定s和t两个字符串,当它们分别被输入到空白的文本编辑器后,如果两者相等,返回true。#代表退格字符。注意:如果对空文本输入退格字符,文本继续为空。

Method1:退格操作-快慢双指针

遇到字母两指针都向前一位,遇到#号快指针向前一位,慢指针后退一位(注意0位置) 就行了。

class Solution {
    public boolean backspaceCompare(String s, String t) {
        char[] ss = s.toCharArray();
        char[] tt = t.toCharArray();
        return helper(ss).equals(helper(tt));
    }
    String helper(char[] c){
        int i = 0,j = 0;
        while(j < c.length){
            if(c[j] != '#'){ 
                c[i++] = c[j++];
            }else {
                j++;
                if(i > 0) i--; 
            }
        }
        return new String(c).substring(0,i);
    }
}
Method2:栈

如果它是退格符,那么我们将栈顶弹出;如果它是普通字符,那么我们将其压入栈中。

class Solution {
    public boolean backspaceCompare(String s, String t) {
        return processString(s).equals(processString(t));
    }
    public String processString(String s) {
        char[] cs = s.toCharArray();
        Deque<Character> S = new LinkedList<>();
        for (char c : cs) {
            if (c == '#') {
                if (!S.isEmpty()) {
                    S.pop();
                }
            } else {
                S.push(c);
            }
        }
        StringBuilder result = new StringBuilder();
        while (!S.isEmpty()) {
            result.append(S.pop());
        }
        return result.reverse().toString();
    }
}

3 有序数组的平方

977.有序数组的平方

给你一个按非递减顺序排序的整数数组nums,返回每个数字的平方组成的新数组,要求也按非递减顺序排序。
输入:nums = [-4,-1,0,3,10] 输出:[0,1,9,16,100]

思路:
Index记录真正有序的下表,双指针两头开始扫,找到最大的放到index底下。

class Solution {
    public int[] sortedSquares(int[] nums) {
        int low=0; int high=nums.length-1;
        int index=nums.length-1;
        int []arr=new int[nums.length];
        while(low<=high){
            if(Math.abs(nums[low])<Math.abs(nums[high])){
                arr[index]=nums[high]*nums[high];
                index--; high--;
            }
            else{
                arr[index]=nums[low]*nums[low];
                index--; low++;
            }
        }
        return arr;
    }
}

4 长度最小的子数组(滑动窗口)

209.长度最小的子数组

给定一个含有 n 个正整数的数组和一个正整数 s ,找出该数组中满足其和 ≥ s 的长度最小的 连续 子数组,并返回其长度。如果不存在符合条件的子数组,返回 0。
输入:s = 7, nums = [2,3,1,2,4,3] 输出:2

思路:
滑动窗口对sum进行收缩,找到最短的满足条件的sum的序列长度。

class Solution {
    public int minSubArrayLen(int target, int[] nums) {
        int curSum = 0;
        int i = 0;
        int minLength = Integer.MAX_VALUE; 
        for (int j = 0; j < nums.length; j++) {
            curSum += nums[j];
            while (curSum >= target) {
                minLength = Math.min(minLength, j - i + 1); 
                curSum -= nums[i];
                i++; 
            }
        }
        return minLength == Integer.MAX_VALUE ? 0 : minLength;
    }
}

904.水果成篮(哈希表结合滑动窗口)

给你正在探访一家农场,农场从左到右种植了一排果树。这些树用一个整数数组fruits表示,其中fruits[i]是第i棵树上的水果种类。你想要尽可能多地收集水果。然而,农场的主人设定了一些严格的规矩,你必须按照要求采摘水果:你只有两个篮子,并且每个篮子只能装单一类型的水果。每个篮子能够装的水果总量没有限制。你可以选择任意一棵树开始采摘,你必须从每棵树(包括开始采摘的树)上恰好摘一个水果。采摘的水果应当符合篮子中的水果类型。每采摘一次,你将会向右移动到下一棵树,并继续采摘。一旦你走到某棵树前,但水果不符合篮子的水果类型,那么就必须停止采摘。给你一个整数数组fruits,返回你可以收集的水果的最大数目。
输入:fruits = [3,3,3,1,2,1,1,2,3,3,4] 输出:5

思路:
滑动窗口对hashmap进行收缩,找到最长的满足条件的hashmap的序列长度。

class Solution {
    public int totalFruit(int[] fruits) {
        LinkedHashMap<Integer, Integer> map = new LinkedHashMap<>();
        int i = 0; 
        int maxLength = 0;
        for (int j = 0; j < fruits.length; j++) {
            map.put(fruits[j], map.getOrDefault(fruits[j], 0) + 1);
            while (map.size() > 2) {
                int leftFruit = fruits[i];
                map.put(leftFruit, map.get(leftFruit) - 1);
                if (map.get(leftFruit) == 0) {
                    map.remove(leftFruit); 
                }
                i++; 
            }
            maxLength = Math.max(maxLength, j - i + 1);
        }
        return maxLength;
    }
}

76.最小覆盖子串

给你一个字符串s、一个字符串t。返回s中涵盖t所有字符的最小子串。如果s中不存在涵盖t所有字符的子串,则返回空字符串""。
输入:s = “ADOBECODEBANC”, t = “ABC” 输出:“BANC”

思路:
将t先放入map集合中,使用vaild来记录当前有效的字符,如果当前有效的字符跟map的大小相同时,对windows中的字符串开始收缩。注意vaild增加和减少的条件,只有当字符在map和windows中一致是,才进行vaild的加减。

class Solution {
    public String minWindow(String s, String t) {
        if (t.length() > s.length()) {
            return "";
        }
        int sn = s.length();
        int tn = t.length();
        char[] ss = s.toCharArray();
        char[] ts = s.toCharArray();
        int vaild = 0;
        int start = 0, len = Integer.MAX_VALUE;
        Map<Character, Integer> map = new HashMap<>();
        Map<Character, Integer> window = new HashMap<>();
        for (char c : t.toCharArray()) {
            map.put(c, map.getOrDefault(c, 0) + 1);
        }
        int l = 0, r = 0;
        while (r < sn) {
            char a = ss[r];
            r++;
            if (map.containsKey(a)) {
                window.put(a, window.getOrDefault(a, 0) + 1);
                if (map.get(a).equals(window.get(a))) {
                    vaild++;
                }
            }
            while (vaild == map.size()) {
                if (r - l < len) {
                    start = l;
                    len = r - l;
                }
                char b = ss[l];
                l++;
                if (map.containsKey(b)) {
                    if (map.get(b).equals(window.get(b))) {
                        vaild--;
                    }
                    window.put(b, window.get(b) - 1);
                }
            }
        }
        return len == Integer.MAX_VALUE ? "" : s.substring(start, start + len);
    }
}

5 螺旋矩阵(边界收缩)

59.螺旋矩阵II

给定一个正整数n,生成一个包含1到n2所有元素,且元素按顺时针顺序螺旋排列的正方形矩阵。
输入:3 输出:[[1,2,3],[8,9,4],[7,6,5]]

思路:
思考一下四个边界:一定是有从左到右;一定也有从上到下;但是如果上下边界重合,从右向左就不需要了;如果左右边界重合,从下到上就不需要了。所以我们遍历的顺序是从左到右、从上到下、从右向左、从下到上。

class Solution {
    public int[][] generateMatrix(int n) {
        int[][] arr = new int[n][n];
        int rowNum = n; int colNum = n; 
        int top = 0, bottom = rowNum - 1; 
        int left = 0, right = colNum - 1; 
        int index = 1;
        while (top <= bottom && left <= right) {
            for (int j = left; j <= right; j++) {
                arr[top][j] = index;
                index++;
            }
            top++;
            for (int i = top; i <= bottom; i++) {
                arr[i][right] = index;
                index++;
            }
            right--;
            if (top <= bottom) {
                for (int j = right; j >= left; j--) {
                    arr[bottom][j] = index;
                    index++;
                }
                bottom--;
            }
            if (left <= right) {
                for (int i = bottom; i >= top; i--) {
                    arr[i][left] = index;
                    index++;
                }
                left++;
            }
        }
        return arr;
    }
}

54.螺旋矩阵

给你一个m行n列的矩阵matrix,请按照顺时针螺旋顺序,返回矩阵中的所有元素。
输入:matrix = [[1,2,3],[4,5,6],[7,8,9]] 输出:[1,2,3,6,9,8,7,4,5]

思路:
与54题类似,只不过需要注意一下边界。


LCR 146. 螺旋遍历二维数组

给定一个二维数组array,请返回「螺旋遍历」该数组的结果。螺旋遍历:从左上角开始,按照向右、向下、向左、向上的顺序依次提取元素,然后再进入内部一层重复相同的步骤,直到提取完所有元素。
输入:array = [[1,2,3],[8,9,4],[7,6,5]] 输出:[1,2,3,4,5,6,7,8,9]

思路:
与54题类似。


6 区间和(前缀数组)

Kama.58.区间和

给定一个整数数组 Array,请计算该数组在每个指定区间内元素的总和。第一行输入为整数数组 Array 的长度 n,接下来 n 行,每行一个整数,表示数组的元素。随后的输入为需要计算总和的区间下标:a,b (b > = a),直至文件结束。

思路:
为了防止重复计算,使用前缀和来进行操作。

import java.util.*;
class Solution {
    private int[] prefixSum;
    public void preprocess(int[] array) {
        int n = array.length;
        prefixSum = new int[n + 1];
        prefixSum[0] = 0;
        for (int i = 0; i < n; i++) {
            prefixSum[i + 1] = prefixSum[i] + array[i];
        }
    }
    public int sum(int left, int right) {
        if (left < 0) left = 0;
        if (right >= prefixSum.length - 1) right = prefixSum.length - 2;
        return prefixSum[right + 1] - prefixSum[left];
    }
}
public class Main {
    public static void main(String[] args) {
        Solution solution = new Solution();
        Scanner sc = new Scanner(System.in);
        int n = sc.nextInt();
        int[] array = new int[n];
        for (int i = 0; i < n; i++) {
            array[i] = sc.nextInt();
        }
        solution.preprocess(array);
        while(sc.hasNext()){
            int a = sc.nextInt();
            int b = sc.nextInt();
            if(a==0&&b==0){
                break;
            }
            int res = solution.sum(a, b);
            System.out.println(res);
        }
    }
}

53.最大子数组和

给你一个整数数组nums,请你找出一个具有最大和的连续子数组(子数组最少包含一个元素),返回其最大和。子数组是数组中连续的非空元素序列,是数组中的一个连续部分。
输入:nums=[-2,1,-3,4,-1,2,1,-5,4] 输出:6

思路:
要找的是子串,我们可以通过前缀和来找到最大的子串。nums = [-2,1,-3,4,-1,2,1,-5,4]的前缀数组就是prenum=[0,-2,-1,-4,0,-1,1,2,-3,1],所以我们用前缀和2减去前缀和-4就可以得到最大子数组和,也就是区间4,-1,2,1的和为6。
举个例子:1,-2,3,5

所以我们需要得到某区间内最大的前缀和maxSum和最小前缀和minSum以及当前前缀和sum;maxSum的初始化为nums[0],minSum的初始化为0;sum+=nums[i]得到每个前缀和,对比一下当前前缀和减最小前缀和谁大,更新最大最小前缀和。

class Solution {
    public int maxSubArray(int[] nums) {
        int len=nums.length;
        int maxSum=nums[0];
        int minSum=0;
        int sum=0;
        for(int i=0;i<len;i++){
            sum+=nums[i];
            maxSum=Math.max(maxSum,sum-minSum);
            minSum=Math.min(minSum,sum);
        }
        return maxSum;
    }
}

7 开发商购买土地(二维前缀数组)

Kama.44.开发商购买土地

在一个城市区域内,被划分成了n*m个连续的区块,每个区块都拥有不同的权值,代表着其土地价值。目前,有两家开发公司,A公司和B公司,希望购买这个城市区域的土地。现在,需要将这个城市区域的所有区块分配给A公司和B公司。然而,由于城市规划的限制,只允许将区域按横向或纵向划分成两个子区域,而且每个子区域都必须包含一个或多个区块。为了确保公平竞争,你需要找到一种分配方式,使得A公司和B公司各自的子区域内的土地总价值之差最小。注意:区块不可再分。

思路:
首先计算整个网格的总价值。对于水平划分,计算每一行的累积和,并在每一个可能的划分点计算上半部分和下半部分的差值。对于垂直划分,计算每一列的累积和,并在每一个可能的划分点计算左半部分和右半部分的差值。找出最小差值:比较所有可能的水平和垂直划分,找出使得差值最小的划分方式。

import java.util.*;
import java.io.*;
class Solution{
    public int payGround(int[][] arr){
        int n = arr.length;
        if (n == 0) return 0;
        int m = arr[0].length;
        long totalSum = 0;
        for(int i = 0; i < n; i++) {
            for(int j = 0; j < m; j++) {
                totalSum += arr[i][j];
            }
        }
        long[] rowSums = new long[n];
        for(int i = 0; i < n; i++) {
            for(int j = 0; j < m; j++) {
                rowSums[i] += arr[i][j];
            }
        }
        long[] colSums = new long[m];
        for(int j = 0; j < m; j++) {
            for(int i = 0; i < n; i++) {
                colSums[j] += arr[i][j];
            }
        }
        long minDiff = Long.MAX_VALUE;
        long topSum = 0;
        for(int i = 0; i < n - 1; i++) {
            topSum += rowSums[i];
            long bottomSum = totalSum - topSum;
            long diff = Math.abs(topSum - bottomSum);
            if(diff < minDiff) {
                minDiff = diff;
            }
        }
        long leftSum = 0;
        for(int j = 0; j < m - 1; j++) {
            leftSum += colSums[j];
            long rightSum = totalSum - leftSum;
            long diff = Math.abs(leftSum - rightSum);
            if(diff < minDiff) {
                minDiff = diff;
            }
        }
        return (int) minDiff;
    }
}
public class Main {
    public static void main(String[] args){
        Solution s = new Solution();
        Scanner sc = new Scanner(System.in);
        int n = sc.nextInt();
        int m = sc.nextInt();
        int [][]arr = new int[n][m];
        for(int i = 0; i < n; i++){
            for(int j = 0; j < m; j++){
                if(sc.hasNextInt()){
                    arr[i][j] = sc.nextInt();
                } else {
                    arr[i][j] = 0;
                }
            }
        }
        System.out.println(s.payGround(arr));
        sc.close();
    }
}

你可能感兴趣的:(代码随想录,算法,数据结构,java)