算法基础课学习笔记:(一)排序

本文介绍了快速排序和归并排序两种经典算法。快速排序利用TwoPoint思想,通过选取主元和递归实现高效排序,平均时间复杂度为O(nlogn),而归并排序则通过不断拆分和合并数组,保证稳定性,同样达到O(nlogn)的时间复杂度。文章还探讨了两种排序算法在实际应用中的场景,并提供了代码模板。
摘要由CSDN通过智能技术生成

算法基础课学习笔记:(一)排序

记录一下算法学习过程,学习最佳的开始时间或是十年前或是现在。
本文全部算法学习均基于Acwing算法基础课程,有兴趣的可以购入视频课程学习
给y总打波广告hh。

本文仅介绍常用快速排序及归并排序,堆排序和希尔排序会放到后面讲。

一、快速排序Quick_sort():

       快速排序基于Two Point思想,将时间复杂度从O( n 2 n^2 n2)降到了( n l o g n nlogn nlogn),适用于 1 0 5 10^5 105及以上的数据。
PS:若数据本身有序,会极大地影响快排的时间复杂度,导致效率降低,酌情考虑使用情景

1.算法思想

先来看一组数据
                            在这里插入图片描述
       排序即是将一组无序的数据变为升序或降序,快速排序借助两个下标指针 l e f t left left r i g h t right right,从前向后或从后向前遍历整个数组。
第一步:保证左右半段各自小于或大于主元
       快速排序需要确定主元,此处我们以A[1]为例,将A[1]存进临时变量 t e m p temp temp中, l e f t left left指向数据最左端A[1], r i g h t right right指向数据最右端A[11]。
       接下来是快速排序的核心思想:

1.只要 r i g h t right right指向的元素A[right]大于 t e m p temp temp,就将 r i g h t right right不断左移;当某个时刻A[right]<= t e m p temp temp时,将元素A[right]移到 l e f t left left指向的元素A[left]处。
2.只要 l e f t t leftt leftt指向的元素A[left]大于 t e m p temp temp,就将 l e f t left left不断左移;当某个时刻A[left]> t e m p temp temp时,将元素A[left]移到 r i g h t right right指向的元素A[right]处。
3.不断重复第二步和第三步,直到 l e f t left left r i g h t right right相遇,将 t e m p temp temp变量中存的数放入相遇的位置

                            在这里插入图片描述

       我们可以发现,此时 l e f t 和 r i g h t left和right leftright指向的A[6]元素存储的就是原先A[1]的元素。

       重要特征:此时A[1]到A[5]严格满足 < = <= <=A[6],A[7]到A[11]严格满足 > > >A[6]
       此时,我们的快速排序第一步就完成了,将A[1]作为中心元素放到A[6]的位置,满足了左半段小于原来A[1]的元素、右半段大于原来A[1]的元素。
第二步:拆解数组
       我们将左半段和右半段拆成两个分离的数组记为 B 数 组 和 C 数 组 B数组和C数组 BC,对 B 、 C 数 组 B、C数组 BC分别进行快速排序,排序完后再将 B 、 C 数 组 B、C数组 BC拆解后再分别排序,直到全部拆解小区间的元素长度为 1 1 1
       这听起来像什么??这不就是无限套娃吗??
       说到套娃,我们想到的是什么??递归啊!!
       对,将左右两半的数组无限递归下去,直到小区间元素长度为 1 1 1,就 r e t u r n return return回上一级。下面给出快排的代码模板,借y总的话说,背就完事了

void quick_sort(int q[],int left,int right){
    if(left>=right) return;
    int tmp=q[left+right>>1];//确定主元
    int i=left-1,j=right+1;//划分区间
    while(i<j){
        do i++;while(q[i]<tmp);
        do j--;while(q[j]>tmp);
        if(i<j) swap(q[i],q[j]);
    }
    quick_sort(q,left,j);
    quick_sort(q,j+1,right);
}

       此时,我们就完成了快速排序,原数组排序完满足升序,如果想要降序,仅需在判断时将 < 和 > <和> <>号互换即可。
PS:快速排序的时间复杂度并不稳定,算法本质也不稳定,但并不影响快排的优越地位。
注意!!选择主元的位置直接决定了排序效率,此处模板直接选择了最安妥的中位数。

二、归并排序merge_sort():

       如果说快速排序是将数组分段后交换元素位置,再不断递归。那归并排序就是一上来将所有元素打散成长度为 n n n的小区间,再在合并区间时做手脚。(乱拳打死老师傅hh)
       小区间长度 n n n为几,就被称为几路归并排序。
       例如序列{66,12,33,57,64,27,18},要将该序列进行 2 2 2路归并排序。

1.两两分组,得到四组:{66,12},{33,57},{64,27},{18}组内单独排序,得到新序列{{12,66},{33,57},{27,64},{18}};
2.合并为两组,{12,66,33,57}和{27,64,18},组内单独排序得{12,33,57,66}和{18,27,64};
3.合并为1组,得到{12,66,33,57,27,64,18},组内单独排序得{12,18,27,33,57,64,66},完成排序

       我在阅读到这一步的时候有所疑问,为什么要将一次排序就能得到的结果拆解为数个小排序呢?
       其实是因为在合并区间的时候可以继承原先区间的单调增减性,只要在合并区间时动些手脚,就能减少重复计算。
       我们可以发现,归并排序的重点就在于合并区间,如何合并区间才能减少重复计算?这里我们将再次借助Two Point思想。
       下面给出归并排序的代码模板,背就完事了。

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);

    int i=l,j=mid+1,k=0;
    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(i=l,j=0;i<=r;j++,i++) q[i]=tmp[j];

}

       此时,我们就完成了归并排序,归并排序的一大应用就是:可以在合并区间或将单独小区间排序的时候,统计一些数据量。

       这里给出具体习题,可以考虑如何用归并排序完成

LeetCode:数组中逆序对的个数

三、万金油—sort():

       我们上面讲了这么多,其实在实际应用中最常使用到的应该就是这个了hh。

	int q[5]={1,3,2,5,4};
	sort(q,q+5);

       考试的时候直接用!没人给你扣分hh。

本文借助Acwing算法基础课程及《算法笔记》完成。
文中图片来源于《算法笔记》。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值