LeetCode题解之排序算法(一)

173 阅读3分钟

1.快速排序

i找到>=x的数后停止,j找到<=x的数后停止,如果i<j就交换,否则就递归分出来的两段

1.1 步骤:

  1. 确定分界点:由于跳出循环时是i == j或者j = i - 1,当j = i - 1时,由于i找到>=x的数停止,当[i]==x时,j走过去也会因为==[x]而停止;当[i]>x时,j会走过去走到左边就遇到小数停止了。所以拿[l,j]和[j+1,r]会将两段分开,[l,i-1]和[i,r]也可以,这样左段是<=x的数,右段是>=x的数。
  2. 调整左段是<=x的数,右段是>=x的数,x不一定在中间
  3. 递归左右两边
  4. 取整方式:采用[l,j]和[j+1,r]需要下取整,因为假如数组[1,2],上取整的话,i==j==1,永远是[1,2]递归;同理[l,i-1]和[i,r]要上取整~

1.2 基本代码

#include <iostream>
using namespace std;

const int N = 1e6 + 10;//开辟一个最大数组
int n;
int q[N];
void quick_sort(int q[], int l, int r)
{
    if(l >= r) return;//为空或者只有一个值返回
    //第一步确定边界点,此处用中间那个值
    int x = q[l + r >> 1], i = l - 1, j = r + 1;//这样可以方便判断第一个数和最后一个数
    while ( i < j)
    {
        do i ++ ; while (q[i] < x);
        do j -- ; while (q[j] > x);
        if ( i < j) swap(q[i], q[j]);//手写swap:int t = q[i];q[i]=q[j];q[j]=t
    }
    quick_sort(q,l,j);
    quick_sort(q,j + 1,r);  
}
int main()
{
    scanf("%d",&n);
    for(int i = 0; i < n; i ++) scanf("%d",&q[i]);//输入数组
    quick_sort(q, 0, n-1);//快排数组q中0到n-1的字段
    for(int i = 0;i < n;i ++) printf("%d ",q[i]);//输出答案,注意q[i]不加&
    return 0;
}

1.3 最小的k个数

#include <iostream>
using namespace std;
const int N = 100010;
int n, k;
int q[N];
int quick_sort(int l,int r,int k)
{
    if(l == r) return q[l];//区间长度是1的时候一定是答案:因为每次都会按照某个值大小从中间分开,所以会不断缩小范围
    int x = q[l],i = l - 1,j = r + 1;
    while(i < j)
    {
        while(q[ ++ i] < x);//求最大的k个数做法一样,左边找>x右边找<x即可
        while(q[ -- j] > x);
        if(i < j) swap(q[i], q[j]);
        
    }
    int sl = j - l + 1;//多了计算个数的部分
    if(k <= sl) return quick_sort(l, j, k);
    return quick_sort(j + 1, r, k-sl);
}
int main()
{
    cin>>n>>k;
    for(int i = 0;i < n;i ++) cin>>q[i];
    cout<<quick_sort(0, n - 1, k)<<endl;
    return 0;
}

1.4 347. 前 K 个高频元素

class Solution {
public:
/*
在快排模版基础上改为PII:值-频率  的数据结构,对频率快排即可
*/
    using PII = pair<int,int>;
    vector<PII> arra;
    vector<int> res;
    vector<int> topKFrequent(vector<int>& nums, int k) {
        unordered_map<int, int> mp;
        for(auto i: nums) mp[i] ++;
        for(auto p: mp) arra.push_back(p);
        quick_sort(k, 0, arra.size() - 1);
        return res;
    }
    void quick_sort(int k, int l, int r)
    {
        if(l == r)
        {
            res.push_back(arra[l].first);
            return;
        }
        PII x = arra[(l + r) >> 1];
        int  i = l - 1, j = r + 1;
        while(i < j)
        {
            do ++ i; while(arra[i].second > x.second);
            do -- j; while(arra[j].second < x.second);
            if(i < j) swap(arra[i],arra[j]);
        }
        if(j - l + 1 >=k) quick_sort(k, l, j);//左边够了
        else
        {
            for(int m = l; m <= j; m ++)
                res.push_back(arra[m].first);
            quick_sort( k - (j - l + 1), j + 1, r);
        }
    }
};

1.5 973. 最接近原点的 K 个点

class Solution {
public:
    vector<vector<int>> res;
    using PII = pair<vector<int>, int>;
    vector<PII> arra;
    vector<vector<int>> kClosest(vector<vector<int>>& points, int k) {
        for(auto p: points) arra.push_back(make_pair(p,p[0]*p[0]+p[1]*p[1]));
        quick_sort(0, arra.size() - 1,k);
        return res;
    }
    void quick_sort(int l, int r, int k)
    {
        if(l == r)
        {
            res.push_back(arra[l].first);
            return;
        }
        PII x = arra[(l + r) >> 1];
        int i = l - 1, j = r + 1;
        while(i < j)
        {
            do i ++; while(arra[i].second < x.second);
            do j --; while(arra[j].second > x.second);
            if(i < j) swap(arra[i], arra[j]);
        }
        int left = j - l + 1;
        if(k <= left) quick_sort(l, j, k);
        else
        {
            for(int t = l; t <= j; t ++) res.push_back(arra[t].first);
            quick_sort(j + 1, r, k - left);
        }
    }
    
};

2.归并排序

2.1 步骤

  1. 确定分界点:q[l + r >> 1]
  2. 递归左右排序
  3. 归并合二为一:o(n)辅助数组tmp

2.2 基本代码

#include <iostream>
using namespace std;
const int N = 1000010;
int n;
int q[N], tmp[N];
void merge_sort(int q[], int l,int r)
{
    if(l >= r) return;
    int mid = (l + r) >> 1;
    //第二步分别递归
    merge_sort(q, l, mid),merge_sort(q, mid + 1, r);
    //第三步归并,用辅助数组tmp
    int k = 0, i = l, j = mid + 1;
    while(i <= mid && j <=r)
        if(q[i] <= q[j]) tmp[k ++] = q[i ++];
        else tmp[k ++] = q[j ++];
    while (i <= mid) tmp[k ++] = q[i ++];
    while (j <= r) tmp[k ++] = q[j ++];
    //归并后把[l,r]复制回去
    for(i = l, j = 0; i<= r;i ++,j ++) q[i] = tmp[j];
}
int main()
{
    scanf("%d",&n);
    for(int i = 0;i < n; i ++) scanf("%d",&q[i]);
    merge_sort(q,0,n-1);
    for(int i = 0;i < n; i ++) printf("%d ",q[i]);
    return 0;
}

2.3 剑指 Offer 51. 数组中的逆序对

class Solution {
public:
    int tmp[50010];
    vector<int> q;
    int reversePairs(vector<int>& nums) {
        int n = nums.size();
        if(nums.empty()) return 0;
        q = nums;
        int res = merge_sort(0, n - 1);
        return res;
    }
    int merge_sort(int l, int r)
    {
        if(l == r) return 0;
        int mid = (l + r) >> 1;
        long long res = merge_sort(l, mid) + merge_sort(mid + 1, r);
        int i = l, j = mid + 1, k = 0;
        while(i <= mid && j <= r)
        {
            if(q[i] <= q[j]) tmp[k ++] = q[i ++];
            else
            {
                res += mid - i + 1;//当第16行的子层返回的是res = 0+0时,这里需要加上那里的结果,再作为子层返回到上一层的16行,其实递归就是这样的,递归在最后,则顺序的执行下去,直到执行结束,在中间则到叶子节点后返回,才会执行下半段的内容
                tmp[k ++] = q[j ++];
            }
        }
        while(i <= mid) tmp[k ++] = q[i ++];
        while(j <= r) tmp[k ++] = q[j ++];
        for(int i = l,j = 0;i <= r;) q[i ++] = tmp[j ++];
        return res;
    }
    
};