7种常见排序(冒泡、选择、插入、堆、希尔、归并、快排)

一、冒泡排序

   

时间复杂度:o(n^2)     空间复杂度:o(1)    稳定排序

算法:(从前往后)

     1.  比较相邻的两个元素,若第一个比第二个大就交换他们

     2.  从第一对比较到最后一对,最后会产生一个最大值

     3.  针对除了最后一个元素在进行相同的操作

     4.  持续上面的步骤,比较次数会越来越少,直到没有数据需要比较

   从后往前道理是一样的,每次产生一个最小值

代码:

  1 #include<stdio.h>
  2 #include<stdlib.h>
  3 //打印函数
  4 void print(int array[],int64_t size)
  5 {
  6         printf("this is:\n");
  7         int i;
  8         for(i=0;i<size;i++){
  9                 printf("%d ",array[i]);
 10         }
 11         printf("\n");
 12 }
 13 //交换函数
 14 void swap(int *a,int *b){
 15         int tmp=*a;
 16         *a=*b;
 17         *b=tmp;
 18 }
 19 //冒泡函数
 20 //void bubble_sort(int array[],int64_t size) { 
 21 //      if(size<=1){//若只有一个则直接返回
 22 //              return;
 23 //      } 
 24 //      int64_t bound=0;  //[0,bound):有序序列   [bound,size):无序序列 
 25 //      for(bound=0;bound<size;bound++){ 
 26 //              int64_t cur=size-1;  //从后往前进行比较,每次找到一个最小值 
 27 //              for(;cur>bound;cur--){
 28 //                      if(array[cur]<array[cur-1]){  //每次和前一个数据进行比较,若前边大于后边
 29 //                              swap(&array[cur],&array[cur-1]);//则进行交换
 30 //                      }//end if
 31 //              }//end for
 32 //      }//end for return;
 33 //}
 34 void bubble_sort(int array[],int64_t size)
 35 {
 36         if(size<=1){//若只有一个则直接返回
 37                 return;
 38         }
 39         int64_t bound=size-1;   //从前往后,每次找到一个最大值 (bound,size]是有序序列
 40         for(;bound>=0;bound--){
 41                 int64_t cur=1;
 42                 for(;cur<=bound;cur++){
 43                         if(array[cur]<array[cur-1])
 44                                 swap(&array[cur],&array[cur-1]);
 45 
 46                 }
 47         }
 48 }
 49 int main()
 50 {
 51         int array[]={9,5,2,7};
 52         bubble_sort(array,sizeof(array)/sizeof(array[0]));
 53         print(array,sizeof(array)/sizeof(array[0]));
 54         return;
 55 }

运行结果:

二、选择排序

   

时间复杂度:o(n^2)   空间复杂度: o(1)   不稳定

算法

                每一次从待排序的数据元素中选出最小(或最大)的一个元素,存放在序列的起始位置,直到全部待排序的数据元素排完

 17 void option_sort(int array[],int64_t size){
 18         if(size<=1){
 19                 return;
 20         }
 21         //[0,bound):有序序列  [bound,size):待排序序列
 22         int64_t bound=0;
 23         for(;bound<size;bound++){
 24                 int64_t cur=bound+1;  
 25                 for(;cur<size;cur++){
 26                         if(array[cur]<array[bound]){ //类似于打擂台,谁小谁就放到bound的位置
 27                                 swap(&array[cur],&array[bound]);
 28                         }//end if
 29                 }//end for
 30         }//end for
 31     return;
 32 }

结果

三、插入排序

时间复杂度:最坏:o(n^2) 最优:o(n)  空间复杂度:o(1)   稳定排序    此排序适合于数据较短或者差不多已经有序的数据

算法:

      将一个数据插入到已经有序的数据中,从而得到一个新的有序序列 。这是将数据分为两部分:第一部分包含了这个数组的所有元素,但将最后一个元素除外(让数组多一个空间才有插入的位置),而第二部分就只包含这一元素(待插入的元素)。在第一部分排序完成后,再将最后这个元素插入到已经排好的第一部分中。

    每步将一个待排序的记录,按其关键码值的大小插入到前面已经排序的数据的适当位置,知道全部插入完为止  

代码~

 12 void insert_sort(int array[],int64_t size)
 13 {
 14     if(size<=1){
 15                 return;
 16         }
 17         int64_t bound=1;
 18         //有序序列:[0,bound)  待排序列:[bound,size)
 19         for(;bound<size;bound++){
 20                 int64_t bound_value=array[bound];//由于后面会覆盖,使用变量将其保存
 21                 int64_t i=bound;
 22                 for(;i>0;--i){
 23                         if(bound_value<array[i-1]){  //若待排的数据小于前面数据,则将前 
                                                                 面的值给它
 24                                 array[i]=array[i-1];
 25                         }
 26                         else{
 27                                 break;
 28                         }
 29                 }//end for
 30                 //这里退出分为两种:
 31                 //1. 由于已经到了数组的第一个元素,那么此时数组的第一个元素就是待排数据的位置
 32                 //2. 找到了一个合适的位置,比它前面的数据大,那么此时这个位置就是它的位置
 33                     array[i]=bound_value;
 34         }//end for
 35         return;
 36 }

四、堆排序

时间复杂度:建堆nlog(n)+删除o(logn)= o(logn)     空间复杂度o(1)      不稳定

算法:       

         堆排序利用了大根堆(或小根堆)堆顶记录的 关键字最大(或最小)这一特征,使得在当前无序区中选取最大(或最小)关键字的记录变得简单。

     1)  建堆 

     2)  循环删除堆顶元素(用堆顶元素和最后一个元素进行交换)

 13 void swap(int *a,int *b);
 14 void adjustdown(int array[],int64_t size,int a)
 15 {
 10     }
 11     printf("\n");
 12 }
 13 void swap(int *a,int *b);
 14 void adjustdown(int array[],int64_t size,int a)
 15 {
 16         int  parent=a;
 17         int  child=2*parent+1;//左孩子
 18        //保证不是叶子结点
 19         while(child<size){
 20         //若右孩子的值大于右孩子,那么最大的孩子就为右孩子子
 21                 if((child+1)<size && array[child+1]>array[child] )
 22                 {
 23                         child=child+1;
 24                 }
 25       //若双亲结点的值大于最大的孩子,那么满足堆的性质
 26                 if(array[parent]> array[child]){
 27                         return;
 28                 }
 29      //不满足则进行交换,完成之后在进行向下调整
 30                 swap(&array[parent],&array[child]);
 31                 parent=child;
 32                 child=2*parent+1;
 33         }
 34         return;
 35 }
 36 void heapcreat(int array[],int64_t size)
 37 {
 38         if(size<=1){
 39                 return;
 40         }
 41         int i;
 42         //size-1:最后一个节点  那么它的双亲节点就为size-1-1/2
 43         for(i=(size-1-1)/2;i>=0;i--)
 44         {
 45                 adjustdown(array,size,i);//采用向下调整的方法进行建堆 
 46                                       //  因为在后面我们在删除堆顶用向下调整
 47                                       // 因此 这里我们就直接采用向下调整,当然也可以采用向上调整
 48         }
 49 }
 50 void swap(int *a,int *b){
 51         int tmp=*a;
 52         *a=*b;
 53         *b=tmp;
 54 }
 55 void popHeap(int array[],int size){
 56         swap(&array[0],&array[size-1]);//交换堆顶和最后一个元素
 57         adjustdown(array,size-1,0); // 此时可能不在满足堆的性质,进行向下调整
 58 }
 59 void heap_sort(int array[],int64_t size)
 60 {
 61         if(size<=1){
 62                 return;
 63         }
 64         //1、 创建堆  这里选择建大堆
 65         heapcreat(array,size);
 66         // 2、替换
 67         int j;
 68         for(j=0;j<size;j++){
 69         popHeap(array,size-j);//删除堆顶元素
 70 
 71 //              swap(&array[0],&array[size-1-j]);
 72 //              adjustdown(array,size-1-j,0);   
 73         }
 74 }

五、希尔排序

    时间复杂度:o(n^2) 最优o(n^1.3)     空间复杂度:o(1)     不稳定

算法:

   是插入排序的一种改进版本,它是先分组,在插入。

 13 void shell_sort(int array[],int64_t size){
 14         if(size<=1){
 15                 return;
 16         }
 17 //先插入第一组的第一个元素
 18 //再插入第二组的第一个元素
 19 //再插入第三组的第一个元素
 20 //....
 21 
 22         int64_t gap=size/2;    //0,gap,2gap,3gap
 23         for(;gap>=1;gap/=2){   //控制步长
 24                 int64_t bound=gap;
 25                 for(;bound<size;++bound){
 26                         int bound_value=array[bound];
 27                         int i=bound;//  其实在希尔排序这里,我们可以想象bound就是插入排序 
 里面的1,
 28                                         //因为它每次比较的是跨过gap个元素的一组数据
 29                         for(;i>=gap;i-=gap){
 30                                 if(bound_value<array[i-gap]){
 31                                         array[i]=array[i-gap];
 32                                 }
 33                                 else{
 34                                     break;
 35                                     }
 36                         }
 37                         array[i]=bound_value;
 38                 }//end for 
 39         }// end for
 40     return;
 41 }

六、归并排序

 时间复杂度: O(NlogN)
 空间复杂度: 对于数组来说, O(N)
稳定性: 稳定排序

 //实现归并
void MergeArray(int array[], int left, int mid, int right, int *tmp){
	int beg1 = left;   //这四个变量是每次需要归并的两个区间[beg1,end1)[beg2,end2)
	int end1 = mid;
	int beg2 = mid;
	int end2 = right;
	int index = left;  //申请空间变量的下标
	         while (beg1<end1 && beg2<end2){  
		                if (array[beg1] <= array[beg2]){
							//就把beg1 的数据放进去
			                         tmp[index++] = array[beg1++];
		}
		                 else{
							 //就把beg2 的数据放进去
							 tmp[index++] = array[beg2++];
		}
		
	}
			 //把剩下的元素追加到tmp末尾
	        while (beg1<end1){
				tmp[index++] = array[beg1++];
		
	}
	        while (beg2<end2){
				tmp[index++] = array[beg2++];
	}
			//将tmp里面的元素拷回原数组,这里要记着array+left和tmp+left
	// memcpy(array + left, tmp + left, sizeof((array[0])*(right - left)));
	
}

void _Mergesort(int array[], int left, int right, int *tmp){
	        if (right - left>1){
				
				int mid = left + ((right - left) >>1 );
				//保证左右区间都有序,才可进行归并
				_Mergesort(array, left, mid, tmp);//左闭右开
				_Mergesort(array, mid, right, tmp);
				MergeArray(array, left, mid, right, tmp);
				memcpy(array + left, tmp + left, sizeof(array[0])*(right - left));
	}
			//数据只有一个,那么必然有序,直接返回
	
}

// 递归的层数 logN
// 总的递归的次数 2logN 
void Mergesort(int array[], int size){
	// 先创建好缓冲区, 以备后面使用, 缓冲区的大小需要和
	// 原有的数组长度相同.
	int *tmp = (int *)malloc(sizeof(int)*size);
	// 下划线版本的函数辅助进行递归
	// 第一个参数, 输入数组的起始位置.
	// 第二个第三个参数表示当前进行归并排序的区间
	// [0, size)
	// 第四个参数表示用于归并的缓冲区起始位置
	_Mergesort(array, 0, size, tmp);
	free(tmp);
}

七、快速排序

       算法:

           是对冒泡的一种改进,通过一趟排序将要排序的数据分割成独立的两部分,其中一部分的所有数据都比另外一部分的所有数据都要小,然后再按此方法对这两部分数据分别进行快速排序,整个排序过程可以递归进行,以此达到整个数据变成有序序列。

      将最后一个设为基准值:


 19 int64_t partion(int array[],int64_t begin,int64_t end){
 20         //定义两个下标  
 21     int64_t left=begin;
 22     int64_t right=end-1;
 23     int tmp=array[end-1];//将最后一个设为基准值
 24     while(left<right){  
 25     while(left<right && array[left] <= tmp){//第一个下标从前往后遍历,找比基本值大的元素
 26         ++left;
 27             }
 28      while(left<right && array[right] >= tmp){//第二个下标从后往前遍历,找比基本值小的元素
 29      --right;
 30             }
 31     if(left<right){
 32         swap(&array[left],&array[right]);//找到之后两个交换位置
 33     }
 34 }    35 //left和right重合之后,就把基准值和left指向的元素进项交换
 36     swap(&array[left],&array[end-1]); 
 37  //前面的都比基本值小,后面的都比基本值大
 38    return left;
 39 }  
 40 
 41 void _quickSort(int array[],int64_t begin,int64_t end){
 42         if(end-begin<=1){
 43         return;
 44         }
 45         int64_t mid=partion(array,begin,end);//借助此函数完成基准值选择及交换,返回基准值最终的位置
 46                                         //基准值的左边都比他小右边都比他大
 47         _quickSort(array,begin, mid);//递归处理左边
 48         _quickSort(array,mid+1, end);//递归处理右边
 49 
 50 }
 51 void quick_sort(int array[],int64_t size){
 52     if(size<=1){
 53         return;
 54         }
 55 //下划线版本的函数辅助递归
 56     _quickSort(array,0,size);
 57 }

将第一个设为基准值:

 22 //前面为基准
 23 int64_t partion3(int array[],int64_t begin,int64_t end){
 24         int out_put=begin;
 25         int64_t tmp=array[out_put]i;//保存基准的值
 26         int64_t left=begin+1;//从基准的下一个位置开始
 27         int64_t right=end-1;
 28         
 29         while(left<right){
 30 //从左向右,若小于基准,则一直—++
 31                 while(left<right && array[left]<tmp){
 32                         left++;
 33                 }
 34 //找到一个比基准大的值,就和right进行交换,right--
 35                 swap(&array[left],&array[right]);
 36                 right--;
 37         }
 38 //判断left的值若大于基准,则left--肯定小于基准
 39     if(array[left]>tmp){
 40         left--;}
 41 //交换两个的值
 42         swap(&array[left],&array[out_put]);
 43         return left;
 44 }

快排----挖坑法:

          

代码实现:

 19 int64_t partion2(int array[],int64_t begin,int64_t end){
 20     int64_t left=begin;
 21     int64_t right=end-1;
 22     int tmp=array[end-1];//将最后一个设为基准值
 23     while(left<right){
 23     while(left<right){
 24 //若没有越界,且left小于基准,就一直++left,循环退出,找到一个比基准大的数
 25         while(left<right && array[left]<=tmp){
 26         left++;
 27         }
 28 //将其值付给right
 29         if(left<right){
 30         array[right]=array[left];
 31         right--;
 32         }
 33 //若没有越界,且right大于基准,就一直--right,循环退出,找到一个比基准小的数
 34         while(left<right && array[right]>=tmp){
 35         right--;
 36         }
 37 //将其值付给left
 38         if(left<right){
 39         array[left]=array[right];
 40         left++;
 41         }
 42     }
 43     array[left]=tmp;//将基准值给left
 44     return left;
 45 }

快排----前后指针法

    

 19 //前后指针法
 20 int64_t partion4(int array[],int64_t begin,int64_t end){
 21         int cur=begin;
 22         int pre=cur-1;
 23         int tmp=array[end-1];
 24                 while(cur<end){
 25 //若cur值小于基准,先让pre++,在判断pre是否和cur相遇,没相遇,就交换,在cur++,相遇,直接cur++
 26                         if(array[cur]<tmp && ++pre!=cur){
 27                                 swap(&array[cur],&array[pre]);
 28                         }
 29                         cur++;
 30                 }
 31 //pre此时还指向最后一个比基准值小的数,让pre++指向比基准值大的数,若pre没有越界,就让pre和基准值交换
 32         if(++pre!=end){
 33                 swap(&array[pre],&array[end-1]);
 34         }
 35         return pre;
 36 }

猜你喜欢

转载自blog.csdn.net/Z_JUAN1/article/details/81208321
今日推荐