给定一个 n 个元素有序的(升序)整型数组 nums 和一个目标值 target ,写一个函数搜索 nums 中的 target,如果目标值存在返回下标,否则返回 -1。
输入: nums = [-1,0,3,5,9,12], target = 9 输出: 4
思路:
正常的二分查找即可,这个代码就不展示了。这里着重说一下判断左边界和右边界
找左边界就是找第一个等于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
给定一个排序数组和一个目标值,在数组中找到目标值,并返回其索引。如果目标值不存在于数组中,返回它将会被按顺序插入的位置。你可以假设数组中无重复元素。
输入: [1,3,5,6], 5 输出: 2
思路:
正常的二分查找即可,这个代码就不展示了。
给定一个按照升序排列的整数数组 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;
}
}
给你一个非负整数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;
}
}
给你一个正整数num。如果num是一个完全平方数,则返回true,否则返回false。完全平方数是一个可以写成某个整数的平方的整数。换句话说,它可以写成某个整数和自身的乘积。
输入:num = 16 输出:true
思路:
一样的思路就不展示代码了。
给你一个数组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;
}
}
给给你一个非严格递增排列的数组nums,请你原地删除重复出现的元素,使每个元素只出现一次,返回删除后数组的新长度。元素的相对顺序应该保持一致。然后返回nums中唯一元素的个数。考虑nums的唯一元素的数量为k,你需要做以下事情确保你的题解可以被通过:更改数组nums,使nums的前k个元素包含唯一元素,并按照它们最初在nums中出现的顺序排列。nums的其余元素与nums的大小不重要。返回k。
输入:nums=[1,1,2] 输出:2,nums=[1,2,_]
思路:
正常遍历,记录好下标即可。
给定一个数组nums,编写一个函数将所有0移动到数组的末尾,同时保持非零元素的相对顺序。请注意,必须在不复制数组的情况下原地对数组进行操作。
输入: nums = [0,1,0,3,12] 输出: [1,3,12,0,0]
思路:
记录一下非0的个数,碰到0就跳过,碰到非0和之前的index进行交换,之后补0即可。
给定s和t两个字符串,当它们分别被输入到空白的文本编辑器后,如果两者相等,返回true。#代表退格字符。注意:如果对空文本输入退格字符,文本继续为空。
遇到字母两指针都向前一位,遇到#号快指针向前一位,慢指针后退一位(注意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);
}
}
如果它是退格符,那么我们将栈顶弹出;如果它是普通字符,那么我们将其压入栈中。
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();
}
}
给你一个按非递减顺序排序的整数数组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;
}
}
给定一个含有 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;
}
}
给你正在探访一家农场,农场从左到右种植了一排果树。这些树用一个整数数组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;
}
}
给你一个字符串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);
}
}
给定一个正整数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;
}
}
给你一个m行n列的矩阵matrix,请按照顺时针螺旋顺序,返回矩阵中的所有元素。
输入:matrix = [[1,2,3],[4,5,6],[7,8,9]] 输出:[1,2,3,6,9,8,7,4,5]
思路:
与54题类似,只不过需要注意一下边界。
给定一个二维数组array,请返回「螺旋遍历」该数组的结果。螺旋遍历:从左上角开始,按照向右、向下、向左、向上的顺序依次提取元素,然后再进入内部一层重复相同的步骤,直到提取完所有元素。
输入:array = [[1,2,3],[8,9,4],[7,6,5]] 输出:[1,2,3,4,5,6,7,8,9]
思路:
与54题类似。
给定一个整数数组 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);
}
}
}
给你一个整数数组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;
}
}
在一个城市区域内,被划分成了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();
}
}