topk算法

方法一 堆排序

自建堆 heapMax方法,从上至下调整堆
pop时,可以使用自上而下调整堆,调用heapMax(arr,0,sz-1);
push时,需要自下到上调整即

从上到下调整:

    void heapDown(vector<int>& arr,int start,int end)
    {
        int dad = start;
        int son = 2 * dad + 1;

        while(son<=end) //可以取到end
        {
            if(son+1<=end && arr[son]<arr[son+1]) ++son;

            if(arr[son]<arr[dad]) return;

            else
            {
                swap(arr[son],arr[dad]);
                dad = son;
                son = 2 * dad + 1;
            }

        }
    }

从下到上:

```cpp
//从下到上调
int son = sz-1;
int dad = (son-1)/2;
while(dad>=0)
{
    if(arr[son]<=arr[dad]) return;
    else
    {
        swap(arr[son],arr[dad]);
        son = dad;
        dad = (son-1)/2;
    }
}
```

 建堆

for(int i=(len/2)-1;i>=0;--i)
{
   heapDwon(heap,i,len-1); 
}

堆代码

class Solution {
public:
    //堆排序 从0 开始
    void heapDown(vector<int>& arr,int start,int end)
    {
        int dad = start;
        int son = 2 * dad + 1;

        while(son<=end) //可以取到end
        {
            if(son+1<=end && arr[son]<arr[son+1]) ++son;

            if(arr[son]<arr[dad]) return;

            else
            {
                swap(arr[son],arr[dad]);
                dad = son;
                son = 2 * dad + 1;
            }

        }
    }
    
    void push(vector<int>& arr,int val)
    {
        arr.push_back(val);
        int sz = arr.size();
        //从下到上调
        int son = sz-1;
        int dad = (son-1)/2;
        while(dad>=0)
        {
            if(arr[son]<=arr[dad]) return;
            else
            {
                swap(arr[son],arr[dad]);
                son = dad;
                dad = (son-1)/2;
            }
        }
    }

    void pop(vector<int>& arr)
    {
        swap(arr[0],arr[arr.size()-1]);
        arr.pop_back();
        int sz = arr.size();
        heapMax(arr,0,sz-1);
    }

    vector<int> smallestK(vector<int>& arr, int k) {
        //堆
        // priority_queue<int,vector<int>,less<int>> heap; //大顶堆
        // //priority_queue<int,vector<int>,greater<int>> c;
        //自定义堆
        
        if(k==0) return {};
        vector<int> heap(k);
        for(int i=0;i<k;++i)
        {
            heap[i] = arr[i];
        }
        
        for(int i=k/2-1;i>=0;--i)
        {
            heapMax(heap,i,k-1);
        }

        for(int i=k;i<arr.size();++i)
        {
            
            if(arr[i]<heap[0])
            {
                pop(heap);
                push(heap,arr[i]);
            }
        }
        return heap;
        
    }
};

 方法二:快排思维(平均时间复杂度O(n)) topk思路

partition函数负责每次找到pivot,并分为2段
//加入随机思路
int randIndex = rand(time) % ((right-left+1)+left);
swap(arr[randIndex],arr[right]);
int pivot = arr[right];

判断条件:
1. k-1<p 说明k在p左边,因此递归查找左边
2. k-1>p 说明k在p右边,因此递归查找左边
3. k-1==p ,说明找到第k大,即左边为k个最小的元素

```cpp
  //快排
    int partition(vector<int>& arr,int left,int right)
    {
        int i = left,j=left;
        int randIndex = rand(time) %((right - left + 1) + left); //长度加left
        swap(arr[randIndex],arr[right]);
        int pivot = arr[right];
        for(;j<right;++j)
        {
            if(arr[j]<pivot)
            {
                swap(arr[i],arr[j]);
                ++i;
            }
        }
        swap(arr[i],arr[right]);
        return i;
    }

    void helper(vector<int>& arr, int k,int left,int right,vector<int>& res)
    {
        if(left>right) return;
        int p = partition(arr,left,right);
        if(k-1==p) //find k
        {
            for(int i=0;i<k;++i)
            {
                res.push_back(arr[i]);
            }
        }

        else if(k-1<p)
        {
            helper(arr,k,left,p-1,res);
        }
        else
        {
            helper(arr,k,p+1,right,res);
        }
        return;
    }
```

posted @ 2021-09-20 10:49  勒勒乐了  阅读(67)  评论(0)    收藏  举报