3.11子串+普通数组

This commit is contained in:
zhangsan 2025-03-11 17:08:00 +08:00
parent f3a0b60f0f
commit a0c7134799
12 changed files with 467 additions and 0 deletions

View File

@ -0,0 +1,33 @@
package array;
/**
* 题目53. 最大子数组和 (maxSubArray)
* 描述给你一个整数数组 nums 请你找出一个具有最大和的连续子数组子数组最少包含一个元素返回其最大和
子数组是数组中的一个连续部分
* 链接https://leetcode.cn/problems/maximum-subarray/
示例 1
输入nums = [-2,1,-3,4,-1,2,1,-5,4]
输出6
解释连续子数组 [4,-1,2,1] 的和最大 6
示例 2
输入nums = [1]
输出1
示例 3
输入nums = [5,4,-1,7,8]
输出23
*/
public class MaxSubArray {
public int maxSubArray(int[] nums) {
int tempsum=nums[0],maxsum=nums[0];
for (int i = 1; i < nums.length; i++) {
if(tempsum<0)
tempsum=0;
tempsum+=nums[i];
if(maxsum<tempsum)
maxsum=tempsum;
}
return maxsum;
}
}

View File

@ -0,0 +1,49 @@
package array;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
/**
* 题目56. 合并区间 (merge)
* 描述以数组 intervals 表示若干个区间的集合其中单个区间为 intervals[i] = [starti, endi] 请你合并所有重叠的区间并返回 一个不重叠的区间数组该数组需恰好覆盖输入中的所有区间
*
* 链接https://leetcode.cn/problems/merge-intervals/description/
*
示例 1
输入intervals = [[1,3],[2,6],[8,10],[15,18]]
输出[[1,6],[8,10],[15,18]]
解释区间 [1,3] [2,6] 重叠, 将它们合并为 [1,6].
示例 2
输入intervals = [[1,4],[4,5]]
输出[[1,5]]
解释区间 [1,4] [4,5] 可被视为重叠区间
*/
public class Merge {
public int[][] merge(int[][] intervals) {
Arrays.sort(intervals,(a,b)->{
if(a[0]!=b[0]){
return a[0]-b[0];
}else
return a[1]-b[1];
});
List<int[]>merged=new ArrayList<>();
int[] current = new int[2];
current[0] = intervals[0][0];
current[1] = intervals[0][1];
for (int i = 1; i < intervals.length; i++) {
if (current[1] >= intervals[i][0]) {
// 重叠时合并区间更新右边界
current[1] = Math.max(current[1], intervals[i][1]);
}
else{
merged.add(new int[]{current[0], current[1]});
current = new int[]{intervals[i][0], intervals[i][1]};
}
}
merged.add(new int[]{current[0], current[1]});
return merged.toArray(new int[merged.size()][]);
}
}

View File

@ -0,0 +1,39 @@
package array;
/**
* 题目189. 轮转数组 (rotate)
* 描述给定一个整数数组 nums将数组中的元素向右轮转 k 个位置其中 k 是非负数
* 链接https://leetcode.cn/problems/rotate-array/
示例 1:
输入: nums = [1,2,3,4,5,6,7], k = 3
输出: [5,6,7,1,2,3,4]
解释:
向右轮转 1 : [7,1,2,3,4,5,6]
向右轮转 2 : [6,7,1,2,3,4,5]
向右轮转 3 : [5,6,7,1,2,3,4]
示例 2:
输入nums = [-1,-100,3,99], k = 2
输出[3,99,-1,-100]
解释:
向右轮转 1 : [99,-1,-100,3]
向右轮转 2 : [3,99,-1,-100]
*/
public class RotateArray {
public void rotateArray(int[] nums, int k) {
int cnt=nums.length,index=0;
int offset=k%cnt;
int[]temp=new int[k];
for (int i = cnt-offset; i < cnt; i++) {
temp[index]=nums[i];
index++;
}
for (int i = cnt-1; i>=offset ; i--) {
nums[i]=nums[i-offset];
}
for (int i = 0; i < offset; i++) {
nums[i]=temp[i];
}
}
}

View File

@ -0,0 +1,82 @@
package substring;
import hash.Test;
import java.util.HashMap;
import java.util.Iterator;
/**
* 题目523. 连续的子数组和 (checkSubarraySum)
* 描述给你一个整数数组 nums 和一个整数 k 如果 nums 有一个 好的子数组 返回 true 否则返回 false
一个 好的子数组
长度 至少为 2
* 子数组元素总和为 k 的倍数
* 注意
* 子数组 是数组中 连续 的部分
* 如果存在一个整数 n 令整数 x 符合 x = n * k 则称 x k 的一个倍数0 始终 视为 k 的一个倍数
*
* 链接https://leetcode.cn/problems/continuous-subarray-sum/
示例 1
输入nums = [23,2,4,6,7], k = 6
输出true
解释[2,4] 是一个大小为 2 的子数组并且和为 6
示例 2
输入nums = [23,2,6,4,7], k = 6
输出true
解释[23, 2, 6, 4, 7] 是大小为 5 的子数组并且和为 42
42 6 的倍数因为 42 = 7 * 6 7 是一个整数
示例 3
输入nums = [23,2,6,4,7], k = 13
输出false
*/
//需重做
//本题不一样的地方是无需统计个数故哈希表中键值可以存索引
public class CheckSubarraySum {
//枚举法超时
public boolean checkSubarraySum1(int[] nums, int k) {
for (int left = 0; left < nums.length; left++) {
int tpsum=nums[left];
for (int right = left+1; right < nums.length; right++) {
tpsum+=nums[right];
if(tpsum%k==0)
return true;
}
}
return false;
}
//前缀和 这题需要判断子数组和是否为 k 的倍数因此我们关心的是前缀和对 k 取余后的值是否相等
public boolean checkSubarraySum(int[] nums, int k) {
// 特殊情况k == 0 的处理寻找连续和为 0 的子数组
if (k == 0) {
for (int i = 0; i < nums.length - 1; i++) {
if (nums[i] == 0 && nums[i+1] == 0) {
return true;
}
}
return false;
}
// 哈希表存储前缀和 mod k 的余数及其第一次出现的下标
HashMap<Integer, Integer> modMap = new HashMap<>();
modMap.put(0, -1); // 余数为0时将下标设为-1方便处理从数组开头开始的情况
int prefixSum = 0;
for (int i = 0; i < nums.length; i++) {
prefixSum += nums[i];
int mod = prefixSum % k;
// 如果这个余数之前出现过则当前下标和之前下标之差至少大于1
if (modMap.containsKey(mod)) {
if (i - modMap.get(mod) > 1) {
return true;
}
} else {
// 记录第一次出现该余数的下标
modMap.put(mod, i);
}
}
return false;
}
}

View File

@ -0,0 +1,89 @@
package substring;
import java.util.*;
/**
* 题目239. 滑动窗口最大值 (maxSlidingWindow)
* 描述给你一个整数数组 nums有一个大小为 k 的滑动窗口从数组的最左侧移动到数组的最右侧你只可以看到在滑动窗口内的 k 个数字滑动窗口每次只向右移动一位
* 返回 滑动窗口中的最大值
*
* 链接https://leetcode.cn/problems/sliding-window-maximum/
*
示例 1
输入nums = [1,3,-1,-3,5,3,6,7], k = 3
输出[3,3,5,5,6,7]
解释
滑动窗口的位置 最大值
--------------- -----
[1 3 -1] -3 5 3 6 7 3
1 [3 -1 -3] 5 3 6 7 3
1 3 [-1 -3 5] 3 6 7 5
1 3 -1 [-3 5 3] 6 7 5
1 3 -1 -3 [5 3 6] 7 6
1 3 -1 -3 5 [3 6 7] 7
示例 2
输入nums = [1], k = 1
输出[1]
*/
//普通方法超时 需重做
public class MaxSlidingWindow {
//优先队列
public int[] maxSlidingWindow1(int[] nums, int k) {
int n = nums.length;
PriorityQueue<int[]> pq = new PriorityQueue<int[]>(new Comparator<int[]>() {
public int compare(int[] pair1, int[] pair2) {
return pair1[0] != pair2[0] ? pair2[0] - pair1[0] : pair2[1] - pair1[1];
}
});
for (int i = 0; i < k; ++i) {
pq.offer(new int[]{nums[i], i});
}
int[] ans = new int[n - k + 1];
ans[0] = pq.peek()[0];
for (int i = k; i < n; ++i) {
pq.offer(new int[]{nums[i], i});
while (pq.peek()[1] <= i - k) { //如果堆顶元素不在滑动窗口内部就移除 非堆顶元素不管
pq.poll();
}
ans[i - k + 1] = pq.peek()[0];
}
return ans;
}
//单调队列
/**
* 维护一个单调队列
在这个滑动窗口最大值的算法中双端队列的两端各自承担不同的职责
队头Deque Front
队头始终保存当前窗口内最大元素的索引由于我们维护队列中元素对应的数值是单调递减的所以队头的元素必定是最大的当窗口滑动时如果队头的索引超出窗口范围会将其移除以保证队头始终代表当前窗口的最大值
队尾Deque Rear
队尾用于存放候选的元素索引它们按照数值从大到小的顺序排列在加入新元素前会从队尾移除所有比新元素小的元素索引因为这些较小的元素在未来的窗口中不可能成为最大值这样既保证了队列的单调递减性质也为未来的窗口维护了正确的候选顺序
*/
public int[] maxSlidingWindow(int[] nums, int k) {
int n = nums.length;
Deque<Integer> deque = new LinkedList<>();
for (int i = 0; i < k; ++i) {
while (!deque.isEmpty() && nums[i] >= nums[deque.peekLast()]) {
deque.pollLast();
}
deque.offerLast(i);
}
int[] ans = new int[n - k + 1];
ans[0] = nums[deque.peekFirst()];
for (int i = k; i < n; ++i) {
while (!deque.isEmpty() && nums[i] >= nums[deque.peekLast()]) { //队尾元素本来就比队头小如果新元素比队尾还大那就没有保留的价值了
deque.pollLast();
}
deque.offerLast(i);
while (deque.peekFirst() <= i - k) {
deque.pollFirst();
}
ans[i - k + 1] = nums[deque.peekFirst()];
}
return ans;
}
}

View File

@ -0,0 +1,61 @@
package substring;
import java.util.HashMap;
/**
* 题目560. 和为 K 的子数组 (subarraySum)
* 描述给你一个整数数组 nums 和一个整数 k 请你统计并返回 该数组中和为 k 的子数组的个数
* 子数组是数组中元素的连续非空序列
* 链接https://leetcode.cn/problems/subarray-sum-equals-k/
*
示例 1
输入nums = [1,1,1], k = 2
输出2
示例 2
输入nums = [1,2,3], k = 3
输出2
*/
//只会暴力解
public class SubarraySum {
//枚举
public int subarraySum(int[] nums, int k) {
int count = 0;
int len = nums.length;
for (int left = 0; left < len; left++) {
int sum = 0;
// 区间里可能会有一些互相抵销的元素
for (int right = left; right < len; right++) {
sum += nums[right];
if (sum == k) {
count++;
}
}
}
return count;
}
/**前缀和
* pre[i]代表 [0,i]内所有元素的和pre[j]代表 [0,j]内所有元素之和那么子串[i,j]之和为a[j]-a[i-1]
*/
public int subarraySum1(int[] nums, int k) {
int count = 0; // 用于累计满足条件的子数组个数
int prefixSum = 0; // 当前前缀和
// 哈希表用于存储前缀和出现的次数初始化时前缀和为 0 出现 1
HashMap<Integer, Integer> map = new HashMap<>();
map.put(0, 1);
// 遍历数组
for (int num : nums) {
prefixSum += num;
// 如果存在前缀和为 prefixSum - k 的情况
// 则从那个位置到当前下标的子数组和为 k
if (map.containsKey(prefixSum - k)) {
count += map.get(prefixSum - k);
}
// 更新哈希表将当前的前缀和计数加 1
map.put(prefixSum, map.getOrDefault(prefixSum, 0) + 1);
}
return count;
}
}

View File

@ -0,0 +1,16 @@
package array;
import org.junit.Test;
import static org.junit.Assert.*;
public class MaxSubArrayTest {
@Test
public void maxSubArray() {
MaxSubArray solution = new MaxSubArray();
int[] nums={-2,1,-3,4,-1,2,1,-5,4};
int res=solution.maxSubArray(nums);
System.out.println(res);
}
}

View File

@ -0,0 +1,16 @@
package array;
import org.junit.Test;
import java.util.Arrays;
public class MergeTest {
@Test
public void testmerge(){
int[][] intervals = {{1,3},{2,6},{8,10},{15,18}};
Merge solution = new Merge();
int[][] res =solution.merge(intervals);
System.out.println(Arrays.deepToString(res));
}
}

View File

@ -0,0 +1,19 @@
package array;
import org.junit.Test;
import java.util.Arrays;
import static org.junit.Assert.*;
public class RotateArrayTest {
@Test
public void rotateArray() {
int[]nums = {1,2,3,4,5,6,7};
int k = 2;
RotateArray solution = new RotateArray();
solution.rotateArray(nums,k);
System.out.println(Arrays.toString(nums));
}
}

View File

@ -0,0 +1,17 @@
package substring;
import org.junit.Test;
import static org.junit.Assert.*;
public class CheckSubarraySumTest {
@Test
public void checkSubarraySum() {
int[] nums={5,0,0,0};
int k=3;
CheckSubarraySum solution = new CheckSubarraySum();
boolean res=solution.checkSubarraySum(nums,k);
System.out.println(res);
}
}

View File

@ -0,0 +1,20 @@
package substring;
import org.junit.Test;
import java.util.Arrays;
import static org.junit.Assert.*;
public class MaxSlidingWindowTest {
@Test
public void maxSlidingWindow() {
MaxSlidingWindow solution = new MaxSlidingWindow();
int[] num={1,3,-1,-3,5,3,6,7};
int k=3;
int[] res=new int[num.length-k+1];
res=solution.maxSlidingWindow(num,3);
System.out.println(Arrays.toString(res));
}
}

View File

@ -0,0 +1,26 @@
package substring;
import org.junit.Test;
import static org.junit.Assert.*;
public class SubarraySumTest {
@Test
public void subarraySum() {
int[] nums={1,1,1};
int k=2;
SubarraySum solution = new SubarraySum();
int cnt=solution.subarraySum(nums,k);
System.out.println(cnt);
}
@Test
public void subarraySum1() {
int[] nums={1,1,1};
int k=2;
SubarraySum solution = new SubarraySum();
int cnt=solution.subarraySum1(nums,k);
System.out.println(cnt);
}
}