关于TopK(leetcode215)

本文仅供自己复习
原本按常理来说这个应该收纳到算法总结里,结果一看那个算法总结太长了哈哈哈,找时间重新理一下那个文档

在这里插入图片描述
TopK类问题一般就是指找第K个最大或者最小
a、排序
V8在7.0前的排序,如果数只有10个以下就是用的插入排序O(n2),如果是以上就用快速排序O(nlogn),但是快速排序是不稳定的

不稳定的意思是如果在一个数组中【1,0,1】排序后需要让相等的值他们的先后位置跟排序前一样,如第一个1必须在第二个1后面,但是快排以及选择排序是非稳定的,只有冒泡、插入、归并是稳定排序
在这里插入图片描述

所以在7.0之后,首先对数据少的先使用插入排序,然后再使用归并排序(这样就相当于使用了混合排序)

	/**
	 * @param {number[]} nums
	 * @param {number} k
	 * @return {number}
	 */
	var findKthLargest = function(nums, k) {
	    nums = nums.sort((a, b) => b - a)
	    return nums[k - 1]
	};

时间复杂度也是O(nlogn)


b、快排
快排在这里的精髓就是,随机找一个基点,毕竟如果用第一个或者最后一个极有可能时间复杂度为O(n2)

	/**
	 * @param {number[]} nums
	 * @param {number} k
	 * @return {number}
	 */
	var findKthLargest = function(nums, k) {
	    let swap = function(nums, n, m) {
	        let temp = nums[n]
	        nums[n] = nums[m]
	        nums[m] = temp
	    }
	    let getPiviot = function(nums, left, right) {
	        let pivot = left
	        // 这里从left开始而不是从pivot+1开始,是因为担心万一只有两个数的情况(这样可以少写一个判断)
	        while(left < right) {
	            while(nums[right] <= nums[pivot] && left < right) {
	                right--
	            }
	            while(nums[left] >= nums[pivot] && left < right) {
	                left++
	            }
	            swap(nums, left, right)
	        }
	        swap(nums, left, pivot)
	        return left
	    }
	    let quickSort = function(nums, left, right) {
	        if(left >= right) return
	        // 求随机整数值Math.floor(Math.random() * (max - min)) + min   结果是[min, max)里的值(取不到max)
	        // 想取到max就让max + 1
	        let pivot = Math.floor(Math.random() * (right + 1 - left)) + left
	        // 让基准放第一个去
	        swap(nums, left, pivot)
	        let index = getPiviot(nums, left, right)
	        if(index < k - 1) {
	            quickSort(nums, index + 1, right)
	        }
	        if(index > k - 1) {
	            quickSort(nums, left, index - 1)
	        }
	    }
	    quickSort(nums, 0, nums.length - 1)
	    return nums[k - 1]
	};

使用快排的时间复杂度是O(n)


c、大顶堆

关于堆排序可以看堆排序图解

首先要明确大顶堆就是父节点一定比孩子大,小顶堆就是父节点一定比孩子小
在这里插入图片描述
从图中我们可以看出,大顶堆是降序,小顶堆是升序
另外从图中也可以看出,当某节点的下标为 i 时,它的父节点下标是 Math.floor(i / 2),左节点的下标是 i * 2 + 1,右节点的下标是 i * 2 + 2
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
大致就这么个意思
所以我们要找topK,其实只要构建一个只有K个元素的大顶堆,堆顶就是要求的结果,在这里首先要说一下,使用堆

	/**
	 * @param {number[]} nums
	 * @param {number} k
	 * @return {number}
	 */
	var findKthLargest = function(nums, k) {
	    let swap = function(n, m) {
	        let temp = nums[n]
	        nums[n] = nums[m]
	        nums[m] = temp
	    }
	
	    let buildHeap = function(index, max) {
	        let newIndex = index
	        while(true) {
	            let left = index * 2 + 1
	            let right = index * 2 + 2
	            if(left < max && nums[left] <= nums[index]) {
	                newIndex = left
	            }
	            // 这里要比较的是新的index!!这是重点
	            if(right < max && nums[right] <= nums[newIndex]) {
	                newIndex = right
	            }
	            if(index !== newIndex) {
	                swap(index, newIndex)
	                index = newIndex
	            } else {
	                break
	            }
	        }
	       
	    }
	    // 维持一个k个元素的小顶堆
	    // 这样在堆里的就是最大的k个元素,而堆顶就是第k大
	    // 从最下面那个叶子节点的父节点开始
	    for(let i = Math.floor((k - 1) / 2); i >= 0; i--) {
	        buildHeap(i, k)
	    }
	    // 之后再把数据和堆顶一个个地比较
	    // 如果堆顶大一点,就不管了,如果堆顶小,就把这个数变为堆顶
	    for(let i = k; i < nums.length; i++) {
	        if(nums[0] < nums[i]) {
	            swap(0, i)
	            buildHeap(0, k)
	        }
	    }
	    return nums[0]
	};

使用堆的时间复杂度是O(nlogn),虽然没快排快,但是碰到动态列表情况就很好


d、冒泡排序

	/**
	 * @param {number[]} nums
	 * @param {number} k
	 * @return {number}
	 */
	var findKthLargest = function(nums, k) {
	    let swap = function(n, m) {
	        let temp = nums[n]
	        nums[n] = nums[m]
	        nums[m] = temp
	    }
	    for(let i = 0; i < k; i++) {
	        // 如果后面都没有进行交换,就说明已经排好序了
	        let flag = false
	        for(let j = 0; j < nums.length - i - 1; j++) {
	            if(nums[j] > nums[j + 1]) {
	                swap(j, j + 1)
	                flag = true
	            }
	        }
	        if(!flag) {
	            break
	        }
	    }
	    return nums[nums.length - k]
	};

不多说,时间复杂度为O(n)

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值