算法模板 5.十大排序

十大排序

在这里插入图片描述

复杂度 / 稳定性

在这里插入图片描述

交换两元素的3种方法

利用临时数tmp

void swap(int q[], int i, int j){
	int tmp = q[i];
	q[i] = q[j];
	q[j] = tmp;
}

加减法

使用加减法,有可能导致溢出。

void swap_cal(int q[], int i, int j){
	if(i == j) return;
	q[i] = q[i] + q[j];
    q[j] = q[i] - q[j];
    q[i] = q[i] - q[j];
}

异或

void swap_xor(int q[], int i, int j){
	if(i == j) return;
	q[i] ^= q[j];//a = a ^ b
	q[j] ^= q[i];//b = (a ^ b) ^ b = a ^ (b ^ b) = a ^ 0 = a
	q[i] ^= q[j];//a = (a ^ b) ^ a = (a ^ a) ^ b = 0 ^ b = b
}

快速排序

具体思路如下:

  1. 在区间中取一个值x
  2. 通过双指针 i , j 的方式,将一个区间分成两段,左边的值小于x,右边的值大于x
  3. 递归,分别排序左边区间[l , j] 和右边区间[j+1,r]
#include <bits/stdc++.h>
using namespace std;
const int N = 100010;
int q[N];
void quick_sort(int q[],int l,int r){
    
    if(l >= r)  return;//如果只有1个元素或者l<r 则返回
    int x = q[l+r>>1],i = l - 1,j = r + 1;//定义x,取中点的值,定义双指针,这里要定义在往区间外部扩1(由于下面双指针编程决定的)
    
    while(i < j){
        while(q[++i] < x);//移动直到左段中q[i]大于x
        while(q[--j] > x);//移动直到右段中q[i]小于x
        if(i < j) swap(q[i],q[j]);//交换这两个元素
        //本来交换完是要再次移动的,但在q[++i]和q[--j]中实现了先移动再判断大小
    }
    //递归快排
    quick_sort(q , l , j);//[l,j]
    quick_sort(q , j + 1 , r);//[j+1,r]
}
int main(){
    int n;
    scanf("%d",&n);
    
    for(int i=0;i<n;i++)    scanf("%d",&q[i]);//读数据
    
    quick_sort(q,0,n-1);
    
    for(int i=0;i<n;i++)    printf("%d ",q[i]);//打印
    
    return 0;
}

归并排序

具体思路如下:

  1. 取区间的中点
  2. 递归排序左边区间和右边区间
  3. 通过双指针 i , j 的方式,将两段有序区间合并成一段区间
  4. 将合并完的区间赋值到原区间
#include <bits/stdc++.h>
using namespace std;

const int N=100010;

int q[N],temp[N];

void merge_sort(int q[],int l,int r){

    if(l >= r)    return;
    int mid = l + r>>1,i = l,j = mid + 1;

    int k = 0;

    merge_sort(q,l,mid);
    merge_sort(q,mid + 1,r);

    while(i <= mid && j <= r){
        if(q[i] <= q[j]){
            temp[k++] = q[i++];
        }
        else temp[k++] = q[j++];
    }
    while(i <= mid) temp[k++]=q[i++];
    while(j <= r) temp[k++]=q[j++];

    for(int t=0;t<k;t++){
        q[l+t]=temp[t];
    }
}
int main(){
    int n;
    scanf("%d",&n);
    for(int i=0;i<n;i++)    scanf("%d",&q[i]);
    //memset(temp,0,sizeof(temp));
    merge_sort(q,0,n-1);

    for(int i=0;i<n;i++)    printf("%d ",q[i]);
    return 0;
}

冒泡排序

思想:每次寻找一组数据中最大的元素,两两比较,找到后放到数据的最后一位。n个数据第一次两两比较n-1次。
第二次比较n-2次,比较的时候如果前一个元素比第二个元素大则相互交换。
总共两层循环:第一层代表需对比几轮;第二层代表需要两两对比多少次.

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef pair<int,int> pii;
const int MOD = 1e9 + 7, N = 1e5 + 10;
int q[N];
int n;
void bubble_sort(){
    for(int i = 0; i < n - 1; i++){//第i轮
        for(int j = 0; j < n - 1 - i; j++){//把第i轮最大的元素移动到第n-i-1的位置
            if(q[j] > q[j+1]) swap(q[j],q[j+1]); 
        }
    }
}

int main()
{
    scanf("%d", &n);
    for(int i = 0; i < n; i++) scanf("%d", &q[i]);
    bubble_sort();
    for(int i = 0; i < n; i++) printf("%d ", q[i]);
    return 0;
}

选择排序

和冒泡排序类似,但是选择排序每轮只交换一次。

每轮记录最小的数字下标,然后和最前面的数字进行交换(swap(q[i] , q[minIdx]) ),重复上述过程,重复n-1轮。是不稳定排序,如772,第一轮会把第一个7和2进行交换。

void selection_sort(){
    for (int i = 0; i < n - 1; i++)
    {
        int minIdx = i;
        for (int j = i + 1; j < n; j++)
        {
            if(q[j] < q[minIdx]) minIdx = j;
        }
        if(minIdx != i) swap(q[i],q[minIdx]);
    }
}

插入排序

对于待排序数组,从第 2 个元素开始 (称作插入对象元素) ,比较它与之前的元素 (称作比较对象元素) ,当插入对象元素小于比较对象元素时,继续往前比较,直到不小于 (≥) 比较对象,此时将插入对象元素插入到该次比较对象元素之后。

重复这个插入过程直到最后一个元素作为插入对象元素完成插入操作。

以下是简单插入排序:

void insertion_sort(){
    if (n < 2) return;   
    for (int i = 1; i < n; i++)
    {
        int target = q[i], j = i - 1;
        //向左搜索插入位置
        while (j >= 0 && target < q[j])
        {
            q[j + 1] = q[j];
            j--;
        }
        q[j + 1] = target; // 插入目标值
    }
}

折半插入排序:(其实就是把向左搜索插入位置改成二分查找),虽然查找的时间复杂度为Ologn但是线性移动复杂度为On,因此总的时间复杂度还是On方

希尔排序

希尔排序(Shell Sort)是插入排序的一种,它是针对直接插入排序算法的改进。

希尔排序实质上是一种分组插入方法。它的基本思想是:对于n个待排序的数列,取一个小于n的整数gap(gap被称为步长)将待排序元素分成若干个组子序列,所有距离为gap的倍数的记录放在同一个组中;然后,对各组内的元素进行直接插入排序。 这一趟排序完成之后,每一个组的元素都是有序的。然后减小gap的值,并重复执行上述的分组和排序。重复这样的操作,当gap=1时,整个数列就是有序的。

void shell_sort1(int a[], int n)
{
    int i,j,gap;

    // gap为步长,每次减为原来的一半。
    for (gap = n / 2; gap > 0; gap /= 2)
    {
        // 共gap个组,对每一组都执行直接插入排序
        for (i = 0 ;i < gap; i++)
        {
            for (j = i + gap; j < n; j += gap) 
            {
                // 如果a[j] < a[j-gap],则寻找a[j]位置,并将后面数据的位置都后移。(下面这个部分就是插入排序)
                if (a[j] < a[j - gap])
                {
                    int tmp = a[j];
                    int k = j - gap;
                    while (k >= 0 && a[k] > tmp)
                    {
                        a[k + gap] = a[k];
                        k -= gap;
                    }
                    a[k + gap] = tmp;
                }
            }
        }

    }
}

堆排序

手写大根堆,主要是建堆和下滤down操作。

以下代码是输入n个数,堆排后(从小到大)输出前m个数

#include <bits/stdc++.h>

using namespace std;

const int N = 100010;

int h[N],cnt;

void down(int u){
    
    int t = u;
    if(u * 2 <= cnt && h[u * 2] < h[t]) t = u * 2;
    if(u * 2 + 1 <= cnt && h[u * 2 + 1] < h[t]) t = u * 2 + 1;
    if(t!=u){
        swap(h[t],h[u]);
        down(t);
    }
}

int main(){
    int n,m;
    scanf("%d%d",&n,&m);
    for(int i = 1;i <= n;i++)  scanf("%d",&h[i]);
    cnt = n;
    
    for(int i = n/2;i;i--)  down(i);
    
    while(m--){
        cout << h[1] << " ";
        h[1] = h[cnt];//把第一个元素 替换为最后一个元素 即最大的元素,然后再down一遍
        cnt--;
        down(1);
    }
    return 0;
}

计数排序

在这里插入图片描述

void counting_sort(){
    if(n < 2) return;
    int minVal = q[0], maxVal = q[0];
    for (int i = 1; i < n; i++)
    {
        minVal = min(minVal, q[i]);
        maxVal = max(maxVal, q[i]);
    }
    vector<int> arr(maxVal - minVal + 1);
    for (int i = 0; i < n; i++)
    {
        arr[q[i] - minVal]++;//计数
    }
    int idx = 0;
    for(int i = 0; i < (maxVal - minVal + 1); i++){
        for(int j = 0; j < arr[i]; j++){
            q[idx] = i + minVal;
            idx++;
        }
    }
}

桶排序

在这里插入图片描述

先对元素进行划分桶,然后对每个桶进行内部排序,最后根据桶序和桶内部序依次输出。

void bucketSort(int a[], int n, int N)
{
    int i,j;
    int buckets[N];//根据值域创建N个桶,假设每个单元都是一个桶

    // 将buckets中的所有数据都初始化为0。
    memset(buckets, 0, N * sizeof(int));

    // 1. 计数
    for(i = 0; i < n; i++) 
        buckets[a[i]]++; 

    // 2. 排序(计数排序)
    for (i = 0, j = 0; i < N; i++) 
    {
        while( (buckets[i]--) >0 )
            a[j++] = i;//输出
    }
}

基数排序

基数排序(Radix Sort)是桶排序的扩展,它的基本思想是:将整数按位数切割成不同的数字,然后按每个位数分别比较。
具体做法是:将所有待比较数值统一为同样的数位长度,数位较短的数前面补零。然后,从最低位开始,依次进行一次排序。这样从最低位排序一直到最高位排序完成以后, 数列就变成一个有序序列。

/*获取数组元素最大值*/
int get_max(int a[], int n)
{
    int i, max;

    max = a[0];
    for (i = 1; i < n; i++)
        if (a[i] > max)
            max = a[i];
    return max;
}

/*
 * 对数组按照"某个位数"进行排序(桶排序)
 *
 * 参数说明:
 *     a -- 数组
 *     n -- 数组长度
 *     exp -- 指数。对数组a按照该指数进行排序。
 *
 * 例如,对于数组a={50, 3, 542, 745, 2014, 154, 63, 616};
 *    (01) 当exp=1表示按照"个位"对数组a进行排序
 *    (02) 当exp=10表示按照"十位"对数组a进行排序
 *    (03) 当exp=100表示按照"百位"对数组a进行排序
 *    ...
 */
void count_sort(int a[], int n, int exp)
{
    int output[n];             // 存储"被排序数据"的临时数组
    int i, buckets[10] = {0};

    // 将数据出现的次数存储在buckets[]中
    for (i = 0; i < n; i++)
        buckets[ (a[i] / exp) % 10 ]++;

    // 更改buckets[i]。目的是让更改后的buckets[i]的值,是该数据在output[]中的位置。
    for (i = 1; i < 10; i++)
        buckets[i] += buckets[i - 1];

    // 将数据存储到临时数组output[]中
    for (i = n - 1; i >= 0; i--)
    {
        output[buckets[ (a[i]/exp) % 10 ] - 1] = a[i];
        buckets[ (a[i] / exp) % 10 ]--;
    }

    // 将排序好的数据赋值给a[]
    for (i = 0; i < n; i++)
        a[i] = output[i];
}

/*
 * 基数排序
 *
 * 参数说明:
 *     a -- 数组
 *     n -- 数组长度
 */
void radix_sort(int a[], int n)
{
    int exp;    // 指数。当对数组按各位进行排序时,exp=1;按十位进行排序时,exp=10;...
    int max = get_max(a, n);    // 数组a中的最大值

    // 从个位开始,对数组a按"指数"进行排序
    for (exp = 1; max/exp > 0; exp *= 10)
        count_sort(a, n, exp);
}
  • 13
    点赞
  • 19
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值