面试笔试 场景题(部分总结)

题目–找出一堆随机数中的前 k 大数字

找出一堆随机数中的前 k 数字(小根堆),找出一堆随机数中的前 k 数字(大根堆)。都一样

方法:快速排序。 最小堆法。

如果不了解堆这个数据结构:点击查看堆

最小堆法:
当数组规模很大时,排序的时间复杂度较高。为了优化,可以使用最小堆(Min-Heap)。在 Java 中可以利用 PriorityQueue 实现一个固定大小为 k 的最小堆来解决问题。

public static void main(String[] args) {
    int[] nums = {57, 32, 56, 20, 54, 99, 47, 8, 35, 23, 30, 29, 63, 69, 58};
    int k = 5; // 前k大

    // 最小堆
    PriorityQueue<Integer> minHeap = new PriorityQueue<>(k);

    // 遍历数组,维护最小堆
    for (int num : nums) {
        minHeap.offer(num);
        if (minHeap.size() > k) {
            minHeap.poll(); // 移除堆顶 即数组第一个元素
        }
    }

    System.out.println("前 " + k + " 大的数: ");
    while (!minHeap.isEmpty()) {
        System.out.print(minHeap.poll() + " ");
    }
}

PriorityQueue 类

PriorityQueue 是 Java 中实现堆(Heap)数据结构的一个重要类,它基于优先级队列(Priority Queue)的概念。它使用来实现,默认是最小堆,即堆顶是队列中最小的元素。也可以通过传递自定义的比较器来实现最大堆。

PriorityQueue的底层实现是使用一个数组来进行节点的存储

PriorityQueue 默认最小堆:
优先级队列表示为平衡二叉堆:queue[n] 的两个子节点为queue[2n+1] 和queue[2(n+1)]。优先级队列按比较器排序,如果比较器为空,则按元素的自然顺序排序:对于堆中的每个节点 n 和 n 的每个后代 d,n <= d。假设队列非空,则具有最小值的元素位于queue[0]中。【默认最小堆】
在这里插入图片描述

PriorityQueue 实现大根堆:
大根堆,则数组第一个元素最大
new PriorityQueue<>(Comparator.reverseOrder()) 传入一个参数即可

 public static void main(String[] args) {
     // 自定义比较器,将 PriorityQueue 变为最大堆
     PriorityQueue<Integer> maxHeap = new PriorityQueue<>(Comparator.reverseOrder());

     maxHeap.offer(10);
     maxHeap.offer(40);
     maxHeap.offer(20);
     maxHeap.offer(30);

     System.out.println("最大堆中的元素:");
     while (!maxHeap.isEmpty()) {
         System.out.print(maxHeap.poll() + " "); // 输出时将从大到小
     }
 }

PriorityQueue 的特性:

  1. 自动排序:

    • PriorityQueue 会根据元素的优先级自动排序,默认排序方式是最小堆,意味着堆顶元素是队列中最小的元素。
    • 如果需要实现最大堆,可以传入一个自定义的比较器,使得元素按照降序排列。
  2. 不允许 null 元素:

    • PriorityQueue 不允许插入 null 元素,否则会抛出 NullPointerException
      在这里插入图片描述
  3. 线程不安全:

    • PriorityQueue 不是线程安全的。如果需要在多线程环境下使用,可以使用 PriorityBlockingQueue
  4. 时间复杂度:

    • 插入(offer())和删除最小元素(poll())的时间复杂度均为 O(log n),其中 n 是队列的大小。
    • 获取队列中最小元素的时间复杂度是 O(1),即通过 peek() 方法。

PriorityQueue 常用方法

方法名描述
add(E e)插入元素 e 到队列中,若违反队列容量限制,抛出 IllegalStateException
offer(E e)插入元素 e 到队列中,返回 true 表示插入成功,若违反容量限制返回 false
poll()获取并移除队列的堆顶元素(即最小元素),若队列为空则返回 null
peek()获取但不移除队列的堆顶元素(即最小元素),若队列为空则返回 null
remove(Object o)从队列中移除指定的元素 o
isEmpty()检查队列是否为空。
size()返回队列的元素个数。
clear()清空队列中的所有元素。
contains(Object o)检查队列中是否包含指定的元素 o

题目–数组中的第 K 个最大元素

这个题也可以利用 PriorityQueue 秒了
在这里插入图片描述

public int findKthLargest(int[] nums, int k) {
      PriorityQueue<Integer> queue = new PriorityQueue<>();
      for (int num : nums) {
          queue.offer(num);
          if (queue.size() > k) {
              queue.poll();
          }
      }
      return queue.peek();
  }

题目–二叉搜索树中第 K 小的元素

这个题也可以利用 PriorityQueue 秒了
在这里插入图片描述

你使用最大堆来找出第 k 小的元素。最大堆的特点是堆顶元素是当前堆中的最大值。

  • 创建一个最大堆(PriorityQueue,指定了 Comparator.reverseOrder() 来使其成为最大堆)。

  • 当堆的大小超过 k 时,移除堆顶元素(即当前最大元素)。这样,堆中总是保持 k 个最小的元素,堆顶始终是这 k 个元素中的最大值。

方法1:

 public int kthSmallest(TreeNode root, int k) {
     if (root == null) return 0;
     ArrayList<Integer> list = new ArrayList<>();

     Queue<TreeNode> queue = new LinkedList<>();
     queue.offer(root);
     while (!queue.isEmpty()) {
         TreeNode node = queue.poll();
         list.add(node.val);
         if (node.left != null) queue.offer(node.left);
         if (node.right != null) queue.offer(node.right);
     }
     // 二叉搜索树中第 K 小的元素
     PriorityQueue<Integer> maxHeap = new PriorityQueue<>(Comparator.reverseOrder());
     for (Integer i : list) {
         maxHeap.offer(i);
         if (maxHeap.size() > k) {
             maxHeap.poll();
         }
     }
     return maxHeap.peek();
 }

方法2:利用二叉搜索树性质,中序遍历是有序的

 public int kthSmallest(TreeNode root, int k) {
        if (root == null) return 0;
        // 利用中序遍历来获取第 k 小的元素
        Stack<TreeNode> stack = new Stack<>();
        TreeNode current = root;
        int count = 0;
        int res = root.val;
        while (current != null || !stack.isEmpty()) {
            // 先遍历左子树
            while (current != null) {
                stack.push(current);
                current = current.left;
            }
            // 当前节点
            current = stack.pop();
            count++;
            // 当第 k 小的元素出现时返回该元素
            if (count == k) {
                res = current.val;
                break;
            }
            // 遍历右子树
            current = current.right;
        }
        return res;
    }

方法3:递归中序遍历

int res = 0;
int count = 0;

public int kthSmallest(TreeNode root, int k) {
    if (root == null) return 0;
    kthSmallest(root.left, k);
    count += 1;
    if (k == count) {
        res = root.val;
    }
    kthSmallest(root.right, k);
    return res;
}


❤觉得有用的可以留个关注~❤

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值