常用排序(一)

排序根据元素是否完全在在内存中分为内部排序和外排序,内部排序指的是元素完全存放在内存中,外部排序是不断在内外存3之间移动的排序

基本的排序算法 中:直接插入排序、气泡排序、选择排序中时间复杂度都是n的二次方,高效排序中 :快排、堆排、归并排序都是O(n log2 n)

起泡排序 
他是从后倒着来,先比较n-1和n-2元素的大小,如果n-1元素小,就将他两交换位置,接下俩再向前移动,比较n-2和n-3的大小,如果n-2小则继续交换并且前移,这样经过一轮之后就将最小的元素放在了第一个的位置上,然后在进行下一趟排序,这个时间复杂度是n的平方,因为要每一趟比较都是N的时间单位,而要比较N-1趟
最原始的起泡排序就不附上代码了,接下来就是对起泡排序进行改进,可以先增加一个exchange的标志,如果发生了互换就exchange=true。否则就是exchange= false,表示全部元素已经排好序了,可以终止了,这样可以使得减去一些步骤,因为有的时候排序已经好了但是还是在进行着一趟趟的排序,但是并没有交换元素,浪费了

void BubbleSort(int V[],int n){
	bool exchange,int i ,int j ;
	for(i=0;i<n;i++){
		exchange = false;
		for(j=n-1;j>=0;j--){
			if(V[j-1]>V[j]){
				int temp = V[j];
				V[j] = V[j-1];
				V[j-1] = temp;
				exchange = true;
			}
		}
		if(exchange == false) break;	//本趟没有逆序的,终止
	}
}


插入排序
当插入新的元素时,原来的是有顺序的,前面的i-1个元素都是排好的,找到新元素的第i号位置后,原来后面的元素向后移动,然后把新元素插进去 ,时间复杂度是O(n*n)
插入排序中还有一种就是折半插入排序,又叫做二分法插入排序,在插入第i个元素的时候采用的是二分法查找元素的位置,时间复杂度是O(nlog2 n),相比起来要好很多

void  BinaryInsertSort(dataList<int>& L,int low,int high){
	Element<int> temp;int i,middle,k;
	for(i=low+1;i<=high;i++){
		//插入第 i 个数,前面一共有i-1个数,寻找区间
		temp = L[i];low = 0;high = i-1;
		while(low<high){
			middle = (low+high)/2;
			if(temp<L[middle]) high = middle-1;
			else low = middle+1;
		}
		//整体后移
		for(k = i-1;k>=low;k--) L[k+1] = L[k];
		L[low] = temp;
	}
}


希尔排序
又叫做缩小增量排序,待排序的有n个,取一个整数作为间隔gap<n,将全部元素分为gap个子序列,所有间隔是gap的放在同一子序列中在么一个子序列中执行插入排序,然后缩小gap,重复子序列的划分和排序,然后不断减小gap直到gap==1,所有元素放在同一个序列中,实行排序,最终得到结果
对于规模较大的序列,希尔排序有较高的效率,应用不同的gap效率差得很多,时间复杂度也没有一个定义公式,通常取gap = gap/3 +1

void ShellSort(dataList<int>& L,int left,int right){
	Element<int> temp;int i,gap = right-left+1;
	do{
		gap = gap/3+1;
		for(i= left+gap;i<=right;i++){	//遍历序列的,子序列一个一个处理
			if(L[i] < L[i-gap]){ //逆序,插排
				temp = L[i];
				j = i-gap;
				do{
					L[j+gap] = L[j];	//后移
					j = j-gap;	//比较前一元素
				}while(j>=left && temp<L[i]);
				L[j+gap] = temp;//这要加gap是再循环中,j=j-gap < left才跳出来的
			}
		}
	}while(gap>1)
}
这里有几个小的细节需要注意一下,第一个就是,在第一次for循环中,i=left+gap,为什么要设置这样呢,因为在子序列中需要比较,出现逆序的话是需要后移的,后移的方向是从后向前这样移动,不会冲掉数据


快速排序
又叫做分区排序,是使用最广泛的排序,他运行快,使用的空间少,是一种不稳定的排序方法
他采用的是分治法,将所有元素分为两个左右子序列,在左边的子序列中的值都小于基准值,右边序列的元素都大于等于基准值,然后对两个子序列分别执行上面说的方法,直到排完,采用递归的方法

void QuickSort(dataList<int>& L,int left,int right){
	if(left<right){
		int pivotpos = L.Partition(left,right);	//划分
		QuickSort(L,left,pivotpos-1);	//对左子序列处理
		QuickSort(L,pivotpos+1,right);	//对右子序列处理
	}
}
int Partition(int low,int high){
	int pivotpos = low;
	Elemenet<int> pivot = Vector[low];	//基准元素
	for(int i = low+1;i<=high;i++){
		if(Vector[i]<pivot){	//元素小于基准元素
			pivotpos++;
			if(pivotpos!=i) Swap(Vector[pivotpos],Vector[i]);//把小的放在前面
		}
	}
	Vector[low] = Vector[pivotpos];
	Vector[pivotpos] = pivot;
	return pivotpos;
}

图中wei表示的就是基准位置


时间复杂度是O(nlog2 n),和折半插入算法时间复杂度是一样的
对于快排法还有改进:
1.在递归的过程中,如果带排序的子序列的规模小于M值时,直接使用插入排序对子序列排序,而不是递归,这样不用递归,效率就会高很多

void QuickSort(dataList<int>& L,int left,int right){
	if(right-left<=M){
		InsertSort(L,left,right);
	}else{
		int pivotpos = L.Partition(left,right);	//划分
		QuickSort(L,left,pivotpos-1);	//对左子序列处理
		QuickSort(L,pivotpos+1,right);	//对右子序列处理
	}
}

2.在划分子序列时,不进行排序直接跳过,划分之后整体上市已经排好序的,这样就用插入排序
上述中if(right-left<=M),直接return,然后在快排之后在进行一遍插排,这样之后就是排好序的序列了

void HybridSort(dataList<int>& L,int left,int right){
	QuickSort(L,left,right);
	InsertSort(L,left,right);
}

快排中,基准元素选择的好不好直接影响到排序的效率,所以改进中,选取基准元素时,在left和right还有中间位置元素middle中取中间值,并交换到right位置上

Element<int>& median(dataList<int>& L,int left,int right){
	int middle = (left + right)/2;
	int k = left;	//k代表的是最小的
	if(L[middle]<L[k]) k = middle;
	if(L[k]>L[right]) k = right;
	if(k!=left) Swap(L[k],L[left]);	//最小的到left
	//L[middle]是中间值,交换到right位置上
	if(middle!=right && L[middle]<L[right]) Swap(L[middle],L[right]);
	return L[right];
}
int Partition(dataList<int>& L,int left,int right){
	int i=left;j=right-1;
	if(left<right){
		Elemenet<int> pivot = median(L,left,right);
		for(;;){
			while(i<j && L[i]<pivot) i++;	//正向走,小于pivot的留在左边
			while(i<j && pivot<L[j]) j--;	//逆向走,大于pivot的留在右边、
			if(i<j) Swap(L[i],L[j]);i++;j--;
			else break;
		}
		if(L[i]>pivot) L[right] = L[i];L[i] = pivot;
	}
}

这样先是快排,接着对基本的进行插排
对于有大量重复元素,快排效率非常的低,可以实行三路划分的方法来做,划分为大于小于以及等于基准元素三部分,在扫描左子序列时把等于的放在最左边,右子序列中等于的放在最右边

void QuickSort(dataList<int>& L,int left,int right){
	int i,j,p,q,k;
	Element<int> pivot = L[right];
	if(right<=left) return;
	i = left-1;j = right;p = left-1;q = right;
	while(1){
		while(L[++i]<pivot) if(i==j) break;
		while(L[--j]>pivot) if(j==i) break;
		if(i>=j) break;
		Swap(L[i],L[j]);
		if(L[i] == pivot) p++;Swap(L[p],L[i]);
		if(L[j] == pivot) q--;Swap(L[q],L[j]);
	}
	if(L[i]>L[right]) Swap(L[i],L[right]);k = right-1;
	else k = right;
	j--;i++;
	while(k>=q) Swap(L[k],L[i]);k--;i++;
	for(k = left;k<=p;k++;j--) Swap(L[k],L[j]);
	QucikSort(L,left,j);
	QucikSort(L,i,right);
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值