冒泡、插入,快排、归并四种排序算法分析

         冒泡与插入,快排与归并,俩俩在设计思想上都是相似的,以下先分别阐述四种排序的设计思想,再通过俩俩比较来体会这四种排序背后的思想。
一、排序方法
    1.冒泡排序
    冒泡由来:越大的元素会经由交换慢慢“浮”到数列的顶端;
    冒泡俩种方式:从左往右把较大者冒到后边,或从右往左把较小者冒到前边;但俩种方式本质一样,重点是通过俩俩比较,把逆序的元素进行交换,直到冒泡到正确的位置,且每次冒泡都能确定它所在的最终位置。

    伪代码:

    BUBBLE_SORT(A):

       for i<-1 to length[A]

           do for j<-length[A] downto i+1

                  do if A[j]<A[j-1]

                     then exchange A[j]<->A[j-1]

    复杂度分析:第i趟冒泡中需执行n-i次比较和交换操作,则i从1到n-1,执行的比较操作次数为:

         (n-1)+(n-2)+···+2+1=n*(n-1)/2=O(n^2)

    可得到冒泡排序执行过程中的数据比较次数与输入序列无关,数据的交换次数与元素的初始序列有关。最好情况下n次比较和0次交换。但平均下来是时间复杂度是O(n^2)。

    2.插入排序

    方法:每步将一个待排序的元素,按其排序码的大小,插入到前面已经排好序的一组元素中的适当位置上,直到元素全部插入为止。

    伪代码:

    INSERT_SORT(A):

        for j<-2 to length[A]

            do key<-A[j]

                //insertA[j] into the right position in the sorted sequence A[1...j-1]

                i<-j-1

                while i>0 and A[i]>key

                      do A[i+1]<-A[i]

                          i<-i-1

                A[i+1]<-key

        插入排序比较次数和移动次数均与元素的初始序列有关,最好情况n-1次比较和0次移动,最坏情况下n*(n-1)/2次比较和(n+4)*(n-1)/2次移动次数(第i移动是i+2次=i-1次移位+3步交换),平均情况的时间复杂度也是O(n^2)。

     3.快速排序

      方法:在每趟排序中找到基准元素,使得左边的所有元素都比它小,右边的都比它大,这样每次都能排好一个最终位置上的元素,再分而治之(俩边元素互不影响),直到所有元素有序。

      伪代码:

       QUICK_SORT(A,p,r)

          if p<r

             then pivot<-PARTITION(A,q,r)

                  QUICK_SORT(A,q,pivot-1)

                  QUICK_SORT(A,pivot+1,r)

      PARTITION(A,p,r)

          x<-A[r]

          i<-p-1

          for j<-p to r-1

               do if A[j]<=x

                   then i<-i+1

                        exchange A[i]<->A[j]

          exchange A[i+1]<->A[r]

          return i+1

     快速排序的运行时间取决于输入序列,理想情况下递归树的高度为O(logn),存储开销为O(logn),最坏情况就是一颗单支树,存储开销到O(n)。但平均情况的时间复杂度为O(nlogn),在内部排序中是较好的排序方式。

     4.归并排序

       方法:将待排序的元素序列分成俩个长度相同的子序列,为每一个子序列排列,然后再将他们合并成一个序列。

       伪代码:

       MERGE_SORT(A,p,r)

           if p<r

              then q<-(p+r)/2  //Choose the least integer more than (p+r)/2

                   MERGE_SORT(A,q,p)

                   MERGE_SORT(A,q+1,r)

                   MERGE(A,p,q,r)

       MERGE(A,p,q,r)

           n1<-q-p+1

           n2<-r-q

           create arrays L[1...n1+1] and R[1...n2+1]

           for i<-1 to n1

                do L[i]<- A[p+i-1]

           for j<-1 to n2

               do R[j]<- A[q+j]

           L[n1+1]<-∞

           R[n2+1]<-

           i<-1

           j<-1

           for k<-p to r

               do if L[i]<=R[j]

                   then A[k]<-L[i]

                        i<-i+1

                   else A[k]<-R[j]

                        j<-j+1

         归并排序是一个很完美的m路分治,递归树高度为O(logn),不依赖于输入序列的初始位置,最好、最坏、平均的时间复杂度都是O(nlogn),但它需要与原待排序一样大小的数组辅助空间。

二、排序思想比较

      1.冒泡与插入排序

        冒泡排序是每俩俩比较,若发生逆序则交换;而插入排序是先比较,找到正确的位置上,再移动和进行一次交换;这俩个排序的思想都是基于俩俩比较,且慢慢找到在已排好序数组中的正确位置。

        从时间复杂度上看都是O(n^2),但在实际的时间效率方面上,插入排序要比冒泡排序快,因为从上面的分析可看到,在每次循环中,虽然冒泡和插入排序的比较次数相同,但在移动次数上,冒泡需要移动3i次,而插入排序只需移动i+2次。

      2.快排和归并排序

         快排和归并都是运用分治法:Divide, Conquer andr Combine

           ①快速排序:

            Divide:找到基准元素pivot,将数组A[p..r]划分为A[p..pivot-1]和A[pivot+1...q];

            Conquer:对俩个划分的数组进行递归排序;

            Combine:因为俩个数组是就地排序,无需合并操作。

           ②归并排序;

            Divide:将n个元素分成各含n/2个元素的子序列;

            Conquer:用合并排序法对俩个子序列递归的排序;

            Combine:合并俩个已排序的子序列已得到排序结果。

         由上面的分析知,快排重点在第一步的分,不仅找到基准元素(确定了这个元素的最终位置)进行排序,而且划分的俩个数组互不影响,达到分而治之的效果,还不需要合并操作;而归并排序是完美分割的,重点在最后一步合并,将俩个排好序的子序列进行合并。

         从时间复杂度上分析,他们都是O(nlogn),但在实际运行时,快排会比归并更快,原因就在他们用分治法的侧重点不同,且快排去掉了合并操作。

三、排序代码

        用C++模板类编写的排序,将所有数据进行封装,并随机产生10组10000个数据进行测试比较平均时间。

//sort.h
#ifndef SORT_H
#define SORT_H
#include 
  
  
   
   
#include 
   
   
    
    
#include 
    
    
     
     
using namespace std;
#define defaultSize 10000

template
     
     
      
      
class Element           //数据表元素类定义 
{
public:
	int key;    //排序码
	Element
      
      
        & operator=(Element 
       
         & x){ key = x.key; return*this; } bool operator==(Element 
        
          &x) { return key == x.key; } bool operator <=(Element 
         
           & x){ return key <= x.key; } bool operator>(Element 
          
            & x){ return key > x.key; } bool operator<(Element 
           
             & x){ return key < x.key; } }; template 
            
              class dataList //数据表类定义 { friend istream& operator>> 
             
               (istream&, dataList 
              
                &); friend ostream& operator<< 
               
                 (ostream&, dataList 
                
                  &); public: dataList(int maxSz = defaultSize) :maxSize(maxSz), currentSize(0){ Vector = new Element 
                 
                   [maxSize]; } void Swap(Element 
                  
                    & x, Element 
                   
                     & y){ Element 
                    
                      temp; temp = x; x = y; y = temp; } int Length(){ return currentSize; } Element 
                     
                       & operator[](int i){ return Vector[i]; } //取第i个元素 int Partition(const int low, const int high); //快速排序划分 Element 
                      
                        * Vector; //存储排序元素中的向量 void randomNumber(dataList 
                       
                         &); private: int maxSize; //向量中最大元素个数 int currentSize; //向量当前元素个数 }; template 
                        
                          int dataList 
                         
                           ::Partition(const int low, const int high) //基准元素 { int pivotpos = low; Element 
                          
                            pivot = Vector[low]; //基准元素 for (int i = low + 1; i <=high; i++) //检测整个序列,进行划分 { if (Vector[i] < pivot) { pivotpos++; if (pivotpos != i) //小于基准的元素交换到左侧,且避免该左侧元素重复 Swap(Vector[pivotpos], Vector[i]); } } Vector[low] = Vector[pivotpos]; //基准元素调整就位 Vector[pivotpos] = pivot; return pivotpos; //返回基准元素 } template 
                           
                             istream& operator>>(istream& input, dataList 
                            
                              & list) { cout << "please enter the sorting dataList:"; for (int i = 0; i < list.maxSize; i++) { input >> list.Vector[i].key; list.currentSize++; } return input; } template 
                             
                               void dataList 
                              
                                ::randomNumber(dataList 
                               
                                 &list2){ srand((unsigned)time(0)); for (int i = 0; i < maxSize; i++){ list2.Vector[i].key=Vector[i].key = rand()%maxSize; currentSize++; list2.currentSize++; } } template 
                                
                                  ostream& operator<<(ostream& output, dataList 
                                 
                                   &list) { output << "The dataList are:"; for (int i = 0; i < list.currentSize; i++) output << list.Vector[i].key<<" "; return output; } template 
                                  
                                    void BubbleSort(dataList 
                                   
                                     &list, const int left, const int right) //起泡排序 { bool exchange; for (int i = left+1; i <= right; i++) { exchange = false; for (int j = right; j >= i; j--) { if (list.Vector[j - 1] > list.Vector[j]) //发生逆序,则轻的元素往前冒 { list.Swap(list.Vector[j - 1], list.Vector[j]); exchange = true; } } if (exchange == false) //本趟已无逆序,说明已全部排好 return; } } template 
                                    
                                      void InsertSort(dataList 
                                     
                                       & list, const int left, const int right) //直接插入排序 { Element 
                                      
                                        temp; int j; for (int i = left+1 ; i <= right; i++) { if (list.Vector[i] < list.Vector[i - 1]) { temp = list.Vector[i]; j = i - 1; do{ list.Vector[j + 1] = list.Vector[j]; //大的元素往后移 j--; } while (j >= left&&temp < list.Vector[j]); list.Vector[j + 1] = temp; } } } template 
                                       
                                         void QuickSort(dataList 
                                        
                                          &list, const int left, const int right) //快速排序 { if (left < right) { int pivotpos = list.Partition(left, right); //划分 QuickSort(list, left, pivotpos); QuickSort(list, pivotpos+1, right); } } template 
                                         
                                           void QuickSort_Insert(dataList 
                                          
                                            & list, const int left, const int right) { if (right - left <= 5) //元素序列长度小于M InsertSort(list, left, right); else { int pivotpos = list.Partition(left, right); QuickSort(list,left, pivotpos-1); QuickSort(list, pivotpos + 1, right); } } template 
                                           
                                             void mergeSort(dataList 
                                            
                                              & L1, dataList 
                                             
                                               &L2, int left, int right){ if (left>=right) return; int mid = (left + right) / 2; mergeSort(L1, L2, left, mid); mergeSort(L1, L2, mid + 1, right); merge(L1, L2, left,mid, right); } template 
                                              
                                                void merge(dataList 
                                               
                                                 &L1, dataList 
                                                
                                                  &L2, int left, int mid, int right){ //L1.vector[left:mid]与L1.vector[mid+1,right]是俩个有序表,将这俩个有序表归并成一个有序表L1.vector[left:right] for (int k = left; k <= right; k++){ L2[k] = L1[k]; } int s1 = left, s2 = mid + 1, t = left; while (s1 <= mid&&s2 <= right){ if (L2[s1] <= L2[s2]) L1[t++] = L2[s1++]; else L1[t++] = L2[s2++]; } while (s1 <= mid) L1[t++] = L2[s1++]; while (s2 <= right) L1[t++] = L2[s2++]; } #endif //main.cpp #include 
                                                 
                                                   #include "Sort.h" #include "WinTree.h" #include 
                                                  
                                                    const int ArraySize = 50; using namespace std; int main() { dataList 
                                                   
                                                     dataInsert[ArraySize]; dataList 
                                                    
                                                      dataBubble[ArraySize]; dataList 
                                                     
                                                       dataQuick[ArraySize]; dataList 
                                                      
                                                        dataMerge[ArraySize]; dataList 
                                                       
                                                         dataTemp; clock_t start,end; double averageMergeTime=0, averageQuickTime=0, averageBubbleTime=0, averageInsertTime=0; for (int i = 0; i < ArraySize; i++){ dataInsert[i].randomNumber(dataBubble[i]); dataQuick[i].randomNumber(dataMerge[i]); start = clock(); InsertSort(dataInsert[i], 0, dataInsert[i].Length() - 1); end = clock(); averageInsertTime += end - start; start = clock(); BubbleSort(dataBubble[i], 0, dataBubble[i].Length() - 1); end = clock(); averageBubbleTime += end - start; start = clock(); QuickSort(dataQuick[i], 0, dataQuick[i].Length() - 1); end = clock(); averageQuickTime += end - start; start = clock(); mergeSort(dataMerge[i], dataTemp, 0, dataMerge[i].Length() - 1); end = clock(); averageMergeTime += end - start; } cout << "The average time of Insert sort from random data is:" << (double)averageInsertTime / ArraySize << endl; cout << "The average time of Bubble sort from random data is:" << (double)averageBubbleTime / ArraySize << endl; cout << "The average time of Quick sort from random data is:" << (double)averageQuickTime/ ArraySize << endl; cout << "The average time of Merge sort from random data is:" << (double)averageMergeTime/ ArraySize << endl; system("pause"); return 0; } 
                                                        
                                                       
                                                      
                                                     
                                                    
                                                   
                                                  
                                                 
                                                
                                               
                                              
                                             
                                            
                                           
                                          
                                         
                                        
                                       
                                      
                                     
                                    
                                   
                                  
                                 
                                
                               
                              
                             
                            
                           
                          
                         
                        
                       
                      
                     
                    
                   
                  
                 
                
               
              
             
            
           
          
         
        
      
     
     
    
    
   
   
  
  



  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值