zoukankan      html  css  js  c++  java
  • 几种简单的排序算法总结

    注:

      (1)以下所有排序算法均按照从小到大的顺序排列

      (2)以下算法中用到的交换函数都一样,如下:

        void Swap(int *a,int i,int j)
        {
            int temp;
            temp = a[i];
            a[i] = a[j];
            a[j] = temp;
        }

        因此不在每个排序算法中进行详解

    1.冒泡排序

      1.最简单的冒泡排序

      思想:

        该排序算法在排序的过程中总共进行n-1趟排序,每一趟排序都将当前的关键字和其后面的每一个关键字进行比较,若当前关键字大于其后面的关键字则进行交换。

      代码实现如下:

        void BubbleSort(int *a)        //a表示待排序的记录集合,这里以数组存储为例,以下排序算法相同,即都是对数组里的记录元素进行排序
        {
            int i,j;
            for(i = 0;i < MAXSIZE-1;i++)
            {
                for(j = (i + 1);j < MAXSIZE;j++)    //注意j从前往后循环与当前记录比较
                {
                    if(a[i] > a[j])            //若当前记录大于其后的记录
                    {
                        Swap(a,i,j);           //两者交换在数组中的位置
                    }
                }
            }
        }

      分析:

        该算法从严格意义上讲并不是标准的冒泡排序,因为不满则“两两比较相邻记录”的冒泡排序思想,它更应该是最最简单的交换排序。

        该算法代码简单易懂,却是有缺陷的。每一趟的比较和交换对下一轮没有任何影响,将导致进行很多重复的比较,算法效率是非常低的  

      2.冒泡排序

        思想

          跟上面的排序算法的思想一样,都是当前趟的记录跟其之后的记录相比,只是该算法不同的是:每一趟都是两个相邻记录比较

        代码实现如下(蓝色加粗的即为和上面算法不同的部分):

          void BubbleSort(int *a)
          {
              int i,j;

            for(i = 0;i < MAXSIZE-1;i++)
               {
                  for(j = MAXSIZE-2;j >= i;j--)    //注意j是从后往前遍历和其前面相邻的记录进行比较
                  {
                      if(a[j] > a[j+1])          //若前者大于后者(注意两者相邻)
                      {
                          Swap(a,j,j+1);
                      }
                  }
              }
          }

      3.冒泡排序算法的优化

        思想:该算法在上诉冒泡排序算法的基础上进行优化,由上面可知,若进过第一趟排序后若结果已经有序,则后面还要进行循环判断工作,因此该算法实现的是当在某一趟排序后若该序列已经有序,则不再进行循环比较了。

        代码实现如下:

          void BubbleSort(int *a)
          {
              int i,j;
              int flag = 1;               //新增flag标志,默认为1
              for(i = 0;i < MAXSIZE-1 && flag;i++)  //若i<n-1趟,且上一趟没有进行任何记录交换时说明该记录集合已经有序了,退出循环
              {
                  flag = 0;
                  for(j = MAXSIZE-2;j >= i;j--)
                  {
                      if(a[j] > a[j+1])          //若前者都小于后者(即不需要交换时),说明该记录集合已经有序,则在当前循环结束后退出总循环
                      {
                          flag = 1;            //若只要有一次记录交换,则说明该集合还没有完全有序,继续循环
                          Swap(a,j,j+1);
                      }
                  }
              }
          }

      4.冒泡复杂度分析

        时间复杂度:

          最好的情况:也就是带排序的记录集合本身是有序的,那么需要进行n-1(n为待排序的记录个数,代码中为MAXSIZE)次比较,没有数据交换,时间复杂度为O(n)

          最坏的情况:也就是待排序的记录集合本市是逆序的,那么时间复杂度为O(n*n)。

          因此平均复杂度为O(n * n)。

        空间复杂度:

          由于该算法没有借助辅助空间,所以空间复杂度为常数,即为O(1)。

        稳定性:

          稳定

    2.简单选择排序

      思想:对待排序的n个记录进行n-1趟循环,每一趟进行两两相邻比较找到该趟最小的记录和当前记录进行比较,若最小记录的位置不是当前记录所在的位置,则两者进行交换。

      代码实现如下:

        void SelectSort(int *a)
        {
            int i,j,min;
            for(i = 0;i < MAXSIZE - 1;i++)
            {
                min = i;              //初始化当前记录为最小的记录
                for(j = i + 1;j < MAXSIZE;j++)  //将最小记录依次与其后面的记录比较
                {
                    if(a[min] > a[j])        //若其后的记录小于该机记录
                    {
                        min = j;          //把其后的记录的下标赋给min
                    }    
                }
                if(i != min)            //若min不等于当前记录的下标
                {
                    Swap(a,min,i);        //进行交换
                }
            }
        }

      简单选择排序复杂度分析:

        时间复杂度:

          最好情况:即待排序记录集合本身是有序的,需要比较的次数是1+2+...+(n-1) = n * (n - 1) / 2,不需要交换数据。由于最终的时间复杂度是比较与交换次数之和,因此总的时间复杂度为O(n * n)。

          最坏情况:即待排序的记录集合本身是逆序的,需要比较的次数同最好情况相同都为n * (n - 1) / 2,交换的次数是n-1次,因此最终的时间复杂度也是O(n * n)。

          因此平均复杂度为O(n * n)。

        空间复杂度:

          由于该算法不需要辅助空间,因此空间复杂度为常数,即为O(1)。

        稳定性:

          不稳定

    3.直接插入排序

      思想:该算法的存储结构数组a[0]不存放记录(不同于上面算法),用作哨兵,每一趟都假设其前面的记录集合是有序的,然后比较当前记录和其前面相邻的记录,若小于其前面相邻记录,则把该值放在a[0]的位置上,然后从其前面相邻记录向前开始遍历,只要记录小于a[0]的记录,则把当前记录后移,直到大于等于a[0]元素则退出循环,然后把a[0]的记录插入到适当位置,则结束当前趟的操作。

      代码实现如下:

        void InsertSort(int *a)
        {
            int i,j;
            for(i = 2;i < MAXSIZE;i++)    //i从2开始循环,默认第一个记录是有序的
            {
                if(a[i] < a[i - 1])        //若当前记录大于前面的记录
                {  
                    a[0] = a[i];          //设置哨兵,把当前记录赋给哨兵
                    for(j = i - 1;a[0] < a[j];j--)
                    {
                        a[j+1] = a[j];      //记录后移
                    }
                      a[j + 1] = a[0];    //插入到正确位置
                }
            }
        }

      直接插入排序算法复杂度分析:

        时间复杂度:

          最好情况:即带排序的集合本身就是有序的,则需要比较n-1次,不需要移动,因此时间复杂度为O(n)。

          最坏情况:即待排序的集合本身是逆序的,则需要比较2+3+...+n = (n + 2) * (n - 1) / 2次,需要移动3+4+...+(n+1) = (n + 4)*(n - 1) /2次,因此时间复杂度为O(n * n)。

          因此平均复杂度为O(n * n)。

        空间复杂度:

          由于该算法需要一个辅助空间(a[0]),因此空间复杂度为常数,即为O(1)。

        稳定性:

          稳定

          

      

  • 相关阅读:
    gThumb 3.1.2 发布,支持 WebP 图像
    航空例行天气预报解析 metaf2xml
    Baruwa 1.1.2 发布,邮件监控系统
    Bisect 1.3 发布,Caml 代码覆盖测试
    MoonScript 0.2.2 发布,基于 Lua 的脚本语言
    Varnish 入门
    快速增量备份程序 DeltaCopy
    恢复模糊的图像 SmartDeblur
    Cairo 1.12.8 发布,向量图形会图库
    iText 5.3.4 发布,Java 的 PDF 开发包
  • 原文地址:https://www.cnblogs.com/xuying/p/4746513.html
Copyright © 2011-2022 走看看