初等排序方法
排序就是将数据按照一定的顺序重新排列。它是许多算法的基础,可以让数据变得更容易处理。
用来排序的算法有很多种,这就关系到了算法的选取问题了。在之前我们提到过,算法选取最重要的就是算法复杂度的问题了,这在排序算法中尤为明显,如果算法太差,可能会导致排序的时间太长,造成代码执行超时的问题。因此,就更应该选取复杂度较小的算法。我在此只介绍简单入门的排序方法,告知原理,高等排序在接下来会详细讲到。
-
插入排序
代码如下:#include<iostream> using namespace std; void insertionSort(int A[],int N); int main() { int A[100]; for(int i=0;i<100;i++) A[i]=100-i; for(int i=0;i<100;i++) cout<<A[i]<<" "; cout<<endl; insertionSort(A,100); for(int i=0;i<100;i++) cout<<A[i]<<" "; cout<<endl; } void insertionSort(int A[],int N)//从小到大排序。 { int tmp, j; for(int i=1;i<N;i++){ tmp=A[i]; j=i-1; while(j>=0 && A[j]>tmp)//将所有比tmp大的元素都排到tmp后面 { A[j+1]=A[j]; j--; } A[j+1]=tmp;//在最后一次时A[j+1]被赋给了A[j+2],且出现了A[j]小于或等于tmp的情况,故将tmp付给A[j+1]。 } //由于输入的是数组的首地址,故无需返回值,直接改变了数组A。 }
插入排序相对优势还是很大的,在最优情况下,插入排序仅需要从头到尾遍历一遍,无需进入while循环,复杂度只有 O ( N ) O(N) O(N),但是当恰好反序的情况下,复杂度就变成了 O ( N 2 ) O(N^2) O(N2),由此可见,输入的数据不同,算法的执行时间也会不同,但是我们一般都要考虑在最坏的情况下的算法效率,因此,插入排序只适合处理相对有序的一组数据。
-
选择排序
代码如下:#include<iostream> using namespace std; void selectionSort(int A[],int N); int main() { int A[100]; for(int i=0;i<100;i++) A[i]=100-i; for(int i=0;i<100;i++) cout<<A[i]<<" "; cout<<endl; selectionSort(A,100); for(int i=0;i<100;i++) cout<<A[i]<<" "; cout<<endl; } void selectionSort(int A[],int N)//从小到大排序。 { for(int i=0;i<N-1;i++) { for(int j=i+1;j<N;j++) { if(A[j]<A[i]) swap(A[j],A[i]);//若A[j]<A[i],则交换两者位置,从而把最小的元素提到当前循环的最前方,每次都选择最小元素,排列起来,就是从小到大的排布。 } } //由于输入的是数组的首地址,故无需返回值,直接改变了数组A。 }
选择排序对于未排列部分也和插入排序一样算法复杂度为 O ( N 2 ) O(N^2) O(N2),也是对排序较好的部分效果较好。
-
冒泡排序
代码如下:#include<iostream> using namespace std; void bubbleSort(int A[],int N); int main() { int A[100]; for(int i=0;i<100;i++) A[i]=100-i; for(int i=0;i<100;i++) cout<<A[i]<<" "; cout<<endl; bubbleSort(A,100); for(int i=0;i<100;i++) cout<<A[i]<<" "; cout<<endl; } void bubbleSort(int A[],int N)//从小到大排序。 { int flag=1; for(int i=0;flag;i++) { flag=0; for(int j=N-1;j>i;j--) { if(A[j]<A[j-1]) { swap(A[j],A[j-1]); flag=1; }//若A[j]<A[j-1],则交换两者位置,从而把较小的元素提到当前循环的最前方,每次都选择较小元素,就像泡泡一样慢慢浮到最前面,故称冒泡排序。 //若不进入if语句,说明已经排序完成,flag=0,跳出循环。 } } //由于输入的是数组的首地址,故无需返回值,直接改变了数组A。 }
冒泡排序对于未排列部分也和插入排序一样算法复杂度为 O ( N 2 ) O(N^2) O(N2),对排序较好的数据可以早早flag=0,从而跳出循环减少运算次数。
排序算法千千万,究其根本就是按顺序排布,但是当遇到相同的数据时又该怎么办呢?这就遇到了排序算法当中的稳定排序和不稳定排序的问题了。
当遇到相同数据时,若排序之后不会改变原来的先后顺序,则成该排序是稳定的,反之则称该排序是不稳定的。如下表:
姓名 | 英语 |
---|---|
A | 95 |
B | 91 |
C | 91 |
D | 90 |
选择排序后:
姓名 | 英语 |
---|---|
D | 90 |
C | 91 |
B | 91 |
A | 95 |
冒泡排序后:
姓名 | 英语 |
---|---|
D | 90 |
B | 91 |
C | 91 |
A | 95 |
可以发现,在选择排序后,B和C原来的顺序也交换了,而在冒泡排序中,B与C的位置却没有发生改变,还是保持原来的先后顺序。所以就可以说,选择排序是不稳定的排序,而冒泡排序是稳定的排序。
在选择排序方法时,其实最重要的还是在于判断语句中是否有等于号,很多时候在添加上一个小小的等于号之后,原来的稳定的排序可能就会变成不稳定的排序,而且还会因此重复比较相同的元素,使得运算次数增加,甚至会产生错误。
最后加一点,在C++标准库中是包含sort函数的,在头文件#include <algorithm>
里面,此处sort函数复杂度只有
O
(
n
∗
log
(
n
)
)
O(n*\log(n))
O(n∗log(n)),是采用了快排算法,建议使用。