1.1. 题目

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

在未排序的数组中找到第 k 个最大的元素。请注意,你需要找的是数组排序后的第 k 个最大的元素,而不是第 k 个不同的元素。

示例 1:

输入: [3,2,1,5,6,4] 和 k = 2
输出: 5

示例 2:

输入: [3,2,3,1,2,4,5,5,6] 和 k = 4
输出: 4

说明:

你可以假设 k 总是有效的,且 1 ≤ k ≤ 数组的长度。




1.1.2. 题解:

  • 数组无序,找出第K个最大的元素,意思就是把数组排序,从大到小,找到从大到小第k个元素是啥
  • 假如第k个元素和第k-1个元素是一样的,不能合并成一个.. 比如 5,4,3,3,3,2 里第4大元素就是 3,并不是2

1.1.3. 思路:

  • 看到题的第一思路是 用个数组做个有序栈,首先是个栈,其次是有序的,栈的大小是k
    • 遍历数组,依次把每个元素都添加到栈里,如果超出栈,那么栈最小那个推出去,并把新的值有序插入到栈里
    • 当遍历完所有的元素之后,栈最后一个元素就是第K个最大的元素
    • 每次入栈都得排序一次,因为栈里都是有序的,所以栈里排序时间复杂度是 O(n),外围遍历元素时间复杂度是 O(n),因为是套着的
    • 所以时间复杂度是 O(n^2)
  • 看官方标签写的是堆排序
    • 我特地去了解了下堆排序,堆排序平均复杂度是O(n log n),那优先选堆排序
    • 花了半天,终于看懂且写下了堆排序了.
    • 堆排序主要思想是先把数组隐射到 二叉树 上(不需要代码实现),然后把数组重新排序,达到 父节点一定要比自己的子节点都大或者都小
    • 其实堆排序好理解,但是不太好写,最主要的是不太好写 创建堆 的代码...
    • 第一, 数组长度 / 2 的节点很可能都是非叶子节点,如果不是 得 判断当前节点的子节点有没有超出长度.. 超出了说明它没有子节点.. 也就不用换了...
    • 第二, 每个 父节点和自己的子节点一旦 发生了换位,那么可能它子节点的子节点可能也要变化,所以需要注意..
    • 第三, 每排完一次,就把根节点 和堆的最后一个节点替换(不等于数组的最后一个节点)
    • 堆排序的顺序是
      • 先构建堆
        • 数组长度的/2之后的值肯定都是叶子节点,无需当做父节点进行构建,所以往前遍历
        • 如果当前节点的左右子节点数组总长度小 且 比父节点小(或者大,看你要怎么排序),那么进行替换
        • 替换后 子节点和父节点进行变换了,但是以前的子节点 可能还有子节点,可能子节点的子节点已经不满足最大或者最小堆了,所以得往下轮查变换一遍
      • 构建成功后,此时根节点(数组首位)就是最大或者最小值,这时候把当前值与的最后一个值进行替换,注意是堆的最后一个值
      • 替换后,把堆最后这个值剔除堆外,剩下的值重新上面的构建堆的过程,注意这时候上面的数组长度其实是堆的长度了..
      • 当把所有元素都遍历完了,那么最后的数组就是一个有序的数组了

1.1.4. 代码:

点击显示
 func findKthLargest(nums []int, k int) int {
     nums = heapSort(nums)
     return nums[k-1]
 }



 func heapSort(nums []int) []int {
     lens := len(nums)
     // 先建立一个堆
     buildHeap(nums,lens)
     // 上面构建堆后,其实已经满足了基本的 最大堆或者最小堆
     // 此时根节点肯定是最大值或者最小值了
     // 从数组最后挨个往前遍历
     for i:=lens-1;i>=0;i-- {
         // 构成堆后的数组的第一个值和最后一个值进行替换
         // 然后把 数组 i 个值之后的数之外的数 继续构建堆
         // 循环往复
         swap(nums,0,i)
         lens -= 1
         heap(nums,0,lens)
     }
     return nums
 }

 // 构建一个堆
 func buildHeap(nums []int,lens int) {
     // 一般数组长度的一半 很可能就不是叶子节点了,但可以确定的是 数组长度一半后面的肯定都是叶子节点
     // 叶子节点是最底下的节点了,所以不需要往下置换了,所以从 长度/2开始算
     for i := lens/2;i>=0;i-- {
         heap(nums,i,lens)
     }
 }

 func heap(nums []int,i,lens int) {
     // 每个父节点的左节点 是当前数组下标i的 2*i + 1
     // 每个父节点的右节点 是当前数组下标i的 2*i + 2
     left := 2*i + 1
     right := 2*i + 2

     // 咱们做最小堆,也就是小的数组排在后面
     // 假设当前的 父节点是最小
     min := i
     // 当 父节点的左子节点 小于 数组总长度 且 左子节点的值小于父节点的时候,所以这个小堆里 左子节点是最小的(暂时不替换)
     // 为什么要 父节点的左子节点 小于 数组总长度, 因为如果父节点的左子节点比 数组总长度还长,那说明 超出了数组范围了..说明该父节点其实没有左子节点
     if left < lens && nums[left] < nums[min] {
         min = left
     }
     // 右子节点跟上面左子节点一个道理
     if right < lens && nums[right] < nums[min] {
         min = right
     }

     // 如果通过上面与左右子节点相比,最小值已经不是当初的父节点了
     // 那么把最小的节点与父节点进行变换,变换后可能会影响该父节点的子节点的子节点,所以还得往下继续对比比换一下
     if min != i {
         swap(nums,min,i)
         heap(nums,min,lens)
     }

 }


 func swap(arr []int,m,n int) []int {
     arr[m],arr[n] = arr[n],arr[m]
     return arr
 }

results matching ""

    No results matching ""