• 各种排序算法的实现和总结(部分原创)


    直接上代码:

    主程序:

    #include <iostream>
    using namespace std;
    
    int main()
    {
        int a[6];
        cout<<"请输入要排序的六个数字:";    //偷个懒,只输入6个数
        for(int k=0;k<6;k++)
        {
          cin>>a[k];
        }
        cout<<"排序前:";
         for(int m=0;m<6;m++)
            cout<<a[m]<<' ';
        cout<<endl;
    
    //此处插入各种排序算法核心代码.......
    
    
        cout<<"排序后:";
        for(int m=0;m<6;m++)
        cout<<a[m]<<' ';

    之后就是各种算法的代码了,在调用相应的算法函数前记得先在main函数中声明哦。

    首先冒泡排序,这个比较简单,就像冒泡一样(可以从小到大,也可以从大到小),大的升上去,小的降下来。我这里用的是小的降下来,如下:

    void popsort(int a[],int n)
    {
    
        int j=0;
        for(int i=0;i<n-1;i++)
        {
            for(j=n-1;j>i;j--)
        {
            if(a[j]<a[j-1])
            {
          swap(a[j],a[j-1]]);
          }
        }
    }

    下面是选择排序,思路很简单,核心是从无序区(i+1~n-1)选择出最小的值与无序区的第一个数值交换。

     1 //选择排序:
     2 void selectsort(int a[],int length)
     3 {
     4     for(int i=0;i<length-1;i++)
     5     {
     6         int min_index=i;
     7         for(j=i+1;j<length;j++)
     8         {
     9             if(a[j]<a[min_index])
    10             {
    11                 min_index=j;
    12             }
    13         }
    14         if(min_index!=i)
    15         swap(a[min_index],a[i]);
    16     }
    17 }

    下面是插入法,跟打扑克牌的思路是一样的,从无序区按顺序拿出一个与有序区进行比较放到合适的位置。

     1 void Insert_sort(int a[],int n)
     2 {
     3     for(int i=1;i<n;i++)
     4     {
     5         for(int j=i;j>0;j--)
     6         {
     7             if(a[j]<a[j-1])
     8             swap(a[j],a[j-1]);
     9 
    10         }
    11     }
    12 
    13 }

    选择排序,冒泡法和插入法的共同点都是分为有序区和无序区,通过一定的方法将无序区的数据迁移到有序区,冒泡法和选择排序都是从无序区找出最大或最小放到有序区,而插入法则是直接从无序区随便选一个数据(从头开始)放到有序区,在放的过程中进行排序。简单说就是冒泡和选择是在无序区进行的排序操作,插入法是在有序区进行的排序操作。

    然后是快速排序法,这个比较重要,采用的递归分治的思想,但是写起来稍微有点麻烦,得经常练练。我这里采用了csdn上的MoreWindows的白话经典上的写法,感觉这个比较清晰,理解起来不难。

    void quick_sort(int a[],int l,int r)     
    {
        if(l<r){
        int i=l,j=r;
        int x=a[l];           //初始化三个变量,其中l和r对应数组的两个边界(0和n-1),关键值x所在的位置相当于一个坑,需要别人来补,最初的坑的值已经保存到了x中。
        while(i<j)
        {
            while(j>i&&a[j]>x)//从右边开始找小于x的值
                j--;
            if(i<j)
                a[i++]=a[j];   //这里i++主要是因为下一步中a[i]不需要跟x比较,而是从a[i+1]开始比较。
            while(i<j&&a[i]<x)//从左边开始找第一个大于x的值
                i++;
            if(i<j)
                a[j--]=a[i];
        }
        a[i] = x;
        quick_sort(a, l, i - 1); // 对左边递归调用
        quick_sort(a, i + 1, r); //对右边递归调用
    
        }

    我最早的思路是这样的,不是采用的赋值,而是用的交换,这样感觉好记忆一些,但是貌似效率不如直接赋值

    void quick_sort(int a[],int l,int r)
    {
        if(l<r){
        int i=l,j=r;
        int x=a[l];          
        while(i<j)
        {
            while(j>i&&a[j]>x)
                j--;
            if(i<j)
                swap(a[i++],a[j]);   //这里采用的swap函数代替的上面的方法1
            while(i<j&&a[i]<x)
                i++;
            if(i<j)
                swap(a[j--],a[i]);
        }
        quick_sort(a, l, i - 1); //此处为跟方法一的另一个不一样的地方,这里不需要另a[i]=x;了,因为之前交换以后a[i]已经是x了
        quick_sort(a, i + 1, r); 
    
        }
    }

    其实上面两个方法的对比正是快排的优化方法之一,另外几个优化方法包括

    1,初始点的选择优化,即任意挑选m(比如3)个,选出最中间的数据作为初始点。

    2,优化小数组排序时的排序方法,即因为快排适合于对大量数据进行排序,对小数量是体现不出优势,所以当数组数量(j-i)小于某个值(一般是7)时采用插入排序代替快排,这样能提高速度。

    3,优化递归,即改成尾递归的形式,也就是把原来的两个递归调用改成一个,这样编译的速度会加快。

    最后是归并排序:

    归并排序写起来也比较复杂,主要分为两步,归和并,归是递归将原始数组分成小的数组,如下图示,最小的一层每个子数组有两个元素。

    然后是并,并的过程并不难,难理解的是最后一步,每一步并完以后要把list1变成合并后的序列。

    //归并排序
    //首先给出合并函数
    void Merge(int *list1,int list1_size,int *list2,int list2_size) { int i,j,k; i=j=k=0; int const maxsize=6; int temp[maxsize]; //按照大小将两个小序列(list1,list2)合并为大序列temp while(i<list1_size&&j<list2_size) { if(list1[i]<list2[j]) { temp[k++]=list1[i++]; } else { temp[k++]=list2[j++]; } } //下面是把list1或list2中还没有排到合并序列temp的数据放到temp里。 while(i<list1_size) { { temp[k++]=list1[i++]; } while(j<list2_size) { temp[k++]=list1[j++]; } } //最后把temp数组的数据传给list1,这样list1在递归往回退的时候就变成了list1和list2合并后的数组,而n在递归时每层已经确定,从而完成每一层的合并。该步骤很关键! for(int m=0;m<(list1_size+list2_size);m++) temp[m]=list1[m]; } void mergesort(int a[],int n) { //通过递归将原来的数组划分成很短的片段 int *list1=a; //这里最关键的就是a,list2可以由a和n所确定。 int list1_size=n/2; int *list2=a+n/2; int list2_size=n-list1_size; if(n>1) { mergesort(list1,list1_size); mergesort(list2,list2_size); Merge(list1,list1_size,list2,list2_size); }

    常用的排序还有堆排序(基于完全二叉树的,思路比较简单,只需将顶的根节点元素拿出来放到最后面,然后重新更新堆,不断重复,,,有点像选择排序)

    希尔排序(基于插入法,将数组进行分组,然后对分的组分别进行插入排序)。

    有时间再写上面两个。

    最后给出各个算法的时间复杂度

    平均时间复杂度

    插入排序  O(n2) 

    冒泡排序 O(n2)  

    选择排序 O(n2)  

    快速排序O(n log n)  

    堆排序 O(n log n)  

    归并排序 O(n log n)  

    希尔排序 O(n1.25)

     over~

  • 相关阅读:
    C++11特性
    DBC文件小结
    关于宏定义
    CentOS 6.5下Zabbix的安装配置
    CentOS下搭建LAMP环境详解
    VS2010中汉字拷贝到Word出现乱码问题解决
    DLL注入
    数组赋值
    CDC的StretchBlt函数载入位图时图片失真问题
    2019年下半年Web前端开发初级理论考试
  • 原文地址:https://www.cnblogs.com/jymblog/p/5408752.html
Copyright © 2020-2023  润新知