(算法题)Day-20230228
1. 滑动窗口
剑指 Offer 30. 包含min函数的栈
/**
* 剑指 Offer 30. 包含min函数的栈
*/
class minStack {
Stack<Integer> A, B;
public minStack() {
A = new Stack<>();
B = new Stack<>();
}
public void push(int x) {
A.add(x);
if (B.empty() || B.peek() >= x) {
B.add(x);
}
}
public void pop() {
if (A.pop().equals(B.peek())) {
B.pop();
}
}
public int top() {
return A.peek();
}
public int min() {
return B.peek();
}
}
滑动窗口的最大值
思路:先得到窗口内数据的单调队列D,这样就可以将D中第一个数作为窗口内的最大值 放入结果数组中;然后迭代,对即将新加入的一个数n进行比较,删除D中比n小的数,得到结果数组
package hhh.slide_window;
import java.util.Deque;
import java.util.LinkedList;
import java.util.Stack;
/**
* @author hdbing
* @create 2023-02-28 15:06
*/
public class SW01 {
public static void main(String[] args) {
int[] nums = {1, 3, -1, -3, 5, 3, 6, 7};
int k = 3;
int[] res = maxSlidingWindow(nums, k);
for (int i : res) {
System.out.println(i);
}
}
/**
* 剑指 Offer 59 - I. 滑动窗口的最大值(单调队列,清晰图解)
* https://leetcode.cn/problems/hua-dong-chuang-kou-de-zui-da-zhi-lcof/solution/mian-shi-ti-59-i-hua-dong-chuang-kou-de-zui-da-1-6/
* @param nums
* @param k
* @return
*/
static int[] maxSlidingWindow(int[] nums, int k) {
if (nums.length == 0 || k == 0) return new int[0];
Deque<Integer> deque = new LinkedList<>();
int[] res = new int[nums.length - k + 1];
for (int j = 0, i = 1 - k; j < nums.length; i++, j++) {
// 删除 deque 中对应的 nums[i-1],避免重复添加当前最大值;但是结果其实都是一致的
if (i > 0 && deque.peekFirst() == nums[i - 1]) {
deque.removeFirst();
}
// 保持 deque 递减
while (!deque.isEmpty() && deque.peekLast() < nums[j]) {
deque.removeLast();
}
deque.addLast(nums[j]);
// 记录窗口最大值
if (i >= 0) {
res[i] = deque.peekFirst();
}
}
return res;
}
}
剑指 Offer 48. 最长不含重复字符的子字符串
动态规划+哈希表
如果s="abcabcbb",\(s[i]\) 表示离 \(s[j]\) 最近的一个重复字符,如 j=3,则 i=0;
然后状态定义为:\(dp[j]\) 表示以 \(s[j]\) 为结尾的 “最长不重复子字符串” 的长度,如 \(dp[2]=3\),即子串 “abc”
这样就可以通过子串是否出现在 i 和 j 之间来定义状态转移方程,这里需要注意边界,在i 和 j之间则 \(d[j]=d[j-1]+1\),否则为 \(j-i\)
最后的问题就是如何计算索引 \(i\) ?采用哈希表在迭代时保存各个字符的索引,然后在需要时去寻找 \(i\) 即可
class Solution {
public int lengthOfLongestSubstring(String s) {
Map<Character, Integer> dic = new HashMap<>();
int res = 0, tmp = 0;
for(int j = 0; j < s.length(); j++) {
int i = dic.getOrDefault(s.charAt(j), -1); // 获取索引 i
dic.put(s.charAt(j), j); // 更新哈希表
tmp = tmp < j - i ? tmp + 1 : j - i; // dp[j - 1] -> dp[j]
res = Math.max(res, tmp); // max(dp[j - 1], dp[j])
}
return res;
}
}
2. 数组
合并两个有序数组
思路:从后往前赋值,然后迭代次数为 m+n
package hhh.array;
/**
* @author hdbing
* @create 2023-02-28 16:49
*/
public class a05 {
public static void main(String[] args) {
int[] nums1 = {1, 2, 3, 0, 0, 0};
int m = 3;
int[] nums2 = {2, 5, 6};
int n = 3;
mergeTwoArray(nums1, m, nums2, n);
for (int i : nums1) {
System.out.println(i);
}
}
static void mergeTwoArray(int[] nums1, int m, int[] nums2, int n) {
int i = m - 1;
int j = n - 1;
for (int k = m + n - 1; k >= 0; k--) {
if (j < 0 || (i >= 0 && nums1[i] >= nums2[j])) {
nums1[k] = nums1[i];
i--;
} else {
nums1[k] = nums2[j];
j--;
}
}
}
}
岛屿的最大面积
思路:遍历然后 由深度优先搜索 迭代得到最大面积
package hhh.array;
/**
* @author hdbing 岛屿的最大面积
* @create 2023-02-28 18:46
*/
public class a06 {
public static void main(String[] args) {
int[][] grid = {{0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0},
{0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0},
{0, 1, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0},
{0, 1, 0, 0, 1, 1, 0, 0, 1, 0, 1, 0, 0},
{0, 1, 0, 0, 1, 1, 0, 0, 1, 1, 1, 0, 0},
{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0},
{0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0},
{0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0}};
int area = maxAreaOfIsland(grid);
System.out.println(area);
}
static int maxAreaOfIsland(int[][] grid) {
int ans = 0;
for (int i = 0; i < grid.length; i++) {
for (int j = 0; j < grid[0].length; j++) {
ans = Math.max(ans, dfs(grid, i, j));
}
}
return ans;
}
static int dfs(int[][] grid, int cur_i, int cur_j) {
if (cur_i < 0 || cur_j < 0 || cur_i == grid.length || cur_j == grid[0].length || (grid[cur_i][cur_j] != 1)) {
return 0;
}
// 将岛屿置为 0 避免重复遍历
grid[cur_i][cur_j] = 0;
// 上下左右 四个方向
int[] di = {0, 0, 1, -1};
int[] dj = {1, -1, 0, 0};
int ans = 1;
for (int index = 0; index < 4; index++) {
int nex_i = cur_i + di[index], next_j = cur_j + dj[index];
ans += dfs(grid, nex_i, next_j); // dfs遍历
}
return ans;
}
}
移除元素
思路:快慢指针,不是目标时 同时加1,遇见目标时只需要快指针加1
package hhh.array;
/**
* @author hdbing
* @create 2022-10-04 18:45
*/
public class a03 {
public static void main(String[] args) {
int[] nums = {1};
Solution s=new Solution();
System.out.println(s.removeElement(nums,1));
}
}
class Solution {
public int removeElement(int[] nums, int val) {
int fastIdx = 0;
int slowIdx = 0;
for(int i=0; i<nums.length; i++){
if (nums[i]!=val){
fastIdx+=1;
slowIdx+=1;
}else{
fastIdx+=1;
}
nums[slowIdx]=nums[fastIdx];
if (fastIdx==nums.length-1){
return slowIdx;
}
}
return nums.length;
}
}
有序数组的平方
思路:快慢指针,一个从头开始,一个从尾开始,然后比较大小 移动指针
class Solution {
public int[] sortedSquares(int[] nums) {
int right = nums.length - 1;
int left = 0;
int[] result = new int[nums.length];
int index = result.length - 1;
while (left <= right) {
if (nums[left] * nums[left] > nums[right] * nums[right]) {
// 正数的相对位置是不变的, 需要调整的是负数平方后的相对位置
result[index--] = nums[left] * nums[left];
++left;
} else {
result[index--] = nums[right] * nums[right];
--right;
}
}
return result;
}
}
长度最小的子数组
思路:快慢指针,不满足条件移动快指针,满足条件就移动慢指针
class Solution {
// 滑动窗口
public int minSubArrayLen(int s, int[] nums) {
int left = 0;
int sum = 0;
int result = Integer.MAX_VALUE;
for (int right = 0; right < nums.length; right++) {
sum += nums[right];
while (sum >= s) {
result = Math.min(result, right - left + 1);
sum -= nums[left++];
}
}
return result == Integer.MAX_VALUE ? 0 : result;
}
}

浙公网安备 33010602011771号