9.10 复习+一些juc

This commit is contained in:
zhangsan 2025-09-10 20:57:03 +08:00
parent 8b91ec0731
commit 585a2321f3
20 changed files with 559 additions and 55 deletions

3
.gitignore vendored
View File

@ -33,4 +33,5 @@ Thumbs.db
# 忽略其他临时文件
*.tmp
*.bak
*.swp
*.swp
/src/main/java/大厂真题/

View File

@ -6,14 +6,12 @@ import java.util.Arrays;
* 题目 674. 最长连续递增序列 (MaxProduct)
* 描述给定一个未经排序的整数数组找到最长且 连续递增的子序列并返回该序列的长度
* 连续递增的子序列 可以由两个下标 l rl < r确定如果对于每个 l <= i < r都有 nums[i] < nums[i + 1] 那么子序列 [nums[l], nums[l + 1], ..., nums[r - 1], nums[r]] 就是连续递增子序列
* 示例 1
输入nums = [1,3,5,4,7]
输出3
解释最长连续递增序列是 [1,3,5], 长度为3
尽管 [1,3,5,7] 也是升序的子序列, 但它不是连续的因为 5 7 在原数组里被 4 隔开
* 链接https://leetcode.cn/problems/longest-continuous-increasing-subsequence/
* 链接<a href="https://leetcode.cn/problems/longest-continuous-increasing-subsequence/">...</a>
*/
//二刷会做
public class FindLengthOfLCIS {

View File

@ -0,0 +1,45 @@
package dynamic_programming;
/**
* 题目931. 下降路径最小和 (minFallingPathSum)
* 描述给你一个 n x n 方形 整数数组 matrix 请你找出并返回通过 matrix 的下降路径 最小和
*
* 下降路径 可以从第一行中的任何元素开始并从每一行中选择一个元素在下一行选择的元素和当前行所选元素最多相隔一列即位于正下方或者沿对角线向左或者向右的第一个元素
* 具体来说位置 (row, col) 的下一个元素应当是 (row + 1, col - 1)(row + 1, col) 或者 (row + 1, col + 1)
*
* 链接https://leetcode.cn/problems/minimum-falling-path-sum/
*
输入matrix = [[2,1,3],[6,5,4],[7,8,9]]
输出13
解释如图所示为和最小的两条下降路径
*/
public class MinFallingPathSum {
public int minFallingPathSum(int[][] matrix) {
int minPath=Integer.MAX_VALUE;
int row=matrix.length,col=matrix[0].length;
int[][]dp=new int[row][col];
for (int i = 0; i < row; i++) {
for (int j = 0; j < col; j++) {
dp[i][j]=Integer.MAX_VALUE;
}
}
for (int i = 0; i < row; i++) {
for (int j = 0; j < col; j++) {
if(i==0) {
dp[0][j] = matrix[0][j];
}else {
if(j-1>=0)
dp[i][j]=Math.min(dp[i][j],dp[i-1][j-1]);
if(j+1<col)
dp[i][j]=Math.min(dp[i][j],dp[i-1][j+1]);
dp[i][j]=Math.min(dp[i][j],dp[i-1][j])+matrix[i][j];
}
}
}
for (int i = 0; i < col; i++) {
minPath=Math.min(dp[row-1][i],minPath);
}
return minPath;
}
}

View File

@ -1,9 +1,6 @@
package graph;
import java.util.ArrayList;
import java.util.LinkedList;
import java.util.List;
import java.util.Queue;
import java.util.*;
/**
* 题目 207. 课程表 (canFinish)
@ -40,7 +37,6 @@ public class CanFinish {
List<List<Integer>> graph = new ArrayList<>();
// 记录每个课程的入度即需要先修课程的数量
int[] indegree = new int[numCourses];
// 初始化图
for (int i = 0; i < numCourses; i++) {
graph.add(new ArrayList<>());

View File

@ -0,0 +1,76 @@
package graph;
/**
* 题目329. 矩阵中的最长递增路径 (longestIncreasingPath)
* 描述给定一个 m x n 整数矩阵 matrix 找出其中 最长递增路径 的长度
* 对于每个单元格你可以往上右四个方向移动 不能 对角线 方向上移动或移动到 边界外即不允许环绕
*
* 链接https://leetcode.cn/problems/longest-increasing-path-in-a-matrix/
*
输入matrix = [[9,9,4],[6,6,8],[2,1,1]]
输出4
解释最长递增路径为 [1, 2, 6, 9]
*/
public class LongestIncreasingPath {
// 右四个方向
public int[][] dirs = {{-1, 0}, {1, 0}, {0, -1}, {0, 1}};
public int rows, columns;
/**
* 主函数计算矩阵中的最长递增路径
*/
public int longestIncreasingPath(int[][] matrix) {
// 边界条件空矩阵直接返回 0
if (matrix == null || matrix.length == 0 || matrix[0].length == 0) {
return 0;
}
rows = matrix.length;
columns = matrix[0].length;
// 记忆化数组memo[i][j] 表示从 (i,j) 出发的最长递增路径长度
int[][] memo = new int[rows][columns];
int ans = 0;
// 遍历所有格子分别作为起点取最大值
for (int i = 0; i < rows; ++i) {
for (int j = 0; j < columns; ++j) {
ans = Math.max(ans, dfs(matrix, i, j, memo));
}
}
return ans;
}
/**
* 深度优先搜索 + 记忆化
* @param row 当前行
* @param column 当前列
* @return (row, column) 出发的最长递增路径长度
*/
public int dfs(int[][] matrix, int row, int column, int[][] memo) {
// 如果已经计算过直接返回
if (memo[row][column] != 0) {
return memo[row][column];
}
// 默认长度至少为 1当前格子本身
memo[row][column] = 1;
// 向四个方向探索
for (int[] dir : dirs) {
int newRow = row + dir[0], newColumn = column + dir[1];
// 检查边界且必须是严格递增
if (newRow >= 0 && newRow < rows &&
newColumn >= 0 && newColumn < columns &&
matrix[newRow][newColumn] > matrix[row][column]) {
// 更新最大路径长度
memo[row][column] = Math.max(
memo[row][column],
dfs(matrix, newRow, newColumn, memo) + 1
);
}
}
return memo[row][column];
}
}

View File

@ -0,0 +1,38 @@
package juc;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.LinkedBlockingQueue;
public class BlockingqueueTest {
private static final BlockingQueue<String> queue = new LinkedBlockingQueue<>();
public static void main(String[] args) {
// 生产者线程
Thread producer = new Thread(() -> {
try {
for (int i = 1; i <= 5; i++) {
String msg = "Message-" + i;
queue.put(msg); // 放入队列
System.out.println("Produced: " + msg);
Thread.sleep(300);
}
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
});
// 消费者线程
Thread consumer = new Thread(() -> {
try {
while (true) {
String msg = queue.take(); // 从队列取出
System.out.println("Consumed: " + msg);
}
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
});
producer.start();
consumer.start();
}
}

View File

@ -0,0 +1,18 @@
package juc;
import java.util.concurrent.CountDownLatch;
public class Countdownlatch {
public static void main(String[] args) throws InterruptedException {
CountDownLatch latch=new CountDownLatch(3);
Runnable runnable=()->{
System.out.println("hello");
latch.countDown();
};
for (int i = 0; i < 3; i++) {
new Thread(runnable,"thread"+i+":").start();
}
latch.await();
System.out.println("main");
}
}

View File

@ -0,0 +1,24 @@
package juc;
import java.util.concurrent.Semaphore;
public class SemaphoreTest {
static Semaphore semaphore=new Semaphore(3);
public static void main(String[] args) {
Runnable task1=()->{
try {
semaphore.acquire();
System.out.println("get");
Thread.sleep(1000);
} catch (InterruptedException e) {
throw new RuntimeException(e);
} finally {
semaphore.release();
}
};
for (int i = 0; i < 5; i++) {
new Thread(task1).start();
}
}
}

View File

@ -0,0 +1,42 @@
package juc;
public class test {
public static final Object lock=new Object();
public static int target=100;
private static int count = 1;
public static void main(String[] args) {
new Thread(()->{
while (count<target){
synchronized (lock){
if(count%2==0) {
System.out.println("thread1:" + count++);
lock.notify();
}else {
try {
lock.wait();
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}
}
}
},"thread1").start();
new Thread(()->{
while (count<target){
synchronized (lock){
if(count%2==1) {
System.out.println("thread2:" + count++);
lock.notify();
}else {
try {
lock.wait();
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}
}
}
},"thread2").start();
}
}

View File

@ -13,7 +13,6 @@ import java.util.List;
//二刷会做
public class IsPalindrome {
public boolean isPalindrome1(ListNode head) {
ListNode temp=head;
List<Integer>list=new ArrayList<>();
while (head!=null){
list.add(head.val);

View File

@ -19,49 +19,32 @@ package linkedlist;
*/
//二刷不会
public class MergeKLists {
//两两合并
public ListNode mergeKLists2(ListNode[] lists) {
ListNode ans = null;
for (int i = 0; i < lists.length; ++i) {
ans = mergeTwoLists(ans, lists[i]);
}
return ans;
}
public ListNode mergeTwoLists(ListNode a, ListNode b) {
if (a == null || b == null) {
return a != null ? a : b;
ListNode dummy = new ListNode(-1), p = dummy;
while (a != null && b != null) {
if (a.val <= b.val) { p.next = a; a = a.next; }
else { p.next = b; b = b.next; }
p = p.next;
}
ListNode head = new ListNode(0);
ListNode tail = head, aPtr = a, bPtr = b;
while (aPtr != null && bPtr != null) {
if (aPtr.val < bPtr.val) {
tail.next = aPtr;
aPtr = aPtr.next;
} else {
tail.next = bPtr;
bPtr = bPtr.next;
}
tail = tail.next;
}
tail.next = (aPtr != null ? aPtr : bPtr);
return head.next;
p.next = (a != null ? a : b);
return dummy.next;
}
//分治合并
// 分治合并入口
public ListNode mergeKLists(ListNode[] lists) {
return merge(lists, 0, lists.length - 1);
return mergeRange(lists, 0, lists.length - 1);
}
public ListNode merge(ListNode[] lists, int l, int r) {
if (l == r) {
return lists[l];
}
if (l > r) {
return null;
}
// 分治合并区间 [l, r]
private ListNode mergeRange(ListNode[] lists, int l, int r) {
if (l == r) return lists[l];
if (l > r) return null;
int mid = (l + r) >> 1;
return mergeTwoLists(merge(lists, l, mid), merge(lists, mid + 1, r));
return mergeTwoLists(
mergeRange(lists, l, mid),
mergeRange(lists, mid + 1, r)
);
}
}

View File

@ -0,0 +1,50 @@
package linkedlist;
/**
* 题目206. 反转链表 (reorderList)
* 描述给定一个单链表 L 的头节点 head 单链表 L 表示为
* L0 L1 Ln - 1 Ln
* 请将其重新排列后变为
* L0 Ln L1 Ln - 1 L2 Ln - 2
* 不能只是单纯的改变节点内部的值而是需要实际的进行节点交换
* 链接https://leetcode.cn/problems/reorder-list/
*/
public class ReorderList {
ListNode findMid(ListNode node){
if (node == null || node.next == null) return node;
ListNode slow=node,fast=node.next;
while (fast!=null && fast.next!=null){
slow=slow.next;
fast=fast.next.next;
}
return slow;
}
ListNode reverse(ListNode node){
ListNode pre=null;
ListNode cur=node;
while (cur!=null){
ListNode next=cur.next;
cur.next=pre;
pre=cur;
cur=next;
}
return pre;
}
public void reorderList(ListNode head) {
ListNode midNode=findMid(head);
ListNode head2=reverse(midNode.next);
midNode.next = null;
while (head2!=null){
ListNode nx1=head.next;
ListNode nx2=head2.next;
head.next=head2;
head2.next=nx1;
head=nx1;
head2=nx2;
}
}
}

View File

@ -1,13 +1,17 @@
package linkedlist;
/**
* 题目 82. 删除排序链表中的重复元素 II (deleteDuplicates)
* 描述给定一个已排序的链表的头 head 删除原始链表中所有重复数字的节点只留下不同的数字 返回 已排序的链表
* 题目 25. K 个一组翻转链表 (ReverseKGroup)
* 描述给你链表的头节点 head k 个节点一组进行翻转请你返回修改后的链表
*
* k 是一个正整数它的值小于或等于链表的长度如果节点总数不是 k 的整数倍那么请将最后剩余的节点保持原有顺序
*
* 你不能只是单纯的改变节点内部的值而是需要实际进行节点交换
示例 2
输入head = [1,2,3,3,4,4,5]
输出[1,2,5]
输入head = [1,2,3,4,5], k = 2
输出[2,1,4,3,5]
* 链接https://leetcode.cn/problems/remove-duplicates-from-sorted-list-ii/
* 链接https://leetcode.cn/problems/reverse-nodes-in-k-group/
*/
public class ReverseKGroup {
// 反转 [head, tail] 这一段返回新的头和新的尾

View File

@ -0,0 +1,11 @@
package sort;
public class ListNode {
int val;
ListNode next;
ListNode(int x) {
val = x;
next = null;
}
ListNode(int val, ListNode next) { this.val = val; this.next = next; }
}

View File

@ -0,0 +1,133 @@
package sort;
import java.util.Random;
public class Test {
public void quicksort(int[] arr,int left,int right){
if(left<right) {
int pos = partition(arr, left, right);
quicksort(arr,left,pos-1);
quicksort(arr,pos+1,right);
}
}
int partition(int[] arr,int left,int right){
Random random=new Random();
int start=random.nextInt(right-left+1)+left;
int temp=arr[left];
arr[left]=arr[start];
arr[start]=temp;
int pivot=arr[left];
int l=left,r=right;
while (l<r){
while (l<r && arr[r]>=pivot) {
r--;
}
arr[l]=arr[r];
while (l<r && arr[l]<=pivot)
l++;
arr[r]=arr[l];
}
arr[l]=pivot;
return l;
}
public int quickselect(int[] arr,int left,int right,int target){
if(left<=right){
int pos=partition(arr,left,right);
if(target==pos) return arr[pos];
else if(target>pos)
return quickselect(arr,pos+1,right,target);
else
return quickselect(arr,left,pos-1,target);
}
return -1;
}
void MergeSort(int[]arr){
mergeSort(arr,0,arr.length-1);
}
public void mergeSort(int[] arr,int left,int right){
if(left<right){
int mid=(left+right)/2;
mergeSort(arr,left,mid);
mergeSort(arr,mid+1,right);
merge(arr,left,mid,right);
}
}
public void merge(int[]arr,int left,int mid,int right){
int idx1=left;
int idx2=mid+1;
int[] temp=new int[right-left+1];
int idx=0;
while (idx1<=mid && idx2<=right){
if(arr[idx1]<=arr[idx2]) {
temp[idx++] = arr[idx1];
idx1++;
}
else {
temp[idx++]=arr[idx2];
idx2++;
}
}
while(idx1<=mid) {
temp[idx++] = arr[idx1];
idx1++;
}
while(idx2<=right) {
temp[idx++] = arr[idx2];
idx2++;
}
for (int i = 0; i < temp.length; i++) {
arr[left + i] = temp[i];
}
}
void bubble(int[]arr){
for (int i = 0; i < arr.length-1; i++) {
boolean flag=false;
for (int j = arr.length-1; j > i; j--) {
if(arr[j]<arr[j-1]){
flag=true;
int temp=arr[j];
arr[j]=arr[j-1];
arr[j-1]=temp;
}
}
if(!flag)return;
}
}
ListNode findMidNode(ListNode head){
ListNode fast=head.next,slow=head;
while (fast!=null &&fast.next!=null){
fast=fast.next.next;
slow=slow.next;
}
return slow;
}
ListNode linkMergeSort(ListNode head) {
if (head == null || head.next == null) return head;
ListNode mid = findMidNode(head);
ListNode head2 = mid.next;
mid.next = null;
ListNode left = linkMergeSort(head);
ListNode right = linkMergeSort(head2);
return mergeLink(left, right);
}
ListNode mergeLink(ListNode l1, ListNode l2) {
ListNode dummy = new ListNode(-1), cur = dummy;
while (l1 != null && l2 != null) {
if (l1.val <= l2.val) {
cur.next = l1;
l1 = l1.next;
} else {
cur.next = l2;
l2 = l2.next;
}
cur = cur.next;
}
cur.next = (l1 != null ? l1 : l2);
return dummy.next;
}
}

View File

@ -27,9 +27,37 @@ import java.util.*;
输入nums = [1], k = 1
输出[1]
*/
//本题求的是每个滑动窗口中最大的数字
//普通方法超时 需重做
//二刷也没想到低复杂度方法
public class MaxSlidingWindow {
/**
* 普通法 暴力求解
*/
public int[] maxSlidingWindowBruteForce(int[] nums, int k) {
if (nums == null || nums.length == 0 || k <= 0) {
return new int[0];
}
int n = nums.length;
int[] ans = new int[n - k + 1];
for (int i = 0; i <= n - k; i++) {
int maxVal = nums[i];
// 遍历当前窗口 [i, i+k-1]
for (int j = i; j < i + k; j++) {
if (nums[j] > maxVal) {
maxVal = nums[j];
}
}
ans[i] = maxVal;
}
return ans;
}
/**
* 思路说明用优先队列大顶堆维护滑动窗口中的最大值
* 滑动窗口长度为 k我们每次都想拿到窗口内的最大值
@ -81,10 +109,12 @@ public class MaxSlidingWindow {
* 维护一个单调队列
在这个滑动窗口最大值的算法中双端队列的两端各自承担不同的职责
队头Deque Front
队头始终保存当前窗口内最大元素的索引由于我们维护队列中元素对应的数值是单调递减的所以队头的元素必定是最大的当窗口滑动时如果队头的索引超出窗口范围会将其移除以保证队头始终代表当前窗口的最大值
队头始终保存当前窗口内最大元素的索引由于我们维护队列中元素对应的数值是单调递减的所以队头的元素必定是最大的
当窗口滑动时如果队头的索引超出窗口范围会将其移除以保证队头始终代表当前窗口的最大值
队尾Deque Rear
队尾用于存放候选的元素索引它们按照数值从大到小的顺序排列在加入新元素前会从队尾移除所有比新元素小的元素索引因为这些较小的元素在未来的窗口中不可能成为最大值这样既保证了队列的单调递减性质也为未来的窗口维护了正确的候选顺序
队尾用于存放候选的元素索引它们按照数值从大到小的顺序排列在加入新元素前会从队尾移除所有比新元素小的元素索引
因为这些较小的元素在未来的窗口中不可能成为最大值这样既保证了队列的单调递减性质也为未来的窗口维护了正确的候选顺序
*/
public int[] maxSlidingWindow(int[] nums, int k) {
int n = nums.length;
@ -117,7 +147,7 @@ public class MaxSlidingWindow {
deque.offerLast(i);
// 如果队头的下标已经滑出窗口左边就移除它
while (deque.peekFirst() <= i - k) {
while (!deque.isEmpty() && deque.peekFirst() <= i - k) {
deque.pollFirst();
}

View File

@ -40,7 +40,7 @@ public class Flatten {
if (root == null) {
return;
}
Deque<TreeNode> stack = new LinkedList<TreeNode>();
Deque<TreeNode> stack = new LinkedList<>();
stack.push(root);
TreeNode prev = null;
while (!stack.isEmpty()) {

View File

@ -42,7 +42,7 @@ public class PathSum {
return 0;
}
int ret = 0;
int ret;
// 更新当前累计前缀和加上当前节点的值
curr += root.val;

View File

@ -5,7 +5,8 @@ package 实现类功能;
* 实现 LRUCache
* LRUCache(int capacity) 正整数 作为容量 capacity 初始化 LRU 缓存
* int get(int key) 如果关键字 key 存在于缓存中则返回关键字的值否则返回 -1
* void put(int key, int value) 如果关键字 key 已经存在则变更其数据值 value 如果不存在则向缓存中插入该组 key-value 如果插入操作导致关键字数量超过 capacity 则应该 逐出 最久未使用的关键字
* void put(int key, int value) 如果关键字 key 已经存在则变更其数据值 value 如果不存在则向缓存中插入该组 key-value
* 如果插入操作导致关键字数量超过 capacity 则应该 逐出 最久未使用的关键字
* 函数 get put 必须以 O(1) 的平均时间复杂度运行
示例 1

View File

@ -0,0 +1,55 @@
package 实现类功能;
/*
数组table核心结构存储键值对Entry的桶bucket
链表当多个键的哈希值落在同一个桶中时会形成一个链表
*/
class MyHashMap<K, V> {
static class Node<K, V> {
K key;
V value;
Node<K, V> next;
Node(K k, V v) {
key = k;
value = v;
}
}
private int SIZE = 16;
private Node<K, V>[] table = new Node[SIZE];
public void put(K key, V value) {
int index = key.hashCode() % SIZE;
Node<K, V> newNode = new Node<>(key, value);
Node<K, V> curr = table[index];
if (curr == null) {
table[index] = newNode;
} else {
while (true) {
if (curr.key.equals(key)) {
curr.value = value;
return;
}
if (curr.next == null) {
curr.next = newNode;
return;
}
curr = curr.next;
}
}
}
public V get(K key) {
int index = key.hashCode() % SIZE;
Node<K, V> curr = table[index];
while (curr != null) {
if (curr.key.equals(key)) {
return curr.value;
}
curr = curr.next;
}
return null;
}
}