一道看似简单的阿里前端算法题

简介: 一道看似简单的阿里前端算法题

博主本次介绍的题目是真实来自阿里前端CBU部门招聘实习生的一道前端算法题,这道题并不是LeetCode上的找出数组中第K大的元素这道题模,而是在这道题目的基础上进行了改编,让我们一起来探索下这道题目该如何解决。

题目描述

image.png

题目分析

我们以下面这个数组为例,我们首先要明白题目中的第2大的元素指的是4,第3大的元素指的是3,也就是说指的是去重后的数组中的排序。我们之所以要建立一个哈希表是因为我们需要知道第k大和第m大的元素总共出现了几次,因为最后需要进行求和。

[1, 2, 4, 4, 3, 5]
复制代码

解题思路

本题博主采用的是哈希表 + 堆排序的方式来求解。

第一步:构建哈希表,键为目标元素,值为目标元素出现的次数

const map = new Map();
for (let v of arr) {
    if (!map.get(v)) {
        map.set(v,1);
    } else {
        map.set(v,map.get(v) + 1)
    }
}
复制代码

第二步:对数组去重

const singleNums = [...new Set(arr)]
复制代码

第三步:构建大顶堆

// 堆的尺寸指的是去重后的数组
let heapSize = singleNums.length;
buildMaxHeap(singleNums, heapSize);
function buildMaxHeap(arr, heapSize) {
    // 从最后一个叶子节点开始进行堆化
    for (let i = Math.floor(heapSize / 2) - 1; i >= 0; i--) {
        // 进行堆化
        maxHeapify(arr, i, heapSize);
    }
}
function maxHeapify(arr, i, heapSize) {
    // 首先假定第i个是最大的
    let max = i;
    let leftChild = 2 * i + 1;
    let rightChild = 2 * i + 2;
    // 如果下标不越界,并且左孩子的比最大值大则更新最大值
    if (leftChild < heapSize && arr[leftChild] > arr[max]) {
        max = leftChild;
    }
    if (rightChild < heapSize && arr[rightChild] > arr[max]) {
        max = rightChild;
    }
    if (max !== i) {
        swap(arr, i, max);
        // 上来的元素的位置往下要接着堆化
        maxHeapify(arr, max, heapSize);
    }
}
// 交换数组中两个元素
function swap(nums, a, b) {
    let temp = nums[a];
    nums[a] = nums[b];
    nums[b] = temp;
}
复制代码

第四步:求第k大的元素和第m大元素

function target(arr, x) {
    for (let i = 0; i < x - 1; i++) {
        // 交换不需要进行堆化的元素
        if (i === min - 1) result.push(arr[0]);
        swap(arr, 0, arr.length - 1 - i);
        arr
        heapSize--;
        maxHeapify(arr, 0, heapSize)
    }
}
target(singleNums, max)
result.push(singleNums[0]);
复制代码

第五步:根据哈希表出现的次数计算并返回结果

return result.reduce((pre,cur) => pre + cur * map.get(cur),0)
复制代码

AC代码

/*
 * @Author: FaithPassion
 * @Date: 2021-07-09 10:06:00
 * @LastEditTime: 2021-08-28 11:09:30
 * @Description: 找出数组中第k大和第m大的数字相加之和
 * let arr = [1,2,4,4,3,5], k = 2, m = 4 
 * findTopSum(arr, k, m); // 第2大的数是4,出现2次,第4大的是2,出现1次,所以结果为10 
 */
/**
 * @description: 采用堆排序求解
 * @param {*} arr 接收一个未排序的数组
 * @param {*} k 数组中第k大的元素
 * @param {*} m 数组中第m大的元素
 * @return {*}  返回数组中第k大和第m大的数字相加之和
 */
function findTopSum(arr, k, m) {
    function buildMaxHeap(arr, heapSize) {
        // 从最后一个叶子节点开始进行堆化
        for (let i = Math.floor(heapSize / 2) - 1; i >= 0; i--) {
            // 进行堆化
            maxHeapify(arr, i, heapSize);
        }
    }
    // 最大堆化函数
    function maxHeapify(arr, i, heapSize) {
        // 首先假定第i个是最大的
        let max = i;
        let leftChild = 2 * i + 1;
        let rightChild = 2 * i + 2;
        // 如果下标不越界,并且左孩子的比最大值大则更新最大值
        if (leftChild < heapSize && arr[leftChild] > arr[max]) {
            max = leftChild;
        }
        if (rightChild < heapSize && arr[rightChild] > arr[max]) {
            max = rightChild;
        }
        if (max !== i) {
            swap(arr, i, max);
            // 上来的元素的位置往下要接着堆化
            maxHeapify(arr, max, heapSize);
        }
    }
    // 交换数组中两个元素
    function swap(nums, a, b) {
        let temp = nums[a];
        nums[a] = nums[b];
        nums[b] = temp;
    }
    let result = []
    // k和m中较大的
    let max = Math.max(k, m);
    // k和m中较小的
    let min = Math.min(k, m);
    const map = new Map();
    for (let v of arr) {
        if (!map.get(v)) {
            map.set(v,1);
        } else {
            map.set(v,map.get(v) + 1)
        }
    }
    // 求第x大的元素
    function target(arr, x) {
        for (let i = 0; i < x - 1; i++) {
            // 交换不需要进行堆化的元素
            if (i === min - 1) result.push(arr[0]);
            swap(arr, 0, arr.length - 1 - i);
            arr
            heapSize--;
            maxHeapify(arr, 0, heapSize)
        }
    }
    const singleNums = [...new Set(arr)]
    // 堆的大小
    let heapSize = singleNums.length;
    // 构建大顶堆
    buildMaxHeap(singleNums, heapSize);
    target(singleNums, max)
    result.push(singleNums[0]);
    return result.reduce((pre,cur) => pre + cur * map.get(cur),0)
}
findTopSum([1, 2, 4, 4, 3, 5], 2, 4)
复制代码

题目反思

  • 学会通过堆排序的方式来求解Top K问题。
  • 学会对数组进行去重。
  • 学会使用reduce Api。
相关文章
|
3月前
|
搜索推荐 前端开发 数据可视化
【优秀python web毕设案例】基于协同过滤算法的酒店推荐系统,django框架+bootstrap前端+echarts可视化,有后台有爬虫
本文介绍了一个基于Django框架、协同过滤算法、ECharts数据可视化以及Bootstrap前端技术的酒店推荐系统,该系统通过用户行为分析和推荐算法优化,提供个性化的酒店推荐和直观的数据展示,以提升用户体验。
155 1
|
1月前
|
移动开发 算法 前端开发
前端常用算法全解:特征梳理、复杂度比较、分类解读与示例展示
前端常用算法全解:特征梳理、复杂度比较、分类解读与示例展示
21 0
|
2月前
|
算法 前端开发 机器人
一文了解分而治之和动态规则算法在前端中的应用
该文章详细介绍了分而治之策略和动态规划算法在前端开发中的应用,并通过具体的例子和LeetCode题目解析来说明这两种算法的特点及使用场景。
一文了解分而治之和动态规则算法在前端中的应用
|
3月前
|
搜索推荐 前端开发 算法
基于用户画像及协同过滤算法的音乐推荐系统,采用Django框架、bootstrap前端,MySQL数据库
本文介绍了一个基于用户画像和协同过滤算法的音乐推荐系统,使用Django框架、Bootstrap前端和MySQL数据库构建,旨在为用户提供个性化的音乐推荐服务,提高推荐准确性和用户满意度。
257 7
基于用户画像及协同过滤算法的音乐推荐系统,采用Django框架、bootstrap前端,MySQL数据库
|
2月前
|
算法 前端开发
一文了解贪心算法和回溯算法在前端中的应用
该文章深入讲解了贪心算法与回溯算法的原理及其在前端开发中的具体应用,并通过分析LeetCode题目来展示这两种算法的解题思路与实现方法。
|
3月前
|
JavaScript 算法 前端开发
"揭秘Vue.js的高效渲染秘诀:深度解析Diff算法如何让前端开发快人一步"
【8月更文挑战第20天】Vue.js是一款备受欢迎的前端框架,以其声明式的响应式数据绑定和组件化开发著称。在Vue中,Diff算法是核心之一,它高效计算虚拟DOM更新时所需的最小实际DOM变更,确保界面快速准确更新。算法通过比较新旧虚拟DOM树的同层级节点,递归检查子节点,并利用`key`属性优化列表更新。虽然存在局限性,如难以处理跨层级节点移动,但Diff算法仍是Vue高效更新机制的关键,帮助开发者构建高性能Web应用。
70 1
|
3月前
|
数据采集 前端开发 算法
基于朴素贝叶斯算法的新闻类型预测,django框架开发,前端bootstrap,有爬虫有数据库
本文介绍了一个基于Django框架和朴素贝叶斯算法开发的新闻类型预测系统,该系统具备用户登录注册、后台管理、数据展示、新闻分类分布分析、新闻数量排名和新闻标题预测等功能,旨在提高新闻处理效率和个性化推荐服务。
|
5月前
|
前端开发 算法 JavaScript
优化算法在前端性能提升中的应用
随着互联网应用的日益复杂,前端性能优化成为开发者关注的焦点。本文探讨了优化算法在前端性能提升中的重要作用,包括对JavaScript代码的优化、资源加载的算法选择以及页面渲染的优化策略。通过合理应用优化算法,可以有效提升前端应用的性能和用户体验。
|
4月前
|
缓存 算法 前端开发
前端 JS 经典:LRU 缓存算法
前端 JS 经典:LRU 缓存算法
94 0
|
27天前
|
算法 安全 数据安全/隐私保护
基于game-based算法的动态频谱访问matlab仿真
本算法展示了在认知无线电网络中,通过游戏理论优化动态频谱访问,提高频谱利用率和物理层安全性。程序运行效果包括负载因子、传输功率、信噪比对用户效用和保密率的影响分析。软件版本:Matlab 2022a。完整代码包含详细中文注释和操作视频。