剑指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);
		}
	}
}
原文地址:https://www.cnblogs.com/void-lambda/p/12379844.html