博客记录-day170-力扣+面试

25 阅读6分钟

一、力扣

1、鸡蛋掉落

887. 鸡蛋掉落

image.png

class Solution {
    public int superEggDrop(int k, int n) {
        // 处理特殊情况:只有1层楼时,只需1次尝试
        if (n == 1) {
            return 1;
        }
        
        // f[i][j] 表示使用i次尝试和j个鸡蛋能确定的最大楼层数
        int[][] f = new int[n + 1][k + 1];
        
        // 初始化:当只有1层楼时,无论有多少鸡蛋,都只需1次尝试
        for (int i = 1; i <= k; ++i) {
            f[1][i] = 1;
        }
        
        int ans = -1;
        // 遍历尝试次数,从2次开始逐步增加
        for (int i = 2; i <= n; ++i) {
            // 对于每个尝试次数i,遍历所有可能的鸡蛋数量j
            for (int j = 1; j <= k; ++j) {
                // 状态转移方程:当前状态的楼层数由两种情况组成:
                // 1. 鸡蛋破碎:剩下i-1次尝试和j-1个鸡蛋,可覆盖f[i-1][j-1]层
                // 2. 鸡蛋未破碎:剩下i-1次尝试和j个鸡蛋,可覆盖f[i-1][j]层
                // 总楼层数为这两种情况之和再加当前尝试的楼层(+1)
                f[i][j] = 1 + f[i - 1][j - 1] + f[i - 1][j];
            }
            // 当当前尝试次数i下k个鸡蛋能覆盖的楼层数超过等于n时,记录答案并终止循环
            if (f[i][k] >= n) {
                ans = i;
                break;
            }
        }
        return ans;
    }
}

2、有效三角形的个数

611. 有效三角形的个数

image.png

class Solution {
    public int triangleNumber(int[] nums) {
        int res = 0;
        Arrays.sort(nums);
        int n = nums.length;
        for (int i = 2; i < n; i++) {
            int left = 0, right = i - 1;
            while (left < right) {
                if (nums[left] + nums[right] > nums[i]) {
                    res += right - left;
                    right--;
                } else {
                    left++;
                }
            }
        }
        return res;
    }
}

3、打乱数组

384. 打乱数组

image.png

class Solution {
    int[] nums;
    Random random = new Random();

    public Solution(int[] nums) {
        this.nums = nums;
    }

    public int[] reset() {
        return nums;
    }

    public int[] shuffle() {
        int[] ans = nums.clone();
        int n = ans.length;
        for (int i = 0; i < n; i++) {
            swap(ans, i, i + random.nextInt(n - i));
        }
        return ans;
    }

    public void swap(int[] ans, int x, int y) {
        int temp = ans[x];
        ans[x] = ans[y];
        ans[y] = temp;
    }
}

二、面试

1、String s = "A"String s = new String("A") 的区别是什么?前者存储在哪个区域?

​区别​​:

  • String s = "A" 使用字面量直接赋值,JVM 会先检查字符串常量池中是否存在值为 "A" 的对象。若存在,则直接复用其引用;若不存在,则在常量池中创建新对象。
  • String s = new String("A") 通过 new 关键字显式创建对象,无论常量池是否存在 "A",都会在堆内存中生成新的 String 对象。

​存储区域​​:

  • String s = "A" 的对象存储在​​字符串常量池​​(JDK 7+ 后位于堆内存的 Metaspace 区域)。
  • String s = new String("A") 的对象存储在​​堆内存​​中,且会额外在常量池中生成 "A" 的字面量对象(除非已存在)。

2、深拷贝和浅拷贝的区别是什么?

image.png

​浅拷贝​​:仅复制对象本身,不复制其引用的其他对象。新旧对象的引用字段指向同一内存地址,修改其中一个会影响另一个。
​深拷贝​​:递归复制对象及其引用的所有子对象,新旧对象完全独立,互不影响。
​示例​​:

// 浅拷贝  
class Person { String name; }  
Person a = new Person("Alice");  
Person b = a; // b 和 a 共享 name 引用  

// 深拷贝  
Person c = new Person(a.name); // c 的 name 是独立的新字符串  

3、Java中有 copyOf 函数吗?它是浅拷贝还是深拷贝?

​存在性​​:Java 提供了 Arrays.copyOf()System.arraycopy() 方法用于数组复制。
​拷贝类型​​:均为​​浅拷贝​​。

  • 对于基本类型数组(如 int[]),复制的是值;
  • 对于引用类型数组(如 Object[]),仅复制引用,不复制实际对象。
    ​示例​​:
Integer[] arr = {1, 2};  
Integer[] copy = Arrays.copyOf(arr, arr.length);  
arr[0] = 100; // copy[0] 仍为 1,但若修改原数组元素的对象属性,会影响拷贝  

4、ArrayListLinkedList 各自的优缺点是什么?插入操作的实现原理?

ArrayList​:

  • ​优点​​:基于动态数组,支持 O(1) 时间的随机访问(通过索引)。
  • ​缺点​​:插入/删除需移动后续元素(平均 O(n) 时间),扩容时可能触发数组复制。
  • ​插入原理​​:在末尾插入直接追加(O(1));在中间插入需将插入点后的元素后移(System.arraycopy)。

LinkedList​:

  • ​优点​​:基于双向链表,插入/删除只需调整指针(O(1) 时间,前提是已知位置)。
  • ​缺点​​:随机访问需遍历链表(O(n) 时间),额外内存开销(存储前后节点引用)。
  • ​插入原理​​:修改相邻节点的 nextprev 指针,无需移动数据。

5、HashMapHashtable 的区别是什么?

​特性​​HashMap​​Hashtable​
​线程安全​非线程安全线程安全(方法同步)
​null 值​允许一个 null 键和多个 null 值不允许 null 键或值
​性能​更高(无同步开销)较低(同步导致竞争)
​迭代器​Fail-Fast(ConcurrentModificationExceptionFail-Safe(弱一致性)
​继承关系​实现 Map 接口继承 Dictionary 类(过时)

6、处理哈希冲突的方法有哪些?

  1. ​开放地址法​​:

    • ​线性探测​​:冲突时顺序查找下一个空槽。
    • ​二次探测​​:按二次方步长(如 1², 2²)探测,减少聚集。
    • ​双重哈希​​:使用第二个哈希函数计算探测步长。
  2. ​链地址法​​:
    每个哈希桶存储链表或红黑树(如 Java 8+ 的 HashMap),冲突元素追加到链表尾部。

  3. ​再哈希​​:
    当负载因子过高时,分配更大的哈希表,并重新计算所有元素的哈希值。

  4. ​公共溢出区​​:
    单独维护一个溢出区存储冲突元素,主表仅存储无冲突的条目。

​应用场景​​:

  • 链地址法适合高冲突场景(如 HashMap);
  • 开放地址法适合内存紧凑且冲突较少的场景。

7、抽奖是并发处理还是串行?如何应对高并发场景(如并发量从10增至1万)?

✅让你设计一个秒杀系统,你会考虑哪些问题?

8、Redis集群是有状态还是无状态?如何保证高可用?

Redis集群是​​有状态​​的,因为每个节点负责存储特定分片的数据,并维护其状态(如键值、主从关系等)。为保证高可用,Redis集群采用​​主从复制+自动故障转移​​机制:每个主节点(Master)挂载一个或多个从节点(Slave),主节点处理写请求并同步数据到从节点;当主节点故障时,集群通过Gossip协议自动选举从节点晋升为新主节点,并更新路由表,确保服务持续可用。同时,集群支持动态扩缩容和数据自动重新分片(Resharding),进一步保障容错性和扩展性。