一、排序方法
1.冒泡排序
冒泡由来:越大的元素会经由交换慢慢“浮”到数列的顶端;
冒泡俩种方式:从左往右把较大者冒到后边,或从右往左把较小者冒到前边;但俩种方式本质一样,重点是通过俩俩比较,把逆序的元素进行交换,直到冒泡到正确的位置,且每次冒泡都能确定它所在的最终位置。
伪代码:
BUBBLE_SORT(A):
for i<-1 to length[A]
do for j<-length[A] downto i+1
do if A[j]<A[j-1]
then exchange A[j]<->A[j-1]
复杂度分析:第i趟冒泡中需执行n-i次比较和交换操作,则i从1到n-1,执行的比较操作次数为:
(n-1)+(n-2)+···+2+1=n*(n-1)/2=O(n^2)
可得到冒泡排序执行过程中的数据比较次数与输入序列无关,数据的交换次数与元素的初始序列有关。最好情况下n次比较和0次交换。但平均下来是时间复杂度是O(n^2)。
2.插入排序
方法:每步将一个待排序的元素,按其排序码的大小,插入到前面已经排好序的一组元素中的适当位置上,直到元素全部插入为止。
伪代码:
INSERT_SORT(A):
for j<-2 to length[A]
do key<-A[j]
//insertA[j] into the right position in the sorted sequence A[1...j-1]
i<-j-1
while i>0 and A[i]>key
do A[i+1]<-A[i]
i<-i-1
A[i+1]<-key
插入排序比较次数和移动次数均与元素的初始序列有关,最好情况n-1次比较和0次移动,最坏情况下n*(n-1)/2次比较和(n+4)*(n-1)/2次移动次数(第i移动是i+2次=i-1次移位+3步交换),平均情况的时间复杂度也是O(n^2)。
3.快速排序
方法:在每趟排序中找到基准元素,使得左边的所有元素都比它小,右边的都比它大,这样每次都能排好一个最终位置上的元素,再分而治之(俩边元素互不影响),直到所有元素有序。
伪代码:
QUICK_SORT(A,p,r)
if p<r
then pivot<-PARTITION(A,q,r)
QUICK_SORT(A,q,pivot-1)
QUICK_SORT(A,pivot+1,r)
PARTITION(A,p,r)
x<-A[r]
i<-p-1
for j<-p to r-1
do if A[j]<=x
then i<-i+1
exchange A[i]<->A[j]
exchange A[i+1]<->A[r]
return i+1
快速排序的运行时间取决于输入序列,理想情况下递归树的高度为O(logn),存储开销为O(logn),最坏情况就是一颗单支树,存储开销到O(n)。但平均情况的时间复杂度为O(nlogn),在内部排序中是较好的排序方式。
4.归并排序
方法:将待排序的元素序列分成俩个长度相同的子序列,为每一个子序列排列,然后再将他们合并成一个序列。
伪代码:
MERGE_SORT(A,p,r)
if p<r
then q<-(p+r)/2 //Choose the least integer more than (p+r)/2
MERGE_SORT(A,q,p)
MERGE_SORT(A,q+1,r)
MERGE(A,p,q,r)
MERGE(A,p,q,r)
n1<-q-p+1
n2<-r-q
create arrays L[1...n1+1] and R[1...n2+1]
for i<-1 to n1
do L[i]<- A[p+i-1]
for j<-1 to n2
do R[j]<- A[q+j]
L[n1+1]<-∞
R[n2+1]<-∞
i<-1
j<-1
for k<-p to r
do if L[i]<=R[j]
then A[k]<-L[i]
i<-i+1
else A[k]<-R[j]
j<-j+1
归并排序是一个很完美的m路分治,递归树高度为O(logn),不依赖于输入序列的初始位置,最好、最坏、平均的时间复杂度都是O(nlogn),但它需要与原待排序一样大小的数组辅助空间。
二、排序思想比较
1.冒泡与插入排序
冒泡排序是每俩俩比较,若发生逆序则交换;而插入排序是先比较,找到正确的位置上,再移动和进行一次交换;这俩个排序的思想都是基于俩俩比较,且慢慢找到在已排好序数组中的正确位置。
从时间复杂度上看都是O(n^2),但在实际的时间效率方面上,插入排序要比冒泡排序快,因为从上面的分析可看到,在每次循环中,虽然冒泡和插入排序的比较次数相同,但在移动次数上,冒泡需要移动3i次,而插入排序只需移动i+2次。
2.快排和归并排序
快排和归并都是运用分治法:Divide, Conquer andr Combine
①快速排序:
Divide:找到基准元素pivot,将数组A[p..r]划分为A[p..pivot-1]和A[pivot+1...q];
Conquer:对俩个划分的数组进行递归排序;
②归并排序;
Divide:将n个元素分成各含n/2个元素的子序列;
Conquer:用合并排序法对俩个子序列递归的排序;
Combine:合并俩个已排序的子序列已得到排序结果。
由上面的分析知,快排重点在第一步的分,不仅找到基准元素(确定了这个元素的最终位置)进行排序,而且划分的俩个数组互不影响,达到分而治之的效果,还不需要合并操作;而归并排序是完美分割的,重点在最后一步合并,将俩个排好序的子序列进行合并。
从时间复杂度上分析,他们都是O(nlogn),但在实际运行时,快排会比归并更快,原因就在他们用分治法的侧重点不同,且快排去掉了合并操作。
三、排序代码
用C++模板类编写的排序,将所有数据进行封装,并随机产生10组10000个数据进行测试比较平均时间。
//sort.h
#ifndef SORT_H
#define SORT_H
#include
#include
#include
using namespace std;
#define defaultSize 10000
template
class Element //数据表元素类定义
{
public:
int key; //排序码
Element
& operator=(Element
& x){ key = x.key; return*this; } bool operator==(Element
&x) { return key == x.key; } bool operator <=(Element
& x){ return key <= x.key; } bool operator>(Element
& x){ return key > x.key; } bool operator<(Element
& x){ return key < x.key; } }; template
class dataList //数据表类定义 { friend istream& operator>>
(istream&, dataList
&); friend ostream& operator<<
(ostream&, dataList
&); public: dataList(int maxSz = defaultSize) :maxSize(maxSz), currentSize(0){ Vector = new Element
[maxSize]; } void Swap(Element
& x, Element
& y){ Element
temp; temp = x; x = y; y = temp; } int Length(){ return currentSize; } Element
& operator[](int i){ return Vector[i]; } //取第i个元素 int Partition(const int low, const int high); //快速排序划分 Element
* Vector; //存储排序元素中的向量 void randomNumber(dataList
&); private: int maxSize; //向量中最大元素个数 int currentSize; //向量当前元素个数 }; template
int dataList
::Partition(const int low, const int high) //基准元素 { int pivotpos = low; Element
pivot = Vector[low]; //基准元素 for (int i = low + 1; i <=high; i++) //检测整个序列,进行划分 { if (Vector[i] < pivot) { pivotpos++; if (pivotpos != i) //小于基准的元素交换到左侧,且避免该左侧元素重复 Swap(Vector[pivotpos], Vector[i]); } } Vector[low] = Vector[pivotpos]; //基准元素调整就位 Vector[pivotpos] = pivot; return pivotpos; //返回基准元素 } template
istream& operator>>(istream& input, dataList
& list) { cout << "please enter the sorting dataList:"; for (int i = 0; i < list.maxSize; i++) { input >> list.Vector[i].key; list.currentSize++; } return input; } template
void dataList
::randomNumber(dataList
&list2){ srand((unsigned)time(0)); for (int i = 0; i < maxSize; i++){ list2.Vector[i].key=Vector[i].key = rand()%maxSize; currentSize++; list2.currentSize++; } } template
ostream& operator<<(ostream& output, dataList
&list) { output << "The dataList are:"; for (int i = 0; i < list.currentSize; i++) output << list.Vector[i].key<<" "; return output; } template
void BubbleSort(dataList
&list, const int left, const int right) //起泡排序 { bool exchange; for (int i = left+1; i <= right; i++) { exchange = false; for (int j = right; j >= i; j--) { if (list.Vector[j - 1] > list.Vector[j]) //发生逆序,则轻的元素往前冒 { list.Swap(list.Vector[j - 1], list.Vector[j]); exchange = true; } } if (exchange == false) //本趟已无逆序,说明已全部排好 return; } } template
void InsertSort(dataList
& list, const int left, const int right) //直接插入排序 { Element
temp; int j; for (int i = left+1 ; i <= right; i++) { if (list.Vector[i] < list.Vector[i - 1]) { temp = list.Vector[i]; j = i - 1; do{ list.Vector[j + 1] = list.Vector[j]; //大的元素往后移 j--; } while (j >= left&&temp < list.Vector[j]); list.Vector[j + 1] = temp; } } } template
void QuickSort(dataList
&list, const int left, const int right) //快速排序 { if (left < right) { int pivotpos = list.Partition(left, right); //划分 QuickSort(list, left, pivotpos); QuickSort(list, pivotpos+1, right); } } template
void QuickSort_Insert(dataList
& list, const int left, const int right) { if (right - left <= 5) //元素序列长度小于M InsertSort(list, left, right); else { int pivotpos = list.Partition(left, right); QuickSort(list,left, pivotpos-1); QuickSort(list, pivotpos + 1, right); } } template
void mergeSort(dataList
& L1, dataList
&L2, int left, int right){ if (left>=right) return; int mid = (left + right) / 2; mergeSort(L1, L2, left, mid); mergeSort(L1, L2, mid + 1, right); merge(L1, L2, left,mid, right); } template
void merge(dataList
&L1, dataList
&L2, int left, int mid, int right){ //L1.vector[left:mid]与L1.vector[mid+1,right]是俩个有序表,将这俩个有序表归并成一个有序表L1.vector[left:right] for (int k = left; k <= right; k++){ L2[k] = L1[k]; } int s1 = left, s2 = mid + 1, t = left; while (s1 <= mid&&s2 <= right){ if (L2[s1] <= L2[s2]) L1[t++] = L2[s1++]; else L1[t++] = L2[s2++]; } while (s1 <= mid) L1[t++] = L2[s1++]; while (s2 <= right) L1[t++] = L2[s2++]; } #endif //main.cpp #include
#include "Sort.h" #include "WinTree.h" #include
const int ArraySize = 50; using namespace std; int main() { dataList
dataInsert[ArraySize]; dataList
dataBubble[ArraySize]; dataList
dataQuick[ArraySize]; dataList
dataMerge[ArraySize]; dataList
dataTemp; clock_t start,end; double averageMergeTime=0, averageQuickTime=0, averageBubbleTime=0, averageInsertTime=0; for (int i = 0; i < ArraySize; i++){ dataInsert[i].randomNumber(dataBubble[i]); dataQuick[i].randomNumber(dataMerge[i]); start = clock(); InsertSort(dataInsert[i], 0, dataInsert[i].Length() - 1); end = clock(); averageInsertTime += end - start; start = clock(); BubbleSort(dataBubble[i], 0, dataBubble[i].Length() - 1); end = clock(); averageBubbleTime += end - start; start = clock(); QuickSort(dataQuick[i], 0, dataQuick[i].Length() - 1); end = clock(); averageQuickTime += end - start; start = clock(); mergeSort(dataMerge[i], dataTemp, 0, dataMerge[i].Length() - 1); end = clock(); averageMergeTime += end - start; } cout << "The average time of Insert sort from random data is:" << (double)averageInsertTime / ArraySize << endl; cout << "The average time of Bubble sort from random data is:" << (double)averageBubbleTime / ArraySize << endl; cout << "The average time of Quick sort from random data is:" << (double)averageQuickTime/ ArraySize << endl; cout << "The average time of Merge sort from random data is:" << (double)averageMergeTime/ ArraySize << endl; system("pause"); return 0; }