• 剑指offer-找到第k大的数,找到数组中个数超过一半的数,找到数组中最小的k个数。


    1.划分

    函数partition用于将数组分为两段,一段返回小于基准值,一段大于基准值。并且基准值到达它应该在的位置。返回基准值的下标。

    代码:

    int Partition(int arr[], int left, int right)
    {
    	int i = left;
    	int j = right;
    	int pivotindex = left; //基准坐标可以任意选
    	int pivotnum = arr[pivotindex]; //基准数
    	cout << "pivotnum = " << pivotnum << endl;
    	while (i < j) //当i==j的时候,i和j的下标就是基准数的正确位置。
    	{
    		while (i < j && arr[j] >= pivotnum) //先从后面往前找小于基准数
    			j--;
    		arr[i] = arr[j];   //复制到前面i的位置,不用交换
    		while (i < j && arr[i] <= pivotnum)  //再从前往后找大于基准数的值
    			i++;
    		arr[j] = arr[i]; //复制到后面j的位置。
    	}
    	arr[i] = pivotnum;  //i,j的位置就是要找的基准数所在的位置。至此数组划分完毕。
    	return i;
    }
    

    2.找第k大的数

    思路:

    用划分的思想,每次找到的下标和k-1做比较,index>k-1,在左边序列找,index<k-1,在右边序列找。

    代码:

    int Findkth(int arr[], int left,int right,int k)
    {
    	int index= Partition(arr, left,right);
    	if (index == k-1 )//第k个数就是下标为k-1
    		return arr[index];
    	else if (index < k-1)
    	{
    		return Findkth(arr, index + 1, right,k);
    	}
    	else if (index > k-1)
    	{
    		return Findkth(arr, left, index-1,k);
    	}
    }
    

    找到数组中个数超过一半的数

    思路1:

    既然这个数的个数超过总个数的一半,那么如果把数组排序,在中间的那个数,必定是所要找的数。所以把问题转化为找n/2大的数。

    代码

    int Findmorehalf(int arr[],int len)
    {
    	Findkth(arr, 0, len - 1, len / 2);
    }
    

    思路2:

    用士兵攻守阵地的思想,用一个变量保存数,遍历数组,遇到一个不同的数,计数减一(同归于尽),否则计数加一(抱团),当计算为0(团灭),重置变量和计数。最后所保存的变量就是个数最大的数。

    代码:

    int Findmorehalf2(int arr[], int len)//攻守阵地的思想
    {
    	if (len == 0)
    		return 0;
    	if (len == 1)
    		return arr[0];
    	int num = arr[0];
    	int cnt = 1;
    	for (int i = 0; i< len; i++)
    	{
    		if (arr[i] != num)
    			cnt--;
    		else
    			cnt++;
    		if (cnt == 0)
    		{
    			num = arr[i];
    			cnt = 1;
    		}
    	}
    	int count = 0;  //验证这个数是否超过半数
    	for (int i = 0; i< len; i++)
    	{
    		if (arr[i] == num)
    			count++;
    	}
    	if (count > len / 2)
    		return num;
    	else
    		return 0;
    }
    

    找到数组中最小的k个数。

    思路1:O(n)

    用划分的思想,找到第下标为k的数,前面的数就是全比这个数小的数字。

    代码:

    int Findkth(int arr[], int left, int right, int k)
    {
    	int index = Partition(arr, left, right);
    	if (index == k - 1)//第k个数就是下标为k-1
    		return index;
    	else if (index < k - 1)
    	{
    		return Findkth(arr, index + 1, right, k);
    	}
    	else if (index > k - 1)
    	{
    		return Findkth(arr, left, index - 1, k);
    	}
    }
    void Find1_7(int arr[], int len,int k)
    {
    	int index=Findkth(arr ,0, len-1,  k);
    	for (int i = 0; i < index+1; i++)
    		cout << arr[i] << ' ';
    }
    

    思路2:O(nlogk)适合处理海量数据

    遍历数组,用一个容器保存最大的k个数,和k个数的最大数。当容器不满时,数字直接加入容器。否则将它与最大数比较,若小于则替换它。红黑树可以在O(logk)时间内插入,删除,找到最大值。

    代码:

    //2.(Onlogk) 处理海量数组  
    typedef multiset<int, greater<int>> intset;  //用红黑树存储7个最小值  greater<int>表示这个集合从大到小排列
    typedef multiset<int, greater<int>>::iterator setit; //迭代器
    void get1_7num(const vector<int>& data, intset& leastnum, int k)  
    {
    	leastnum.clear();
    	if (k <= 0 || data.size() < 7)
    		return;
    	vector<int>::const_iterator it = data.begin();
    	for (; it != data.end(); it++)
    	{
    		if (leastnum.size() < 7)
    			leastnum.insert(*it);
    		else
    		{
    			setit greatit = leastnum.begin();  //取代最大值
    			if (*it < *leastnum.begin())
    				leastnum.erase(*greatit);
    			leastnum.insert(*it);
    		}
    	}
    }
    
  • 相关阅读:
    05Linux系统常用命令
    04Linux系统目录结构
    02Linux文件系统基本结构
    01Linux_BASH基本操作
    [HTML] HTML常用标签及HTML语义化理解
    [Tools] MDN简介及如何使用MDN查找资料
    [HTTP] 初识HTTP
    [Git] Git 入门(常用命令使用)
    [CL](入门)命令行常用命令使用技巧
    IFE_part2_JavaScript_Ajax学习
  • 原文地址:https://www.cnblogs.com/void-lambda/p/12379844.html
Copyright © 2020-2023  润新知