基础算法--排序

排序算法

  • 我们想要进行某些操作,例如找到班级里某一科目成绩最高的几位学生时,我们一定是先要对成绩进行一个从高到低的排序,然后才能直观的看出成绩的排名。
  • 在计算机中,最朴素的排序算法应该是冒泡排序,也即暴力排序,每次将相邻的两个元素比较&交换,时间复杂度约为 O ( n 2 ) O(n^2) O(n2),如果要排序的数据量非常大,如 1 0 6 10^6 106级甚至以上,用冒泡排序的效率必然是十分低下。那么,有没有高效的算法进行排序呢?

快速排序
  • 快速排序,时间复杂度约为 O ( n l o g n ) O(nlogn) O(nlogn),是一种相对高效的算法。

  • 原理:每次排序之前找到一个基准值,然后以该基准值为基础,将大于基准值的值往后放,小于基准值的值往前放。最后将基准值放到最终位置,再以基准值为分界线,左右部分进行相应的操作。直到全部排完序为止。(为了简单起见,我们一般都是取最左边的元素为基准值)。

  • 注意:排序时,前后两个指针,要先从后往前开始,否则会有部分元素排序失败。
    ![在这里插入图片描述](https://img-blog.csdnimg.cn/direct/4b5b9d1340b7452abfe1104ea8b10a6c.png

  • 核心代码如下:

    int QPass(int elem[], int low, int high)
    {
    	int l = low, r= high;
    	int standard = elem[low];
    	while(l < r)
    	{
    		//顺序不可更改
    		while(l < r && elem[r] >= standard) r--; //从右往左找第一个小于基准值standard的value
    		if(l < r) elem[l++] = elem[r];
    		while(l < r && elem[l] <= standard) l++; //从左往右找第一个小于基准值standard的value
    		if(l < r) elem[r--] = elem[l];
    	}
    	elem[l] = standard;
    	return l;//mid
    }
    void Qsort(int elem[], int low, int high)
    {
    	//不要忘了加终止条件,否则会死循环
    	if(low > high) return;
    	int mid;
    	mid = QPass(elem, low, high);
    	Qsort(elem, low, mid-1);
    	Qsort(elem, mid+1, high);
    }
    
  • 例题:AcWing 785: 快速排序

    给定你一个长度为 n n n的整数数列。请你使用快速排序对这个数列按照从小到大进行排序。

    输入格式:

    输入共两行,第一行包含整数n。
    第二行包含n个整数(所有整数均在 1 ∼ 1 0 9 1∼10^9 1109范围内),表示整个数列。

    输出格式:

    输出共一行,包含n个整数,表示排好序的数列。
    并将排好序的数列按顺序输出。

    数据范围: 1 < = n < = 1 e 6 1 <= n <=1e^6 1<=n<=1e6

  • 代码如下:

    #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]);
        }
        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);
        
        for(int i = 0; i < n; i++) printf("%d ", q[i]);
        
        return 0;
    }
    

归并排序
  • 相较于快速排序,归并排序的一大特点是其具有稳定性。时间复杂度定为 n l o g ( n ) nlog(n) nlog(n)

    • 其思想是:将数组进行二分,然后对子数组排好序之后,在依次合并为大数组。具体如本例,存在数组[12,1,32,6,7,5],利用归并思想将其排序:
      在这里插入图片描述
  • 模板代码如下:

    #include<iostream>
    using namespace std;
    const int N = 1e5+10;
    int q[N], tmp[N];//tmp为临时数组
    
    void merge_sort(int q[], int l, int r)
    {
        if(l >= r) return; //表示二分到底了
        int mid = l + (r-l)/2; //求二分中点
        //递归求左右子数组(以mid划分)
        merge_sort(q, l, mid); merge_sort(q, mid+1, r);
        
        //合并操作
        int i = l, j = mid + 1;
        int k = 0; 
        //开始将两个子数组合并为一个子数组
        while(i <= mid && j <= r)
        {
            if(q[i] <= q[j]) tmp[k++] = q[i++]; //小的元素先放入临时数组tmp
            else tmp[k++] = q[j++];
        }
        //合并完后发现,一定是其中一个数组先合并完成,另一个数组仍可能有元素,且归并自底向上完成,一定是有序的。
        while(i <= mid) tmp[k++] = q[i++];
        while(i <= mid) tmp[k++] = q[j++];
        
        //将值放回q数组。
        for(int i = l, j = 0; i <= r; i++, j++)
            q[i] = tmp[j];
    }
    
  • 例题:AcWing 787:归并排序

    题目:给定你一个长度为 n的整数数列。请你使用归并排序对这个数列按照从小到大进行排序。并将排好序的数列按顺序输出。
    输入输出与上面快速排序一致,范围一致。

  • 代码如下:

    #include<iostream>
    using namespace std;
    
    const int N = 1e6+10;
    
    int q[N], tmp[N];
    
    
    void merge_sort(int q[], int l, int r)
    {
        if(l >= r) return;
        int mid = l + (r - l) / 2;
        merge_sort(q, l, mid);
        merge_sort(q, mid+1, r);
        int i = l, j = mid + 1;
        int k = 0; //tmp
        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++];
        for(int i = l, j = 0; i <= r; i++, j++)
            q[i] = tmp[j];
    }
    
    
    int main()
    {
        ios::sync_with_stdio(0);
        cin.tie(0);cout.tie(0);
        int n;
        cin>>n;
        
        for(int i = 0 ;i < n; i++)
            cin>>q[i];
        
        merge_sort(q, 0, n - 1);
        
        for(int i = 0 ; i < n; i++)
            cout<<q[i]<<" ";
        return 0;
        
    }
    
  • 拓展应用:求数组中的逆序对数量(逆序对定义参考线性代数)

    void merge_sort(int a[], int l, int r, long long &count) //数据量很大,最大接近0.5e5!个元素
    {
        if( l >= r) return;
        int mid = (l+r)>>1;
        merge_sort(a, l, mid, count); merge_sort(a, mid + 1, r, count);
        
        int k = 0;
        int i = l,  j = mid + 1;//mid+1右半边起始边界
        while(i <= mid && j <= r)
        {
            if(a[i] <= a[j]) b[k++] = a[i++];
            else
            {
                count += (mid - i + 1 ); //即:假设前半部分和后半部分已排好序,前半部分a[i]元素大于后半部分a[j]元素,
                //则a[i]及其后面的mid-i个都有:大于a[j].所以共mid-i+1个逆序对
                b[k++] = a[j++];
            }
        }
        while(i<=mid) b[k++] = a[i++];
        while(j<=r) b[k++] = a[j++];
        for(int x = l, y = 0; x <= r; x++,y++) a[x] = b[y];
    }
    
  • 20
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值