package main 

import (
	"fmt"
)

// 方法一:堆排序 O(NlogK)
// 小顶堆(特点:只找到topk,不排序topk)
func topk_minHeap(nums []int, k int) []int {
	length := len(nums)
	// 数组长度小于k,直接返回
	if length < k {
		return nums
	}

	// 数组前k个数据取出,并生成小顶堆
	minHeap := make([]int, 0)
	minHeap = append(minHeap, nums[:k]...)
	CreateMinHeap(minHeap)

	// 遍历数组剩余数据,大于堆顶数据时,替换堆顶,重新维护小顶堆
	for i := k; i < length; i++ {
		if nums[i] > minHeap[0] {
			minHeap[0] = nums[i]
			minHeapify(minHeap, 0, k)
		}
	}

	return minHeap
}

// 自底向上创建小顶堆
func CreateMinHeap(nums []int) {
	length := len(nums)
	for i := length - 1; i >= 0; i-- {
		minHeapify(nums, i, length)
	}
}

// 维护小顶堆
func minHeapify(nums []int, posIndex, length int) {
	// 堆左孩子节点索引
	leftIndex := 2 * posIndex + 1
	// 堆右孩子节点索引
	rightIndex := 2 * posIndex + 2
	// 当前节点以及左右孩子节点中最小值的索引,初始化为当前节点索引
	minIndex  := posIndex
	// 左孩子存在并且小于当前节点值时,最小值索引替换为左孩子索引
	if leftIndex < length && nums[leftIndex] < nums[minIndex] {
		minIndex = leftIndex
	}
	// 右孩子存在并且小于当前节点值时,最小值索引替换为右孩子索引
	if rightIndex < length && nums[rightIndex] < nums[minIndex] {
		minIndex = rightIndex
	}
	// 最小值节点索引不等于当前节点时,替换当前节点和其中较小孩子节点值
	if minIndex != posIndex {
		nums[posIndex], nums[minIndex] = nums[minIndex], nums[posIndex]
		minHeapify(nums, minIndex, length)
	}
}


// 方法二:快排 O(N)
func topk_quickSort(nums []int, k int) []int {
	length := len(nums)
	/// 数组长度小于k,直接返回
	if length < k {
		return nums
	}

	// 数组进行快排,左侧边界
	left := 0
	// 数据进行快排,右侧边界
	right := length
	// 第一次快排后,获取分界点index
	pivotIndex := partition(nums, left, right)

	// 循环快排,找到分界点index刚好等于k
	for pivotIndex != k {
		if pivotIndex < k {
			// 分界点index小于k,继续对分界点右侧进行快排,重新获取分界点index
			left = pivotIndex + 1
		}else {
			// 分界点index大于k, 缩小快排范围为上次分界点与本次分界点之间数据,重新获取分界点index
			right = pivotIndex
		}
		pivotIndex = partition(nums, left, right)
	}

	return nums[:k]
}

func partition(nums []int, left, right int) int {
	// 初始化分界点为左边界值
	pivot := nums[left]
	// 所有大于分界值的数据边界index
	pos := left

	// 小于分界值时,边界扩展,将数据替换到边界值index位置
	for i := left; i < right; i++ {
		if nums[i] > pivot {
			pos++
			nums[i], nums[pos] = nums[pos], nums[i]
		}
	}

	// 交换分界值的数据边界index和分界点index,使得分界点左侧均大于边界点,右侧均小于分界点
	nums[pos], nums[left] = nums[left], nums[pos]

	return pos
}


func main() {
	a := []int{5,3,7,1,8,2,9,4,7,2,6,6}
	b := topk_minHeap(a, 5)
	for k, v := range b {
		fmt.Printf("%d => %d\n", k, v)
	}

	fmt.Println();

	c := topk_quickSort(a, 5)
	for k, v := range c {
		fmt.Printf("%d => %d\n", k, v)
	}
}