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

Posted void-lambda

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了剑指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);
        }
    }
}

以上是关于剑指offer-找到第k大的数,找到数组中个数超过一半的数,找到数组中最小的k个数。的主要内容,如果未能解决你的问题,请参考以下文章

剑指 Offer 45. 把数组排成最小的数 剑指 Offer 61. 扑克牌中的顺子 剑指 Offer 40. 最小的k个数

剑指offer40最小的K个数

剑指offer系列54---数组中出现次数超过一半的数

剑指offer(29)最小的K个数

剑指offer数组中只出现一次的数

剑指offer-010-和为k的子数组