攻克数据结构和算法——第六天:排序

简介: 若关键字是主关键字(关键字值不重复),这无论采用何种排序方法,排出的结果都是唯一的;若关键字是次关键字(关键字值可以重复),则排出的结果可能不唯一。

一,排序的基本概念


(1).排序:


将一组数据元素序列重新排列,使得数据元素序列按某个数据项(关键字)有序。


排序依据:是依据数据元素的关键字。

若关键字是主关键字(关键字值不重复),这无论采用何种排序方法,排出的结果都是唯一的;

若关键字是次关键字(关键字值可以重复),则排出的结果可能不唯一。


一般情况下,

假设含n个记录的序列为{ R1, R2,...Rn}

其相应的关键字序列为{ K1, K2, ...Kn}


这些关键字相互之间可以进行比较,即在它们之间存在着这样一个关系:Kp1≤Kp2≤...≤Kpn


按此固有关系将上式记录序列重新排列为{Rp1,Rp2, ....,Rpn}的操作称作排序。


(2).稳定排序和不稳定排序


对于任意的数据元素序列,若排序前后所有相同关键字的相对位置都不变,则称该排序方法称为稳定的排序方法。


若存在一组数据序列,在排序前后,相同关键字的相对位置发生了变化,则称该排序方法称为不稳定的排序方法。


例如,对于关键字序列3,2, 3, 4,若某种排序方法排序后变为2,3,3,4,则该排序方法是不稳定的。


(3).内部排序和外部排序


若整个排序过程不需要访问外存便能完成,则称此类排序问题为内部排序;


反之,若参加排序的记录数量很大,整个序列的排序过程不可能在内存中完成,则称此类排序问题为外部排序。


(4).内部排序的方法


内部排序的过程是个逐步扩 大记录的有序序列长度的过程。


ce51fce0a4394025a147550741091867.png


二,简单插入排序


基本思想:将无序子序列中的一个或几个记录"插入”到有序子序列中,从而增加有序子序列的长度。


d2bf557b7b6e46a2bd05967291307869.png

1bb34c00537a40328dd29f5d55de98aa.png


不同的定位方法导致不同的插入算法。


直接插入排序(基于顺序查找定位)


排序过程:整个排序过程为n- 1趟插入,即先将序列中第1个记录看成是一个有序子序列,然后从第2个记录开始,逐个进行插入,直至整个序列有序


2c65a9eed10748ca9ca83f7f21d0bc58.png


代码实现:


typedef struct
{     int key;//关键字
    float info;//其他信息
}JD;
void straisort(D r[],int n)//对长度为n的序列排序
{     int ij;
    for(i=2;i<=n;i++)
        {   r[0]=r[i];
            j=i-1;
            while(r[0].key<r[jJ.key)
                {   r[j+1]=r[j];
                    j-;
                }
            r[j+1]=r[0];
        }
}    


折半插入排序(基于折半查找定位)

希尔排序(基于逐趟缩小增量)


acadeb5a5db1409f8228f31afca81764.png


21f2727bbe054b8e8efd38bb15d53a91.png


用直接插人排序,数据序列越接近有序,比较次数越少。


简单插入排序的本质?

       比较和交换

       序列中逆序的个数决定交换次数

       平均逆序数量为C(n,2)/2 ,所以T(n)= 0(n2)


简单插入排序复杂度由什么决定?        逆序个数


如何改进简单插入排序复杂度?


●分组,比如C(n,2)/2> 2C((n/2),2)/2

●3,2,1有3组逆序对(3,1) (3,2) (2,1)需要交换3次。但相隔较远的

3,1交换一次后1,2,3就没有逆序对了。

●基本有序的插入排序算法复杂度接近O(n)


三,希尔排序(缩小增量法)


1,基本思想:


分割成若干个较小的子文件,对各个子文件分别进行直接插入排序,当文件达到基本有序时,再对整个文件进行一-次直接插入排序。


对待排记录序列先作“宏观”调整,再作“微观”调整。

"宏观"调整,指的是,"跳跃式”的插入排序。


2,排序过程:


首先将记录序列分成若干子序列,然后分别对每个子序列进行直接插入排序,最后待基本有序时,再进行一次直接插入排序


例如:将n个记录分成d个子序列:

{ R[1],R[1+d], R[1+2d], .. R[1+kd]}

{ R[2], R[2+d], R[2+2d], ... R[2+kd]}

{ R[d], R[2d], R[3d], .... R[kd], R[(k+1)d] }

其中,d称为增量,它的值在排序过程中从大到小逐渐缩小,直至最后一趟排序减

为1。


199acd619dd648fcbd778246f5117455.png


void shellsort(JD r[],int n,intd[],int T)
{   int i.j,k;
    JD X;
    k=0;
    //循环每 一趟进行分组,组内进行简单插入排序
    while(k<T)
        {   for(i=d[k]+1;i<=n;i++)
            //i为未排序记录的位置
                {   x=r[i];
                    j=i-d[k]//j为本组i前面的记录位置
                    while((j>0)&&(x.key<r[j].key))
                    //组内简单插入排序
                        { r[j+d[k]]=r[j];
                            j=jd[k];
                        }
                        r[j+d[k]]=x; 
                    }
                    k++;
        }


3,排序特点:


●子序列的构成不是简单的”逐段分割”,而是将相隔某个增量的记录组成一个子序列

●希尔排序可提高排序速度,因为分组后n值减小,n2更小,而T(n)=O(n2),所以T(n)从总体上看是减小了关键字较小的记录跳跃式前移,在进行最后一趟增量为1的插入排序时,序列已基本有序

●增量序列取法


无除1以外的公因子


最后一个增量值必须为1


希尔排序算法本身很简单,但复杂度分析很复杂.他适合于中等数据量大小的排序(成千上万的

数据量)


4,性能分析:


e73b21a6f89742e881cd2c6ef4afcaa4.png


四,选择排序


1,基本思想:


从无序子序列中“选择”关键字最小或最大的记录,并将它加入到有序子序列中,以此方法增加记录的有序子序列的长度。


假设排序过程中,待排记录序列的状态为:


83cd9b55bb714516966f1dfff115af37.png


2,排序过程:


●首先通过n-1次关键字比较,从n个记录中找出关键字最小的记录,将它与

第一个记录交换

●再通过n-2次比较,从剩余的n-1个记录中找出关键字次小的记录,将它与

第二个记录交换

●重复上述操作,共进行n-1趟排序后,排序结束


93424aa828254f0aa64e64308b8fd734.png


3,代码实现:


void smp_ selesort(JD r[], int n){
    int i,j,k;
    JD x;
    for (i = 1;i<n;i++){
        k=i;
        for (j =i+ 1;j <= n;j++)
            if (r[j]. key<r[k].key) k = j;
            if (i != k){
                x = r[i];
                r[i] = r[k];
                r[k] = x;
            }
        }
}


4,算法评价:


727332b9cbca409c8d25e29297a310a3.png


稳定性分析:


7f8e12a3c2b441b69b39d3b062553161.png


堆排序:


85cd1d6606b64ec083734b6e2bd6f93a.png


void Heap Sort ( ElementType A[],int N )
{   BuildHeap(A) ; /* O(N) */
    for ( i=0; i<N; i++ )
        TmpA[i] = DeleteMin(A) ; /* O(1ogN) */
    for ( i=0; i<N; i++ ) /* O(N) */
        A[i] = TmpA[i] ;
}


b1a9c4240b3843b591132cfd09cea0fa.png



void Heap Sort ( ElementType A[],int N )
{   for ( i=N/2-1; i>=0; i-- ) /* BuildHeap */
        PercDown( A,i, N ) ;
    for ( i=N-1; i>0; i-- ) {
        Swap( &A[0] ,&A[i] ) ; /* DeleteMax */
        PercDown( A,0,i ) ;
    }
}


4dd16da10d9a4bf6bc7c3d50512b3546.png


五,归并排序


1,合并两个有序表


307ecdcafd2541499e05b0a399ee8646.png


归并——将两个或两个以上的有序表组合成一个新的有序表,叫归并排序


2-路归并排序


排序过程

设初始序列含有n个记录,则可看成n个有序的子序列,每个子序列长度为1

两两合并,得到n/2个长度为2或1的有序子序列

再两两合并,...如此重复,直至得到一个长度为n的有序序列为止


1cd0ec3d1e5548d5af6ca929b27ccb06.png


迭代算法:


将序列的每一个数据看成一个长度为1的有序表,然后,将相邻两组进行归并得到长度为2的有序表(一 趟归并)再对相邻两组长度为2的有序表进行下一趟归并得到长度为4的有序表,这样一直进行下去,直到整个表归并成有序表。如果某一趟归并过程中,单出一个表,该表轮空,等待下一趟归并。


递归思想


将无序序列划分成大概均等的2个子序列,然后用同样的方法对2个子序列进行归并排序得到2个有序的子序列,再用合并2个有序表的方法合并这2个子序列,得到n个元素的有序序列。


void MSort( ElementType A[ ] ElementType TmpArray[ ] ,int Left, int Right )
{
    int Center;
    if( Left< Right ) { /* 待排序的数据在数组的下标位置*/
        Center=(Left十Right)/2;
        MSort( A, TmpArray, Left, Center ); /*T( N / 2) */
        MSort( A, TmpArray, Center十1, Right );
        /*T(N/2)*/
        Merge( A, TmpArray, Left, Center + 1, Right ); /* O( N ) */
    }
}
void Mergesort( ElementType A[ ],int N )
{   ElementType *TmpArray; /* need O(N) extra space */
    TmpArray = malloc( N* sizeof( ElementType ) );
    if ( TmpArray != NULL) {
        MSort( A, TmpArray, 0,N-1 );
        free( TmpArray );
    }
    else FatalError( "No space for tmp array!!" );
}


/* Lpos = start of left half, Rpos = start of right half */
void Merge( ElementType A[ ] ElementType TmpArray[ ],int Lpos, int Rpos, int RightEnd )
{
    int i, LeftEnd, NumElements, TmpPost
    LeftEnd = Rpos- 14;
    TmpPos = Lpos;
    NumElements = RightEnd- Lpos十1;
    while( Lpos <= LeftEnd && Rpos <= RightEnd ) /* main loop */
        if(A[Lpos]<=A[Rpos])
            TmpArray[ TmpPos++ ]三A[ Lpos++ ];
        else 
            TmpArray[ TmpPos++ ] 3 A[ Rpos++ ];
        while( Lpos <= LeftEnd ) /* Copy rest of first half */
            TmpArray[ TmpPos++ ] a A[ Lpos++ ];
        while( Rpos <= RightEnd ) /* Copy rest of second half * /
            TmpArray[ TmpPos++ ] = A[ Rpos++ ];
        for( ie 0; i < NumElements; i+ +, RightEnd-- ) 
            /* Copy TmpArray back */
        {
            A[ RightEnd ] = TmpArray[ RightEnd ];
            printf(%od " A[RightEnd]);
        }
}


2,算法评价:


时间复杂度:每-趟归并的时间复杂度为O(n),总共需要归并log2n趟,因而,总的时间复杂度为O(nlog2n)。


空间复杂度: 2-路归并排序过程中, 需要一个与表等长的存储单

元数组空间,因此,空间复杂度为O(n)。


ed987986592f411fbd0bc05b05393a03.png


c5c4b6b9e8df4cb3ba0b0cb37c73e191.png


六,交换排序


通过"交换"无序序列中的记录从而得到其中关键字最小或最大的记录,并将它加入到有序子序列中,以此方法增加记录的有序子序列的长度。


47ab0b87110244618bea62565b92ef3a.png


冒泡排序算法评价:


7920503f828844eb8370bb0ae45400e5.png


冒泡排序的改进:


快速排序算法评价:


e17a3d7a4dc5448c91bf4707947b6c0f.png


算法特点:


●快速排序算法是不稳定的

对待排序序列49 49' 38 65,

快速排序结果为: 38 49' 49 65

●快速排序的性能跟初始序列中关键字的排列和选取的枢纽有关

●当初始序列按关键字有序(正序或逆序)时,性能最差,蜕化为冒泡

排序,时间复杂度为O(n2)

●常用“三者取中”法来选取划分记录,即取首记录r[s].key.尾记录r[t].key和中间记录r[(S+ t)/2].key三者的中间值为划分记录。

●快速排序算法的平均时间复杂度为O(nlogn)


7ee867ef89a34eb385683cc50d0b8448.png


七,基数排序


多关键字排序:


设序列{R1, R..... R},关键字有d位,

关键字(K0, k1.... Kd-1)有序是指:

对于序列中任意两个数据R;和R;(1≤i<j≤n)

都满足: (K:, K....K.-1)<. (K:0, K....K.-1)

其中:

K°称为"最主”位关键字,Kd-1称为 "最次” 位关键字

多关键字排序方法分为:

高位优先多关键字排序

低位优先多关键字排序


高位优先多关键字排序


先对K进行排序,并按K0的不同值将记录序列分成若干子序列之后,分别对K1进行排序,....依次类推,直至对最次位关键字Kd-1排序完成为止。


f690e61ad3964a3e89ea66179c86fe9d.png


低位优先多关键字排序


首先按关键字Kd- 1进行排序,

然后按关键字Kd-2进行排序,。。。。依次类推,直至最后对最主位关键字K排序完成为止。


32fc799bc24d46f6afc82f8c4262f68e.png


两种排序方法实现上有什么区别?哪种方法更好?为什么?

目录
相关文章
|
3天前
|
搜索推荐 算法 Java
Java数据结构与算法:排序算法之插入排序
Java数据结构与算法:排序算法之插入排序
|
3天前
|
搜索推荐 算法 Java
Java数据结构与算法:排序算法之冒泡排序
Java数据结构与算法:排序算法之冒泡排序
|
3天前
|
搜索推荐 算法 Java
Java数据结构与算法:排序算法之归并排序
Java数据结构与算法:排序算法之归并排序
|
3天前
|
搜索推荐 算法 Java
Java数据结构与算法:排序算法之选择排序
Java数据结构与算法:排序算法之选择排序
|
3天前
|
存储 设计模式 算法
数据结构,算法宏观印象构建
数据结构,算法宏观印象构建
|
2天前
|
存储 算法 调度
算法与数据结构-栈篇
算法与数据结构-栈篇
11 0
|
3天前
|
人工智能 算法
程序技术好文:算法与数据结构
程序技术好文:算法与数据结构
|
1天前
|
算法 安全 数据库
基于结点电压法的配电网状态估计算法matlab仿真
**摘要** 该程序实现了基于结点电压法的配电网状态估计算法,旨在提升数据的准确性和可靠性。在MATLAB2022a中运行,显示了状态估计过程中的电压和相位估计值,以及误差随迭代变化的图表。算法通过迭代计算雅可比矩阵,结合基尔霍夫定律解决线性方程组,估算网络节点电压。状态估计过程中应用了高斯-牛顿或莱文贝格-马夸尔特法,处理量测数据并考虑约束条件,以提高估计精度。程序结果以图形形式展示电压幅值和角度估计的比较,以及估计误差的演变,体现了算法在处理配电网状态估计问题的有效性。
|
5天前
|
机器学习/深度学习 自然语言处理 算法
m基于深度学习的OFDM+QPSK链路信道估计和均衡算法误码率matlab仿真,对比LS,MMSE及LMMSE传统算法
**摘要:** 升级版MATLAB仿真对比了深度学习与LS、MMSE、LMMSE的OFDM信道估计算法,新增自动样本生成、复杂度分析及抗频偏性能评估。深度学习在无线通信中,尤其在OFDM的信道估计问题上展现潜力,解决了传统方法的局限。程序涉及信道估计器设计,深度学习模型通过学习导频信息估计信道响应,适应频域变化。核心代码展示了信号处理流程,包括编码、调制、信道模拟、降噪、信道估计和解调。
27 8
|
7天前
|
算法
基于GA遗传优化的混合发电系统优化配置算法matlab仿真
**摘要:** 该研究利用遗传算法(GA)对混合发电系统进行优化配置,旨在最小化风能、太阳能及电池储能的成本并提升系统性能。MATLAB 2022a用于实现这一算法。仿真结果展示了一系列图表,包括总成本随代数变化、最佳适应度随代数变化,以及不同数据的分布情况,如负荷、风速、太阳辐射、弃电、缺电和电池状态等。此外,代码示例展示了如何运用GA求解,并绘制了发电单元的功率输出和年变化。该系统原理基于GA的自然选择和遗传原理,通过染色体编码、初始种群生成、适应度函数、选择、交叉和变异操作来寻找最优容量配置,以平衡成本、效率和可靠性。