赞
踩
r[n-1]
属于有序状态,所以第二趟我们只需要对r[0]~r[n-2]
进行交换排序,再将当前最大的数沉到底部。原始冒泡排序算法1.0
void BubbleSort(int *r)
{
int i,j;
for(i=0;i<Maxsize;i++)
for(j=1;j<Maxsize-i;j++)
if(r[j-1]>r[j])
Swap(r[j-1],r[j]);//此处是伪代码
}
flag初始化为Flase
,设定如果当这一趟发生了交换,那么说明此趟还未排好序,那么我们将flag
设置为True
;如果此趟未发生交换,则flag的值仍为False
,循环退出!冒泡排序的优化版本2.0 void BubbleSort2(int *r) { int j,k; Bool flag;//c语言没有布尔类型,此处写的是python的语法!C语言自行转换成0和1 k=Maxsize; flag=True; while (flag) { flag=False; for (j=1;j<k;j++) { if (r[j-1]>r[j]) { Swap(r[j-1],r[j]);//此处是伪代码 flag=True; } } k--; } }
lastSwappedLoc
用于记录此趟的最后交换位置,在lastSwappedLoc
位置之前的是待排序的无序序列,在lastSwappedLoc
之后的是排好序的有序序列。也就是说lastSwappedLoc
是有序与无序的边界(Border)。lastSwappedLoc
边界之后的数据本身已经有序,而对于有序的部分进行比较是没有意义的,相当于在白白浪费资源,所以我们每次与边界前的元素进行比较即可。冒泡排序的优化版本3.0 void BubbleSort3(int *r) { int i,j; Bool flag; //用于标记数组是否有序 int lastSwappedLoc=0; //记录最后一次交换的位置 int sortBorder=Maxsize-1; //将有序和无序部分的边界初始化为最后一个元素 while(flag) { flag=False; //初始化为 false for(j=1;j<sortBorder;j++) { if (r[j-1]>r[j]) { Swap(r[j-1],r[j]); flag=True; lastSwappedLoc=j; } } sortBorder=lastSwappedLoc; } }
冒泡排序的优化版本3.0+ void BubbleSort3(int *r) { int j,k; int flag; flag=Maxsize; while(flag>0) { k=flag; flag=0; for(j=1;j<k;j++) if(r[j-1]>r[j]) { Swap(r[j-1],r[j]); flag=j; } } }
Left=0;
Right=Maxsize-1;
将枢轴挖出形成第一个坑r[Left]
。Right--;
由后向前找比它小的数,找到后挖出此数填前一个坑r[Left]
中。Left++
由前向后找比它大的数,找到后也挖出此数填到前一个坑r[Right]
中。Left==Right
,将枢轴填入r[Left] //r[Right]也行
中。pivot=r[left]; // left为0
left=0;
和right=Maxsize-1;
,第一次选取的pivot
是r[0]
,将r[0]
保存到pivot
即pivot=r[0];
此时因为已经用pivot
保存了r[0]
,所以r[0]
可看做是我们挖到第一个坑。然后用right指针
从右向左找比pivot
小的元素,然后放到(填补)第一个坑中,那么填一个坑的同时,该元素自身的原位置就产生出了新坑(第二个坑),由此往复,直到left指针
和right指针
都走到中间位置(此趟的最后一个坑)时,即left==right
时,退出循环,我们将最开始储存的pivot放入最后一个坑中,即r[left]=pivot; //此时left==right,所以写成 r[right]=Pivot也可以
,就完成了一趟快排。left==right;
时,递归结束!快速排序算法4.0 int Partition(int *r,int left,int right) { int pivot;//枢轴 pivot=r[left];//从枢轴从左开始 while(left<right)//挖坑填数 { while(left<right&&r[right]>=pivot)//右:找比pivot小的数(枢轴从左边开始,就先处理右边;否则相反) { right--; } r[left]=r[right]; while(left<right&&r[left]<=pivot)//左:找比pivot大的数 { left++; } r[right]=r[left]; } //pivot插在中间 r[left]=pivot; //此时left==right,所以写成 r[right]=Pivot也可以 return left; //同理,此处写成return right 也可 } void Quick_Sort(int *r,int left,int right) { int PivotLoc;//枢轴的位置 if(left<right)//区间划分的条件,当left==right;即划分的区间只有一个元素时,退出递归! { PivotLoc=Partition(r,left,right);//第一趟快排,获取枢轴位置PivotLoc Quick_Sort(r,left,PivotLoc-1); //递归处理枢轴左边 Quick_Sort(r,PivotLoc+1,right); //递归处理枢轴右边 } } void QuickSort(int *r)//初始数据源的预输入 { Quick_Sort(r,0,Maxsize-1); }
整体个算法时间复杂度 = 递归层数 x Partition的时间复杂度O(n)
,得:随机选取枢轴法可以有效的解决 升序数组 和 降序数组 的问题,但是随机选取并不能保证每次都选取到最优枢轴位置,但是能使快速排序有效的避免最坏情况。
快速排序升级算法4.1 int RandomPivot(int *r,int left,int right) { int pivotloc; int temp; //随机在left和right之间选取pivot srand((unsigned)time(NULL)); //随机播种(详细请看C语言srand函数用法) pivotloc=rand()%(right-left)+left; //(详细请看C语言rand函数用法) Swap(r[pivotloc],r[left]); /*此处要把选出来的pivot的值换到当前left的位置, 因为Partition函数中设定了枢轴从左边开始选, 从右边开始处理,所以每次都要讲pivot换到当前left的位置*/ return r[left]; } int Partition(int *r,int left,int right) { int pivot;//枢轴 pivot=RandomPivot(r,left,right);//随机选取pivot,每次在当前low和high之间随机选取枢轴 while(left<right)//挖坑填数 { while(left<right&&r[right]>=pivot)//右(枢轴从左边开始,就先处理右边;否则相反) { right--; } r[left]=r[right]; while(left<right&&r[left]<=pivot)//左 { left++; } r[right]=r[left]; } //pivot插在中间 r[left]=pivot; //此时left=right,所以写成 r[right]=Pivot也可以 return left; //同理,此处写成return right 也可 } void Quick_Sort(int *r,int left,int right) { int PivotLoc;//枢轴的位置 if(left<right)//区间划分的条件,当left==right;即划分的区间只有一个元素时,退出递归! { PivotLoc=Partition(r,left,right);//第一趟快排,获取枢轴位置PivotLoc Quick_Sort(r,left,PivotLoc-1); //递归处理枢轴左边 Quick_Sort(r,PivotLoc+1,right); //递归处理枢轴右边 } } void QuickSort(int *r)//数据预输入 { Quick_Sort(r,0,Maxsize-1); }
随机枢轴(基准)选取的随机性,使得它并不能很好的适用于所有情况(即使是同一个数组,多次运行的时间也大有不同)。目前,比较好的方法是使用三数取中选取枢轴(基准)。它的思想是:选取数组开头,中间和结尾的元素,通过比较,选择介于中间大小的值作为快排的枢轴(基准)。这种方式能很好的解决待排数组基本有序的情况。
快速排序升级算法4.2 #include <stdio.h> #define Maxsize 10 int Median_of_Three(int *r,int left,int right) { int mid=left+((right-left)/2);//取中值 if(r[mid]>r[right]) { Swap(r[mid],r[right]); } if(r[left]>r[right]) { Swap(r[left],r[right]); } if(r[mid]>r[left])//注意此处,正常应该是 r[left]≤r[mid]≤r[right],但是还要把Swap(r[mid],r[left]); { Swap(r[left],r[mid]); } //此时r[mid]≤r[left]≤r[right] return r[left]; } int Partition(int *r,int left,int right) { int pivot;//枢轴 pivot=Median_of_Three(r,left,right);//三数取中选取枢轴 while(left<right)//挖坑填数 { while(left<right&&r[right]>=pivot)//右(枢轴从左边开始,就先处理右边;否则相反) { right--; } r[left]=r[right]; while(left<right&&r[left]<=pivot)//左 { left++; } r[right]=r[left]; } //pivot插在中间 r[left]=pivot; //此时left=right,所以写成 r[right]=Pivot也可以 return left; //同理,此处写成return right 也可 } void Quick_Sort(int *r,int left,int right) { int PivotLoc;//枢轴的位置 if(left<right)//区间划分的条件,当left==right;即划分的区间只有一个元素时,退出递归! { PivotLoc=Partition(r,left,right);//第一趟快排,获取枢轴位置PivotLoc Quick_Sort(r,left,PivotLoc-1); //递归处理枢轴左边 Quick_Sort(r,PivotLoc+1,right); //递归处理枢轴右边 } } void QuickSort(int *r)//数据预输入 { Quick_Sort(r,0,Maxsize-1); }
当划分序列很小的时候,快排的交换已经完成的差不多了,此时再对的基本有序的小序列使用快速排序效率不高,改用插入排序。由 《数据结构与算法分析》(Mark Allen Weiness所著) 可知,当待排序列长度为5~20之间,此时使用插入排序能避免一些有害的退化情形。
快速排序升级算法4.3 void Quick_Sort(int *r,int left,int right) { int PivotLoc;//枢轴的位置 if((right-left+1)<10) { InsertSort(r,left,right); return; } if(left<right)//区间划分的条件,当left==right;即划分的区间只有一个元素时,退出递归! { PivotLoc=Partition(r,left,right); Quick_Sort(r,left,PivotLoc-1); Quick_Sort(r,PivotLoc+1,right); } }
使用尾递归优化后,可以缩减堆栈的深度,由原来的O(n)缩减为O(logn)。
快速排序升级算法4.4 void Quick_Sort(int *r,int left,int right) { int PivotLoc;//枢轴的位置 if((right-left+1)<10) { InsertSort(r,left,right); return; } while(left<right)//注意尾递归此处要换成循环语句while { PivotLoc=Partition(r,left,right); Quick_Sort(r,left,PivotLoc-1); left=PivotLoc+1;//用循环语句代替递归操作 } }
在一次分割结束后,将与本次基准相等的元素聚集在一起,再分割时,不再对聚集过的元素进行分割。
快速排序升级算法4.5 int Quick_Sort(int *r,int left,int right) { int low=left; int high=right; int lowlen=0; int highlen=0; int start=left; int end=right; int pivot;//枢轴 int temp; if(right-left<10)//当数据规模较小的时候改用插入排序 { InsertSort(r,left,right); return; } //若此处不用这种方法,需要加上递归的结束条件! pivot=Median_of_Three(r,left,right);//从枢轴从左开始 while(left<right)//挖坑填数 { while(left<right&&r[right]>=pivot)//右(枢轴在左边开始,从右边开始扫描; 否则相反) { if(r[right]==pivot)//处理相等的元素 { //Swap(r[high},r[right]); temp=r[high]; r[high]=r[right]; r[right]=temp; high--; highlen++; } right--; } r[left]=r[right]; while(left<right&&r[left]<=pivot)//左 { if(r[left]==pivot)//处理相等的元素 { //Swap(r[low],r[left]); temp=r[low]; r[low]=r[left]; r[left]=temp; low++; lowlen++; } left++; } r[right]=r[left]; } //pivot插在中间 r[left]=pivot; //此时left=right,所以写成 r[right]=Pivot也可以 //一次快排结束 //把与枢轴pivot相同的元素移到枢轴最终位置周围 int i=low-1; int j=start; while(j<low&&r[i]!=pivot) { //Swap(r[i],r[j]); temp=r[i]; r[i]=r[j]; r[j]=temp; i--; j++; } i=low+1; j=end; while(j>high&&r[i]!=pivot) { //Swap(r[i],r[j]); temp=r[i]; r[i]=r[j]; r[j]=temp; i++; j--; } Quick_Sort(r,start,low-1-lowlen); //递归处理枢轴左边 Quick_Sort(r,low+1+highlen,end); //递归处理枢轴右边 }
(此处借用insistGoGo、Tyler_Zx的测试数据,后续会上传自己的测试数据)
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。