• 选择排序(简单选择排序,树形选择排序,堆排序)


    简单选择排序

    基本思想:第i趟排序经过n-i次关键字的比较在n-i+1(i=1,2,3,......n-1)个记录中选取关键字最小的记录作为序列的第i个记录 


     直接看代码!没啥好说的这个!

    void SelectSort(SqList *L)
    {
        for(int i=0;i<L->length-1;i++){//总共进行n-1趟排序,便可使整个序列有序
        {
            int index=i;//假设第i个记录就是最小值
            for(int j=i+1;j<L->length;j++)
                if(L->r[j]<L->r[index])//找到比index指向记录有更小关键字的记录
                    index=j;//index指向带新最小关键字的记录
            if(index!=i)
            {
                int key=L->r[index];
                L->r[index]=L->r[i];
                L->r[i]=key;//交换记录
            }
        }   
        return;
    }
        

    简单选择排序:

    记录移动次数最小为0次,最大值为3*(n-1)次

    记录比较次数:第1趟排序比较n-1次,第i趟排序比较n-i次,共有n-1趟,故共比较n(n-1)/2次

    故:简单选择排序的时间复杂度为O(n^2)


    树形选择排序(锦标赛排序)

    树形选择排序是对简单选择排序的改进。简单选择排序的主要操作是进行关键字间的比较,因此我们通过减少比较次数而改进简单选择排序。


    改进思想:在n个关键字中选出最小值,需要进行n-1次比较,然而,继续在剩余的n-1个关键字中选择次小值并非一定要进行n-2次比较,若能利用前n-1次比较所得信息,则可减少以后各趟选择排序中比较次数。

    主要步骤:

    1. 对n个记录的叶子节点进行两两比较,从根结点得到最小关键字的记录
    2. 找到与根结点关键字相同的叶子节点记录,更改关键字为最大值
    3. 从该叶子节点开始,和其左兄弟或者右兄弟进行比较,修改从叶子结点到根路径上各结点的关键字,得到次小关键字
    4. 重复上述第2~3步依次选出从小到大的所有关键字
    #include <limit.h>
    void TreeSelectSort(SqList *L)
    {
         int n;//根据叶子节点个数计算整棵树的结点数
         if(L->length%2==0)
            n=L->length*2-1;//此时为叶子节点为L->length的满二叉树,叶子节点个数为偶数个
         else
            n=L->length*2;//此时为叶子节点为L->length的完全二叉树,叶子节点个数为奇数个
         int *Tree=(int*)malloc((n+1)*sizeof(int);//分配大小为n+1的数组,使用Tree[1]到Tree[n]
         if(!Tree)  exit(-1);
         for(int i=n,int j=L->length-1;i>=0;i--,j--){
             if(j>=0)    Tree[i]=L->r[j];
             else        Tree[i]=0;
         }//将顺序表记录依次从Tree[n-L->length+1]到Tree[n]进行赋值,其他赋值为0
         for(int k=n/2;k>0;k--){
            if(2*k+1<=n)
                Tree[k]=Tree[2*k]<Tree[2*k+1]?Tree[2*k]:Tree[2*k+1];
            else 
                Tree[k]=Tree[2*k];
         }//比较得到最小关键值
         L->r[0]=Tree[1];
         for(int i=1;i<L->length;i++){//进行L->length-1趟剩余最小关键字的选择
            int j=1;
            while(2*j<=n){//寻找次小关键字所在叶子节点
                if(Tree[j]==Tree[2*j])
                    j=2*j;
                else
                    j=2*j+1;
            }
            Tree[j]=MAX_INT;//最小关键字所在的叶子结点赋值为MAX_INT
            while(j!=1){//选出次小关键字
                j=j/2;
                if(2*j+1>n||Tree[2*j]<Tree[2*j+1])//如果j没有右孩子结点或者左结点<右结点
                     Tree[j]=Tree[2*j];//将左结点关键值赋值Tree[j]
                else
                     Tree[j]=Tree[2*j+1];//将右结点关键值赋值Tree[j]
                    
            }
            L->r[i]=Tree[1];//得到次小关键值
        }
        return;
    }   
         
                
            

    时间复杂度: 

    含有n个叶子结点的完全二叉树的深度为left lceil Log_{_2{}}n 
ight 
ceil+1

    则除了最小关键字的那一趟选择,每选择一个次小关键字仅需要比较left lceil Log_{_2{}}n 
ight 
ceil次,因此时间复杂度为O(nlogn)

    缺点:

    • 需要的辅助存储空间比较多
    • 需要和变成最大值的叶子结点进行比较

    堆排序

    堆的含义:完全二叉树的所有非终端结点的值均不大于(或不小于)其左、右孩子结点的值。若系列是堆,则堆顶元素必定为序列中n个元素的最小值(或最大值)。


    实现堆排序面临两问题:

    1. 如何由一个无序序列建成一个堆
    2. 在输出堆顶元素之后,如何调整剩余元素成为一个新的堆

    先解决第二个问题:以一维数组作为此序列的存储结构,假设为初始堆为大顶堆

    1. 将堆顶元素和最后一个元素互换,此时根结点的左右子树均为大顶堆
    2. 若根结点存在左右子树,则比较根结点和左右子树根结点的值,否则调整结束,跳到步骤1
    3. 若根结点值依旧均大于左右子树的根结点的值,则跳转到第1步执行
    4. 否则将根结点和左右子树根结点较大值互换,跳转到第2步,以被交换的子树的根结点作为第2步根结点执行

    解决第一个问题:

    从无序序列建堆的过程就是反复调整筛选的过程,设序列大小为n,将其看做是是一个完全二叉树,则第一个非终端结点为第left lfloor n/2 
ight 
floor个元素,从第left lfloor n/2 
ight 
floor个元素开始到第一个元素结束,分别以其作为根结点进行筛选调整。


    typedef SqList HeapType;//给SqList定义别名为HeapType
    //做非递减堆排序
    void HeapAdjust(HeapType *H,int first,int end)//堆顶关键字小于左右子树根结点,需调整堆
    {
        int i=first;//i指向堆顶,从堆顶开始进行堆调整
        while(2*i<=end){//当i不是叶子结点时,进入循环
            int key=H->r[i];//用key临时保存堆顶关键字
            int j=2*i;//用j指向最大子树根结点,假设此时左子树根结点为最大,用j指向它
            if(j+1<=end&&H->r[j]<H->r[j+1]) j++;//如果右子树根结点存在,大于左子树根结点,则j++
            if(Key>H->r[j]) break;//如果i大于其最大子树根结点,说明已经成堆,无需再进行调整,break
            H->r[i]=H->r[j];//否则,将最大子树根结点关键字赋值堆顶元素
            i=j;//最大子树结点成为子堆堆顶
            H->r[i]=key;//原有堆顶关键字赋值子堆堆顶
        }//以子堆堆顶i重新进行堆调整
        return;
    }
    //SqList序列表长度为H->length+1,但实际记录为H->length个,i=0不使用,从i=1到H->length
    void HeapSort(HeapType *H)
    {   
        int n=H->length;
        for(int i=H->length/2;i>0;--i)//对无序堆进行调整,从i=H->length/2开始到i=1;
            HeapAdjust(H,i,H->length);
        for(int i=H->length;i>1;i--)//只需进行H->length-1趟排序就OK
        {
            int key=H->r[1];
            H->r[1]=H->r[i];
            H->r[i]=key;//将堆顶的最大值记录和尾部记录交换
            HeapAdjust(H,1,i-1);//从1到i-1进行堆调整
        }
        return;
    }

    时间复杂度:堆排序的时间复杂堆为O(nlogn)

    空间复杂度:只需要1个记录大小供交换用的辅助空间 O(1)

  • 相关阅读:
    Python修饰符实践
    回文
    Linux下安装Qt
    Linux下安装PyQT
    Python闭包实践
    杂乱
    windows下脚本转到linux下,文件保存格式要转换
    lua table.sort的bug
    shell截取某段
    coredump
  • 原文地址:https://www.cnblogs.com/zhichao-yan/p/13368501.html
Copyright © 2020-2023  润新知