常用排序(二)

选择排序

选择排序说的是:每一趟在后面没有排序n-i个元素中,选择一个最小的放在第 i 个位置上,接下来选择i+1位置上的元素,一共需要执行n-2趟操作,他的时间复杂度是O(N*N)

选择排序有三种实现方式;直接选择排序、锦标赛排序、堆排序

1.直接选择排序

选择最小的元素,如果不是第一个就和第一个对调位置,这样他就是第一个了,重复直到排序结束

void SelectSort(dataList<int>& L,int left,int right){
	for(int i =left;i<right;i++){
		int k = i;	//代表最小码
		for(int j = i+1;j<=right;j++){
			if(L[k]>L[j]) k=j;
		}
		if(k!=i) Swap(L[i],L[k]);	//交换
	}
}
最坏情况下总的移动次数是3(N-1)次,总的比较次数是N(N-1)/2次

2.锦标赛排序

锦标赛排序的思想是:取N个元素的,两两进行比较,较小的保留下来,会得到N/2个元素,接下俩对着N/2个元素继续两两比较,直到选出最小的为止

胜者树是最底层的叶子节点是元素原本的样子,然后每两个叶节点进行比较,比较的结果变成他们的父节点,父节点之间在进行比较,最终根节点就是选择出来最小的结点,最底层的叶节点叫做胜者树的外部节点,非叶节点叫做内部节点

用胜者树对N个元素进行排序时,时间代价是O(nlog2 n)

void TournmentSort(int a[],int left,int right){
	size = right-left+1;
	WinnerTree<int> wt(size);
	int  data[size+1];
	for(int i =1;i<=size;i++){
		a[i+left-1] = data[i];
	}
	wt.Initial(data,size,Winner);	//初始化胜者树
	for(i=1;i<=size;i++){
		a[i+left-1] = wt.Winner();	//
		wt.Update();	//修改胜者
		wt.rePlay(t[1],Winner);	//重构,选出新的胜者
		if(wt.Winner()==maxValue) break;
	}
}
3.堆排序

先是根据元素数据利用堆的调整算法siftDown实现初始堆,接下来元素交换调整进行排序

先来说说最小堆的调整,一般而言有两种调整策略,从上到下的下滑调整和从下到上的上滑调整

下滑调整是:从父节点出发,看他的左右两个儿子中有没有比他大的,如果有的话就小的上移

上滑调整:和下滑正好相反,他是从儿子出发,看看父节点是否比他小,如果父节点小的话就不调整,若父节点大的话就调整、

void siftDown(int start,int m){	//从start开始到m为止
	int i= start;int j = 2*i+1;	//父节点是I,左子女是2*i+1
	int temp  = heap[i];
	while(j<=m){
		if(j<=m && heap[j]>heap[j+1]) j++;	//找到左右子女最小的
		if(temp<=heap[j]) break;
		else{
			heap[i] = heap[j];// i 节点的值现在是小的,然后 j 的值现在还是原来的
			i=j;j=2*j+1;
			}	//原来的J结点变成父节点,继续找最小值,下移了一层
	}
	heap[i] = temp;	//把最开始的I值放在最后的父节点上 
}
void siftUp(int start){	//从start向上到节点0
	int j=start,i = (j-1)/2,temp = heap[j];
	while(j>0){
		if(heap[i]<=temp) break;
		else{
			heap[j] = heap[i];
			j = i;
			i = (i-1)/2;
		}
	}
	heap[j] = temp;
	
}

在到堆排序中用的是最大堆,上面的代码是最小堆,当把最大的也就是堆顶的元素得到之后放在最后一个n-1的位置上,再找下一个元素

void HeapSort(maxHeap<int>& H){	//对H.heap[0]到H.heap[currentSize-1]排序
	for(int i = (currentSize-2)/2;i>=0;i--){	//表转化为堆
		siftDown(i,currentSize-1);
	}
	for(i = currentSize-1;i>=0;i--){	//表排序
		Swap(0,i);siftDown(0,i-1);	//交换重建最大堆
	}
}

主要解释一下第一个for循环,i=curr-2/2,这指的是最后两个元素所对应的父节点的位置,因为 j = 2*i+1是左节点,j = 2*i+2是右节点,这里先从最右边父节点就开始建立堆,从(curr-2)/2开始到curr-1,建立堆,只有一个父节点,然后开始i--,建立下一个父节点,直到所有的节点都被建立完全,然后第二个for循环是找到最大的数然后放在后面,当前最大的数是堆顶元素,是heap(0)的元素,把他和curr-1交换,这样他就在最后一个,接下来重新建立前面n-2和元素的最大堆,取出堆顶元素在放在curr-2的位置上,这样一次循环下去直到所有的都排好序


归并排序

归并排序也采用的是分治法,将大的序列分解为两个小的序列,序列长度相等,然后再将两个合并起来,其中的难点是合并

void merge(dataList<int>& L1,dataList<int>& L2,int left,int mid,int right){
	int s1 = left,s2 = right,t = left,k;	//s1和s2是检测指针,t是存放指针
	for(k = left;k<=mid;k++){
		L2[k] = L1[k];	//将L1的正向全部复制到L2
	}
	for(k = mid+1;k<=right;k++){
		L2[right-(k-mid-1)] = L1[k];	//反向复制
	}
	while(t<=right){	//归并
		if(L2[s1]<=L2[s2]) L1[t++] = L2[s1++];<span style="white-space:pre">	</span>//S1的数值小取出来放在L[t]中
		else L1[t++] = L2[s2--];<span style="white-space:pre">	</span>//s2小取出来放入T中
	}
}


基数排序

每个元素的排序码有多个,排序时要用多排序码排序,利用多排序码排序实现对单个排序码的排序叫做基数排序,多排序码排序包含有最高位优先MSD和最低位优先LSD两种。

 在MSD中,把单排序码看作是一个子排序码组,由好几个组成,例如三位数可以看成是有百位、十位、个位三个排序码组在一起形成的,这样排序时可以按照一个一个的排序码进行排序,比如先排百位再排十位个位

void RadixSort(dataList<int>& L,int left,int right,int d){
		//d表达的是第几位数,d=1是最低位
		int radix = 10;
		int i,j,count[radix],p1,p2;//count[k]处理第 i 位时,第 i 位值等于k的个数
		Element<int> array[right-left+1];//存放按桶分配结果
		if(d<=0) return;
		if(right-left+1<=M) InsertSort(L,left,right);//小的序列插排
		for(j=0;j<radix;j++) count[j]=0;//初始化
		for(i=left;i<=right;i++) //统计各桶元素
			count[getDigit(L[i],d)]++;
		for(j=1;j<radix;j++) 	//各桶元素存放位置
			count[j]=count[j]+count[j-1];
		for(i=left;i<=right;i++){	//分配桶中
			j = getDigit(L[i],d);	//取L[i]的第 d 位值
			array[count[j]-1] = L[i];
			count[j]--;
		}
		for(i=left,j=0;i<=right;i++,j++) 
			L[i]=array[j];	//写入原数组
		for(j=0;j<radix;j++){递归对d-1位处理
			p1 = count[j];	//开始端
			p2 = count[j+1]-1;	//尾端
			RadixSort(L,p1,p2,d-1);
		}
}


另外附上一个学习网站,当你不了解他是如何工作的时候,可以看看他的动画演示https://www.cs.usfca.edu/~galles/visualization/Algorithms.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值