博客记录-day145-力扣+编程题

一、力扣

1、缺失的第一个正数

41. 缺失的第一个正数

java 复制代码
class Solution {
    public int firstMissingPositive(int[] nums) {
        int n=nums.length;
        for(int i=0;i<n;i++){
            if(nums[i]<=0){
                nums[i]=n+1;
            }
        }
        for(int i=0;i<n;i++){
            int target=Math.abs(nums[i]);
            if(target<=n){
                nums[target-1]=-Math.abs(nums[target-1]);
            }
        }
        for(int i=0;i<n;i++){
            if(nums[i]>0){
                return i+1;
            }
        }
        return n+1;
    }
}

2、最小覆盖子串

76. 最小覆盖子串

java 复制代码
class Solution {
    public String minWindow(String s, String t) {
        int[] target=new int[128];
        for(var e:t.toCharArray()){
            target[e]++;
        }
        int[] count=new int[128];
        int left=0,right=0;
        int n=s.length();
        int resleft=0,resright=Integer.MAX_VALUE/2;
        while(right<n){
            count[s.charAt(right)]++;
            while(check(count,target)){
                if(right-left<resright-resleft){
                    resright=right;
                    resleft=left;
                }
                count[s.charAt(left++)]--;
            }
            right++;
        }
        return resright==Integer.MAX_VALUE/2?"":s.substring(resleft,resright+1);
    }
    public boolean check(int[] count,int[] target){
        for(int i=0;i<128;i++){
            if(count[i]<target[i]){
                return false;
            }
        }
        return true;
    }
}

3、字符串相乘

43. 字符串相乘

java 复制代码
class Solution {
    public String multiply(String num1, String num2) {
        if (num1.equals("0") || num2.equals("0")) {
            return "0";
        }
        String ans = "0";
        int m = num1.length(), n = num2.length();
        for (int i = n - 1; i >= 0; i--) {
            StringBuffer curr = new StringBuffer();
            int add = 0;
            for (int j = n - 1; j > i; j--) {
                curr.append(0);
            }
            int y = num2.charAt(i) - '0';
            for (int j = m - 1; j >= 0; j--) {
                int x = num1.charAt(j) - '0';
                int product = x * y + add;
                curr.append(product % 10);
                add = product / 10;
            }
            if (add != 0) {
                curr.append(add);
            }
            ans = addStrings(ans, curr.reverse().toString());
        }
        return ans;
    }

    public String addStrings(String num1, String num2) {
        int i = num1.length() - 1, j = num2.length() - 1, add = 0;
        StringBuffer ans = new StringBuffer();
        while (i >= 0 || j >= 0 || add != 0) {
            int x = i >= 0 ? num1.charAt(i) - '0' : 0;
            int y = j >= 0 ? num2.charAt(j) - '0' : 0;
            int result = x + y + add;
            ans.append(result % 10);
            add = result / 10;
            i--;
            j--;
        }
        ans.reverse();
        return ans.toString();
    }
}

4、二叉搜索树中的插入操作

701. 二叉搜索树中的插入操作

迭代法:

java 复制代码
class Solution {
    public TreeNode insertIntoBST(TreeNode root, int val) {
        TreeNode node = new TreeNode(val);
        if (root == null) {
            return node;
        }

        TreeNode cur = root;
        while (true) {
            if (cur.val > val) {
                if (cur.left == null) {
                    cur.left = node;
                    break;
                }
                cur = cur.left;
            } else {
                if (cur.right == null) {
                    cur.right = node;
                    break;
                } 
                cur = cur.right;
            }
        }
        return root;
    }
}

递归写法:

java 复制代码
class Solution {
    public TreeNode insertIntoBST(TreeNode root, int val) {
        if (root == null) {
            return new TreeNode(val);
        }
        if (root.val < val) {
            root.right = insertIntoBST(root.right, val);
        } else {
            root.left = insertIntoBST(root.left, val);
        }
        return root;
    }
}

5、删除二叉搜索树中的节点

450. 删除二叉搜索树中的节点

java 复制代码
class Solution {
    public TreeNode deleteNode(TreeNode root, int key) {
        // 1. 递归终止条件:当前节点为空,直接返回null
        if (root == null) return null;

        // 2. 查找待删除节点
        if (root.val == key) {
            // 情况1:左子树为空,直接返回右子树作为新的子树根
            if (root.left == null) return root.right;
            // 情况2:右子树为空,直接返回左子树作为新的子树根
            if (root.right == null) return root.left;
            
            // 情况3:左右子树均存在
            // 找到左子树中的最大节点(最右节点)
            TreeNode t = root.left;
            while (t.right != null) t = t.right;
            // 将原右子树挂到左子树最大节点的右子树
            t.right = root.right;
            // 返回左子树作为新的根节点
            return root.left;
            
        } else if (root.val < key) {
            // 递归在右子树中删除节点,并更新当前节点的右指针
            root.right = deleteNode(root.right, key);
        } else {
            // 递归在左子树中删除节点,并更新当前节点的左指针
            root.left = deleteNode(root.left, key);
        }
        
        // 返回处理后的当前节点(可能已被修改)
        return root;
    }
}

6、修剪二叉搜索树

669. 修剪二叉搜索树

java 复制代码
class Solution {
    public TreeNode trimBST(TreeNode root, int low, int high) {
        // 递归终止条件:当前节点为空,直接返回null
        if (root == null) return null;

        // 当前节点值小于下界:说明当前节点及其左子树都不符合要求
        // 根据BST性质,左子树所有节点值都更小,因此只需递归处理右子树
        if (root.val < low) {
            return trimBST(root.right, low, high);
        }
        // 当前节点值大于上界:说明当前节点及其右子树都不符合要求
        // 根据BST性质,右子树所有节点值都更大,因此只需递归处理左子树
        else if (root.val > high) {
            return trimBST(root.left, low, high);
        }

        // 当前节点值在范围内,递归修剪左右子树
        // 注意:需要将修剪后的子树重新赋值给当前节点的左右指针
        root.left = trimBST(root.left, low, high);   // 修剪左子树
        root.right = trimBST(root.right, low, high); // 修剪右子树

        // 返回当前已修剪的节点(作为父节点的子节点)
        return root;
    }
}

二、语雀-编程题

1、如何用队列实现一个栈?

✅如何用队列实现一个栈?

225. 用队列实现栈

  1. 将新元素添加到inque队尾
  2. 将outque中的全部元素依次转移到inque中
  3. 交换两个队列的引用,使得outque始终保存最新栈状态
java 复制代码
import java.util.ArrayDeque;

class MyStack {
    // 使用两个双端队列模拟栈的操作
    // inque 用于入栈时的临时存储
    // outque 用于出栈操作,保持栈顶元素在队首
    ArrayDeque<Integer> inque = new ArrayDeque<>();
    ArrayDeque<Integer> outque = new ArrayDeque<>();

    public MyStack() {
        // 构造函数无需特殊初始化
    }
    
    /**
     * 入栈操作
     * @param x 待压入栈的元素
     * 实现思路:
     * 1. 将新元素添加到inque队尾
     * 2. 将outque中的全部元素依次转移到inque中(反转原有顺序)
     * 3. 交换两个队列的引用,使得outque始终保存最新栈状态
     * 此操作确保新元素始终位于outque队首,符合栈的后进先出特性
     */
    public void push(int x) {
        inque.offerLast(x);          // 将新元素加入inque队尾
        while (!outque.isEmpty()) {  // 将outque现有元素全部转移到inque
            inque.offer(outque.poll());
        }
        // 交换队列引用,使outque始终持有当前栈内容
        ArrayDeque<Integer> temp = inque;
        inque = outque;
        outque = temp;
    }
    
    /** 弹出栈顶元素 */
    public int pop() {
        return outque.poll();  // 直接移除并返回outque队首元素(即栈顶)
    }
    
    /** 获取栈顶元素但不弹出 */
    public int top() {
        return outque.peek();       // 查看outque队首元素
    }
    
    /** 判断栈是否为空 */
    public boolean empty() {
        return inque.isEmpty() && outque.isEmpty();  // 两队列均为空时栈为空
    }
}

2、如何用栈实现一个队列

✅如何用栈实现一个队列?

232. 用栈实现队列

将一个栈当作输入栈,用于压入 push 传入的数据;另一个栈当作输出栈,用于 pop 和 peek 操作。

每次 pop 或 peek 时,若输出栈为空则将输入栈的全部数据依次弹出并压入输出栈,这样输出栈从栈顶往栈底的顺序就是队列从队首往队尾的顺序。

java 复制代码
class MyQueue {
    ArrayDeque<Integer> stack1=new ArrayDeque<>();
    ArrayDeque<Integer> stack2=new ArrayDeque<>();
    public MyQueue() {
        
    }
    
    public void push(int x) {
        stack1.push(x);

    }
    
    public int pop() {
        if(stack2.isEmpty()){
            while(!stack1.isEmpty()){
                stack2.push(stack1.pop());
            }
        }
        return stack2.pop();
    }
    
    public int peek() {
        if(stack2.isEmpty()){
            while(!stack1.isEmpty()){
                stack2.push(stack1.pop());
            }
        }
        return stack2.peek();
    }
    
    public boolean empty() {
        return stack1.isEmpty()&&stack2.isEmpty();
    }
}

3、快速排序

912. 排序数组

java 复制代码
class Solution {
    Random random=new Random();
    public int[] sortArray(int[] nums) {
        quicksort(nums,0,nums.length-1);
        return nums;
    }
    public void quicksort(int[] nums,int left,int right){
        if(left>right) return;
        int partiton=left+random.nextInt(right-left+1);
        swap(nums,partiton,left);
        int i=left+1,j=right;
        while(true){
            while(i<=j&&nums[i]<nums[left]) i++;
            while(i<=j&&nums[j]>nums[left]) j--;
            if(i>=j) break;
            swap(nums,i,j);
            i++;
            j--;
        }
        swap(nums,left,j);
        quicksort(nums,left,j-1);
        quicksort(nums,j+1,right);
    }
    public void swap(int[] nums,int left,int right){
        int temp=nums[left];
        nums[left]=nums[right];
        nums[right]=temp;
    }
}

4、有一个包含N个整数的数组,请编写一个算法,找到其中的两个元素,使它们之差最小。时间复杂度必须为O(n)。

✅有一个包含N个整数的数组,请编写一个算法,找到其中的两个元素,使它们之差最小。时间复杂度必须为O(n)。

  • 该算法时间复杂度为O(n + k)(k为数值范围),适用于整数数组且数值范围可控的场景
  • 当前桶宽度设置为1(每个桶对应一个具体数值),可根据实际需求调整桶宽以优化性能

在元素均匀分布且桶内元素数量较小时,时间复杂度接近线性;否则可能退化为O(n log n)

java 复制代码
import java.util.Arrays;

public class MinDiff {

    public static void main(String[] args) {
        int[] arr = {3, 1, 4, 5, 9, 2, 6, 8, 7};
        int minDiff = findMinDiff(arr);
        System.out.println("最小差为:" + minDiff);
    }

    public static int findMinDiff(int[] arr) {
        int n = arr.length;
        if (n < 2) {
            return -1; // 数组元素不足,无法计算差值
        }

        // 步骤1:查找数组中的最小值和最大值
        int minVal = Integer.MAX_VALUE;
        int maxVal = Integer.MIN_VALUE;
        for (int i = 0; i < n; i++) {
            minval=Math.min(minVal,arr[i]);
            maxval=Math.max(maxVal,arr[i]);
        }

        // 步骤2:初始化桶结构
        int bucketCount = maxVal - minVal + 1; // 桶的数量等于数值范围的大小
        int[][] buckets = new int[bucketCount][n]; // 存储各桶元素
        int[] bucketSizes = new int[bucketCount]; // 记录各桶实际元素个数

        // 步骤3:将元素分配到对应的桶中
        for (int i = 0; i < n; i++) {
            int index = arr[i] - minVal; // 计算元素所属桶的索引
            buckets[index][bucketSizes[index]++] = arr[i]; // 将元素放入桶中
        }

        // 步骤4:对每个非空桶进行排序
        for (int i = 0; i < bucketCount; i++) {
            if (bucketSizes[i] > 0) {
                // 仅对桶内实际存在的元素进行排序
                Arrays.sort(buckets[i], 0, bucketSizes[i]);
            }
        }

        // 步骤5:计算相邻非空桶之间的最小差值
        int minDiff = Integer.MAX_VALUE;
        int prevMax = buckets[0][bucketSizes[0] - 1]; // 初始化为第一个桶的最大值(已排序)
        
        for (int i = 1; i < bucketCount; i++) {
            if (bucketSizes[i] == 0) {
                continue; // 跳过空桶
            }
            
            int currMin = buckets[i][0]; // 当前桶的最小值(已排序)
            int diff = currMin - prevMax; // 计算相邻桶差值
            
            if (diff < minDiff) {
                minDiff = diff; // 更新最小差值
            }
            
            prevMax = buckets[i][bucketSizes[i] - 1]; // 更新prevMax为当前桶的最大值
        }

        return minDiff;
    }
}

5、实现一个LRU缓存淘汰策略,支持get和put操作

✅实现一个LRU缓存淘汰策略,支持get和put操作

146. LRU 缓存

java 复制代码
class LRUCache {
    class ListNode{
        int key,val;
        ListNode prev,next;
        public ListNode(){};
        public ListNode(int key,int val){
            this.key=key;
            this.val=val;
        }
    }
    ListNode dummy;
    Map<Integer,ListNode> map;
    int capacity;
    public LRUCache(int capacity) {
        this.capacity=capacity;
        dummy=new ListNode();
        dummy.next=dummy;
        dummy.prev=dummy;
        map=new HashMap<>();
    }
    
    public int get(int key) {
        if(map.containsKey(key)){
            ListNode target=map.get(key);
            remove(target);
            pushFirst(target);
            return target.val;
        }else{
            return -1;
        }
    }
    
    public void put(int key, int value) {
        if(map.containsKey(key)){
            ListNode target=map.get(key);
            target.val=value;
            remove(target);
            pushFirst(target);
        }else{
            ListNode target=new ListNode(key,value);
            map.put(key,target);
            pushFirst(target);
            if(map.size()>capacity){
                ListNode last=dummy.prev;
                remove(last);
                map.remove(last.key);
            }
        }
    }
    public void remove(ListNode target){
        target.prev.next=target.next;
        target.next.prev=target.prev;
    }
    public void pushFirst(ListNode target){
        dummy.next.prev=target;
        target.next=dummy.next;
        target.prev=dummy;
        dummy.next=target;
    }
}

6、判断101-200之间有多少个质数,并输出所有质数

✅判断101-200之间有多少个质数,并输出所有质数

java 复制代码
public class PrimeNumbers {

    public static void main(String[] args) {
        int count = 0; // 统计质数的数量

        for (int i = 101; i <= 200; i++) {
            if (isPrime(i)) {
                System.out.println(i); // 输出质数
                count++;
            }
        }

        System.out.println("Total prime numbers between 101 and 200: " + count);
    }
    
    public static boolean isPrime(int n) {
        // 处理小于等于1的非质数情况
        if (n <= 1) {
            return false;
        }

        // 检查从2到n的平方根是否存在因子
        for (int i = 2; i <= Math.sqrt(n); i++) {
            if (n % i == 0) {
                return false; // 发现因子,非质数
            }
        }

        // 无因子,确认是质数
        return true;
    }
}

7、请分别写出一个Java堆、栈、元空间溢出的代码

✅请分别写出一个Java堆、栈、元空间溢出的代码

1. Java堆溢出

java 复制代码
import java.util.ArrayList;
import java.util.List;

public class HeapOverflow {

    public static void main(String[] args) {
        List<Object> objects = new ArrayList<>();
        while (true) {
            objects.add(new Object()); // 不断创建对象并保留引用
        }
    }

}

2. Java 栈溢出

java 复制代码
public class StackOverflow {

    public static void main(String[] args) {
        recursiveMethod(1); // 递归调用,没有终止条件
    }

    private static void recursiveMethod(int i) {
        recursiveMethod(i);
    }

}

3. 元空间溢出

java 复制代码
import javassist.ClassPool;

public class MetaspaceOverflow {

    public static void main(String[] args) {
        ClassPool classPool = ClassPool.getDefault();
        for (int i = 0; ; i++) {
            classPool.makeClass("Class" + i).toClass(); // 动态创建大量的类
        }
    }

}

8、给定一个二叉搜索树,请找出其中第k小的元素

✅给定一个二叉搜索树,请找出其中第k小的元素

java 复制代码
class TreeNode {
    int val;
    TreeNode left;
    TreeNode right;
    TreeNode(int x) { val = x; }
}

public class Solution {
    private int count = 0; // 用于计数已遍历的节点
    private int result = Integer.MIN_VALUE; // 存储第k小的元素

    public int kthSmallest(TreeNode root, int k) {
        inOrderTraverse(root, k);
        return result;
    }

    private void inOrderTraverse(TreeNode node, int k) {
        if (node == null) return;

        // 先遍历左子树
        inOrderTraverse(node.left, k);

        // 访问节点
        count++;
        if (count == k) {
            result = node.val;
            return; // 找到第k小的元素后返回
        }

        // 遍历右子树
        inOrderTraverse(node.right, k);
    }
}

9、两个线程,一个打印奇数,一个打印偶数,然后顺序打印出1-100

✅两个线程,一个打印奇数,一个打印偶数,然后顺序打印出1-100

java 复制代码
public class AlternatePrinting {
    private static int count = 1;
    private static final Object lock = new Object();

    public static void main(String[] args) {
        // 线程A负责打印奇数
        Thread threadA = new Thread(() -> {
            while (count <= 100) {
                synchronized (lock) {
                    if (count % 2 == 1) { // 打印奇数
                        System.out.println(Thread.currentThread().getName() + ": " + count++);
                        lock.notify(); // 唤醒线程B
                    } else {
                        try {
                            lock.wait(); // 让出锁并等待
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                    }
                }
            }
        }, "Thread-A");

        // 线程B负责打印偶数
        Thread threadB = new Thread(() -> {
            while (count <= 100) {
                synchronized (lock) {
                    if (count % 2 == 0) { // 打印偶数
                        System.out.println(Thread.currentThread().getName() + ": " + count++);
                        lock.notify(); // 唤醒线程A
                    } else {
                        try {
                            lock.wait(); // 让出锁并等待
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                    }
                }
            }
        }, "Thread-B");

        threadA.start();
        threadB.start();
    }
}

10、两个线程,一个打印123,一个打印ABC,交替输出1A2B3C

✅两个线程,一个打印123,一个打印ABC,交替输出1A2B3C

java 复制代码
public class PrintingWithWaitNotify {
    private static final Object lock = new Object();
    private static boolean printNumber = true;

    public static void main(String[] args) {
        Thread numberThread = new Thread(new NumberPrinter());
        Thread letterThread = new Thread(new LetterPrinter());

        numberThread.start();
        letterThread.start();
    }

    static class NumberPrinter implements Runnable {
        @Override
        public void run() {
            synchronized (lock) {
                try {
                    for (int i = 1; i <= 3; i++) {
                        while (!printNumber) {
                            lock.wait();
                        }
                        System.out.print(i);
                        printNumber = false;
                        lock.notify();
                    }
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }
    }

    static class LetterPrinter implements Runnable {
        @Override
        public void run() {
            synchronized (lock) {
                try {
                    for (char c = 'A'; c <= 'C'; c++) {
                        while (printNumber) {
                            lock.wait();
                        }
                        System.out.print(c);
                        printNumber = true;
                        lock.notify();
                    }
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }
    }
}

11、10个线程模拟赛马,所有马就绪后才能开跑,所有马到达终点后裁判宣布赛马成绩

✅10个线程模拟赛马,所有马就绪后才能开跑,所有马到达终点后裁判宣布赛马成绩

要实现这个功能,可以使用 CountDownLatch 来确保所有马都就绪后再开始比赛,并使用 CyclicBarrier 来确保所有马到达终点后裁判再宣布成绩。

java 复制代码
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.CyclicBarrier;

public class HorseRace {
    // 用于存储赛马成绩
    private static List<String> results = Collections.synchronizedList(new ArrayList<>());

    // 用于控制所有马同时开跑
    private static CountDownLatch startLatch = new CountDownLatch(1);

    // 用于确保所有马到达终点后再宣布成绩
    private static CyclicBarrier finishBarrier = new CyclicBarrier(10, new Runnable() {
        @Override
        public void run() {
            // 裁判宣布成绩
            System.out.println("Race finished! Announcing results:");
            for (String result : results) {
                System.out.println(result);
            }
        }
    });

    public static void main(String[] args) {
        for (int i = 1; i <= 10; i++) {
            new Thread(new Horse("Horse " + i)).start();
        }

        // 所有马就绪后开跑
        try {
            System.out.println("All horses ready. Race starts now!");
            startLatch.countDown(); // 马开跑
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    static class Horse implements Runnable {
        private String name;

        public Horse(String name) {
            this.name = name;
        }

        @Override
        public void run() {
            try {
                // 马就绪,等待开跑信号
                startLatch.await();

                // 马开始跑
                long raceTime = (long) (Math.random() * 10000); // 模拟跑的时间
                Thread.sleep(raceTime);

                // 马到达终点
                results.add(name + " finished in " + raceTime + " ms");

                // 等待其他马到达终点
                finishBarrier.await();
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }
}

12、线程池中怎么设置超时时间?一个线程如果要运行10s,怎么在1s就抛出异常

✅线程池中怎么设置超时时间?一个线程如果要运行10s,怎么在1s就抛出异常

java 复制代码
import java.util.concurrent.*;

public class ThreadPoolTimeoutExample {

    public static void main(String[] args) {
        // 创建一个线程池
        ExecutorService executor = Executors.newFixedThreadPool(2);

        // 创建一个Callable任务
        Callable<String> task = () -> {
            // 模拟执行10秒的任务
            Thread.sleep(10000);
            return "Task completed";
        };

        // 提交任务并获取Future对象
        Future<String> future = executor.submit(task);

        try {
            // 设置任务的超时时间为1秒
            String result = future.get(1, TimeUnit.SECONDS);  // 1秒超时
            System.out.println(result);
        } catch (TimeoutException e) {
            System.out.println("Task timed out!");
            future.cancel(true); // 取消任务
        } catch (InterruptedException | ExecutionException e) {
            e.printStackTrace();
        } finally {
            executor.shutdown();
        }
    }
}
相关推荐
刃神太酷啦13 小时前
C++入门(下)--《Hello C++ World!》(2)(C/C++)
java·c语言·c++·git·算法·github
正经码农2 天前
团队协作的润滑剂——GitHub与协作流程
github
北上ing2 天前
Github上如何准确地搜索开源项目
开源·github
ElenaYu2 天前
使用Homebrew下载配置git和连接GitHub(Mac版)
git·macos·github
CoderJia程序员甲3 天前
RAG_Techniques:探索GitHub热门RAG技术开源项目
ai·llm·github·ai教程·rag技术
孔令飞3 天前
如何从GitHub上调研优秀的开源项目,并魔改应用于工作中?
ai·云原生·golang·kubernetes·开源·github
qianmoQ3 天前
GitHub 趋势日报 (2025年05月03日)
github
露临霜3 天前
vue实现AI问答Markdown打字机效果
前端·javascript·vue.js·ai·github
z5z3c3 天前
如何用git将项目上传到github
git·github
森叶3 天前
Windows11 VS code 安装 Cline 调用 Github MCP 配置过程坑点汇总
github·ai编程