215. 数组中的第K个最大元素 JavaScript实现

215. 数组中的第K个最大元素

一、topk问题

考察的核心是可以进行topk排序算法。即不一定要全部排序好再去拿,只针对部分元素进行排序,解决topk问题的排序算法有快速排序、堆排序。

二、代码实现

1、调库函数sort()

var findKthLargest = function(nums, k) {
    
    nums.sort((a,b) => b-a);
     return nums[k-1];

};

在这里插入图片描述

2、快速排序

a、思想

快排的思想,以及时间复杂读的分析

1、划分操作:选择一个基准(通常选择第一个或者是最后一个元素)。然后从右往左进行遍历,直到找到一个比基准小的元素,将它放到左边。从左往右遍历,直到找到一个比基准大的元素,将它放到右边。这样当控制左右遍历的指针相遇时,停止遍历。这个位置就是基准的最后位置。
2、递归操作:划分之后,基准左边是比它小的数字,基准右边是比它大的数字。所以分别在基准的左右两边进行划分操作。

var quicksort = function(nums,low,high){
    if(low < high){
        let index = partition(nums,low,high)
        // 以第一轮得出的index为基准划分出左半区和右半区 对数组的左半区进行递归 将其全部变为有序
        quicksort(nums,low,index-1);
        quicksort(nums,index+1,high);
        }
    return nums;
};

var partition = function(nums,low,high){
    // 选定第一个元素为基准值 把它拿出来 即为“挖坑”.low和high就是指向左右两边的指针
    // let pivot = nums[low];
    
    // 更好的基准选择,用random取low-high之间的随机整数
    let pivot_idx = Math.floor(Math.random()*(high-low+1))+low;
    
    // pivot放置到最左边,两个值是相互替换。
    nums[low], nums[pivot_idx] = nums[pivot_idx], nums[low] ;    
    let pivot = nums[low];
    
    // 开始遍历
    while(low < high){
        // 挖了坑就需要填坑~从high指针开始向左找
        while (low < high && nums[high] >= pivot) high--;
        // 一旦找到比坑对应值pivot小的 就扔到low那侧的坑里
        nums[low] = nums[high];

        while (low < high && nums[low] < pivot) low++;
        nums[high] = nums[low];
    }
    // 经过上面的不断迭代 low===high 此时这个位置即为基准位置
    nums[low] = pivot;
    // 分区成功!返回定海神针~(此时low=high哦~)
    return low;
};

b、本题的代码实现

直接利用快排划分之后的index,和k-1 进行比较。

  1. 检查一下,index是不是为 len(nums) - k,如果是,就直接返回。 如果index比len(nums)
  2. 如果index比len(nums) -k要小,说明我们要找的数在右半边,我们只对右半边进行快速排序,左半边就不管了。
  3. 如果index比len(nums) - k要大,说明我们要找的数在左半边,我们只对左半边进行快速排序,忽略右半边。
var findKthLargest = function(nums, k) {
    const n = nums.length;
    const target_index = n-k;
    let left = 0,
        right = n-1;
    
    // 注意只有一个元素的用例
    while(left <= right){
        // 目的是反复的调整区间,得到目标中的index。所以是放在循环里面
        const index = partition(nums,left,right);
        
        // 将index和n-k相比
        if(index == target_index){
            return nums[target_index];
        }
        // n-k小了,就需要到左边去找
        else if(index > target_index){
           right = index-1;
        }
        else{
            left = index+1;
        }        
    }   

};

// 进行划分
var partition = function(nums,low,high){
    // 选定第一个元素为基准值 把它拿出来 即为“挖坑”.low和high就是指向左右两边的指针
    // let pivot = nums[low];
    
    // 更好的基准选择,用random取low-high之间的随机整数
    let pivot_idx = Math.floor(Math.random()*(high-low+1))+low;
    
    // pivot放置到最左边,两个值是相互替换。
    nums[low], nums[pivot_idx] = nums[pivot_idx], nums[low] ;    
    let pivot = nums[low];
    
    // 开始遍历
    while(low < high){
        // 挖了坑就需要填坑~从high指针开始向左找
        while (low < high && nums[high] >= pivot) high--;
        // 一旦找到比坑对应值pivot小的 就扔到low那侧的坑里
        nums[low] = nums[high];

        while (low < high && nums[low] < pivot) low++;
        nums[high] = nums[low];
    }
    // 经过上面的不断迭代 low===high 此时这个位置即为基准位置
    nums[low] = pivot;
    // 分区成功!返回定海神针~(此时low=high哦~)
    return low;
};

3、堆排序

a、思想

各种排序算法
堆排序解决topk问题

b、本题的代码实现

使用堆排序来解决这个问题——建立一个大顶堆,做k−1 次删除操作后,堆顶元素就是我们要找的答案(堆排序过程中,不全部下沉,下沉nums.length-k+1,然后堆顶可以拿到我们top k答案了)

 // 整个流程就是上浮下沉
var findKthLargest = function(nums, k) {
   let heapSize=nums.length

    // 1、构建好了一个大顶堆
    buildMaxHeap(nums,heapSize) 

    // 2、进行下沉 大顶堆是最大元素下沉到末尾
    for(let i=nums.length-1;i>=nums.length-k+1;i--){
        swap(nums,0,i)
        --heapSize // 下沉后的元素不参与到大顶堆的调整
        
        // 3、重新调整大顶堆
         maxHeapify(nums, 0, heapSize);
    }
    return nums[0]
};

// 自下而上构建一颗大顶堆
function buildMaxHeap(nums,heapSize){
    // 从最后一个非叶节点开始调整
    for(let i=Math.floor(heapSize/2)-1;i>=0;i--){
        maxHeapify(nums,i,heapSize)
    }
}

// 从左向右,自上而下的调整节点。i就是要调整节点的序号
function maxHeapify(nums,i,heapSize){
    // 左子节点,右子节点
    let l=i*2+1, r=i*2+2;

    // 拿到左右子节点中的最大
    let largest=i
    if(l < heapSize && nums[l] > nums[largest]) largest=l
    if(r < heapSize && nums[r] > nums[largest]) largest=r

    if(largest!==i){
        swap(nums,i,largest) // 当前节点和最大子节点交换
        // 继续调整下面的非叶子节点
        maxHeapify(nums,largest,heapSize)
    }
}
function swap(a,  i,  j){
    [a[i],a[j]] = [a[j],a[i]]
    // let temp = a[i];
    // a[i] = a[j];
    // a[j] = temp;
}
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值