zoukankan      html  css  js  c++  java
  • 面试题25:最小的K个数


    方法一:对n个整数进行排序(快速排序或堆排序),取出前K个元素(最容易想到的最笨的方法,不可取)

    时间复杂度:O(n*logn) + O(k) = O(n*logn)

    采用快速排序的代码:

     

    #include "stdafx.h"
    #include <iostream>
    using namespace std;
    
    //划分数组,找到枢轴元素下标,使得其左边的元素都比其小,右边的元素都比其大
    int Partition(int nArr[], int nLength)
    {
    	//初始状态下,选取数组的第一个元素作为枢轴元素
    	int nPivot = nArr[0];
    
    	//设置两个游标
    	int nLow = 0;
    	int nHigh = nLength - 1;
    
    	while (nLow < nHigh)
    	{
    		while (nLow < nHigh && nArr[nHigh] >= nPivot)
    		{
    			nHigh--;
    		}
    		nArr[nLow] = nArr[nHigh];
    
    		while (nLow < nHigh && nArr[nLow] <= nPivot)
    		{
    			nLow++;
    		}
    		nArr[nHigh] = nArr[nLow];
    	}
    
    	nArr[nLow] = nPivot;
    	return nLow;
    }
    
    void FastSort(int nArr[], int nLength)
    {
    	if (nArr == NULL || nLength <= 0)
    	{
    		return;
    	}
    
    	int nMidIndex = Partition(nArr, nLength);
    	FastSort(nArr, nMidIndex);
    	FastSort(nArr + nMidIndex + 1, nLength - 1 - nMidIndex);
    }
    
    int _tmain(int argc, _TCHAR* argv[])
    {
    	int nArr1[8] = {4, 5, 1, 6, 2, 7, 3, 8};
    	int nArr2[8] = {4, 5, 2, 6, 2, 5, 3, 5};
    	int nArr3[8] = {1, 1, 1, 1, 1, 1, 1, 1};
    	
    	while (1)
    	{
    		int k = 0;
    		cout << "请输入最小数的个数:";
    		cin >> k;
    		if (k == 0)
    		{
    			break;
    		}
    
    		FastSort(nArr1, 8);	
    		cout << "第一个数组的最小的" << k << "个数为:";
    		for (int i=0; i<k; i++)
    		{
    			cout << nArr1[i] << " ";
    		}
    		cout << endl;
    
    		FastSort(nArr2, 8);
    		cout << "第二个数组的最小的" << k << "个数为:";
    		for (int i=0; i<k; i++)
    		{
    			cout << nArr2[i] << " ";
    		}
    		cout << endl;
    
    
    		FastSort(nArr3, 8);
    		cout << "第三个数组的最小的" << k << "个数为:";
    		for (int i=0; i<k; i++)
    		{
    			cout << nArr3[i] << " ";
    		}
    		cout << endl;
    	}
    	
    	system("pause");
    	return 0;
    }
    

    运行结果:



    采用堆排序的代码:

     

    #include "stdafx.h"
    #include <iostream>
    using namespace std;
    
    //调整堆(大根堆)函数,待调整元素的下标nIndex,
    void AdjustHeap(int nHeap[], int nLength, int nIndex)
    {	
    	int key = nHeap[nIndex];//待调整元素的值
    	for (int i=2*nIndex+1; i<nLength; i=2*i+1)
    	{		
    		if (i+1 < nLength && (nHeap[i] < nHeap[i+1]))//有右孩子且右孩子的值比左孩子大
    		{			
    			i++;//i指向表示较大孩子下标					
    		}
    		if (nHeap[i] < key)//不需要调整
    		{
    			break;
    		}
    		nHeap[nIndex] = nHeap[i];
    		nIndex = i;		
    	}
    	nHeap[nIndex] = key;
    }
    
    //堆排序,从小到大
    void HeapSort(int nArr[], int nLength)
    {
    	if (nArr != NULL && nLength > 0)
    	{		
    		for (int i=nLength/2-1; i>=0; i--)
    		{
    			AdjustHeap(nArr, nLength, i);//调整为大根堆
    		}
    
    		for (int j=nLength-1; j>=0; j--)
    		{	
    			int temp = nArr[j];
    			nArr[j] = nArr[0];
    			nArr[0] = temp;
    			AdjustHeap(nArr, j, 0);		
    		}		
    	}
    	else
    	{
    		return ;
    	}
    }
    
    int _tmain(int argc, _TCHAR* argv[])
    {
    	int nArr1[8] = {4, 5, 1, 6, 2, 7, 3, 8};
    	int nArr2[8] = {4, 5, 2, 6, 2, 5, 3, 5};
    	int nArr3[8] = {1, 1, 1, 1, 1, 1, 1, 1};
    
    	while (1)
    	{
    		int k = 0;
    		cout << "请输入最小数的个数:";
    		cin >> k;
    		if (k == 0)
    		{
    			break;
    		}
    
    		HeapSort(nArr1, 8);	
    		cout << "第一个数组的最小的" << k << "个数为:";
    		for (int i=0; i<k; i++)
    		{
    			cout << nArr1[i] << " ";
    		}
    		cout << endl;
    
    		HeapSort(nArr2, 8);
    		cout << "第二个数组的最小的" << k << "个数为:";
    		for (int i=0; i<k; i++)
    		{
    			cout << nArr2[i] << " ";
    		}
    		cout << endl;
    
    
    		HeapSort(nArr3, 8);
    		cout << "第三个数组的最小的" << k << "个数为:";
    		for (int i=0; i<k; i++)
    		{
    			cout << nArr3[i] << " ";
    		}
    		cout << endl;
    	}
    
    	system("pause");
    	return 0;
    }
    

    运行结果:


    方法二:使用选择排序或冒泡排序,进行K次选择,可得到最小的k个数

    时间复杂度:O(n*k)

    选择排序代码:

     

    #include "stdafx.h"
    #include <iostream>
    using namespace std;
    
    //利用选择排序找到最小的k个数,进行k次选择即可
    void SelectSort_KMinNum(int nArr[], int nLength, int k)
    {
    	if (nArr == NULL || nLength <= 0 || k>nLength)
        {
    		cout << "输入有误!" << endl;
    		return;
        }
    
    	int nMin = 0;
    	int nMinIndex = 0;
    	for (int i=0; i<k; i++)//进行k次选择
    	{
    		nMin = nArr[i];
    		nMinIndex = i;
    		for (int j=i+1; j<nLength; j++)
    		{
               if (nArr[j] < nMin)
               {
    			   nMin = nArr[j];
    			   nMinIndex = j;
               }
    		}
    
    		if (i != nMinIndex)
    		{
    			int temp = nArr[i];
    			nArr[i] = nArr[nMinIndex];
    			nArr[nMinIndex] = temp;
    		}
    		
    		cout << nArr[i] << " ";
    	}
    	cout << endl;
    
    }
    
    int _tmain(int argc, _TCHAR* argv[])
    {
    	int nArr1[8] = {4, 5, 1, 6, 2, 7, 3, 8};
    	int nArr2[8] = {4, 5, 2, 6, 2, 5, 3, 5};
    	int nArr3[8] = {1, 1, 1, 1, 1, 1, 1, 1};
    
    	while (1)
    	{
    		int k = 0;
    		cout << "请输入最小数的个数:";
    		cin >> k;
    		if (k == 0)
    		{
    			break;
    		}
    
    		cout << "第一个数组的最小的" << k << "个数为:";
    		SelectSort_KMinNum(nArr1, 8, k);		
    		
            cout << "第二个数组的最小的" << k << "个数为:";
    		SelectSort_KMinNum(nArr2, 8, k);		
    
            cout << "第三个数组的最小的" << k << "个数为:";
    		SelectSort_KMinNum(nArr3, 8, k);		
    	}
        system("pause");
    	return 0;
    }
    

    运行结果:



    冒泡排序代码:

     

    #include "stdafx.h"
    #include <iostream>
    using namespace std;
    
    //利用冒泡排序找到最小的k个数,进行k次选择即可
    void BubbleSort_KMinNum(int nArr[], int nLength, int k)
    {
    	if (nArr == NULL || nLength <= 0 || k>nLength)
    	{
    		cout << "输入有误!" << endl;
    		return;
    	}
    	
    	for (int i=1; i<=k; i++)//进行k次冒泡过程
    	{		
    		for (int j=0; j<nLength-i; j++)
    		{			
    			if (nArr[j] < nArr[j+1])
    			{
    				int temp = nArr[j];
    				nArr[j] = nArr[j+1];
    				nArr[j+1] = temp;
    			}
    		}	
    
    		cout << nArr[nLength-i] << " ";
    	}
    	cout << endl;
    }
    
    int _tmain(int argc, _TCHAR* argv[])
    {
    	int nArr1[8] = {4, 5, 1, 6, 2, 7, 3, 8};
    	int nArr2[8] = {4, 5, 2, 6, 2, 5, 3, 5};
    	int nArr3[8] = {1, 1, 1, 1, 1, 1, 1, 1};
    
    	while (1)
    	{
    		int k = 0;
    		cout << "请输入最小数的个数:";
    		cin >> k;
    		if (k == 0)
    		{
    			break;
    		}
    
    		cout << "第一个数组的最小的" << k << "个数为:";
    		BubbleSort_KMinNum(nArr1, 8, k);		
    
    		cout << "第二个数组的最小的" << k << "个数为:";
    		BubbleSort_KMinNum(nArr2, 8, k);		
    
    		cout << "第三个数组的最小的" << k << "个数为:";
    		BubbleSort_KMinNum(nArr3, 8, k);		
    	}
    	system("pause");
    	return 0;
    }
    

    运行结果:



     

    方法三 计数排序 + 数组实现  适合数据量小的数据,不提倡使用

    使用计数排序,另开辟一个数组,记录每个整数出现的次数,然后再从大到小取最大的 K 个。

    代码:

     

    #include "stdafx.h"
    #include <iostream>
    using namespace std;
    
    //利用计数+数组找到最小的k个数
    void CountArr_KMinNum(int nArr[], int nLength, int k)
    {
    	if (nArr == NULL || nLength <= 0 || k>nLength)
    	{
    		cout << "输入有误!" << endl;
    		return;
    	}
    
    	int nMax = nArr[0];
    	for (int i=1; i<nLength; i++)
    	{
            if (nMax < nArr[i])
            {
    			nMax = nArr[i];
            }
    	}
    
    	int *pArr = new int[nMax+1];//开辟一个数组
    	memset(pArr, 0, (nMax+1)*sizeof(int));
    
    	for (int j=0; j<nLength; j++)
    	{	
    		pArr[nArr[j]]++;			
    	}
    	
    	int nCount = 0;
    	for (int z=0; z<nMax+1; z++)
    	{		
    		if (pArr[z]>0)
    		{
    			while ((pArr[z]--)>0)
    			{
    				if (nCount < k)
    				{
    					cout << z << " ";
    					nCount++;
    				}
    				else
    				{
                        break;
    				}
    			}			
    		}	
    	}
    
    	delete []pArr;
    	pArr = NULL;
        cout << endl;
    }
    
    int _tmain(int argc, _TCHAR* argv[])
    {
    	int nArr1[8] = {4, 5, 1, 6, 2, 7, 3, 8};
    	int nArr2[8] = {4, 5, 2, 6, 2, 5, 3, 5};
    	int nArr3[8] = {1, 1, 1, 1, 1, 1, 1, 1};
    
    	while (1)
    	{
    		int k = 0;
    		cout << "请输入最小数的个数:";
    		cin >> k;
    		if (k == 0)
    		{
    			break;
    		}
    
    		cout << "第一个数组的最小的" << k << "个数为:";
    		CountArr_KMinNum(nArr1, 8, k);		
    
    		cout << "第二个数组的最小的" << k << "个数为:";
    		CountArr_KMinNum(nArr2, 8, k);		
    
    		cout << "第三个数组的最小的" << k << "个数为:";
    		CountArr_KMinNum(nArr3, 8, k);		
    	}
    	system("pause");
    	return 0;
    }
    

    运行结果:



    方法四、利用STL中的map保存每个数出现的次数,找到K个数

    时间复杂度O(n*logn)     空间复杂度O(n)

    注意:1、不能使用CMap实现,因为Cmap不能根据key自动为其排序;2、map内部是由红黑树实现的,每次插入都是logn,总的复杂度为n*logn。

    代码:

     

    #include "stdafx.h"
    #include <iostream>
    #include <map>
    using namespace std;
    
    //利用map计数找到最小的k个数
    void MapCount_KMinNum(int nArr[], int nLength, int k)
    {
    	if (nArr == NULL || nLength <= 0 || k>nLength)
    	{
    		cout << "输入有误!" << endl;
    		return;
    	}	
    
        map<int,int> countMap;
    
    	for (int j=0; j<nLength; j++)
    	{	
    		if (countMap[nArr[j]]==0)
    		{
    			countMap[nArr[j]] = 1;
    		}
    		else
    		{
    			countMap[nArr[j]]++;
    		}					
    	}	
    
    	int nCount = 0;
    	for (map<int, int>::iterator itr=countMap.begin(); itr!=countMap.end(); itr++)
    	{
    		if (itr->second > 0)
    		{
    			while ((itr->second--) > 0)
    			{
                    if (nCount < k)
                    {
    					cout << itr->first << " ";
    					nCount++;
                    }
    				else
    				{
    					break;
    				}
    			}
    		}
    	}
    	cout << endl;
    }
    
    int _tmain(int argc, _TCHAR* argv[])
    {
    	int nArr1[8] = {4, 5, 1, 6, 2, 7, 3, 8};
    	int nArr2[8] = {4, 5, 2, 6, 2, 5, 3, 5};
    	int nArr3[8] = {1, 1, 1, 1, 1, 1, 1, 1};
    
    	while (1)
    	{
    		int k = 0;
    		cout << "请输入最小数的个数:";
    		cin >> k;
    		if (k == 0)
    		{
    			break;
    		}
    
    		cout << "第一个数组的最小的" << k << "个数为:";
    		MapCount_KMinNum(nArr1, 8, k);		
    
    		cout << "第二个数组的最小的" << k << "个数为:";
    		MapCount_KMinNum(nArr2, 8, k);		
    
    		cout << "第三个数组的最小的" << k << "个数为:";
    		MapCount_KMinNum(nArr3, 8, k);		
    	}
    	system("pause");
    	return 0;
    }
    

    运行结果:



    方法五、维护一个大小为k的大根堆,初始化为数组中前k个元素,调整为大根堆。对于数组中的剩下的数,判断与堆顶的大小。如果比堆顶大,则不需要改变原来的堆;如果比堆顶小,要将其替换堆顶的元素调整为大根堆。

     

    时间复杂度: O (N * log2 K ),算法只需扫描所有的数一次,调整堆的时间复杂度为O(log2K)

    空间复杂度:O(k),需一个大小为k 的堆。

    代码:

     

    #include "stdafx.h"
    #include <iostream>
    using namespace std;
    
    //调整堆(大根堆)函数,待调整元素的下标nIndex,
    void AdjustHeap(int nHeap[], int nLength, int nIndex)
    {	
    	int key = nHeap[nIndex];//待调整元素的值
    	for (int i=2*nIndex+1; i<nLength; i=2*i+1)
    	{		
    		if (i+1 < nLength && (nHeap[i] < nHeap[i+1]))//有右孩子且右孩子的值比左孩子大
    		{			
    			i++;//i指向表示较大孩子下标					
    		}
            if (nHeap[i] < key)//不需要调整
            {
                break;
            }
    		nHeap[nIndex] = nHeap[i];
    		nIndex = i;		
    	}
    	nHeap[nIndex] = key;
    }
    
    //找到数组nArr中最小的k个数
    int* MinKNum(int nArr[], int nLength, int k)
    {
    	if (nArr != NULL && nLength > 0 && k <= nLength)
    	{
    		//维护一个大小为k的大根堆,初始化为数组的前k个元素
    		int *nHeap = new int[k];
            for (int i=0; i<k; i++)
            {
    			nHeap[i] = nArr[i];
            }
    		for (int t=k/2-1; t>=0; t--)
    		{
    			AdjustHeap(nHeap, k, t);//调整为大根堆
    		}
    		
            for (int j=k; j<nLength; j++)
            {
    			if (nArr[j] < nHeap[0])//剩下的元素依次与堆顶元素进行比较,若比其小则替换堆顶元素
    			{
                   nHeap[0] = nArr[j];
    			   AdjustHeap(nHeap, k, 0);			   
    			}
            }
    		return nHeap;
    	}
    	else
    	{
    		return NULL;
    	}
    }
    
    int _tmain(int argc, _TCHAR* argv[])
    {
    	int nArr[8] = {4, 5, 1, 6, 2, 7, 3, 8};
    	//int nArr[8] = {1, 2, 3, 4, 5, 6, 7, 8};
    	//int nArr[8] = {2, 2, 2, 2, 2, 2, 2, 2};
    	int k = 0;
    	while (1)
    	{
    		cout << "请输入最小数的个数:";
    		cin >> k;
    		if (k == 0)//输入0表示程序结束
    		{
    			break;
    		}
    		int *p_nHeap = MinKNum(nArr, 8, k);
    		cout << "最小的" << k << "个数为:";
    		for (int i=0; i<k; i++)
    		{
    			cout << p_nHeap[i] << " ";
    		}
    		cout << endl;
    
    		delete [] p_nHeap;	
    		p_nHeap = NULL;
    	}	
    
    	system("pause");
    	return 0;
    }
    

    运行结果:







  • 相关阅读:
    剑指offer-翻转单词序列
    剑指offer-丑数
    剑指offer-把数组排成最小的数
    mysql笔记(13)-视图的概念和使用
    mysql笔记(12)-外键约束的添加和删除
    mysql笔记(11)-约束的添加、修改和删除
    mysql笔记(10)-数据的插入和更新(insert/update/case)
    mysql笔记(9)-表的创建和删除(drop/truncate/delete)
    mysql笔记(8)-嵌套查询之in、exists
    mysql笔记(7)-多表查询之自然连接、外连接
  • 原文地址:https://www.cnblogs.com/javawebsoa/p/3212599.html
Copyright © 2011-2022 走看看