0
点赞
收藏
分享

微信扫一扫

[算法] 剑指offer 30 最小的k个数 golang

蛇发女妖 2022-03-12 阅读 27

[算法] 剑指offer 30 最小的k个数 golang

题目

题目:输入n个整数,找出其中最小的k个数。例如输入451627388个数字,则最小的4个数字是1234

解法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 参数判断, 有时候输入的参数没有存在数量超过一半的值
举报

相关推荐

0 条评论