排序
一、选择排序
选择排序有简单选择排序和堆排序(对选择排序的改进)
①简单选择排序
从未排序的序列中,选出最小的元素(先以该未排序序列的首个元素作为min)和该未排序序列的首个元素进行交换。
再在剩下的未排序的序列重复上述步骤直到该未排序序列的长度为1
//先写一个交换元素的函数
//因为我们用的是数组进行存储,则传入函数中的是一个指针便于访问
#include<stdio.h>
#include<math.h>
#include<stdlib.h>
void swap(ElementType*a,ElementType*b);
void SimpleSelectSort(ElementType A[],int N);
int main() {
return 0;
}
void swap(ElementType*a,ElementType*b){
ElementType t=*a;
*a=*b;
*b=t;
}
void SimpleSelectSort(ElementType A[],int N){
int i,j,min;
for(i=0;i<N-1;i++){//当i迭代到N-2时,未排序序列长度为1,此时循环就可以结束
min = i;//min每次迭代开始时指向未排序序列的第一个元素
for(j=i;j<N;j++){ //在未排序序列中找到最小元素
if(A[j]<A[min]) min = j;
swap(A[min],A[i]);//找到未排序序列最小元素 与未排序序列的第一个元素进行交换 交换后将未排序序列的第一个元素纳入到已排序序列中
}
}
}
②堆排序
这里利用树堆来进行排序
二、插入排序
①简单插入排序
将待排序的一组序列分为已排好序的和未排序的两个部分,将未排序序列中的元素逐一插入到已排序的元素当中。一开始时以第一个元素为已排序序列,则经过N-1次插入后,排序结束
第k-1次排序:将它和第k-1个元素(已排序序列的最后一个)比较。若大于,则循环结束。若小于,则交换两个元素的位置,将该元素继续与k-2个元素进行比较。
直到该树比它前一个位置的元素大 或者 该数已经在第一个位置上
#include<stdio.h>
#include<math.h>
#include<stdlib.h>
int main(){
return 0;
}
void SimpleInsertSort(ElementType A[],int N){
int p,i;
ElementType tmp;//新建一个tmp来储存未排序序列的第一个元素 防止右移导致覆盖丢失
for(p=1;p<N;p++){ //因为开始时默认A[0]为已排序元素,所以从i=1开始迭代
tmp = A[p];//储存未排序序列的第一个元素
for(i=p;i>0&&A[i-1]>tmp;i--) A[i]=A[i-1]; //如果前比后大 执行右移操作
A[i]=tmp;
}
}
②希尔排序
简单插入排序效率不高一个原因时因为每次只能交换相邻两个元素
希尔排序试图每次交换一定间隔的元素
原理:将待排序元素按一定间隔分为若干序列,分别进行插入排序。开始时设置的“间隔较大”,在每轮排序中将间隔逐渐减小直到间隔(增量)为1,此时进行的是最后的整个序列进行一次排序----简单插入排序
“间隔”定义为一组递减的增量序列(如sedgewick序列)。位置之差等于当前增量(步长)的元素归属于同一个子序列。排好后再选取下一个增量。
缺点:不是稳定的排序。可能导致数值相同的两个元素发生相对位置上的变化
#include<stdio.h>
#include<math.h>
#include<stdlib.h>
int main(){
return 0;
}
void ShellSort(ElementType A[],int N){
//我们用Sedgewick序列作为递减增量序列
//首先先确定Sedgewick中比N要小的第一个步长
int Si,D,P,i;//Si表示步长下标,D表示步长
ElementType tmp;
int Sedgewick[929,505,209,109,41,19,5,1,0];
for (Si=0;Sedgewick[Si]>=N;Si++) ;//用这个循环找到了相对应的Si下标
for (D=Sedgewick[Si];D>0;D++){//D>0表示最后一次进行的是步长为1的简单插入排序
for(P=D;P<N;p++){ //注意现在的P代表的是每一个子序列的待排序列的第一个元素的下标
//每个子序列并不是单独进行排序 ,而是子序列都进行完第k次插入后,再进行下一次的插入
tmp=A[p];//tmp用来存储当前子序列的未排序序列的第一个元素,之后会存储下一个子序列的未排序序列的第一个元素
for(i=P;i>=D&&A[i-D]>Tmp;i-=D){ //i>=D是判断是否已经右移了已排序序列的第一个元素
A[i]=A[i-D];
}
A[i]=tmp;
}
}
}
三、交换排序
①冒泡排序
进行N-1次循环
在第k次循环中对从第1到第n-k个位置上的元素从前往后进行比较。若前一个元素大于后一个元素,则两者交换位置。
这样就把第k大的元素移动到第移到到第N-k个位置上,称为第k趟的冒泡。
#include<stdio.h>
#include<math.h>
#include<stdlib.h>
void swap(ElementType*a,ElementType*b);
void BubbleSort(ElementType A[],int N);
int main(){
return 0;
}
void BubbleSort(ElementType A[],int N){
int P,i;//P表示剩下需要进行循环的次数
bool flag;
for(P=N-1;P>=0;P--){ //一共进行N-1次循环(剩下第N次不用进行交换)
flag=false;//标记该次循环中是否交换,若无,则说明整个序列有序,直接终止循环
for(i=1;i<=P;i++){ //一共进行P次冒泡(P次比较) 剩下需要进行循环的次数与循环中需要交换的次数相同
if(A[i]>A[i+1]) {
swap(&A[i],&A[i+1]);//只交换元素
flag=ture
}
if(flag==false) break; //如果在单次循环中无交换 则说明已经有序 直接退出循环
}
}
}
void swap(ElementType*a,ElementType*b){
ElementType t=*a;
*a=*b;
*b=t;
}
②快速排序
自己写很容易写错
快速排序算法是运用到递归来进行实现
具体步骤:
①选择一个主元(一般采用取中位数法), 并和倒数第二个元素进行交换
②设置两个下标指针Low和High,初值指向第一个元素和倒数第二个元素(主元的前一个元素)
③Low从左向右扫描,其位置左侧为已知遍历或交换过的比主元小的元素;High从右往左扫描,其位置右侧为已遍历或交换过的比主元大的元素。首先从Low指向的位置向右扫描,当遇到比主元大的元素则停止;然后从High指向的位置向左搜索,若遇到比主元小的元素则停止
④若Low和High没有错误,指向的元素交换位置
⑤重复3、4知道Low和High错位。此时将基准与A[Low]对调
⑥对划分好的两个子序列进行递归 当子序列一定小时则可以进行插入排序
#include<stdio.h>
#include<math.h>
#include<stdlib.h>
ElementType Median3(ElementType A[],int Left,int Right);
int main() {
return 0;
}
ElementType Median3(ElementType A[],int Left,int Right){//return三个数的中位数 并排序
Center = (Left+Right)/2;
if(A[Left]>A[Center]) swap(&A[Left],&A[Center]);
if(A[Left]>A[Right]) swap(&A[Left],&A[Center]);
if(A[Center]>A[Right]) swap(&A[Left],&A[Center]);
swap(&A[Center],&A[Right-1]);//把基准放在Right-1的位置
return A[Right-1];
}
void QuickSort(ElementType A[],int Left,int Right){
int Pivot,Cutoff,Low,High; //Pivot是基准,cutoff是用插入排序和快速排序的分界线 Low High是下标指针
if(Cutoff<=Left-Right){ //如果进行排序的元素的数量超过了Cutoff 则进行快速排序 否则进行插入排序
Pivot = Median3(A[],Left,Right);
Low = Left;
High = Right -1;//注意进行排序的序列中 主元的右边还有一个比主元大的元素 即 A[Right]
while(1){ //当比较元素与Pivot相等的时候也停止 可以使Pivot最后调换到较为中间的位置,使两个子序列长度相近
while(A[++Low]>Pivot);//为什么是从第二个元素开始比较?因为第一个元素是A[Left]一定比Pivot小
while(A[--High]<Pivot);//从倒数第三个元素进行比较 因为倒数第二个元素是Pivot 倒数第一个是A[Right]比Pivot小
if(Low<High) swap(&A[Low],&A[High]);
else break;//此时Low比High大,循环终止 进行Pivot的调换
}
swap(&A[Low],&A[Right-1]);//将基准与A[Low]进行对调
Qsort(A,Left,Low-1);//基准此时在A[Low],左子序列范围是Left-Low-1
Qsort(A,Low+1,Right);
}
else InsertSort(A+Left,Right-Left+1);//Right-Left+1=N
}
//统一接口
void QuickSort(ElementType A[],int N){
Qsort(A,0,N-1);//该接口可以自定义
}
四、归并排序
将两个已排序的子序列合并程一个有序序列的过程
一开始时N个长度为1的子序列 通过不断X2进行合并直到合并为一个大小为N的序列
则将一个大小为N的序列 对左右递归排序
①首先申请额外的空间用于