• 几种简单的排序算法总结


    注:

      (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)。

        稳定性:

          稳定

          

      

  • 相关阅读:
    git 常用命令
    mac 显示隐藏文件
    android 图片缓存
    字符串与枚举相互转换
    ios 消息通知
    ios 真机调试
    ios 宏定义 系统版本 判定
    autolayout autoresizing
    c++对象创建带括号与无括号的区别
    内存对齐
  • 原文地址:https://www.cnblogs.com/xuying/p/4746513.html
Copyright © 2020-2023  润新知