js实现在一个全是整型的大数组中,找出最大的前10个数

这个题目原本是一个面试题,我写了三种用JS实现的方式,做个笔记,就当做是温习知识点吧!有别的实现方式欢迎评论。

先造五千条随机且不重复的假数据,代码如下:

js 复制代码
function genData() {
    const arr = [];
    while(arr.length < 5000) {
        const num = Math.floor(10000 * Math.random()) + 1;
        if (!arr.includes(num)) {
            arr.push(num);
        }
    }
    return arr;
}

一、数组的sort方法实现

利用数组提供的API:sortslice就可以非常快速的实现。这种方式很easy,就不过多赘述了,直接上代码。

js 复制代码
 function getTop10() {
    const list = genData();
    if (!list || list.length === 0) return [];
    
    // 使用sort配合reverse多此一举。
    // list.sort((a, b) => a - b).reverse();
    // const top10 = list.slice(0, 10);
    
    const top10 = list.sort((a, b) => b - a).slice(0, 10);
    return top10;
}

二、使用快排的方法实现

快速排序,这种排序方式在日常开发中很常见,为了巩固知识点,先来回顾一下快速排序的思想。

快速排序的排序过程只需要三步即可:

  1. 在要排序的数组中,找一个元素作为一个基准点;
  2. 准备两个空数组,将所有小于基准点的元素放在左边,所有大于基准点的元素放在右边;
  3. 重复上面两个步骤,递归执行完成,直到每个数组中只有一个元素为止;

为了实现标题中提到的功能,需要稍微改动一下,将大于基准元素的数据放在左边,将小于基准元素的数据放在右边,就能得到一个由大到小排序的数组了。

首先定义一个快排函数quickSort,函数接收一个数组作为参数,函数体内有以下逻辑:

  1. 先判断数组长度是否小于等于1,为真就直接返回;
  2. 找到基准点对应的元素,一般取数组总长度的中间值;
  3. 定义两个空数组,right存放大于基准元素的数据,left存放小于基准元素的数据;
  4. 遍历数组;
  5. 递归调用quickSort方法;
js 复制代码
function quickSort(arr) {
    if (arr.length <= 1) return arr;
    const piovtIndex = Math.floor(arr.length / 2);
    // 利用数组splice方法,将基准元素从数组中删除
    const piovt = arr.splice(piovtIndex, 1)[0];
    const left = [];
    const right = [];
    for(let i = 0, len = arr.length; i < len; i++) {
        if (arr[i] > piovt) {
            left.push(arr[i]);
        } else {
            right.push(arr[i]);
        }
    }
    // 递归调用quickSort方法
    return quickSort(left).concat([piovt], quickSort(right));
}      

通过上面的代码得到了一个由大到小排序的数组,而数组中最前面的十个元素就是我们想要的结果了。 具体代码如下:

js 复制代码
function findTop10() {
    const arr = quickSort(genData()); 
    console.log(arr)
    return arr.splice(0, 10);
}

效果截图:

三、使用小顶堆方式实现

注意:

小顶堆这种方式性能还算OK,空间复杂度低,不过IO读取非常频繁,对系统压力比较大,在写业务的时候,如若遇到需要处理超大型数据的时候,前端若要用此种方式来处理大数据,需要综合考虑,慎重慎重!

在实现最小堆之前,需要了解一些前置知识

  1. 什么是小顶堆?
    • 堆是一个完全二叉树
    • 堆上任意一个节点的值,都必须小于等于其左右两个子节点的值。(大顶堆正好相反)
    • 在小顶堆中,根节点是堆中最小的元素
  2. 用数组实现的小顶堆,每个节点中,它的父节点、左右子节点对应的位置是什么?
    • 小顶堆可以用一个数组表示,给定一个节点i,那么它的父节点一定为A(i/2),左子节点为A(2i),右子节点为A(2i+1)

放上一张图再配合上面的概念,理解起来会轻松些~

上面这张图来自于前端进阶算法9,想要了解更多关于堆的知识,可以看下这篇文章。

有了前面的知识铺垫后,接下来开始构想实现思路:

  1. 维护一个最小堆,这个堆最多包含10个元素(10个最大的数)
  2. 遍历数组中的每个元素,判断最小堆的长度,如果堆的长度不足10个:
    1. 就将当前元素作为插入元素,直接插入到最小堆中,同时将该元素与其父节点做比较,如果插入元素小于父节点,则插入元素与父节点交换位置,确保父节点总是小于子节点的。
  3. 如果最小堆的长度已满10个元素,则判断当前元素与最小堆的根节点进行比较:(在小顶堆中,根节点是堆中最小的元素)
    1. 当前元素比最小堆中的第一个元素大,把最小堆中的根节点替换掉。
    2. 进行下沉操作,即将当前元素放到合适的位置上面。
  4. 最终遍历完成后,最小堆中存放的10个数就是数组中最大的10个数。

代码示例:

js 复制代码
function findTopTen(array) {  
    if (!array || array.length === 0) {
        return [];
    }
    
    if (array.length <= 10) {
       return array.sort((a, b) => b - a);
    }
    // 创建一个最小堆,用于存储最大的10个数  
    const minHeap = new MinHeap(10);  

    // 遍历数组,将元素插入最小堆  
    for (let i = 0; i < array.length; i++) {  
        minHeap.insert(array[i]);  
    }  

    // 从最小堆中取出最大的10个数  
    const topTen = minHeap.extractTopTen();  

    return topTen;  
} 
// 最小堆类  
class MinHeap {  
    constructor(maxSize) {  
        this.heap = [];  
        this.maxSize = maxSize;  
    }  
  
    // 插入元素到堆中  
    insert(element) {  
        if (this.heap.length < this.maxSize) {  
            this.heap.push(element);  
            this.bubbleUp(this.heap.length - 1);  
        } else if (element > this.heap[0]) {  
            this.heap[0] = element;  
            this.sinkDown(0);  
        }  
    }  
  
    // 上浮操作,确保父节点小于子节点  
    bubbleUp(index) {  
        const parentIndex = Math.floor((index - 1) / 2);  
        if (index && this.heap[parentIndex] > this.heap[index]) {  
            this.swap(parentIndex, index);  
            this.bubbleUp(parentIndex);  
        }  
    }  
  
    // 下沉操作,确保父节点小于子节点  
    sinkDown(index) {  
        const length = this.heap.length;  
        let smallest = index;  
        const left = 2 * index + 1;  
        const right = 2 * index + 2;  
  
        if (left < length && this.heap[left] < this.heap[smallest]) {  
            smallest = left;  
        }  
  
        if (right < length && this.heap[right] < this.heap[smallest]) {  
            smallest = right;  
        }  
  
        if (smallest !== index) {  
            this.swap(index, smallest);  
            this.sinkDown(smallest);  
        }  
    }  
  
    // 交换堆中两个元素的位置  
    swap(i, j) {  
        [this.heap[i], this.heap[j]] = [this.heap[j], this.heap[i]];  
    }  
  
    // 提取堆中最大的10个数  
    extractTopTen() {  
        return this.heap.sort((a, b) => b - a).slice(0, 10);  
    }  
}

在上面的示例代码中,MinHeap 类维护了一个最小堆,最多包含10个元素。当插入一个新元素时,如果堆的大小还没有达到10,就直接插入并上浮;如果堆已满且新元素大于堆顶元素,就替换堆顶元素并下沉。最终,extractTopTen 方法将堆中的元素排序并返回前10个最大的数。

打印结果:

本文内容就是这些了,有不正确的地方欢迎掘友们纠正~

相关推荐
文刀竹肃6 分钟前
DVWA -SQL Injection-通关教程-完结
前端·数据库·sql·安全·网络安全·oracle
LYFlied11 分钟前
【每日算法】LeetCode 84. 柱状图中最大的矩形
前端·算法·leetcode·面试·职场和发展
Bigger13 分钟前
Tauri(21)——窗口缩放后的”失焦惊魂”,游戏控制权丢失了
前端·macos·app
Bigger32 分钟前
Tauri (20)——为什么 NSPanel 窗口不能用官方 API 全屏?
前端·macos·app
bug总结33 分钟前
前端开发中为什么要使用 URL().origin 提取接口根地址
开发语言·前端·javascript·vue.js·html
zwjapple1 小时前
全栈开发面试高频算法题
算法·面试·职场和发展
程序员爱钓鱼1 小时前
Node.js 编程实战:数据库连接池与性能优化
javascript·后端·node.js
程序员爱钓鱼1 小时前
Node.js 编程实战:Redis缓存与消息队列实践
后端·面试·node.js
Gomiko2 小时前
JavaScript DOM 原生部分(二):元素内容修改
开发语言·javascript·ecmascript
一招定胜负2 小时前
网络爬虫(第三部)
前端·javascript·爬虫