当前位置: 首页 > article >正文

面试经典问题 —— 最大/小前K个数问题(top - K)问题

目录

  • 常见思路
  • 更优的解法(面试官喜欢的)

在这里插入图片描述

常见思路

要选出最小的前K个数首先我们会想到排排升序建大堆,排降序建小堆

一个直观的想法是使用(小根堆),起始将所有元素放入堆中,然后再从堆中
 取出k 个元素并「顺序」构造答案。

你写出了用小堆解决的代码


void swap(int* a, int* b) {
    int t = *a;
    *a = *b;
    *b = t;
}


void minHeapify(int arr[], int n, int i) {
    int smallest = i;
    int left = 2 * i + 1;
    int right = 2 * i + 2; 


    if (left < n && arr[left] < arr[smallest])
        smallest = left;

   
    if (right < n && arr[right] < arr[smallest])
        smallest = right;


    if (smallest != i) {
        swap(&arr[i], &arr[smallest]);
        
        minHeapify(arr, n, smallest);
    }
}


void buildMinHeap(int arr[], int n) {
    // Index of last non-leaf node
    int startIdx = (n / 2) - 1;

    for (int i = startIdx; i >= 0; i--)
        minHeapify(arr, n, i);
}


int extractMin(int arr[], int* n) {
    if (*n <= 0)
        return INT_MAX;
    if (*n == 1) {
        (*n)--;
        return arr[0];
    }


    int root = arr[0];
    arr[0] = arr[*n - 1];
    (*n)--;
    minHeapify(arr, *n, 0);

    return root;
}

void findKSmallest(int arr[], int n, int k) {

    buildMinHeap(arr, n);

    printf("The k smallest elements are: ");
    for (int i = 0; i < k; i++) {
        printf("%d ", extractMin(arr, &n));
    }
    printf("\n");
}

分析一下不难发现
建堆的复杂度是 O(N)
每次提取的复杂度是 O(logN)
总的复杂度O(NlogN)

面试官看到你的代码后,并不满意并要求你进行优化

更优的解法(面试官喜欢的)

使用(大根堆)!
当处理到原始 arr[i] 时,根据堆内元素个数,以及其与堆顶元素的关系分情况讨论:
堆内元素不足 k 个:直接将 arr[i] 放入堆内;
堆内元素为 k 个:根据 arr[i] 与堆顶元素的大小关系分情况讨论:
arr[i]>=heapTop:arr[i] 不可能属于第 k 小数(已有 k 个元素在堆中),直接丢弃 arr[i];
arr[i]<heapTop:arr[i] 可能属于第 k 小数,弹出堆顶元素,并放入 arr[i]。

在这里插入图片描述

你出写了如下代码

void swap(int* a, int* b) {
    int t = *a;
    *a = *b;
    *b = t;
}


void maxHeapify(int arr[], int n, int i) {
    int largest = i;
    int left = 2 * i + 1;
    int right = 2 * i + 2; 

    // 如果左子节点大于当前节点,则更新最大值索引
    if (left < n && arr[left] > arr[largest])
        largest = left;

    // 如果右子节点大于当前节点,则更新最大值索引
    if (right < n && arr[right] > arr[largest])
        largest = right;

    // 如果最大值索引不是当前节点,则交换并递归调整子树
    if (largest != i) {
        swap(&arr[i], &arr[largest]);
        maxHeapify(arr, n, largest);
    }
}


void buildMaxHeap(int arr[], int n) {
    // 最后一个非叶子节点的索引
    int startIdx = (n / 2) - 1;

    // 从最后一个非叶子节点开始,逆序遍历并调整每个节点
    for (int i = startIdx; i >= 0; i--)
        maxHeapify(arr, n, i);
}

// 提取堆顶元素并调整堆的函数
int extractMax(int arr[], int* n) {
    if (*n <= 0)
        return INT_MAX; // 堆为空时返回最大整数值
    if (*n == 1) {
        (*n)--;
        return arr[0]; // 堆只有一个元素时直接返回
    }

    // 存储并移除堆顶元素
    int root = arr[0];
    arr[0] = arr[*n - 1];
    (*n)--;
    maxHeapify(arr, *n, 0); // 调整堆以保持大根堆性质

    return root;
}


void findKSmallest(int arr[], int n, int k) {
    // 构建包含前k个元素的大根堆
    int heap[k];
    for (int i = 0; i < k; i++) {
        heap[i] = arr[i];
    }
    buildMaxHeap(heap, k);

    // 遍历剩余元素
    for (int i = k; i < n; i++) {
        // 如果当前元素小于堆顶元素,则替换堆顶元素并重新调整堆
        if (arr[i] < heap[0]) {
            heap[0] = arr[i];
            maxHeapify(heap, k, 0);
        }
    }
}

时间复杂度O(NlogK),当N无限大时logK可以忽略
比O(NlogN)更优


http://www.kler.cn/a/452714.html

相关文章:

  • Linux之ARM(MX6U)裸机篇----2.汇编LED驱动实验
  • 【解决报错】AttributeError: ‘NoneType‘ object has no attribute ‘group‘
  • Java技术专家视角解读:SQL优化与批处理在大数据处理中的应用及原理
  • AIA - IMSIC之二(附IMSIC处理流程图)
  • vue3标签中的ref属性如何使用$refs获取元素
  • 工业相机镜头选型知识详解
  • postgresql ERROR: cannot drop the currently open database
  • Java处理视频思路
  • 【接口自动化连载】使用yaml配置文件自动生成接口case
  • Postman最新接口自动化持续集成
  • 虚拟机桥接模式网络连接不上解决方法
  • SQL server学习10-数据库编程(中)
  • 常用的 JVM 调优的参数
  • 【C++多重类循环依赖问题】基于class前向声明与类定义和实现分离的解决方案与分析
  • 解决 Docker 中 DataLoader 多进程错误:共享内存不足
  • ES 集群 A 和 ES 集群 B 数据流通
  • 【数据分析】似然和极大似然估计
  • SQLSERVER、MYSQL LIKE查询特殊字符和转义字符相同与不同
  • 用Python开发高级游戏:实现3D迷宫游戏
  • 【Ubuntu】如何轻松设置80和443端口的防火墙
  • 如何使用Windows快捷键在多显示器间移动窗口
  • Git 代理配置——克隆仓库时遇到 OpenSSL SSL_ERROR_SYSCALL 的解决方案
  • 详解Ollama api (Windows环境)
  • 【QT开发自制小工具】PDF/图片转excel---调用百度OCR API接口
  • 【问题实录】服务器ping不通win11笔记本
  • 【每日学点鸿蒙知识】挖空样式、解密库性能问题、按钮下拉列表弹窗、Scroll组件回调事件问题、判断当前时间之后方法