C语言实现10种排序方式

排序方式

  1. 冒泡排序
  2. 直接插入排序
  3. 折半插入排序
  4. 希尔排序
  5. 选择排序
  6. 鸡尾酒排序
  7. 堆排序
  8. 快速排序
  9. 归并排序
  10. 计数排序

1.冒泡排序

算法思路:冒泡排序就是把小的元素往前调或者把大的元素往后调,比较是相邻的两个元素比较,交换也发生在这两个元素之间。
时间复杂度:冒泡排序的最好时间复杂度是O(n),最差时间复杂度是O(n^2)。
代码:

void bubble_sort(int arr[],size_t n){
 	size_t i,j;
 	for(i=0;i<n;i++){
  		bool hasSwap = false;//优化,判断数组是否发生了交换,如果没有发生交换说明有序可以直接退出循环
  		for(j=1;j<n-i;j++){//因为每次比较后都会把大的放到后面,因此n-i的位置一定是最大的不需要比较
  			 if(arr[j-1]>arr[j]){
    				swap(&arr[j-1],&arr[j]); //交换值的位置的函数
   				 hasSwap = true;//发生了交换
  			 }
 		 } 
 		 if(!hasSwap){//判断是否发生了交换
  			break; 
		 }
	 }
}

2.直接插入排序

算法思路:把一个数插入到一个有序的数列中,把数组中的元素逐个插入到一个有序的数列当中,从数组中的第一个元素开始,
把这个元素记录为key,把key插入到前面有序的数组中。
时间复杂度:O(n^2)
代码:
void insert_sort(int arr[],size_t n){		
	int i,j;
 	for(i=1;i<n;i++){//遍历数组中的元素 依次插入到前面
 		int key = arr[i];//记录需要插入的数据
  		for(j=i-1;j>=0&&arr[j]>key;--j){//找到插入位置
 	  		arr[j+1] = arr[j]; //把插入位置后面的元素一次后移
 	 	}
 	 	arr[j+1] = key;//插入该元素
 	}
}

3.折半插入排序

算法思路:本质上与直接插入无异,但通过折半查找的方式找到插入位置,提高效率。
时间复杂度:O(n^2)
代码:
void bin_insert_sort(int arr[],size_t n){
	int i,j;
	for(i=1;i<n;i++){
 		int key = arr[i];
		int left = 0;//左边下标
 		int right = i-1;//右边下标
		while(left <= right){//折半查找
   			int mid = (left+right)/2;//中间节点
 			if(key<arr[mid]){
  		  		right = mid-1;
		   	}else{//
 		   		left = mid+1; 
 		  	}
	  	}
 	 	for(j=i-1;j>=left;j--){//把元素往后移
 			arr[j+1] = arr[j]; 
	 	}
 	 	arr[j+1] = key;
	 }
}

4.希尔排序

算法思路:本质也是插入,分组,组内进行插入排序,避免像直接插入时大量地移动一个数据,把一个数据放到大致的位置。
时间复杂度:O(n^1.3)
代码:
void shell_sort(int arr[],size_t n){
 	size_t step;//定义一个组数
 	int i,j;
 	for(step=n/2;step>0;step=step/2){//先从n/2组开始排序
  		for(i=step;i<n;i++){//除了每组第一个元素以外,其它元素要插入
  			int key = arr[i];
   			for(j=i-step;j>=0&&arr[j]>key;j=j-step){
   				arr[j+step] = arr[j];//本质与直接插入相同 
  			}
   			arr[j+step] = key;
 		} 
	}
}

5.选择排序

算法思路:每次循环找到一个最大值,将最大值与最后一个值进行交换,交换后确定最后一个是最大的,数组长度减小1.
时间复杂度:O(n^2)
代码:
void choice_sort(int arr[],size_t n){
 	size_t i,j;
 	for(i=0;i<n-1;i++){
		int m = 0;//先假设最大值的下标为0
  		for(j=1;j<n-i;j++){
   			if(arr[j]>arr[m]){//找到比m下标更大的值
    				m = j; //找到最大值的下标
   			} 
  		}
  		if(m != n-1-i){//只要最大值的下标不是数组末尾的下标就把最大值方到数组末尾。
   			swap(&arr[m],&arr[n-1-i]); 
  		}
 	}
}

6.鸡尾酒排序

算法思路:每次遍历找到一个最大值和一个最小值,将最大值放到数组末尾,将最小值放到数组开始位置。
时间复杂度:O(n^2)
代码:
void cook_tail_sort(int arr[],size_t n){
 	size_t i,j;
 	for(i=0;i<n/2;i++){//因为每次能找到一个最大值一个最小值,因此只需要遍历n/2次
 		int max = i;//最大值下标
  		int min = i;//最小值下标
 		for(j=i+1;j<n-i;j++){
  			if(arr[max]<arr[j]){
   				max = j; 
  			} 
 			if(arr[min]>arr[j]){
  				min = j;
 			}
		}
		//下面这一块预防的是假如交换了最大值的位置后,正好将最小值交换到开始位置的情况,重复交换,位置将不变。因此有了下面的条件。
  		if(max != n-1-i)
  			swap(&arr[max],&arr[n-1-i]);
  		if(min == n-1-i)
 			min = max;
  		if(min != i)
  			swap(&arr[min],&arr[i]);
 	}
}

7.堆排序

算法思路:把数组调整成大堆,把第一个元素和最后一个元素进行交换,不考虑最后一个元素,重新调整成大堆重复上面的步骤。
时间复杂度:O(nlgn)
代码:
void reheap(int arr[],size_t index,size_t n){//将数组调整成大堆的形式	
	size_t child = 2*index+1;//左子节点的下标
 	int key = arr[index];
 	while(child < n){
  		if(child+1<n && arr[child]<arr[child+1]){//如果右子节点存在并且右子树比左子树的值更大
   			++child; 
  		} 
  		if(key < arr[child]){//如果子树比key值大,将index上的值改成child上的值
   			arr[index] = arr[child]; 
  		}else{
   			break;//否则跳出 
  		}
  		index = child;
  		child = 2*index+1;
 	}
 	arr[index] = key;
}
//堆排序
void heap_sort(int arr[],size_t n){
 	int i;
 	for(i=n/2;i>=0;i--){
  		reheap(arr,i,n); //将第i个元素大堆调整
 	}
 	for(i=n-1;i>0;i--){
  		swap(&arr[0],&arr[i]);//交换第一个和最后一个元素
  		reheap(arr,0,i);//对一个元素大堆化
 	}
}

8.快速排序

算法思路:通过一趟排序将要排序的数据分割成独立的两部分,其中一部分的所有数据都比另外一部分的所有数据都要小,
然后再按此方法对这两部分数据分别进行快速排序,整个排序过程可以递归进行,以此达到整个数据变成有序序列。
时间复杂度:O(nlog2n)
代码:
t arr[],int left,int right){
 	if(left >= right)//只有一个元素可以直接返回
  		return;
 	int i=left;
 	int j=right;
 	int key = arr[i];//基准
 	while(i<j){//找到基准的位置
  		while(i<j&&arr[j]>=key){//找到基准右边的值都比基准的大,直到有一个比基准值小,拿到这个值。
   			--j; 
  		} 
  		arr[i] = arr[j];
  		while(i<j&&arr[i]<=key){//找到基准左边的值都比基准的小,直到有一个比基准值大,拿到这个值。
   			++i; 
  		}
  		arr[j] = arr[i];
 	}
 	arr[i] = key;
 	if(i-1-left+1>1)//进行递归调用
  		quick(arr,left,i-1);
 	if(right-(i+1)+1>1)
  		quick(arr,i+1,right);
}

9.归并排序

算法思路:归并排序(MERGE-SORT)是建立在归并操作上的一种有效的排序算法,该算法是采用分治法(Divide and Conquer)的一个非常典型的应用。
将已有序的子序列合并,得到完全有序的序列;即先使每个子序列有序,再使子序列段间有序。若将两个有序表合并成一个有序表,称为二路归并。
归并排序是一种稳定的排序方法。
时间复杂度:O(nlog2n) 
代码:
void mergeArr(int arr[],size_t n){
 	if(n<=1)
  		return;
 	int mid = n/2;//[0,mid-1]  [mid,n-1]
 	int len = mid;//prr的长度,数组前半部分
 	int *prr = malloc(sizeof(int)*len);
	size_t i;
	for(i=0;i<len;i++){
		prr[i] = arr[i]; //把arr中前len个元素复制到arr中
 	}
 	size_t j,k;
 	i=0;//prr
 	j=mid;//arr后半部分的数据
 	k=0;//存放的位置
 	while(i<len && j<n){
  		if(prr[i]<arr[j]){//把临时数组的元素和 [mid+1,right]这部分的元素一个一个的进行比较,如果谁小,那么arr里就存放谁的元素
   			arr[k++] = prr[i++]; 
  		}else{
   			arr[k++] = arr[j++]; 
  		}
 	}
 	while(i<len){
  		arr[k++] = prr[i++]; 
 	}
 	free(prr);
}
void merge(int arr[],int left,int right){
 	if(left >= right)
 		return;
 	int mid = (left+right)/2;//[left,mid] [mid+1,right]
}
void merge_sort(int arr[],size_t n){// [5,3]  mid=1 [0,1),[1,1] 
 	//merge(arr,0,n-1);
	if(n<=1){
 		return; 
 	} 
 	int mid = n/2; //[0,mid-1] [mid,n-1]
 	merge_sort(arr,mid);//对前半部分排序
 	merge_sort(arr+mid,n-mid);//后半部分排序
 	mergeArr(arr,n);
}

计数排序

算法思路:计数排序是一个非基于比较的排序算法,该算法于1954年由 Harold H. Seward 提出。
时间复杂度:Ο(n+k)(其中k是整数的范围)
代码:
void count_sort(int arr[],size_t n){
 	int max = arr[0];//最大值
 	int min = arr[0];//最小值
 	size_t i;
 	for(i=1;i<n;i++){
  		if(max < arr[i]){//找到最大值
   			max = arr[i]; 
  		} 
  		if(min >  arr[i]){//找到最小值
  			 min = arr[i]; 
  		}
 	}
 	int cnt = max-min+1;//范围
 	int *prr = malloc(cnt*sizeof(int));
 	for(i=0;i<n;i++){//临时空间全部置0
  		prr[i] = 0; 
 	}
 	for(i=0;i<n;i++){//对需要排序的数组进行遍历
  		prr[arr[i]-min]++;//临时数组值+1 
 	}
 	size_t j=0;
 	for(i=0;i<cnt;i++){//遍历临时数组
  		while(prr[i] > 0){//值比0大
   			arr[j++] = i+min;//让需要排列的数组的值为i+min
   			--prr[i];
 		 } 
 	}
}
  • 3
    点赞
  • 13
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值