剑指offer 最小的K个数

半世苍凉 提交于 2020-02-01 14:37:12

1.题目

输入n个整数,找出其中最小的K个数。例如输入4,5,1,6,2,7,3,88个数字,则最小的4个数字是1,2,3,4

来源:剑指offer
链接:https://www.nowcoder.com/practice/6a296eb82cf844ca8539b57c23e6e9bf?tpId=13&tqId=11182&rp=1&ru=/ta/coding-interviews&qru=/ta/coding-interviews/question-ranking

2.我的题解

2.1最小堆

使用最小堆实现:

  • 实现一个最小堆,借助vector,下标从1开始存储;
  • 实现删除最小元素的函数del,要求删除后保持堆有序,返回删除的元素;
  • 利用输入数据建堆;
  • 连续执行Kdel,并将函数返回值添加到输出结果。
  • 时间复杂度:O(n+klogn)
  • 空间复杂度:O(n)

下沉操作构建堆:

  • 确定堆的大小,开辟空间并填入数值;
  • 从第一个非叶节点开始做下沉操作,直到第一个节点。

构建堆的复杂度计算:n个节点,树高h的满二叉树(堆)为例,显然h=log(n+1)
各层节点关系: 设想一个满二叉树,下一层的节点数总是翻倍,而总结点个数约等于最后一层节点个数的两倍(实际上就少1);故每一层的节点数和都可以由n表示。

堆的层数 下沉时比较次数 最大节点数 最大总操作数
1 h 1/2h * (n+1) h/2h * (n+1)
2 h-1 1/2h-1 * (n+1) (h-1)/2h-1 * (n+1)
3 h-2 1/2h-2 * (n+1) (h-2)/2h-2 * (n+1)
…… …… …… ……
k h-k 1/2h+1-k * (n+1) (h-k)/2h+1-k * (n+1)
…… …… …… ……
h 1 1/2 * (n+1) 1/2 * (n+1)

T=h2h(n+1)+h12h1(n+1)++12(n+1)=(n+1)k=1hk2k<(n+1)2 \begin{aligned} T=&\frac{h}{2^h}(n+1)+\frac{h-1}{2^{h-1}}(n+1)+\cdots+\frac{1}{2}(n+1) \\ =&(n+1)\sum_{k=1}^{h}\frac{k}{2^k} \\ <&(n+1)*2 \end{aligned}

因此T=O(n)

class Solution {
	vector<int> vec;//小顶堆
	int N = 0;
	void swap(int * a, int * b) {
		int tmp = *a;
		*a = *b;
		*b = tmp;
	}
	void sink(int k) {
		while ((k<<1) <= N) {
			int j = k << 1;
			if (j+1<=N && vec[j] > vec[j + 1])j++;
			if (vec[k] > vec[j])swap(&vec[k], &vec[j]);
			k = j;
		}
	}
	int del() {
		swap(&vec[1], &vec[N--]);
		sink(1);
		return vec[N+1];
	}
	void makeHeap(vector<int> &data) {
		N = data.size();
		vec.resize(N << 1);
        for(int i=0;i<data.size();i++)vec[i+1]=data[i];
		for(int i=N/2;i>=1;i--)
            sink(i);
	}
public:
	vector<int> GetLeastNumbers_Solution(vector<int> input, int k) {
		vector<int> res;
		if (k == 0 || input.empty() || k>input.size())return res;
		//make heap
		makeHeap(input);
		//find
		while (k-- > 0) {
			int tmp = del();
			//cout << tmp << " ";
			res.push_back(tmp);
		}
		return res;
	}
};

3.别人的题解

3.1 大顶堆

  • 时间复杂度:O(nlogk)
  • 空间复杂度:O(k)

3.2 partition思想

  • 时间复杂度:O(n)~O(n^2)
  • 空间复杂度:O(1)

4.总结与反思

(1)Markdown数学公式。
(2)partition思想。
(3)大顶堆、小顶堆。适合处理海量数据。

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!