应用交换排序基本思想的主要排序方法有:冒泡排序和快速排序。这里将主要介绍快速排序,以及快速排序的递归与非递归的实现和对快速排序的优化
冒泡排序
冒泡排序是将大数或小数不断后移的一种思想,比较和交换都发生在两个相邻元素之间。
思想:
1.单趟排序:比较相邻的元素,如果第一个比第二个大(小),交换这两个元素,直到最后元素,则最后元素的值应该为最大或最小值,前面的元素可能有序也可能无序。
2.对前面元素在进行单趟排序,改变每趟排序的下标,进行单趟排序,直到没有一对数据进行比较时,则排序完成。
优化:
对于冒泡排序,经过单趟排序后,前面的元素可能有序,若前面元素已经有序,则可终止排序,提高效率。(设置标志flag=false,如果在每次单趟排序有数据交换,则将flag=true,, 每次单趟排序完可对flag进行判断,若flag==false,说明前面元素已经有序,直接跳出循环,否则,继续进行冒泡。)
图解:
实现:
void BubbleSort(int* a, size_t n) { assert(n); bool finish = false; //堆排序进行优化 size_t end = n; while(end > 0) //end表示每次冒泡的终止位置 { //单趟冒泡 for(size_t i=1; i<end; i++) { if(a[i-1]>a[i]) { swap(a[i-1],a[i]); finish = true; //若单趟排序有数据交换,则将finish置为true } } if(finish == false) //判断单趟排序中是否有交换 { return; } --end; } }
算法分析:
算法时间复杂为O(n^2),属于稳定性算法
快速排序
快速排序由C. A. R. Hoare在1962年提出。它的基本思想是:通过一趟排序将要排序的数据分割成独立的两部分,其中一部分的所有数据都比另外一部分的所有数据都要小,然后再按此方法对这两部分数据分别进行快速排序,整个排序过程可以
递归
进行,以此达到整个数据变成有序
序列
。
图解:
分析:快速排序应用分治法的思想,将大问题分解为小问题,先求解子问题,以得到对原问题的求解。
递归实现:
//左右指针法 找一个key,将大数右移,小数左移,最后将数组区间划分为两部分 [] key [] int PartSort1(int* a, int begin, int end) { int left = begin, right = end; //right赋值为 end 还是 end-1 int key = a[right]; //选择最右边为key值 //单趟排序 while(begin < end) { //begin找大于 while(begin < end && a[begin] <= key) //注意条件a[begin] <= key { ++begin; } //end找小 while(begin < end && a[end] >= key) { --end; } swap(a[begin], a[end]); } swap(a[begin],a[right]); //a[right]相当于中间划分的位置 return begin; } void QuickSort(int* a, int left, int right) { assert(a); if(left >= right) //注意:结束条件的判断 { return; } int div = PartSort1(a, left, right); QuickSort(a, left,div-1); QuickSort(a, div+1, right); //right为什么不-1; }
算法分析
最坏情况:
如果我们在选取基准p的时候,每次选取的都是当前数组中最小的一个元素,那么每次划分都只是让数组中的元素少1(被筛选出来的那个元素当然有序),这样一来就需要反复遍历数组导致复杂度变成了
O(n2)。
最好情况:
如果我们在选取基准p的时候,每次选取的都是当前数组中最中间的一个元素(是中位数,而不是元素位置上的中间),那么每次划分都把当前数组划分成了长度相等的两个子数组,这样一来复杂度变成了
O(nlog2n)。
基准的选择
1.对于基准元素的选取,也可以采用随机数选取的方式
2.如果要按照元素在位置上的中间来选取基准元素,还可以将中间位置上的元素与第一个元素进行对换
优化:
三平均分区法
这一改进与其它的快速排序方法不同,它并不是选择待排数组的第一个元素作为中轴,而是选用待排数组最左边、最右边和最中间的三个元素的中间值作为中轴。这一改进的优势:
(1)首先,它使得最坏情况发生的几率减小了。
(2)其次,未改进的快速排序算法为了防止比较时数组越界,在最后要设置一个哨点。
非递归实现快排:
借助栈,栈中保存左右下标,先压右、在压左,栈为空时循环结束
注:
压栈应注意左右下标有意义时才压栈及(left<right).
void QuickSortFD(int* a, int left, int right) { stack<int> s; //栈中存放下标 if(left < right) //首先要保证下标有意义再压栈 { s.push(right); s.push(left); } while(s.size()>0) { int left = s.top(); s.pop(); int right = s.top(); s.pop(); if(right - left <= 20) { InsertSort(a+left,right-left+1); return; } else { int div = PartSort1(a,left,right); if(div - 1 >left) //区间不合法时,不入栈 { s.push(div-1); s.push(left); } if(right > div+1) { s.push(right); s.push(left); } } } }
扫描二维码关注公众号,回复:
1496097 查看本文章