剑指offer-面试题30:最小的K个数

来源:互联网 发布:知己而知彼论坛 编辑:程序博客网 时间:2024/05/17 22:57

题目:输入n个数,找出其中最小的K个数。例如输入4、5、1、6、2、7、3、8这8个数字,则最小的4个数字是1,2,3,4。

思路:这道题目是典型的top K问题。两种方法:

(1)如果允许改变数列,半快速排序,是基准值正好为第K个数,那么基准值左边的都是小于它的,即可得到最小的K个数(求最大的过程类似),时间复杂度O(n)。

(2)不允许改变数列,采用维护数组的方法,建立一个最大堆。遍历数列,如果数字小于堆顶,那么堆顶很明显不是最小的K个数,更新堆顶,直到所有数字都已经与堆顶比较,堆上的数字即为最小的K个数。

半快速排序在面试题29里讲过,在此不再讨论。见连接:http://blog.csdn.net/moses1213/article/details/51072029

堆的实现可以用数组,也可以利用STL里的multiset容器,下面是分别用两种实现过程的求解代码:

1.用multiset实现

typedef multiset<int, greater<int> >    intSet;typedef multiset<int, greater<int> >::iterator setIterator;void GetLeastNumbers(Const vector<int>& data, intSet& leastNumbers, int k){    leastNumbers.clear();        if(k < 1 || data.size() < k)        return;            vector<int>::const_iterator iter = data.begin();    for(; iter != data.end(); ++iter)    {        if(leastNumbers.size() < k)            leastNumbers.insert(*iter);                    else        {            setIterator iterGreatest = leastNumbers.begin();                        if(*iter < *(leastNumbers.begin())            {                leastNumbers.erase(iterGreatest);                leastNumbers.insert(*iter);            }        }    }}

2.用数组实现

如果数组从0开始计数(也有从1开始),顺序存储堆的结点,那么对于结点i,它的左右子结点和父亲有这样的关系:

左儿子:2*i+1

右儿子:2*i+2

父亲:    (i-1)/2

建堆过程:上滤建堆。因为数组总是在末尾插入新的元素,这时候比较它和父亲的大小,比父亲小则交换,逐步上移,直到大于父结点。

插入过程:和建堆过程类似,上滤法。

维护二叉堆的过程是每次和堆顶元素比较,此时需要采取的是下滤过程,因此这里需要上滤和下滤两个例程。

void Swap(int& a, int& b){    int tmp = a;    a = b;    b = tmp;}void SiftUp(int* a, int n){    int c;    for(int i = n - 1; i > 0 && a[i] > a[c=(i-1)/2]; i = c)        Swap(a[i], a[c]);}void SiftDown(int* a, int n){    int c;    for(int i = 0; (c = 2*i+1) <= n-1; i = c)    {        if(c+1 <= n-1 && a[c+1] > a[c])            ++c;        if(a[i] >= a[c])            break;        Swap(a[i], a[c]);    }}
TopK算法:

void TopK(int *a, int n, int k)  {      int *heap = new int[k];      for(int i = 0; i < k; ++i)          heap[i] = a[i];          //堆初始化数据      for(int i = 1; i < k; ++i)          SiftUp(heap, i+1);       //建堆        for(int i = k; i < n; ++i)   //与堆顶元素比较,如果小于堆顶则替换堆顶      {          if(a[i] < *heap)          {              *heap = a[i];              SiftDown(heap, k);          }      }      for(int i = 0; i < k; ++i)          cout << heap[i] << " ";      cout << endl;      delete[] heap;}




0 0
原创粉丝点击