查找与排序小结

//顺序查找
//主要是为了说明引入"哨兵"的作用
typedef struct {		//查找表的数据结构
	ElemType *elem;		//元素存储空间基址,建表时按实际长度分配,0号单元留空
	int TableLen;		//表的长度
}SSTable;
int Search_Seq(SStable ST,ElemType key){	//顺序表ST中顺序查找键字为key的元素。若找到则返回该元素在表中的位置
	ST.elem[0]=key;							//"哨兵"
	for(i=ST.TableLen;ST.ele[i]!=key;--i);	//从后往前找
	return i;								//若表中不存在关键字为key的元素,将查找到i为0时退出for循环
}
//折半查找
int Binary_Search(SeqList L,ElemType key){	//在有序表L中查找关键字为key的元素,若存在则返回其位置,不存在则返回-1
	int low=0,high=L.TableLen-1,mid;
	while(low<=high){
		mid=(low+high)/2;					//取中间位置
		if(L.elem[mid]==key)
			return mid;						//查找成功则返回所在位置
		else if(L.elem[mid]>key)
			high=mid-1;						//从前半部分继续查找
		else
			low=mid+1;						//从后半部分继续查找
	}
	return -1;
}
//折半查找的递归算法
int BinSearchRec(SStable ST,ElemType key,int low,int high){
	if(low>high)
		return 0;
	mid=(low+high)/2;
	if(key>ST.elem(mid)
		BinSearchRec(ST,key,mid+1,high);
	else if(key<ST.elem[mid])
		BinSearchRec(ST,key,low,mid-1);
	else
		return mid;
}
//直接插入排序(对比下面的希尔排序)
void InsertSort(ElemType A[],int n){
	int i,j;
	for(i=2;i<=n;i++)
		if(A[i].key<A[i-1].key){			//防止过多不必要的操作
			A[0]=A[i];						//复制为哨兵,A[0]不存放元素
			for(j=i-1;A[0].key<A[j].key;--j)
				A[j+1]=A[j];
			A[j+1]=A[0];
		}
}
//我一般这样写:
void InsertSort(ElemType A[],int n){
	int i,j;
	ElemType temp;
	for(i=0;i<n;i++){
		temp=A[i];
		for(j=i-1;j>=0&&A[j].key>temp.key;j--)
			A[j+1]=A[j];
		A[j+1]=temp;
	}
}
//折半插入排序
void InsertSort(ElemType A[],int n){
	int i,j,low,high,mid;
	for(i=2;i<=n;i++){
		A[0]=A[i];
		low=1;high=i-1;						//设置折半查找的范围
		while(low<=high){
			mid=(low+high)/2;
			if(A[mid].key>A[0].key) high=mid-1;
			else low=mid+1;
		}
		for(j=i-1;j>=high+1;--j)
			A[j+1]=A[j];
		A[high+1]=A[0];
	}
}
//希尔排序
void ShellSort(ElemType A[],int n){
	//对顺序表作希尔排序,本算法和直接插入排序相比,作了以下修改:
	//1.前后记录位置的增量是dk,不是1
	//r[0]只是暂存单元,不是哨兵,当j<0时,插入位置已到
	for(dk=len/2;dk>=1;dk=dk/2)
		for(i=dk+1;i<=n;i++)				//因为A[0]作为暂存单元,所以i初始值为dk+1
			if(A[i].key<A[i-dk].key){
				A[0]=A[i];
				for(j=i-dk;j>0&&A[0].key<A[j].key;j-=dk)
					A[j+dk]=A[j];
				A[j+dk]=A[0];
			}
}
//冒泡排序
void BubbleSort(ElemType A[],int n){
	for(i=0;i<n-1;i++){
		flag=false;
		for(j=n-1;j>i;j--)					//从后往前比较,小元素往前走
			if(A[j-1].key>A[j].key){
				swap(A[j-1],A[j])
					flag=true;
			}
		if(flag==false)
			return ;
	}
}
//我一般这么写
void BubbleSort(ElemType A[],int n){
	for(i=0;i<n-1;i++){
		flag=false;
		for(j=0;j<n-1-i;j++)				//从前往后比较,大元素往后走
			if(A[j].key>A[j+1].key){
				swap(A[j],A[j+1];
				flag=true;
			}
		if(flag==false)
			return ;
	}
}
//双向冒泡排序算法:在正反两个方向交替进行扫描,即第一趟把关键字最大的元素和在序列的最后面,第二趟把关键字最小的元素主在序列的最前面。如此反复进行
void BubbleSort_DoubleDirection(ElemType A[],int n){
	int low=0,high=n-1;
	bool flag=true;
	while(low<high && flag){
		flag=false;
		for(i=low;i<high;i++)
			if(A[i]>A[i+1]){
				swap(a[i],a[i+1]);
				flag=true;
			}
		high--;
		for(i=high;i>low;i--)
			if(a[i]<a[i-1]){
				swap(a[i],a[i-1]);
				flag=true;
			}
		low++;
	}
}
//快速排序(是对冒泡排序的一种改进)
void QuickSort(ElemType A[],int low,int high){
	if(low<high){
		int pivotpos=Partition(A,low,high);		//划分
		QuickSort(A,low,pivotpos-1);
		Quicksort(A,pivotpos+1,high);
	}
}
int Partion(ElemType A[],int low,int high){		//严版教材中的划分算法(一趟排序过程)
	ElemType pivot=A[low];						//将当前表中第一个元素设为枢轴值,对表进行划分
	while(low<high){
		while(low<high && A[high]>=pivot) --high;	
		A[low]=A[high];
		while(low<high && A[low]<=pirot) ++low;
		A[high]=A[low];
	}
	A[low]=pivot;
	return low;
}
while(low<high){								//上面Partion()中第一个while可以写成如下形式:
	while(low<high && A[high]>=pivot) --high;
	while(low<high && A[low]<=pivot) ++low;
	if(low<high){
		swap(A[low],A[high]);
		if(A[low]==pivot && A[high]==pivot)
			low++;
	}
}
//快速排序的非递归算法
#define MAX 100
typedef struct {
	int low;
	int high;
} elem;
void QuiceSort_Non_RC(ElemType L,int length){
	elem S[MAX],temp;
	int top=-1;
	S[++top].low=0;
	S[top].high=length-1;
	while(top!=-1){
		temp=S[top--];
		low=temp.low;
		high=temp.high;
		pivot=L[low];
		while(low<high){
			while(low<high && L[high]>=pivot) high--;
			L[low]=L[high];
			while(low<high && L[low]<=pivot) low++;
			L[high]=L[low];
		}
		L[low]=pivot;
		if(temp.low<low-1){
			S[++top].low=temp.low;
			S[top].high=low-1;
		}
		if(low+1<temp.high){
			S[++top].low=low+1;
			S[top].high=temp.high;
		}
	}
}
/*
基于快排的划分操作求数组第k小元素,其思想是:
	从数组L[1...n]中选择枢轴pivot(随机地或者直接取第一个)进行和快速排序一样的划分操作后,表L[1...n]被划分为L[1...m-1]和L[m+1...n],其中L(m)=pivot.
	讨论m与k的大小关系:
	(1)当m=k时,显然pivot就是所要寻找的元素,直接返回pivot即可。
	(2)当m<k时,则所要寻找的元素一定落在L[m+1...n]中,从而可以对L[m+1...n]递归地查找第k-m小的元素。
	(3)当m>k时,则所要寻找的元素一定落在L[1...m-1]中,从而可以对L[1...m-1]递归地查找第k小的元素。
	这个算法的时间复杂度在平均情况下可以达到O(n)。而所占空间复杂度则取决于划分的方法。
*/
int kth_elem(int A[],int low,int high,int k){
	int pivot=A[low];
	int low_temp=low;
	int high_temp=high;
	while(low<high){
		while(low<high && A[high]>=pivot) --high;
		A[low]=A[high];
		while(low<high && A[low]<=pivot) ++low;
		A[high]=A[low];
	}
	A[low]=pivot;
	if(low==k)
		return A[low];
	else if(low>k)
		return kth_elem(A,low_temp,low-1,k);
	else
		return kth_elem(A,low+1,high_temp,k-m);
}
/*
荷兰国旗问题:设有一个仅由红、白、蓝三种颜色的条块组成的条块序列,请编写一个时间复杂度为O(n)的算法,使得这些条块按红、白、蓝的顺序排好,即排成荷兰国旗的图案。
算法的思想:顺序扫描线性表,将红色块交换到线性表的最前面,蓝色条块交换到线性表的最后面,为此设立三个指针,其中,j为工作指针表示当前扫描的元素,i以前的元素全部为
红色,k以后的元素全部为蓝色。根据j所指示元素的颜色,决定将其交换到序列的前部或者尾部。初始时,i=0,k=n-1,算法的实现如下:
*/
#define RED	-1
#define WHITE 0
#define BLUE 1
typedef enum{RED,WHITE,BLUE} color;		//设置枚举数组
void Flag_Arrange(color A[],int n){
	int i=0,j=0,k=n-1;
	while(j<=k)
		switch(A[j]){
		case RED:Swap(A[i],A[j]);i++;j++;break;
		case WHITE:j++;break;
		case BLUE:Swap(A[j],A[k]);k--;	//注意:这里没有j++语句以防止交换后A[j]仍为蓝色的情况!
	}
}
/*如将元素值为正数、负数和零排序成前面都是负数,接着是0,最后是正数,也是用同样的方法。
思考:为什么case RED时不用考虑交换后A[j]仍为红色,而case BLUE却需要考虑交换后A[j]仍为蓝色?
因为初始时i,j相同,且当为case RED时,i与j同步加加,所以不可能出现i与j同时指向红色且i与j不同的情况,也即要么i与j相同且同时指向红色,要么i与j不同且i与j之间全是白的。而i与k没能保证这样的关系。
*/
//简单选择排序
void SelectSort(ElemType A[],int n){
	for(i=0;i<n-1;i++){
		min=i;
		for(j=i+1;j<n;j++)
			if(A[j]<A[min]) min=j;
		if(min!=i) swap(A[i],A[min]);
	}
}
//堆排序,是一种树形选择排序方法。
void BuildMaxHeap(ElemType A[],int len){
	for(int i=len/2;i>0;i--)				//从i=[n/2]~1,反复调整堆
		AdjustDown(A,i,len);				//函数AdjustDown将元素i向下进行调整
}
void AdjustDown(ElemType A[],int k,int len){
	A[0]=A[k];								//A[0]暂存
	for(i=2*k;i<=len;i*=2){					//沿key较大的子结点向下筛选
		if(i<len && A[i]<A[i+1])			//有右孩子且右孩子比左孩子大
			i++;
		if(A[0]>=A[i]) break;
		else{
			A[k]=A[i];
			k=i;
		}
	}
	A[k]=A[0];
}
//堆排序算法
void HeapSort(ElemType A[],int len){
	BuildMaxHeap(A,len);
	for(i=len;i>1;i--){						//n-1趟的交换和建堆过程
		swap(A[i],A[1]);					//输出堆顶元素(和堆底元素交换),是原地排序从小到大
		AdjustDown(A,1,i-1);				//整理,把剩余的i-1个元素整理成堆
	}
}
/*堆也支持删除与插入的操作。由于堆顶元素或为最大值或为最小值,删除堆顶元素时,先将堆的最后一个元素与堆顶元素交换,由于此时堆的性质被破坏,需对此时的根
结点进行向下调整操作。对堆进行插入操作时,先将新结点放在堆的末端,再对这个新结点执行向上调整操作,大根堆插入时向上调整的算法如下:
*/
void AdjustUp(ElemType A[],int k){			//参数k为向上调整的结点,也为堆的元素个数
	A[0]=A[k];
	int i=k/2;
	while(i>0 && A[i]<A[0]){
		A[k]=A[i];
		k=i;
		i=k/2;
	}
	A[k]=A[0];
}
//归并排序
ElemType *B=(ElemType *)malloc((n+1)*sizeof(ElemType());	//辅助数组
void Merge(Elemtype A[],int low,int mid,int high){			//设表A的两段A[low...mid]和A[mid+1...high]各自有序,将它们合并成一个有序表
	for(int k=low;k<=high;k++)
		B[k]=A[k];
	for(i=low,j=mid+1,k=i;i<=mid && j<=high;k++){
		if(B[i]<=B[j])
			A[k]=B[i++];
		else
			A[k]=B[j++];
	}
	while(i<=mid) A[k++]=B[i++];
	while(j<=high) A[k++]=B[j++];
}
//合并,合并两个有序表得到排序结果
void MergeSort(ElemType A[],int low,int high){
	if(low<high){
		int mid=(low+high)/2;
		MergeSort(A,low,mid);
		MergeSort(A,mid+1,high);
		Merge(A,low,mid,high);
	}
}
//基数排序
//看这个博客吧:http://www.cnblogs.com/Braveliu/archive/2013/01/21/2870201.html
/*计数排序:对一个待排序的表(用数组表示)进行排序,并将排序结果存放到另一个新的表中。必须注意的是:
表中的所有待排序的关键码互不相同,计数排序算法针对表中的每个记录,扫描待排序的表一趟,统计表中有多少
个记录的关键码比该记录的关键码小,假设针对某一个记录,统计出的计数值为c,那么,这个记录在新的有序表中
的合适的存放位置即为c.*/
void CountSort(RecType A[],RecType B[],int n){
	int cnt;
	for(i=0;i<n;i++)}{
		for(j=0,cnt=0;j<n;j++)
			if(A[j].key<A[i].key)
				cnt++;
		B[cnt]=A[i];
	}
}
//上面的双for可以改一个,以达到“任意两个记录之间只进行一次比较”:
for(i=0;i<n;i++)
	A[i].count=0;
for(i=0;i<n;i++){
	for(j=i+1;j<n;j++)
		if(A[i].key<A[j].key)
			A[j].count++;
		else
			A[i].count++;
}
//外部排序
//看这个博客吧:http://blog.chinaunix.net/uid-25324849-id-2182916.html

  • 3
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值