七大排序实测(计数、快速、归并、大堆、希尔、插入、选择)

来源:互联网 发布:anitama知乎 编辑:程序博客网 时间:2024/06/18 07:59

<span style="font-family: 'Microsoft YaHei'; font-size: 14px; background-color: rgb(255, 255, 255);"><span style="white-space:pre"></span>虽然主算法采用泛型语法,但这里主要测试int整型类型,因为计数排序只能测试整型类型,不过其它排序算法可以不限于整型(只要实现了operator<和operator=即可,比如字符串)。为了便于测试整型,特地写了一个伪随机数生成算法。</span>

一、理论与测试结果(整数)

GCC4.8.2测试结果(lg:以2为底的对数,全部使用std::vector容器):

排序名称

最坏时间界

平均时间界

实测结果(仅供参考)

计数(Counting)

O(N)

O(N)

2^27 : 37s

快速(Quick)

O(N^2)

O(NlgN)

2^27 : 73s

归并(Merge)

O(NlgN)

O(NlgN)

2^27 : 133s

大堆(Max Heap)

O(NlgN)

O(NlgN)

2^27 : 212s

希尔(Shell)

O(N^2)

O(N^(3/2))

2^20 : 7s

插入(Insertion)

O(N^2)

O(N^2)

2^15 : 3s

选择(Selection)

O(N^2)

O(N^2)

2^15 : 4s


二、实测图

1、小规模数据测试准确性:






2、大规模数据测试时间花费:





三、测试架构

1、自己动手造轮子:Random,此类负责解决随机数生成的问题,并且内置一个方法,该方法随机生成[min, max]区间内的任意一个数,并且确保每次生成在该方法内部循环的次数不超过2次(min不能为负数);

     2、所有的一般测试函数都放在一个类之中,但为了避免和另外类同名的冲突,将其两者置于不同的命名空间内。为了避免多次编译,加入条件编译指令。对需要依赖的头文件,直接导入。为了支持不同的类型排序以及容器大小的动态变化,引入泛型和std::vector;

       3、对于每个测试过程都需要的辅助函数,统一放在一个类之中,以满足所需;

       4、所有的测试均在CMD环境下进行,为了支持自动化测试,采用内置批处理命令;

       5、测试时间的函数均采用GCC标准库的函数。对于不同的算法,优化效果是很明显的!


四、模块与算法浅析

1、Random.h

A、代码

#ifndef _Random_#define _Random_class Random {public:static const int seq_amount = 48271;static const int seq_scope = 2147483647;static const int divisor = seq_scope / seq_amount;static const int remainder = seq_scope % seq_amount;explicit Random( int init_Value = 1 );//默认构造函数int randomInt();//随机产生[0, 2147483647)范围内的一个整数double randomDouble();//随机产生[0, 1)范围内的一个浮点数static int random_field( int, int, Random& );//随机产生[min_bound, max_bound]范围内的一个整数private:int state;};Random::Random( int init_Value ) {if( init_Value < 0 )init_Value += seq_scope;state = init_Value;if( state == 0 )state = 1;}int Random::randomInt() {int tmpState = seq_amount * ( state % divisor ) - remainder * ( state / divisor );if( tmpState >= 0 )state = tmpState;elsestate = tmpState + seq_scope;return state;}double Random::randomDouble() {return (double)randomInt() / seq_scope;}int Random::random_field( int min_bound, int max_bound, Random& rand ) {int rf_value = rand.randomInt();if( rf_value < 0 )rf_value = -rf_value;rf_value %= ( max_bound + 1 );while( rf_value < min_bound || max_bound < rf_value ) {rf_value += min_bound;rf_value = max_bound < rf_value ? ( rf_value % ( max_bound - min_bound + 1 ) ) : rf_value;}return rf_value;}#endif
B、算法浅析

a.以上是一个伪随机数生成器,基于Lehmer的线性同余数生成器描述:Xi+1 = AXi Mod M。为了开始这个序列,必须给出X0的值,该值称为种子。如果X0 = 0,那么这个序列将无意义,但是如果A和M选的正确,那么任何其它的1<=X0 < M都是有效的。如果M=11,A=7,而X0=1,那么生成的序列为:

7, 5, 2, 3, 10, 4, 6, 9, 8, 1, 7, 5, 2, ………

以上序列在M-1=10后重复,因此这个序列的周期为M-1,它很大。如果M是素数,那么总存在对A的一些特殊选择能够给出全周期M-1。如果M选的很大,比如31位的素数,那么它的全周期应该是很长的:对于M=2^31-1=2147483647,A=48271是给出全周期生成器的许多值之中的一个,我们这儿予以采用!注意类中的state私有变量相当于Xi,以便于下次生成Xi+1使用;

b.关于random_field函数,这个函数之中的while循环次数将不会超过2次:在while循环之前,随机数rf_value绝不会超过max_bound,在[ 0, max_bound )之间,接下继续判断rf_value是否小于min_bound,若不小于,直接返回;若小于,则将其加上min_bound,此时又有两种可能:第一,rf_value小或等于max_bound,此时while不再循环,直接返回;第二,rf_value大于max_bound,此时置rf_value在[ 0, max_bound-min_bound]区间内,再次循环时一定使其落在[min_bound, max_bound]区间,故循环次数将不会超过2次!

2、sort.h

CountingSort(计数排序:只能用于非负整数的排序,且大小限于2^27之内,但是效率最高!)

QuickSort(快速排序:此排序最坏的下界是平方阶,但是经过优化后很难触犯这个下界,效率次之)

MergeSort(归并排序:此排序需要的内存是快速排序的两倍)

HeapSort(大堆排序:此排序需要建立特殊的树形数据结构模型——大堆)

ShellSort (希尔排序:此排序需要按照一定的增量序列采用插入排序方式进行排序)

InsertionSort (插入排序:此排序需要确定当前元素在前面已序的序列之中的位置,并依次移动)

SelectionSort(选择排序:此排序需确定当前序列之中最小元素的位置,并将其和最小位置的元素交换)

<span style="font-family:Microsoft YaHei;font-size:14px;">#ifndef _sort_#define _sort_#include "test_helper.h"namespace lxc_sort {class sort {public:template<typename Type>static void counting_sort( std::vector<Type>& in, std::vector<Type>& out, int in_max ) {if( in.size() < 2 )return;std::vector<Type> tmp( in_max, 0 );int i;for( i = 0; i < in.size(); ++i )++tmp[ in[i] ];for( i = 1; i < in_max; ++i )tmp[i] += tmp[i - 1];for( i = in.size() - 1; i >= 0; --i ){out[ tmp[ in[i] ] - 1 ] = in[i];--tmp[ in[i] ];}}template<typename Type>static void counting_sort( std::vector<Type>& in ) {std::vector<Type> temp( in.size(), 0 );counting_sort( in, temp, test_helper::find_max( in ) + 1 );for( int i = in.size() - 1; i >= 0; --i )in[i] = temp[i];}template<typename Type>static const Type& divide_value( std::vector<Type>& in, int begin, int end ) {int center = (begin + end) / 2;if( in[center] < in[begin] )std::swap( in[begin], in[center] );if( in[end] < in[begin] )std::swap( in[begin], in[end] );if( in[end] < in[center] )std::swap( in[center], in[end] );std::swap( in[center], in[end - 1] );return in[end - 1];}template<typename Type><span style="white-space:pre"></span>static void quick_sort( std::vector<Type>& in, int leftPos, int rightPos ) {<span style="white-space:pre"></span>if( leftPos + 1 >= rightPos )<span style="white-space:pre"></span>if( in[rightPos] < in[leftPos] ) { <span style="white-space:pre"></span>std::swap( in[leftPos], in[rightPos] );<span style="white-space:pre"></span>return; <span style="white-space:pre"></span>} <span style="white-space:pre"></span>else<span style="white-space:pre"></span>return;<span style="white-space:pre"></span>Type divide_element = divide_value( in, leftPos, rightPos );<span style="white-space:pre"></span>int incLeft = leftPos, incRight = rightPos - 1;<span style="white-space:pre"></span><span style="white-space:pre"></span>for( ; ; ) {  //Begin partitioning<span style="white-space:pre"></span>while( in[++incLeft] < divide_element ) {}<span style="white-space:pre"></span>while( divide_element < in[--incRight] ) {}<span style="white-space:pre"></span>if( incLeft < incRight )<span style="white-space:pre"></span>std::swap( in[incLeft], in[incRight] );<span style="white-space:pre"></span>else<span style="white-space:pre"></span>break;<span style="white-space:pre"></span>}<span style="white-space:pre"></span>std::swap( in[incLeft], in[rightPos - 1] );<span style="white-space:pre"></span>quick_sort( in, leftPos, incLeft - 1 );<span style="white-space:pre"></span>quick_sort( in, incLeft + 1, rightPos );<span style="white-space:pre"></span>}template<typename Type>static void quick_sort( std::vector<Type>& in ) {quick_sort( in, 0, in.size() - 1 );}template<typename Type>static void merge_sort( typename std::vector<Type>::iterator i_start, typename std::vector<Type>::iterator i_center, typename std::vector<Type>::iterator i_end, std::vector<Type>& tempArray ) {int front_size = i_center - i_start, behind_size = i_end - i_center + 1;int cur_index = 0, front_index = 0, behind_index = 0;/* while( front_index < front_size && behind_index < behind_size )if( i_start[front_index] <= i_center[behind_index] )tempArray[cur_index++] = i_start[front_index++];elsetempArray[cur_index++] = i_center[behind_index++]; */typename std::vector<Type>::iterator i_tempArray = tempArray.begin();while( front_index < front_size && behind_index < behind_size )i_tempArray[cur_index++] = i_start[front_index] <= i_center[behind_index] ?  i_start[front_index++] : i_center[behind_index++];while( front_index < front_size )i_tempArray[cur_index++] = i_start[front_index++];while( behind_index < behind_size )i_tempArray[cur_index++] = i_center[behind_index++];while( --cur_index >= 0 )i_start[cur_index] = i_tempArray[cur_index];}template<typename Type>static void merge_sort( typename std::vector<Type>::iterator i_start, typename std::vector<Type>::iterator i_end, std::vector<Type>& tempArray ) {if( i_start < i_end ) {int pos = ( i_end - i_start ) / 2;merge_sort<Type>( i_start, i_start + pos, tempArray );merge_sort<Type>( i_start + pos + 1, i_end, tempArray );merge_sort<Type>( i_start, i_start + pos + 1, i_end, tempArray );}}template<typename Type>static void merge_sort( std::vector<Type>& in ) {std::vector<Type> tempArray( in.size() );merge_sort<Type>( in.begin(), --in.end(), tempArray );}inline static int leftChild( int i ){ //Returns the index of the left childreturn 2 * i;    }template<typename Type>static void percDown( std::vector<Type>& in, int cur_index, int in_size ) {//This operation will have dropped an element to a appropriate position.int child;Type cur_value;for( cur_value = in[cur_index]; leftChild( cur_index ) < in_size; cur_index = child ) {child = leftChild( cur_index );if( child != in_size - 1  &&  in[child] < in[child + 1] )++child;//Returns the max index between left and right child/*If the max element between left and right child is bigger than the current element,  the position of the current element will have assiged the max element.  Otherwise,the max element have dropped a appropriate position,  this loop will have broke. */if( cur_value < in[child] )in[cur_index] = in[child];elsebreak;}in[cur_index] = cur_value;//The current element should be assiged a appropriate position.}template<typename Type>static void heap_sort( std::vector<Type>& in ){//The main function of heap sortingint in_size = in.size();Type max_cur;for( int i = in_size / 2; i >= 0; --i )percDown( in, i, in_size );  //This operation will have built a structure of max heapfor( int j = in_size - 1; j > 0; --j ) {/* The first element is the biggest element in the elements of vector,we save it.   We move an element of the last position in position of the first element,   then we move the biggest element to the last position. */max_cur = in[0];in[0] = in[j];in[j] = max_cur;/* Now the first element we move it should be drop a appropriate position   in order to keep this structure of model. */percDown( in, 0, j );}}template<typename Type>static void shell_sort( std::vector<Type>& in ) {for( int incre = in.size() / 2; incre > 0; incre /= 2 )for( int inc_behind = incre; inc_behind < in.size(); ++inc_behind ) {Type temp_value = in[inc_behind];int inc_front = inc_behind;while( inc_front >= incre && temp_value < in[inc_front - incre] ) {in[inc_front] = in[inc_front - incre];inc_front -= incre;}in[inc_front] = temp_value;}}template<typename Type>static void insertion_sort( typename std::vector<Type>::iterator i_start,  typename std::vector<Type>::iterator i_end ) {for( int inc = 1; inc < i_end - i_start; ++inc ) {Type temp_value = i_start[inc];int front_index = inc - 1;while( front_index >= 0   && temp_value < i_start[front_index] ) {i_start[front_index + 1] = i_start[front_index];--front_index;}i_start[front_index + 1] = temp_value;}}template<typename Type>static void insertion_sort( std::vector<Type>& in ) {insertion_sort<Type>( in.begin(), in.end() );}template<typename Type>static void selection_sort( std::vector<Type>& in ) {//More Effectiveint min_index, in_size = in.size();for( int inc = 0; inc < in_size; ++inc ) {Type temp_value = in[inc];min_index = inc;for( int inc_min = inc + 1; inc_min < in_size; ++inc_min )min_index = in[inc_min] < in[min_index] ? inc_min : min_index;in[inc] = in[min_index];in[min_index] = temp_value;}}};}#endif</span>

3、test_helper.h

<span style="font-family:Microsoft YaHei;">#ifndef _TEST_TIME_#define _TEST_TIME_#include "Random.h"#include<sys/time.h>#include<iomanip>class test_helper {public:template<typename Type>static void insert_time( std::vector<Type>& in, int how_much, Type how_large, int rand_val = 3 ) {timeval tv;int us_time, i; Random rand(rand_val);gettimeofday( &tv, NULL );us_time = tv.tv_sec * 1000000 + tv.tv_usec;for( i = 0; i < how_much; ++i )in.push_back( Random::random_field( 0, how_large, rand ) );gettimeofday( &tv, NULL );us_time = ( tv.tv_sec * 1000000 + tv.tv_usec ) - us_time;std::cout << "The time required for insertion: " << us_time << "us" << std::endl;}template<typename Type>static Type find_max( std::vector<Type>& in ) {typename std::vector<Type>::iterator i_max = in.begin(), i_move = ++i_max;while( i_move != in.end() ) {if( *i_max < *i_move )i_max = i_move;++i_move;}return *i_max;}template<typename Type>static void print_vector( std::vector<Type>& in ) {int widith = 8;if( in.size() <= 128 ) {typename std::vector<Type>::iterator i_start = in.begin(), i_end = in.end();for( ; i_start < i_end; ++i_start )std::cout << std::setw(widith) << *i_start;std::cout << std::endl;}else {int i, front_size = 64, behind_size = in.size();for( i = 0; i < front_size; ++i )std::cout << std::setw(widith) << in[i];std::cout << std::endl;std::cout << "----------(The middle of data are " << in.size() - 128  << " have been ignored!)----------" << std::endl;for( i = in.size() - 64; i < behind_size; ++i )std::cout << std::setw(widith) << in[i];std::cout << std::endl;}}};#endif</span>


4、具体测试代码:

(1) gcc_CountSort.cpp

<span style="font-family:Microsoft YaHei;">#include<iostream>#include<sys/time.h>#include<vector>#include "sort.h"#include "test_helper.h"const int HOW_LARGE = (1 << 8);const int HowMuch = (1 << 5);int main(){timeval tv;int max_value, us_time;std::vector<int> in;std::cout << "----------Counting sortion----------" << std::endl;test_helper::insert_time( in, HowMuch, HOW_LARGE, 2 );max_value = test_helper::find_max( in );std::cout << "The quantity of testing data is: " << HowMuch << std::endl;std::cout << "The max element in the vector is: " << max_value   << ", I limited: " << HOW_LARGE << std::endl;std::cout << "Before insert: " << std::endl;if( in.size() < 128 )test_helper::print_vector( in );gettimeofday( &tv, NULL );us_time = tv.tv_sec * 1000000 + tv.tv_usec;lxc_sort::sort::counting_sort( in );gettimeofday( &tv, NULL );us_time = ( tv.tv_sec * 1000000 + tv.tv_usec ) - us_time;std::cout << "The time required for sorting: "  << us_time << "us" << std::endl;std::cout << "After insert: " << std::endl;if( in.size() < 128 )test_helper::print_vector( in );return 0;}</span>

(2) gcc_QuickSort.cpp

<span style="font-family:Microsoft YaHei;">#include<iostream>#include<sys/time.h>#include<vector>#include "sort.h"#include "test_helper.h"const int HOW_LARGE = (1 << 8);const int HowMuch = (1 << 5);int main(){timeval tv;int us_time, max_value;std::vector<int> in;std::cout << "----------Quick sortion----------" << std::endl;test_helper::insert_time( in, HowMuch, HOW_LARGE );max_value = test_helper::find_max( in );std::cout << "The quantity of testing data is: " << HowMuch << std::endl;std::cout << "The max element in the vector is: " << max_value << std::endl;std::cout << "Before insert: " << std::endl;if( in.size() < 128 )test_helper::print_vector( in );gettimeofday( &tv, NULL );us_time = tv.tv_sec * 1000000 + tv.tv_usec;lxc_sort::sort::quick_sort( in );gettimeofday( &tv, NULL );us_time = ( tv.tv_sec * 1000000 + tv.tv_usec ) - us_time;std::cout << "The time required for sorting: "  << us_time << "us" << std::endl;std::cout << "After insert: " << std::endl;if( in.size() < 128 )test_helper::print_vector( in );return 0;}</span>

(3)gcc_MergeSort.cpp

<span style="font-family:Microsoft YaHei;">#include<iostream>#include<sys/time.h>#include<vector>#include "sort.h"#include "test_helper.h"const int HOW_LARGE = (1 << 8);const int HowMuch = (1 << 5);int main(){timeval tv;int us_time, max_value;std::vector<int> in;std::cout << "----------Merge sortion----------" << std::endl;test_helper::insert_time( in, HowMuch, HOW_LARGE, 5 );max_value = test_helper::find_max( in );std::cout << "The quantity of testing data is: " << HowMuch << std::endl;std::cout << "The max element in the vector is: " << max_value << std::endl;std::cout << "Before insert: " << std::endl;if( in.size() < 128 )test_helper::print_vector( in );gettimeofday( &tv, NULL );us_time = tv.tv_sec * 1000000 + tv.tv_usec;lxc_sort::sort::merge_sort( in );gettimeofday( &tv, NULL );us_time = ( tv.tv_sec * 1000000 + tv.tv_usec ) - us_time;std::cout << "The time required for sorting: " << us_time << "us" << std::endl;std::cout << "After insert: " << std::endl;if( in.size() < 128 )test_helper::print_vector( in );return 0;}</span>

(4) gcc_HeapSort.cpp

<span style="font-family:Microsoft YaHei;">#include<iostream>#include<sys/time.h>#include<vector>#include "sort.h"#include "test_helper.h"const int HOW_LARGE = (1 << 8);const int HowMuch = (1 << 5);int main(){timeval tv;int max_value, us_time;std::vector<int> in;std::cout << "----------Heap sortion----------" << std::endl;test_helper::insert_time( in, HowMuch, HOW_LARGE, 7 );max_value = test_helper::find_max( in );std::cout << "The quantity of testing data is: " << HowMuch << std::endl;std::cout << "The max element in the vector is: " << max_value << std::endl;std::cout << "Before insert: " << std::endl;if( in.size() < 128 )test_helper::print_vector( in );gettimeofday( &tv, NULL );us_time = tv.tv_sec * 1000000 + tv.tv_usec;lxc_sort::sort::heap_sort( in );gettimeofday( &tv, NULL );us_time = ( tv.tv_sec * 1000000 + tv.tv_usec ) - us_time;std::cout << "The time required for sorting: " << us_time << "us" << std::endl;std::cout << "After insert: " << std::endl;if( in.size() < 128 )test_helper::print_vector( in );return 0;}</span>

(5) gcc_ShellSort.cpp

<span style="font-family:Microsoft YaHei;">#include<iostream>#include<sys/time.h>#include<vector>#include "sort.h"#include "test_helper.h"const int HOW_LARGE = (1 << 8);const int HowMuch = (1 << 5);int main(){timeval tv;int us_time, max_value;std::vector<int> in;std::cout << "----------Shell sortion----------" << std::endl;test_helper::insert_time( in, HowMuch, HOW_LARGE, 11 );max_value = test_helper::find_max( in );std::cout << "The quantity of testing data is: " << HowMuch << std::endl;std::cout << "The max element in the vector is: " << max_value << std::endl;std::cout << "Before insert: " << std::endl;if( in.size() < 128 )test_helper::print_vector( in );gettimeofday( &tv, NULL );us_time = tv.tv_sec * 1000000 + tv.tv_usec;lxc_sort::sort::shell_sort( in );gettimeofday( &tv, NULL );us_time = ( tv.tv_sec * 1000000 + tv.tv_usec ) - us_time;std::cout << "The time required for sorting: " << us_time << "us" << std::endl;std::cout << "After insert: " << std::endl;if( in.size() < 128 )test_helper::print_vector( in );return 0;}</span>

(6) gcc_InsertionSort.cpp

<span style="font-family:Microsoft YaHei;">#include<iostream>#include<sys/time.h>#include<vector>#include "sort.h"#include "test_helper.h"const int HOW_LARGE = (1 << 8);const int HowMuch = (1 << 5);int main(){timeval tv;int us_time, max_value;std::vector<int> in;std::cout << "----------Insertion sortion----------" << std::endl;test_helper::insert_time( in, HowMuch, HOW_LARGE, 1 );max_value = test_helper::find_max( in );std::cout << "The quantity of testing data is: " << HowMuch << std::endl;std::cout << "The max element in the vector is: " << max_value << std::endl;std::cout << "Before insert: " << std::endl;if( in.size() < 128 )test_helper::print_vector( in );gettimeofday( &tv, NULL );us_time = tv.tv_sec * 1000000 + tv.tv_usec;lxc_sort::sort::insertion_sort( in );gettimeofday( &tv, NULL );us_time = ( tv.tv_sec * 1000000 + tv.tv_usec ) - us_time;std::cout << "The time required for sorting: " << us_time << "us" << std::endl;std::cout << "After insert: " << std::endl;if( in.size() < 128 )test_helper::print_vector( in );return 0;}</span>

(7) gcc_SelectionSort.cpp

<span style="font-family:Microsoft YaHei;">#include<iostream>#include<sys/time.h>#include<vector>#include "sort.h"#include "test_helper.h"const int HOW_LARGE = (1 << 8);const int HowMuch = (1 << 5);int main(){timeval tv;int us_time, max_value;std::vector<int> in;std::cout << "----------Selection sortion----------" << std::endl;test_helper::insert_time( in, HowMuch, HOW_LARGE, 13 );max_value = test_helper::find_max( in );std::cout << "The quantity of testing data is: " << HowMuch << std::endl;std::cout << "The max element in the vector is: " << max_value << std::endl;std::cout << "Before insert: " << std::endl;if( in.size() < 128 )test_helper::print_vector( in );gettimeofday( &tv, NULL );us_time = tv.tv_sec * 1000000 + tv.tv_usec;lxc_sort::sort::selection_sort( in );gettimeofday( &tv, NULL );us_time = ( tv.tv_sec * 1000000 + tv.tv_usec ) - us_time;std::cout << "The time required for sorting: " << us_time << "us" << std::endl;std::cout << "After insert: " << std::endl;if( in.size() < 128 )test_helper::print_vector( in );return 0;}</span>

(8) test.bat(批处理指令)

<span style="font-family:Microsoft YaHei;">g++ -o count_sort gcc_CountSort.cppcount_sort.exe@echo 计数排序完成!g++ -o quick_sort gcc_QuickSort.cppquick_sort.exe@echo 快速排序完成!g++ -o merge_sort gcc_MergeSort.cppmerge_sort.exe@echo 归并排序完成!g++ -o heap_sort gcc_HeapSort.cppheap_sort.exe@echo 堆排序完成!g++ -o shell_sort gcc_ShellSort.cppshell_sort.exe@echo 希尔排序完成!g++ -o insert_sort gcc_InsertionSort.cppinsert_sort.exe@echo 插入排序完成!g++ -o select_sort gcc_SelectionSort.cppselect_sort.exe@echo 选择排序完成!</span>


五、七大排序算法与测试过程浅析

1、计数排序的原理:此算法只能运用于非负整数的情况,原因是数组下标的取值限制。必须找到整个数组中的最大数,以便分配最大数加1的数组大小,然后进行统计每个数出现的次数,再从0开始的次数依次叠加在后面的数上,就得到了原数在已序数组中的下标,最后从后往前遍历原数组,将得到的每一个数放在新数组的正确位置上,排序即完成!此算法至此又产生了一个缺陷,至少需要新建另一个和原数组一样大小的数组,这对内存的要求比较高,同时计数所产生的数组依据于数组中最大值的元素,若该值非常大,则对内存的要求甚至比前两者还要高,之所以要限制其上界就是出于这个目的。我在用C++测试时,当该值达到2^27时,内存分配就失败!不过即便有如此多的限制:在一定小的范围内排序非负整数、内存要求高,但是它是线性阶的算法,效率比同等条件下的快速排序要快!

2、快速排序原理:此算法是目前针对随机序列元素排序无任何其它限制的最高效算法,由于采用了泛型手法,在C++中,比较类型只需要实现operator<重载函数和赋值操作符“operator=”,就可以对比较类型进行排序,不仅仅只限于数字和字符串;在Java中只需要实现Comparable接口的函数compareTo即可。该算法正确和高效运行的关键在于数字大小的划分,必须确保小于等于和大于的两边有尽可能相等数量的元素:每次划分取头部、尾部、中间三个元素的中间大小者,确保两边均有元素,这样不会因为一边没有元素而触犯该算法的二次下界。首先将整个数组按照某一个值的大小,小于等于的部分放在该值位置的最前面、大于的部分放在该值位置的最后面,直到两个位置指示值的大小关系发生逆转,说明本次划分已完成,这样确保该值后面的元素一定大于前面的元素。然后递归划分下去,直到划分到只有一个元素,然后马上返回,再递归后面的,使其前后变得完整有序,从而完成排序!此排序均是在原数组上进行,如果比较类型的复制操作是拷贝小单位数据或者不是使用的深拷贝大单位数据,那么该算法对内存的要求并不会太高,而且运行效率的瓶颈主要是在于递归的划分:每次二分拆解数组,直到处理完成,所以该算法平均时间界是O(NlgN)。但是当该算法将降序序列排成升序序列时,该算法的最坏情况时间界将被触发:O(N^2)。

3、归并排序原理:此排序需要新建一个和原数组一样大小的数组,并且初始化完成后需要立即进行随机访问和存取。该算法的关键仍然是递归,主要思想是递归合并:如果两个数组已经是已序状态,通过位置指示,可以比较出两个位置中较小的元素,然后将该元素放入新建立的数组,按此规则,直到某一方全部放入完成,然后再把另一方剩余的部分放入,则该两个数组的元素在一个数组中将成为已序状态,最后将已序的部分直接覆盖待排序的两部分,原数组的该部分即是已序。递归的作用是持续左分解和右分解,然后对双方进行合并,从而完成整体的已序。该算法对内存的要求也比较高,需要2倍数组大小的内存!

4、堆排序原理:此排序需要建立一个树形结构——大堆,该大堆的模型是父结点的元素永远比左右子女结点的元素大,如果该关系不满足,则通过一个下滤操作,将待处理位置的元素下降到树形结构的合适位置,与此同时,将左右子树之中的最大元素提到该位置,这样一处理就满足大堆结构模型了。这其中就蕴含着,如果父结点为N,则它的左结点元素位置为2N,则右结点元素位置则为2N+1,依次规律,可用数组模型来构建一棵树。大堆正是基于这个模型来进行排序:数组的最前面元素(位置0不算),在当前最大下标的指示下为当前数组的最大元素,然后将该元素与最大下标的元素进行交换,并使最大下标减1,同时对该交换到最前面的元素进行下滤操作,最大的当前元素已放入合适的位置,同时大堆结构继续保持,继续循环处理该过程,将完成整个数组的排序,而且该算法也是在元素组上进行排序,但是循环和下滤,尤其是下滤操作严重的拖慢了该算法的效率:因为每次交换上来的数大部分情况下都是非常小的数,这使得下滤操作要遍历一整棵子树!

5、希尔排序原理:此排序需要按照一定的增量序列采用插入排序方式进行排序。这个增量指的是每个序列之中的元素在原数组之中位置的差距,对具有定长位置差距的元素序列采用插入排序,使得该序列成为已序状态,然后再缩小这个差距,使得小的元素保持在前面、大的元素保持在后面,直到最后完全的两级、甚至多级分化,这可以大幅度减小需要移位插入的次数,使得元素保持有序,我给出的该算法的一般时间界为O(N^(3/2)),证明过程非常复杂,略过!

6、插入排序原理:该算法的原理是,对前面已序的元素序列,处理当前元素,从前面的序列之中找到第一个比当前元素小的元素的后一位置,从当前元素的前一个位置到找到的位置,将各个元素均向后移动一个位置,以便给当前元素移出一个位置,然后将当前元素复制到最后移出的位置,这样当前处理的序列仍然已序。该算法继续循环,直到处理完最后一个元素,则整个序列已序!可以看出该算法在处理数据时就在原数组排序,如果处理数据足够小则效率非常高,但是它的时间界始终是平方阶,故处理大规模数据时,将毫无效率可言!该算法处理小规模数据高效的奥秘在于,如果CPU的高速缓存能够一次装下要排序的所有数据,那么内存访问的高昂开销就不会成为瓶颈,而且CPU的处理速度至少是内存的几十倍!

7、选择排序原理:此算法是最简单的算法,就是把未排序的序列之中的最小元素放在当前最小的下标位置,直到处理完未排序的序列的所有元素,则该序列已序。由于需要每次都访问一次整个序列,因而该算法没有任何效率可言,直接就是平方阶时间界!

8、测试过程分析:C++中的时间测试函数是取之于GCC标准库sys/time.h的高精度函数gettimeofday,其可以精确到微妙层级。
0 0
原创粉丝点击