第四章排序笔记

本文详细介绍了内部排序中的冒泡排序、选择排序、插入排序和希尔排序算法,包括它们的实现原理、步骤和复杂度分析。通过实例演示了如何在代码中应用这些排序方法。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

排序

排序笔记
内部排序(数据对象只在内存里进行排序)
外部排序(数对象过多不能同时放入内存,需要借助外存进行排序)

此处只总结内部排序:

冒泡排序

在数组中每次选择两个元素,按照需求进行交换,循环n次(n为总元素个数),每趟循环把最大的泡泡的冒到最下方。

void  bubbleSort(int arr[],int n){
	int i,j,temp;
	for(int i=1;i<n-1;i++){
		for(int j=0;j<n-i;j++){
			if(arr[j]>arr[j+1]){
				temp=arr[j];
				arr[j]=arr[j+1];
				arr[j+1]=temp;
			}
		}
	}
}//冒泡排序 

带入样例中的测试代码如下

#include<cstdio>
void  bubbleSort(int arr[],int n){
	int i,j,temp;
	for(int i=1;i<n-1;i++){
		for(int j=0;j<n-i;j++){
			if(arr[j]>arr[j+1]){
				temp=arr[j];
				arr[j]=arr[j+1];
				arr[j+1]=temp;
			}
		}
	}
}//冒泡排序 
int main(){
	int arr[]={1,13,63,5,78,9,12,52,8};
	int n=sizeof(arr)/sizeof(int);
	bubbleSort(arr,n);
	for(int i=0;i<n;i++){
		printf("%d ",arr[i]);
	}
	return 0;
}

选择排序:

简单选择排序:
对一个序列A中的元素A[1]~A[n],令i从1到n枚举,进行n趟操作,每趟从待排序部分[i,n]中选择最小的元素,令其与待排序部分的第一个元素A[i]进行交换,这样元素A[i]就会与当前有序区间[1,i]。n趟操作后,所有元素就会是有序的。

算法逻辑明显:共进行n趟操作,每趟操作选出待排序部分[i,n]中最小的元素,令其与A[i]交换。因此复杂度为O(n2)

void selectSort(){
	for(int i=1;i<=n;i++){//进行n趟操作 
		int k=i;
		for(int j=i;i<=n;j++){//找出[i,n]中最小的元素,下标为k 
			if(A[j]<A[k]){
				k=j;
			}
		}
		int temp=A[i];//交换 A[k]与A[i] 
		A[i]=A[k];
		A[k]=temp;
	}
}

插入排序

直接插入排序
对于序列A的n个元素A[1]~A[n],令i从2到n枚举,进行n-1趟操作。
假设某一趟时,序列A的前i-1个元素A[1]到A[i-1]已经有序,而范围[i,n]还未有序,那么该趟从范围[1,i-1]中找某个位置j,使得将A[i]插入位置j后。(此时A[j]到A[i-1]会后移一位至A[j+1]到A[i])范围[1,i]有序。

int A[maxn],n;  //n为元素个数,数组下标为1到n 
void insertSort(){
	for(int i=2;i<=n;i++){  //进行n-1趟排序 
		int temp=A[i],j=i;   //temp临时存放A[i],j从i开始往前枚举 
		while(j>1&&temp<A[j-1]){//只要temp小于前一个元素A[j-1] 
			A[j]=A[j-1];//把A[j-1]后移一位至A[j] 
			j--;
		}
		A[j]=temp;//插入位置为j 
	}
}
void insertSort(int arr[],int length) {
	int i,j;
	for(i=1;i<length;i++){
		int tmp=arr[i];
		for(j=i;j>0&&arr[j-1]>tmp;j--){
			arr[j]=arr[j-1];
		}
		arr[j]=tmp;
	}
} 

希尔排序

希尔排序是插入排序的一种;又称为“缩小增量排序”,是直接插入排序算法的更新高效的版本。希尔排序是非稳定排序算法。
希尔排序把记录按照下标的一定增量分组,对每组使用直接插入排序算法排序;随着增量逐渐减少(增量即为分成了一组),每组包含的关键词越来越多,当增量减至1时,整个文件最后插入排序微调后,算法终止。
选择增量gap=length/2,缩小增量继续以gap=gap/2的方式;所有增量的集合称为增量序列。希尔排序的增量序列的选择和证明是数学难题,这里选择的增量序列是常用的,也是建议使用的,称为希尔增量。但其实这个增量序列不是最优的。

int shellSort(int arr[],int n){
	for(int gap=n/2;gap>0;gap/=2){
		for(int i=gap;i<n;i+=1){
			int temp=arr[i];
			int j;
			for(j=i;j>=gap&&arr[j-gap]>temp;j-=gap){
				arr[j]=arr[j-gap];
			}
			arr[j]=temp;
		}
	}
	return 0;
}

归并排序

采用归并的思想实现的排序方法,采用分治策略(分治法将问题成一些小的问题然后递归求解,而的阶段则分的阶段得到的各答案“修补”在一起,即分而治之)。
将一个大的无序数组有序,我们可以把大的数组分成两个,然后对这两个数组分别进行排序,之后把这两个数组合并成一个有序的数组。由于两个小的数组都是有序的,所以在合并的时候是很快的。
通过递归的方式将大的数组一直分割,直到数组的大小为1,此时只有一个元素,那么该数组有序,之后再使用递归将两个数组大小为1的合并为2个,再把两个大小为2的合并为4个。。。直到全部小的数组合并起来。

//merge函数实现将已经 排好序的两个小段组合成一个有序的大段 
void merge(int arr[],int l,int m,int r){
	int i,j,k;
	int n1=m-l+1; 
	int n2=r-m;
	
	int L[n1],R[n2];
	for(i=0;i<n1;i++){
		L[i]=arr[l+i];  
    }
    for(j=0;j<n2;j++){
    	R[j]=arr[m+1+j];
	}
	i=0;
	j=0;
	k=1;
	while(i<n1&&j<n2){
		if(L[i]<=R[j]){
			arr[k]=L[i];
			i++;
		}else{
			arr[k]=R[j];
			j++;
		}
		k++;
	}
	while(i<n1){
		arr[k]=L[i];
		i++;
		k++;
	}
	while(j<n2){
		arr[k]=R[j];
		j++;
		k++;
	}
}
void mergeSort(int arr[],int l,int r){

	if(l<r){	
		int m=l+(r-l)/2;
		mergesort(arr,l,m);
		mergesort(arr,m+1,r);
		/*递归把数组不断分割成小的数组,分割为单个元素的数组
		递归的终止就是达到递归终止条件时(将数组分割成单个元素
		的数组时),开始执行merge()函数,将两个单个元素组合为
		两个排好序的数组,接着两个排为四个,以此类推。 */ 
		merge(arr,l,m,r);
	}
}

堆排序

利用堆这种数据结果而设计的排序算法,堆排序是一种选择排序,它的最坏,最好的平均时间复杂度均为O(nlogn),他是稳定排序。

拓展:堆:
堆是具有以下性质的完全二叉树。每个节点的值都大于或等于其左右孩子结点的值称为大顶堆;每个节点的值都小于或等于其左右孩子结点的值称为小顶堆,

基本思想:
a、将无序序列构造为一个堆(一般升序采用大顶堆,降序采用小顶堆)
b、将堆顶元素与末尾元素交换,将最大的元素沉到数组末端;
c、重新调整结构,使其满足堆定义,然后继续交换堆顶元素与当前元素末尾,反复执行调整+交换步骤,直到整个序列有序。

//建立堆函数
void heapify(int arr[],int n,int i){
	int largest=i;
	int l=2*i+1;
	int r=2*i;
	
	if(l<n&&arr[l]>arr[largest]){
		largest=l;
	}//如果left比root大的话 
	if(r<n&&arr[r]>arr[largest]){
		largest=r;
	}//如果right比root大的话 
	if(largest!=i){
		swap(arr[i],arr[largest]);
		heapify(arr,n,largest);//递归地定义子堆 
	}	
} 

//堆排序函数
void heapSort(int arr[],int n){
	for(int i=n/2-1;i>=0;i--){
		heapify(arr,n,i);
	}
	for(int i=n-1;i>=0;i--){
		swap(arr[0],arr[i]);
		heapify(arr,i,0);
	}
} 

桶排序

原理是将数组分到有限数量的桶中,在对每个桶子分别排序(可能使用别的排序算法或以递归的方式继续使用桶排序进行排序),最后将各个桶子中的数据有序的合并起来。

void bucketSort(float arr[],int n){
	vector<float> b[n];//创建一个空桶
	for(int i=0;i<n;i++){
		int bi=n*arr[i];
		b[bi].push_back(arr[i]);
	} //将数组元素放入不同的桶中 
	for(int i=0;i<n;i++){
		sort(b[i].begin(),b[i].end());
	}//排序 
	int index=0;
	for(int i=0;i<n;i++){
		for(int j=0;j<b[i].size(),j++){
			arr[index++]=b[i][j];
		}
	}//把所有桶中数据放入数组中 
}

基数排序

它是一种非比较型整数排序算法,是桶排序的拓展。其原理是将整数按位数切割成不同的数字,然后按每个位数分别比较。排序过程是将所有待比较数值统一为同样的数位长度,数位短的数字前面补零,然后从最低位开始,依次进行依次排序。从最低位排序一直到最高位排序完成之后,数列就变成一个有序序列。

//获取数组的最大值 
int getmax(int arr[],int n){
	int max=arr[0];
	for(int i=1;i<n;i++){
		if(arr[i]>max){
			max=arr[i];
		}
	}
	return max;
}

//对数组按照"某个位数"进行排序 (桶排序) 
void countsort(int arr[],int n,int exp){
	int output[n];//存储“被排序数据”的临时数组 
	int i,count[10]={0};//count:数据出现的次数 
	for(i=0;i<n;i++){
		count[(arr[i]/exp%)10]++;
	}  //将数据出现的次数存储在count[]中
	for(i=1;i<10;i++){
		count[i]+=count[i-1];
	} //更改count[i] ,目的:让更改后的count[i]的值,是该数据在output[]中的位置 
	for(i=n-1;i>=0;i--){
		output[count[(arr[i]/exp)%10]-1]=arr[i];
		count[(arr[i]/exp)%10]--;
	}//将数据存储 到临时数组output[]中 
	for(i=0;i<n;i++){
		arr[i]=output[i];
	}	 //将排序好的数据赋值给arr[] 
}


void radixSort(int arr[],int n){
	int exp;
	int max=getmax(arr,n);
	for(exp=1;max/exp>0;exp*=10){
		countsort(arr,n,exp);
	}//从个位开始,对数组按指数进行排序 
}

快速排序

快排也是分治的算法,每次选择一个元素并且将整个数组以那个元素分为两个部分,根据实现的算法不同,元素的选择一般有如下几种:
永远选第一个元素;
永远选最后一个元素;
随机选取元素;
取中间值;

快速排序的核心是分区,分区的目的是传入一个数组和选定的一个元素,把所有小于那个元素的其他元素放在左边,大于的放在右边。

1、先从队尾开始扫描且当low<high
若a[high]>temp,high–
若a[high]<temp,arr[low]=a[high],同时从队首开始扫描。
2、从队首开始扫描
若a[low]<temp,low++
若a[low]>temp,a[high]=arr[low],同时从队尾开始扫描。

#include<stdio.h>
void quickSort(int arr[],int low,int high){
	if(low>=high)
	    return;
	int first=low;
	int last=high;
	int temp=arr[first];
	while(first<last){
		while(first<last&&arr[last]>=temp){
			last--;
		}
		arr[first]=arr[last];
		while(first<last&&arr[first]<=temp){
			first++;
		}
		arr[last]=arr[first];
	}
	arr[first]=temp;
	quickSort(arr,low,first-1);
	quickSort(arr,first+1,high);	
}
int main(){
	int arr[]={49,38,65,97,23,22,76,1,5,8};
	for(int i=0;i<10;i++){
		printf("%d  ",arr[i]);
	}
	printf("\n");
	quickSort(arr,0,9);
	for(int i=0;i<10;i++){
		printf("%d  ",arr[i]);
	}
	return 0;
} 
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值