十种排序思路详解

1.冒泡排序

举个栗子:arr[10]={22,7,9,10,4,3,11,13,5,1};
对任意两个相邻的元素进行大小比较,第一次遍历,即从下标为1的元素开始,
每一个元素都与它前面的元素进行比较,大的放后面,小的放前面(也可以小的在后),
之后考虑的都是递增的情况。这样经过一次遍历之后,最大的元素已经到达数组最末。
然后进行第二次遍历,遍历的元素为n-1个(即9个),最后一个元素已经是最大的了,
不用考虑。如果有n个元素,只要重复上述步骤n-1次即可。(遍历到第n-1次时,最小的
元素已经在第一位,所以不需要继续遍历了)。
优化:如果经过一次遍历之后没有任何元素发生变化,就已经是有序了。
以下为代码部分:
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++){
   			if(arr[j-1]>arr[j]){
    				swap(&arr[j-1],&arr[j]); 
    				hasSwap = true;	//发生元素交换
   			}
  		} 
  		if(!hasSwap){	//如果没有元素交换,即是有序,直接退出。
   			break; 
  		}
 	}
}

2.直接插入

举个栗子:arr[10]={8,3,6,10,15,1,7,22,15,11};
                  j i
将从i开始的元素插入到i之前的有序数组中。将下标为i的元素记为key,将key插入到
arr[0]----arr[i-1]之间,使这个数组保持有序。j从i-1开始遍历,如果arr[j]>key,
arr[j+1]=arr[j],直到j<0或者arr[j]<key,arr[j+1]=key;

以下是代码部分:
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.折半插入

折半插入的思路和直接插入有些许类似,只是要考虑一个左右区间的问题。
以下为代码部分:
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.希尔排序

希尔排序的本质也是插入,就是要先对数据进行分组。
举个栗子:arr[10]={8,4,2,1,10,15,6,7,3,11};
定义一个步长 step;
step = 10/2=5;先将10个数分为5组:
对应关系:
arr[0]-----arr[5];
arr[1]-----arr[6];
arr[2]-----arr[7];
arr[3]-----arr[8];
arr[4]-----arr[9];
step = step/2=2;再将数据分为2组:
第一组:arr[0] arr[2] arr[4] arr[6] arr[8];
第二组: arr[1] arr[3] arr[5] arr[7] arr[9];
step = step/2=1;就是直接插入了,分为一组。
    	
以下是代码部分:
void shell_sort(int arr[],size_t n){
 	size_t step;
 	int i,j;
 	for(step=n/2;step>0;step=step/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.选择排序

第一次遍历将数组中的最大值放在数组的最末;
第二次遍历不考虑最后一个元素,将最大的元素放在倒数第二个位置;
每次在一个区间找一个最大值,将它放在数组的最末即可。
void choice_sort(int arr[],size_t n){
 	size_t i,j;
 	for(i=0;i<n-1;i++){	//循环n-1次即可,最后一个元素肯定是最小值。
 		int m = 0;
  		for(j=1;j<n-i;j++){//每次循环去掉最后i个元素
   			if(arr[j]>arr[m]){
    				m = j; 
  			 } 
  		}
  		if(m != n-1-i){ //如果m已经是最大值的下标,就不需要交换。
   			swap(&arr[m],&arr[n-1-i]); 
  		}
 	}
}

6.鸡尾酒排序

遍历一次能够记录最大值和最小值。
将最大值放在区间最末,最小值放在区间首。
改变区间的大小。

代码部分:
void cook_tail_sort(int arr[],size_t n){
 	size_t i,j;
	for(i=0;i<n/2;i++){
  		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){//如果最大值已经发生了交换,最小值下标又等于末尾下标,将max赋值为min;
   			min = max;
   		}
  		if(min != i){//最小值下标不等于数组开始下标才发生交换
   			swap(&arr[min],&arr[i]);
   		}
 	}
}

7.堆排序

将数据调整成大堆,将第一个元素与最后一个元素交换。
然后不考虑最后一个元素,重复上述步骤。

代码如下:
void reheap(int arr[],size_t index,size_t n){
 	size_t child = 2*index+1;//由堆排序性质可知下标index的左子树下标
 	int key = arr[index];	//记录index下标的值
 	while(child < n){
  		if(child+1<n && arr[child]<arr[child+1]){//存在右子树且右子树的值较大
  			++child; 
  		} 
  		if(key < arr[child]){//将较大值放在index处
   			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); 
 	}
 	for(i=n-1;i>0;i--){
  		swap(&arr[0],&arr[i]);
  		reheap(arr,0,i);
 	}
}

8.归并排序

对一个数组不断分组,直到组内只有一个元素为止。
代码如下:
//归并排序,前后数组是有序的
void mergeArr(int arr[],size_t n){
 	if(n<=1){
  		return;
  	}
 	int mid = n/2;//[0,mid-1]  [mid,n-1]
 	int len = mid;
 	int *prr = malloc(sizeof(int)*len);
 	size_t i;
 	for(i=0;i<len;i++){
  		prr[i] = arr[i]; 
 	}	
 	size_t j,k;
 	i=0;//prr
 	j=mid;//arr后半部分的数据
 	k=0;//存放的位置
 	while(i<len && j<n){
 		 if(prr[i]<arr[j]){
   			arr[k++] = prr[i++]; 
  		}else{
  		 arr[k++] = arr[j++]; 
  		}
 	}
 	while(i<len){
 		 arr[k++] = prr[i++]; 
 	}
 	free(prr);
}
void merge_sort(int arr[],size_t n){
 	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);
}

9.快速排序

找一个基准值 把该基准值放到合适的位置
左边的元素小等于该基准值   右边的元素大小等于该基准值 
左右重复该动作即可
在[left,right]区间里找。
int key = arr[left];
i=left,j=right;
只要i<j 
首先从右边找小于key的值放到arr[i]位置
然后从左边找大于key的值放到arr[j]位置
当i<j不成立时  i==j 
 arr[i] = key。

代码如下:
void quick(int arr[],int left,int right){
 	if(left >= right){
 	 	return;
 	 }
 	int i=left,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);
}

10.计数排序

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++){
  		prr[i] = 0; 
 	}
 	for(i=0;i<n;i++){
  		prr[arr[i]-min]++; 
 	}
 	size_t j=0;
 	for(i=0;i<cnt;i++){
  		while(prr[i] > 0){
   			arr[j++] = i+min;
   			--prr[i];
  		} 
 	}
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值