数据结构-排序算法(1)

冒泡排序

冒泡排序是一种极其简单的排序算法,也是我所学的第一个排序算法。它重复地走访过要排序的元素,依次比较相邻两个元素,如果他们的顺序错误就把他们调换过来,直到没有元素再需要交换,排序完成。这个算法的名字由来是因为越小(或越大)的元素会经由交换慢慢“浮”到数列的顶端。

冒泡排序算法的运作如下:

1)比较相邻的元素,如果前一个比后一个大,就把它们两个调换位置。
2)对每一对相邻元素作同样的工作,从开始第一对到结尾的最后一对。这步做完后,最后的元素会是最大的数。
3)针对所有的元素重复以上的步骤,除了最后一个。
持续每次对越来越少的元素重复上面的步骤,直到没有任何一对数字需要比较

这里写图片描述

/////////////////////////////
//冒泡排序(倒序,从后往前冒)
//时间复杂度:O(N^2) 空间复杂度:O(1) 
//稳定排序
///////////////////////////////
void BubleSort(int arr[  ],size_t size)
{
      //判断size是否合法
      if(  size<=1)
      {
            return ;
      }
      yy
            size_t cur=size-1;
            for(  ;cur>bound;cur--)
            {

                  if(arr[cur-1]>arr[cur])
                  {
                        Swap(&arr[cur-1],&arr[cur]);
                  }
            }  
      }//end of bound
      return ;


}

这里写图片描述

选择排序

选择排序也是一种简单直观的排序算法。它的工作原理很容易理解:初始时在序列中找到最小(大)元素,放到序列的起始位置作为已排序序列;然后,再从剩余未排序元素中继续寻找最小(大)元素,放到已排序序列的末尾。以此类推,直到所有元素均排序完毕。

注意选择排序与冒泡排序的区别:冒泡排序通过依次交换相邻两个顺序不合法的元素位置,从而将当前最小(大)元素放到合适的位置;而选择排序每遍历一次都记住了当前最小(大)元素的位置,最后仅需一次交换操作即可将其放到合适的位置。

这里写图片描述

////////////////////////////////
///选择排序 不稳定排序
//时间复杂度:O(N^2)  空间复杂度 O(1) 
//////////////////////////////
void SelectSort(int arr[  ],size_t size)
{

      //判断size是否合法
      if(  size<=1)
      {
            return ;
      }
      //[  0,bound]有序区间

      //[  bound,size]无需区间
      size_t bound=0;
      //第一重循环,每循环一次,有序区间加1
      for(  ;bound<size;bound++)
      {
          size_t cur=bound+1;
          for(;cur<size;cur++)
          {
                //如果擂主值大于cur的值,则更换擂主的值
                if(  arr[bound]<arr[cur])
                {
                      Swap(&arr[bound],&arr[cur]);
                }
          }
      } 
      return ;
}


选择排序宏观图
这里写图片描述

插入排序

插入排序是一种简单直观的排序算法。它的工作原理非常类似于我们抓扑克牌

对于未排序数据(右手抓到的牌),在已排序序列(左手已经排好序的手牌)中从后向前扫描,找到相应位置并插入。

插入排序在实现上,通常采用in-place排序(即只需用到O(1)的额外空间的排序),因而在从后向前扫描过程中,需要反复把已排序元素逐步向后挪位,为最新元素提供插入空间。

具体算法描述如下:

1)从第一个元素开始,该元素可以认为已经被排序

2)取出下一个元素,在已经排序的元素序列中从后向前扫描

3)如果该元素(已排序)大于新元素,将该元素移到下一位置

4)重复步骤3,直到找到已排序的元素小于或者等于新元素的位置

5)将新元素插入到该位置后

6)重复步骤2~5
这里写图片描述

插入排序宏观图
这里写图片描述


//////////////////////
//插入排序
//时间复杂度O( N^2 ),空间复杂度O(1)稳定排序  
//////////////////////


void InsertSort(int arr[  ],size_t size )
{
      if(  size<=1)
      {
            return ;
      }
      //[  0,bound]有序区间
      //[bound,size]带排序区间
      //插入排序把有序区间当作一个有序链表
      //然后把bound位置的元素插入到合适位置
      size_t bound=1;
      for(;bound<size;bound++)
      {
            //此时保存起来用于后面的搬运
            //一旦arr[bound]被保存起来就可以被修改
            int bound_value=arr[bound];
            //此处cur辅助我们进行搬运的下标
            //会从后往前遍历,找到合适的位置存放bound_value的值
            size_t cur=bound;
            for(  ;cur>0;cur--)
            {
                  //此处,我们拿线性表的最后一个元素和bound_value比较
                  if(arr[cur-1]>arr[bound])
                  {
                        //此时进行搬运
                        arr[cur]=arr[cur-1];

                  }
                  else
                  {
                        //说明已经找到合适位置
                        break;
                  }

            }//end for(  ;cur>0;cur--)
            //安置bound_value
            arr[cur]=bound_value;
      }

}

插入排序不适合对于数据量比较大的排序应用。但是,如果需要排序的数据量很小,比如量级小于千,那么插入排序还是一个不错的选择。 插入排序在工业级库中也有着广泛的应用,在STL的sort算法和stdlib的qsort算法中,都将插入排序作为快速排序的补充,用于少量元素的排序(通常为8个或以下)

堆排序

堆排序是指利用堆这种数据结构所设计的一种选择排序算法。堆是一种近似完全二叉树的结构(通常堆是通过一维数组来实现的),并满足性质:以最大堆(也叫大根堆、大顶堆)为例,其中父结点的值总是大于它的孩子节点。

我们可以很容易的定义堆排序的过程:

1) 由输入的无序数组构造一个最大堆,作为初始的无序区

2)把堆顶元素(最大值)和堆尾元素互换

3)把堆(无序区)的尺寸缩小1,并调用heapify(A, 0)从新的堆顶元素开始进行堆调整

4)重复步骤2,直到堆的尺寸为1

堆排序是不稳定的排序算法,不稳定发生在堆顶元素与A[i]交换的时刻。

比如序列:{ 9, 5, 7, 5 },堆顶元素是9,堆排序下一步将9和第二个5进行交换,得到序列 { 5, 5, 7, 9 },再进行堆调整得到{ 7, 5, 5, 9 },重复之前的操作最后得到{ 5, 5, 7, 9 }从而改变了两个5的相对次序。堆排序是不稳定的排序算法,不稳定发生在堆顶元素与A[i]交换的时刻。

比如序列:{ 9, 5, 7, 5 },堆顶元素是9,堆排序下一步将9和第二个5进行交换,得到序列 { 5, 5, 7, 9 },再进行堆调整得到{ 7, 5, 5, 9 },重复之前的操作最后得到{ 5, 5, 7, 9 }从而改变了两个5的相对次序。
这里写图片描述


/////////////////////////////
//下沉函数

void AdjustDown(int arr[  ],int size,int root)
{
     int parent=root;
     int child=root*2+1;
     while(child<size)
     {
           //使child指向较大的那个孩子
           if(child+1<size&&arr[child+1]>arr[child])
           {
                 ++child;
           }
           //如果此时child的值比parent大,
           //就需要交换child的值和parent的值
           //把大值往上换,并向下继续调整
           if(arr[child]>arr[parent])
           {
                 Swap(&arr[child],&arr[parent]);
                 parent=child;
                 child=parent*2+1;
           }
           //parent比child节点值还大,跳出循环
           else
           {
                 break;
           }
     }
}
//////////////////////////////////
//堆排序
//时间复杂度:O(NlogN)
//空间复杂度:O(1)  不稳定排序
/////////////////////////////////
void HeapSort(int arr[  ],int size)
{
      if(  size<=1)
      {
            return ;
      }
      //建堆
      int i=size/2-1;
      for( ;i>=0;--i )
      {
            AdjustDown(arr,size,i);
      }
      //定义一个end,[0,end) 表示未排序的堆区间
      int end=size-1;
      while(  end>0)
      {
            Swap(&arr[0],&arr[end]);
            AdjustDown(arr,end,0);
            --end;
      }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值