[算法] 剑指offer 30 最小的k个数 golang
题目
题目:输入n个整数,找出其中最小的k个数。例如输入4、5、1、6、2、7、3、8这8个数字,则最小的4个数字是1、2、3、4。
解法1: 数组
思路
使用一个递增的链表记录最小的k个数字
遍历数组
- 如果链表长度小于4, 直接将元素添加到链表
- 如果链表长度大于等于4并且元素小于链表元素的最大值, 就将元素插入链表中
代码
//最小的k个数字
func minK(arr []int, k int) (resp []int) {
//参数处理
if len(arr) == 0 || len(arr) < k || k < 1 {
fmt.Printf("%s\n", "err: len(arr) == 0 || len(arr) < k || k < 1")
return nil
}
mList := list.New()
length := 0
for i := 0; i < len(arr); i++ {
if length < 4 {
insertAscList(mList, arr[i])
length++
} else {
if mList.Back().Value.(int) > arr[i] {
insertAscList(mList, arr[i])
mList.Remove(mList.Back())
}
}
}
resp = make([]int, k)
node := mList.Front()
for i := 0; i < len(resp); i++ {
resp[i] = node.Value.(int)
node = node.Next()
}
return
}
//将元素插入到递增的list中
func insertAscList(mList *list.List, element int) {
node := mList.Front()
//第一个节点
if node == nil {
mList.PushFront(element)
}
//插入节点
//如果当前值就插入当前值的左边
//如果大于当前值并且下一个值为空 或 小于下一个值就插入右边
for node != nil {
val := node.Value.(int)
if element <= val {
mList.InsertBefore(element, node)
break
} else if element > val && node.Next() == nil || element > val && element <= node.Next().Value.(int) {
mList.InsertAfter(element, node)
break
}
node = node.Next()
}
}
测试
package main
import (
"container/list"
"fmt"
"testing"
"github.com/stretchr/testify/assert"
)
func TestP30(t *testing.T) {
cases := []struct {
arr []int
k int
expect []int
}{
{
arr: []int{4, 5, 1, 6, 2, 7, 3, 8},
k: 4,
expect: []int{1, 2, 3, 4},
},
{
arr: []int{},
k: 1,
expect: nil,
},
}
for _, c := range cases {
actual := minK(c.arr, c.k)
assert.Equal(t, c.expect, actual)
}
}
解法2: 类似快速排序的 partition 拆分+
类似题目 剑指offer 29. 数组中出现次数超过一半的数字 我们可以通过 partition 函数来解决这个问题.
如果将比k个数字小的所有数字移动到数组的左边(左边的k个数不一定是有序的)
我们就能很快得到答案.
代码
//类似快速排序的 partition 拆分
func minK2(arr []int, k int) (resp []int) {
//参数处理
if len(arr) == 0 || len(arr) < k || k < 1 {
fmt.Printf("%s\n", "err: len(arr) == 0 || len(arr) < k || k < 1")
return nil
}
start := 0
end := len(arr) - 1
index := partition(arr, start, end)
for index != k-1 {
if index < k-1 {
start = index + 1
index = partition(arr, start, end)
} else {
end = index - 1
index = partition(arr, start, end)
}
}
return arr[:k]
}
- partition 将会改变原有的数组, 题目要求不能改变原有数组需要数据拷贝, 如果不允许数据拷贝则不能使用该方法
- 最后的 resp 是无序的, 如果结果需要有序需要自己排序
- 最后没有做 arr 参数判断, 有时候输入的参数没有存在数量超过一半的值