各种排序算法的分析与实现(C++版)

冒泡排序、选择排序、插入排序、希尔排序、快速排序、归并排序、堆排序、桶排序的实现


在待排序的记录序列中,可能存在两个或两个以上关键字相等的记录,若在排序后这些相等的关键字的前后顺序仍与之前的顺序保持不变,则称该排序方法是稳定的;反之,若排序后相等关键字的前后顺序可能发生变化,则称所用的排序方法是不稳定的


冒泡排序

思路:从第一个元素开始,对数组中两两相邻的元素比较,将值较小的元素放在前面,值较大的元素放在后面,一轮比较完毕,一个最大的数沉底成为数组中的最后一个元素,一些较小的数如同气泡一样上浮一个位置。n个数,经过n-1轮比较后完成排序。

//冒泡排序 
//平均时间复杂度为O(n²),最坏时间复杂度为O(n²),最好时间复杂度为O(n)
//空间复杂度为O(1)
//稳定排序
void bubbleSort( int *iarr, int length ){
	
	//非法情况:iarr为空或者数组长度小于1
	if( !iarr || length < 1 )
		return;

	int tmp;
	bool flags;	
	for( int i = 1; i < length; i++ ){
		flags = true;
		for( int j = 0; j < length - i; j++ ){
			if( iarr[j] > iarr[j+1] ){
				flags = false;
				tmp = iarr[j];
				iarr[j] = iarr[j+1];
				iarr[j+1] = tmp;
			}
		}
		if( flags )
			break;
	}
}

选择排序

思路:每一趟从待排序的数据元素中选出最小(或最大)的一个元素,顺序放在已排好序的数列的最后,直到全部待排序的数据元素排完。

//选择排序 
//平均时间复杂度为O(n²),最坏时间复杂度为O(n²),最好时间复杂度为O(n²)
//空间复杂度为O(1)
//不稳定排序
void selectSort( int *iarr , int length ){

	//非法情况:iarr为空或者数组长度小于1
	if( !iarr || length < 1 )
		return;

	int tmp;
	int min_index;
	for( int i = 0; i < length; i++ ){
		min_index = i;
		for( int j = i+1; j < length; j++ ){
			if( iarr[min_index] > iarr[j] ){
				min_index = j;
			}
		}
		if( min_index != i ){
			tmp = iarr[i];
			iarr[i] = iarr[min_index];
			iarr[min_index] = tmp;
		}
	}
}

插入排序

思路:将一个记录插入到已经排好序的有序表中,从而得到一个新的、记录增1的有序表。

//插入排序 
//平均时间复杂度为O(n²),最坏时间复杂度为O(n²),最好时间复杂度为O(n)
//空间复杂度为O(1)
//稳定排序
void insertSort( int *iarr , int length ){

	//非法情况:iarr为空或者数组长度小于1
	if( !iarr || length < 1 )
		return;

	int tmp;
	int i, j;
	for( i = 1; i < length; i++ ){		
		tmp =iarr[i];
		j = i-1;
		while( j >= 0 && iarr[j] > tmp ){
			iarr[j+1] = iarr[j];
			j--;
		}
		iarr[j+1] = tmp;
	}
}

希尔排序

思路:先将整个待排记录序列分割成为若干子序列分别进行直接插入排序,待整个序列中的记录“基本有序”时,再对全体记录进行一次直接插入排序。即先取一个小于 n 的整数 d1 作为第一个增量,把文件的全部记录分成 d1 个组。所有距离为 d1 的倍数的记录放在同一个组中。先在各组内进行直接插人排序;然后,取第二个增量 d2<d1 重复上述的分组和排序,直至所取的增量 dt = 1 (dt<dt-l<…<d2<d1),即所有记录放在同一组中进行直接插入排序为止。【若待排记录序列为正序,其时间复杂度可提高到O(n),所以希尔排序的时间复杂度为O(nlogn)。


void shellInsert( int *iarr , int length , int step ){
	
	int tmp;
	int i, j;
	for( i = step; i < length; i++ ){
		tmp = iarr[i];
		j = i-step;
		while( j >= 0 && iarr[j] > tmp ){
			iarr[j+step] = iarr[j];
			j -= step;
		}
		iarr[j+step] = tmp;
	}
}

//希尔排序 
//平均时间复杂度为O(nlogn),最坏时间复杂度为O(nlogn),最好时间复杂度为O(nlogn)
//空间复杂度为O(1)
//稳定排序
void shellSort( int *iarr, int length ){

	//非法情况:iarr为空或者数组长度小于1
	if( !iarr || length < 1 )
		return;
	
	int step = length/2 + 1;
	for( ; step > 2; step = step/2 + 1 ){
		shellInsert( iarr, length, step );
	}
	shellInsert( iarr, length, 1 );

}


快速排序

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

void qSort( int *iarr , int begin , int end ){
	if( begin > end )
		return;

	int i = begin, j = end;
	int pivot = iarr[i];		//枢轴
	while( i < j ){
		while( i < j && pivot <= iarr[j] ){
			j--;
		}
		if( i < j ){
			iarr[i] = iarr[j];
			i++;
		}
		while( i < j && pivot >= iarr[i] ){
			i++;
		}
		if( i < j ){
			iarr[j] = iarr[i];
			j--;
		}
	}
	iarr[i] = pivot;
	qSort( iarr, begin, i-1 );
	qSort( iarr, i+1, end );
}

//快速排序 
//平均时间复杂度为O(nlogn),最坏时间复杂度为O(n²),最好时间复杂度为O(nlogn)
//空间复杂度为O(logn)
//不稳定排序
void quickSort( int *iarr , int length ){

	//非法情况:iarr为空或者数组长度小于1
	if( !iarr || length < 1 )
		return;
	
	qSort( iarr, 0, length-1 );
}

归并排序

思路:将两个或两个以上的有序表组合成一个新的有序表。

void merge( int *iarrA , int *iarrB , int begin , int mid , int end ){
	int i = begin, j = mid+1, k = begin;
	while( i <= mid && j <= end ){
		if( iarrA[i] < iarrA[j] ){
			iarrB[k++] = iarrA[i++];
		}else{
			iarrB[k++] = iarrA[j++];
		}
	}
	while( i <= mid ){
		iarrB[k++] = iarrA[i++];
	}
	while( j <= end ){
		iarrB[k++] = iarrA[j++];
	}
	for( i = begin; i <= end; i++ ){
		iarrA[i] = iarrB[i];
	}
	print( iarrA , end+1 );
	print( iarrB , end+1 );
}

void mSort( int *iarrA, int *iarrB, int begin, int end ){
	if( begin == end ){
		iarrB[begin] = iarrA[begin];
	}else{
		int mid = ( begin + end )/2;
		mSort( iarrA, iarrB, begin, mid );
		mSort( iarrA, iarrB, mid+1, end );
		merge( iarrB, iarrA, begin, mid, end );
	}
}

//归并排序 
//平均时间复杂度为O(nlogn),最坏时间复杂度为O(nlogn),最好时间复杂度为O(nlogn)
//空间复杂度为O(n)
//稳定排序
void mergeSort( int *iarr, int length ){

	//非法情况:iarr为空或者数组长度小于1
	if( !iarr || length < 1 )
		return;

	int *iarrB = new int[length];
	mSort( iarr, iarrB, 0, length-1 );
	delete [] iarrB; //申请之后一定要释放
}

堆排序

思路:将数组 A 创建为一个最大堆,然后交换堆的根(最大元素)和最后一个叶节点 x,将x从堆中去掉形成新的堆 A1,调整 A1堆。重复以上动作,直到堆中只有一个节点。

void heapAdjust( int *iarr , int s , int m ){
	int i = 2*s +1, tmp = iarr[s];
	while( i <= m ){
		if( i+1 <= m && iarr[i] < iarr[i+1] ){
			i++;
		}
		if( tmp > iarr[i] ){
			break;
		}else{
			iarr[s] = iarr[i];
			s = i;
			i = 2*s +1;
		}
	}
	iarr[s] = tmp;
}

//堆排序 
//平均时间复杂度为O(nlogn),最坏时间复杂度为O(nlogn),最好时间复杂度为O(nlogn)
//空间复杂度为O(n);也可以做到O(1),将调整堆程序写入堆排序中,可以节省空间。
//不稳定排序
void heapSort( int *iarr , int length ){

	//非法情况:iarr为空或者数组长度小于1
	if( !iarr || length < 1 )
		return;
	
	int i, tmp;
	//调整堆,时间复杂度为O(n)
	for( i = length/2 -1; i >=0; i-- ){
		heapAdjust( iarr, i, length-1 );
	}

	for( i = length-1; i >= 0 ; i-- ){
		tmp = iarr[0];
		iarr[0] = iarr[i];
		iarr[i] = tmp;
		heapAdjust( iarr , 0 , i-1 );
	}
}

桶排序

思路:假设有一组长度为N的待排关键字序列 K[1....n]。首先将这个序列划分成 M 个的子区间(桶) 。然后基于某种映射函数 ,将待排序列的关键字 key 映射到第 i 个桶中,那么该关键字 key 就作为第 i 个桶的元素。接着对每个桶中的所有元素进行比较排序(可以使用快排、插排等)。然后依次从桶中倒出所有元素。

int divide10( int &num ){
	return num/10;
}

//桶排序 
//平均时间复杂度为O(n),最坏时间复杂度为O(nlogn),最好时间复杂度为O(n)
//空间复杂度为O(n)
//稳定性取决于桶内的排序算法
//下面的实现主要针对 0~99 之间的数字的,入桶用hash,除以10取商,桶内快排,
void bucketSort( int *iarr , int length ){

	//非法情况:iarr为空或者数组长度小于1
	if( !iarr || length < 1 )
		return;
	
	vector< vector<int> > ivec;
	int i, j, k, bucket_num = 10; // 桶的数量设置为10
	for( i = 0; i < bucket_num; i++ ){
		vector<int> vec;
		ivec.push_back( vec );
	}

	//对每个数字做除以10的处理后入桶
	for( i = 0; i < length; i++ ){
		ivec[ divide10(iarr[i]) ].push_back( iarr[i] );
	}

	//对每个桶做快速排序(或插入排序)
	for( i = 0; i < bucket_num; i++ ){
		if( ivec[i].size() )
			quickSort( &ivec[i][0], ivec[i].size() );
	}

	for( i = 0, j = 0; i < bucket_num; i++ ){
		for( k = 0; k < ivec[i].size(); k++ ){
			iarr[j++] = ivec[i][k];
		}
	}
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值