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);
}
}
}