最近数据结构学到 DoubleLinkedList, 当然在DLL(呵呵)上实现各种排序算法的任务也就如期而至了。这时我才发现原来以前学数组时练的排序算法都不能熟练地写出来了,于是这就复习了一下以前的知识,以备将来复习之用,呵呵......
为了能直接把代码拷到Visual C++ 里面运行,我先把6种排序的思想说一下,然后再贴上代码(每种方法的绿色字体上有锚记到.cpp 实现文件)。
1、简单排序类:
1> 冒泡法: 最基本的排序算法,只是一个两层循环,(n-1)次将最大或最小的放到数组最前或最后。时间复杂度为O(n^2),当然冒泡法优化的方法有很多,但个人觉得没有必要,只需要能写出其基本的写法就行了。
2> 插入法: 所谓插入,在这里就是往已经排好序的数组中按固定顺序插入新数,这样在插入后数组仍然是有序的。所以我们首先把带排序数组的第一个元素看成有序的,从 2 到 n-1 循环往前插入。时间复杂度为O(n^2)
3> 选择法: 所谓选择,就是不停地选择数组每个位置上的元素,比如该找第i个位置的值,我们假设现在的a[i]就是剩下数组( i 到 n-1 )里的最小值,然后我们将剩下的数组里的每个元素和它比较,若后者更小,就交换他们。时间复杂度为O(n^2)。
4> 希尔排序: 又称增量递减(diminishing increment)排序,是由D.L. Shell发明的,通过一个逐渐减小的增量使一个数组逐渐趋近于有序从而达到排序的目的。增量就是用来从待排数组中选择出新数组的一个interval量,就像我们在公路旁边每隔一个interval 就栽一棵树一样的增量。当然最后一次的增量必须为1. 需要该算法演示动画的话,留下e-mail。需要注意的是,对于同样数组,特别是个数较多时,不同的增量选择会带来差别巨大的排序效率。不信,你可以自己举个例子算算看。可惜的是,到现在为止,还没有推导出一个求增量的公式。时间复杂度为O(n ^1.5)。
两种“分治法”思想的排序:
5> 归并排序: 要归并,首先得要分开不是吧,所以这个算法就利用了递归地把数组分成两部分,然后对两部分进行归并排序,排好序后再对两个数组进行合并。因为最后我们会递归到每部分只有一个元素,所以每次每次回溯的时候,就是排序完成的时候。时间复杂度为O(nlogn)。
6> 快速排序: 既然归并和排序都是“分治法”的体现,二者必然有很多相同之处,那区别在哪呢,就在二者的侧重点上面。归并注重的是对排好序的两个数组的归并,对一个数组的分割则是简单地按照正中或左中下标来划分。 快速排序则着重在分(partition) 上面,不用归并了,因为每次作分割点的元素都出现在它最后该出现的位置上了。时间复杂度为O(nlogn)。(这里我实现partition的函数与书上的不一样,个人觉得我的方法会更容易看懂,易读,因为我只是从一边到另一边,而不是以往的在两边间不停地来回。希望各位高手给点建议。)
Utilities.h 文件:
void swap( int & a, int & b ) ;
void select_sort ( int a[], int n );
void print( int a[], int n );
void insert_sort ( int a[], int n );
void bubble_sort ( int a[], int n );
void quick_sort( int a[], int low, int high );
int partition( int a[], int low, int high ) ;
void shell_sort( int a[], int n );
void shell_insertion_sort( int a[], int start, int increment, int n );
void merge( int a[], int left, int midle, int right );
void merge_sort( int a[], int begin, int end );
Utllities.cpp 文件:
#include "Utilities.h"
#include
using namespace std;
void swap( int & a, int & b ) {
int temp = a; a = b; b = temp;
}
void print( int a[], int n ) {
for ( int i = 0; i< n; ++i )
cout<< a[i]<<" "<
select_sort ( int a[], int n ) {
for( int i = 0; i
for ( int j = i+1; j< n; ++j )
if( a[j]< min ) swap( min, a[j] );
swap( min, a[i] );
}
}
insert_sort ( int a[], int n ) {
for ( int i = 1; i
int j = i -1;
for( ; j>=0 && a[j] > temp; --j )
a[j+1] = a[j];
a[j+1] = temp;
}
}
bubble_sort ( int a[], int n ) {
for( int i = 0; i
if( a[j-1] > a[j] ) swap( a[j-1], a[j] );
}
partition( int a[], int low, int high ) {
int last_small = low;
int pivot = a[low];
for( int i = low+1; i<=high ; ++i )
if( a[i] < pivot && a[i-1] >= pivot ) {
last_small++; swap( a[last_small], a[i] ); swap( a[last_small], a[last_small-1] ); }
return last_small;
}
void quick_sort( int a[], int low, int high ) {
int pivot_position;
if ( low
quick_sort( a, low, pivot_position-1 );
quick_sort( a, pivot_position+1, high );
}
}
shell_sort( int a[], int n ) {
int increment = n,start = 0;
do {
increment = increment/3 + 1;
shell_insertion_sort( a, start,increment, n );
} while( increment > 1);
}
void shell_insertion_sort( int a[], int start, int increment, int n ){
for( int i = start+increment; i< n; i += increment ) {
int temp = a[i];
int j = i - increment;
for( ; j>=0 && a[j]>temp; j-=increment )
a[j+increment] = a[j];
a[j+increment] =temp;
}
}
int k = left; delete [] L; void merge_sort( int a[], int begin, int end ) { void main() { // insert_sort( arr, 6 ); print( arr, 6); // bubble_sort( arr, 6 ); print( arr, 6 ); // quick_sort( arr,0, 5 ); print( arr, 6 ); // shell_sort( arr,6 ); print( arr, 6 ); merge_sort( arr,0, 5 ); print( arr, 6 ); system( "pause" );
void merge( int a[], int left, int middle, int right ) {
int n1 = middle-left +1;
int n2 = right - middle;
int* L = new int [n1];
for( int i = 0; i
for( int i = 0; i
int i1 = 0, i2 =0;
while( i1
a[k] = L[i1]; ++ i1; ++k; }
else { a[k] = R[i2]; ++ i2; ++k; }
}
while( i2
delete[] R;
}
if( begin
merge_sort( a, begin, i);
merge_sort( a, i+1, end );
merge( a, begin, i, end );
}
}
Main.cpp 文件:(注意,如果只用一个数组,每次只能在main中用一种排序函数,否则上个函数的结果成为下个函数的待排数组,到时你怎么知道哪个函数有没有错呢,呵呵)
#include "Utilities.h"
#include
using namespace std;
int arr[ 6] = { 3, 2, 9, 0, 3, -1 };
// select_sort( arr, 6 ); print( arr, 6 );
}