堆排序优化与几个排序算法时间复杂度

前端之家收集整理的这篇文章主要介绍了堆排序优化与几个排序算法时间复杂度前端之家小编觉得挺不错的,现在分享给大家,也给大家做个参考。

我们通常所说的堆是指二叉堆,二叉堆又称完全二叉树或者叫近似完全二叉树。二叉堆又分为最大堆和最小堆。

堆排序(Heapsort)是指利用堆这种数据结构所设计的一种排序算法,它是选择排序的一种。可以利用数组的特点快速定位指定索引的元素。数组可以根据索引直接获取元素,时间复杂度为O(1),也就是常量,因此对于取值效率极高。

这里以最大堆为例:

最大堆的特性如下:

父结点的键值总是大于或者等于任何一个子节点的键值

每个结点的左子树和右子树都是一个最大堆

最大堆的算法思想是:

先将初始的R[0…n-1]建立成最大堆,此时是无序堆,而堆顶是最大元素

再将堆顶R[0]和无序区的最后一个记录R[n-1]交换,由此得到新的无序区R[0…n-2]和有序区R[n-1],且满足R[0…n-2].keys ≤ R[n-1].key

由于交换后,前R[0…n-2]可能不满足最大堆的性质,因此再调整前R[0…n-2]为最大堆,直到只有R[0]最后一个元素才调整完成。

最大堆排序完成后,其实是升序序列,每次调整堆都是要得到最大的一个元素,然后与当前堆的最后一个元素交换,因此最后所得到的序列是升序序列。

构建堆:

 1 #ifndef INC_06_HEAP_SORT_HEAP_H
 2 #define INC_06_HEAP_SORT_HEAP_H
 3 #include <algorithm>
 4 #include <cassert>
 5 using namespace std;
 6 template<typename Item>
 7 class MaxHeap{
 8 private:
 9     Item *data;
10     int count;
11      capacity;
12 
13     void shiftUp( k){
14         while( k > 1 && data[k/2] < data[k] ){
15             swap( data[k/2],data[k] );
16             k /= ;
17         }
18     }
19 
20     void shiftDown(21         while( 2*k <= count ){
22             int j = 2*k;
23             if( j+1 <= count && data[j+1] > data[j] ) j ++24             if( data[k] >= data[j] ) break25             swap( data[k],data[j] );
26             k = j;
27 28 29 
30 public31 
32     // 构造函数,构造一个空堆,可容纳capacity个元素
33     MaxHeap( capacity){
34         data = new Item[capacity+1];
35         count = 036         this->capacity =37 38     39      该构造堆的过程,时间复杂度为O(n)
40     MaxHeap(Item arr[], n){
41         data = new Item[n+42         capacity = n;
43         for( int i = 0 ; i < n ; i ++ )
44             data[i+1] = arr[i];
45         count =46 
47         int i = count/2 ; i >= 1 ; i --48             shiftDown(i);
49 50     ~MaxHeap(){
51         delete[] data;
52 53 
54      返回堆中的元素个数
55      size(){
56         return57 58 
59      返回一个布尔值,表示堆中是否为空
60     bool isEmpty(){
61         return count == 62 63 
64      像最大堆中插入一个新的元素 item
65     void insert(Item item){
66         assert( count + 1 <= capacity );
67         data[count+ item;
68         shiftUp(count+);
69         count ++70 71 
72      从最大堆中取出堆顶元素,即堆中所存储的最大数据
73     Item extractMax(){
74         assert( count >  );
75         Item ret = data[76         swap( data[77         count --78         shiftDown(79          ret;
80 81 
82      获取最大堆中的堆顶元素
83     Item getMax(){
84         assert( count > 85         return data[86 87 };
88 
89 #endif @H_987_404@

简单堆排序:

#ifndef INC_06_HEAP_SORT_HEAPSORT_H
#define INC_06_HEAP_SORT_HEAPSORT_H
 3 #include "Heap.h"
 4  heapSort1,将所有的元素依次添加到堆中,在将所有元素从堆中依次取出来,即完成了排序
 6  无论是创建堆的过程,还是从堆中依次取出元素的过程,时间复杂度均为O(nlogn)
 整个堆排序的整体时间复杂度为O(nlogn)
 8 template<typename T>
 9 void heapSort1(T arr[],1)">10 
11     MaxHeap<T> maxheap = MaxHeap<T>(n);
12     13         maxheap.insert(arr[i]);
14 
15     int i = n-1 ; i >= 0 ; i--16         arr[i] = maxheap.extractMax();
}
 heapSort2,借助我们的heapify过程创建堆
19  此时,创建堆的过程时间复杂度为O(n),将所有元素依次从堆中取出来,实践复杂度为O(nlogn)
20  堆排序的总体时间复杂度依然是O(nlogn),但是比上述heapSort1性能更优,因为创建堆的性能更优
21 template<typename T>
22 void heapSort2(T arr[],1)">23 
24     MaxHeap<T> maxheap = MaxHeap<T>(arr,n);
25     26         arr[i] =#endif @H_987_404@

插入排序:

#ifndef INC_06_HEAP_SORT_INSERTIONSORT_H
#define INC_06_HEAP_SORT_INSERTIONSORT_H
 3 #include <iostream>
 4 #include <algorithm>
 6 template<typename T>
void insertionSort(T arr[],1)"> 8 
 9     1 ; i < n ; i ++ ) {
11         T e =12         13         for (j = i; j > 0 && arr[j-1] > e; j--)
14             arr[j] = arr[j-15         arr[j] = e;
16 17 
18     20 
21  对arr[l...r]范围的数组进行插入排序
22 template<typename T>
23 int l,1)"> r){
24 
int i = l+1 ; i <= r ; i ++26 
27         T e =28         29         for (j = i; j > l && arr[j-30             arr[j] = arr[j-31         arr[j] =32 33 
34     35 36 
#endif@H_987_404@

归并排序:

#ifndef INC_06_HEAP_SORT_MERGESORT_H
#define INC_06_HEAP_SORT_MERGESORT_H
 3 
 4 #include <iostream>
 5 #include <algorithm>
 6 #include InsertionSort.h 7 
 9 
11  将arr[l...mid]和arr[mid+1...r]两部分进行归并
12  其中aux为完成merge过程所需要的辅助空间
13 template<typename  T>
14 void __merge(T arr[],T aux[],1)">int mid,1)">15 
16      由于aux的大小和arr一样,所以我们也不需要处理aux索引的偏移量
17      进一步节省了计算量:)
int i = l ; i <= r; i ++19         aux[i] =21      初始化,i指向左半部分的起始索引位置l;j指向右半部分起始索引位置mid+1
22     int i = l,j = mid+23     int k = l ; k <= r; k ++ ){
25         if( i > mid ){   如果左半部分元素已经全部处理完毕
26             arr[k] = aux[j]; j ++else if( j > r ){   如果右半部分元素已经全部处理完毕
29             arr[k] = aux[i]; i ++31         if( aux[i] < aux[j] ) {   左半部分所指元素 < 右半部分所指元素
32             arr[k] = aux[i]; i ++33 34         else{   左半部分所指元素 >= 右半部分所指元素
35             arr[k] = aux[j]; j ++36 38 
39 40 
41  使用优化的归并排序算法,对arr[l...r]的范围进行排序
42 43 template<typename T>
44 void __mergeSort(T arr[],1)">45 
46      对于小规模数组,使用插入排序
47     if( r - l <= 15        insertionSort(arr,l,r);
49         50 51 
52     int mid = (l+r)/53     __mergeSort(arr,aux,mid);
54     __mergeSort(arr,mid+,1)">55 
56      对于arr[mid] <= arr[mid+1]的情况,不进行merge
57      对于近乎有序的数组非常有效,但是对于一般情况,有一定的性能损失
58     if( arr[mid] > arr[mid+] )
59         __merge(arr,mid,1)">60 61 
62 
63 template<typename T>
64 void mergeSort(T arr[],1)">65 
66      在 mergeSort中,我们一次性申请aux空间,67      并将这个辅助空间以参数形式传递给完成归并排序的各个子函数
68     T *aux = new T[n];
69 
70     __mergeSort( arr,0,n-delete[] aux;    使用C++,new出来的空间不要忘记释放掉:)
74 
75 #endif @H_987_404@

单路快排:

#ifndef INC_06_HEAP_SORT_QUICKSORT_H
#define INC_06_HEAP_SORT_QUICKSORT_H
 5 #include <ctime>
 6 #include <algorithm>
 7 #include  对arr[l...r]部分进行partition操作
10  返回p,使得arr[l...p-1] < arr[p] ; arr[p+1...r] > arr[p]
11 template <typename T>
int _partition(T arr[],1)">13 
14      随机在arr[l...r]的范围中,选择一个数值作为标定点pivot
15     swap( arr[l],arr[rand()%(r-l+1)+l] );
16 
17     T v = arr[l];
int j = l;
19     int i = l + 20         if( arr[i] < v ){
21             j ++            swap( arr[j],arr[i] );
    swap( arr[l],arr[j]);
27      对arr[l...r]部分进行快速排序
31 template <typename T>
void _quickSort(T arr[],使用插入排序进行优化
35     37         38 39 
40     int p = _partition(arr,1)">41     _quickSort(arr,p-42     _quickSort(arr,p+43 44 
45 template <typename T>
46 void quickSort(T arr[],1)">47 
    srand(time(NULL));
49     _quickSort(arr,1)">#endif @H_987_404@

双路快排:

#ifndef INC_06_HEAP_SORT_QUICKSORT2WAYS_H
#define INC_06_HEAP_SORT_QUICKSORT2WAYS_H
 双路快速排序的partition
12 template <typename T>
int _partition2(T arr[],1)">16     swap( arr[l],1)">18 
 arr[l+1...i) <= v; arr(j...r] >= v
1,j = r;
while( true22          注意这里的边界,arr[i] < v,不能是arr[i] <= v
23         
24         while( i <= r && arr[i] < v )
25             i ++27         28         
while( j >= l+1 && arr[j] >30             j --32   
34 
35         if( i > j )
36             37 
        swap( arr[i],arr[j] );
39         i ++40         j --42 
45     49 template <typename T>
void _quickSort2Ways(T arr[],1)">53     54 55         56 57 
 调用双路快速排序的partition
 _partition2(arr,1)">60     _quickSort2Ways(arr,1)">61     _quickSort2Ways(arr,1)">64 template <typename T>
65 void quickSort2Ways(T arr[],1)">66 
67 68     _quickSort2Ways(arr,1)">69 70 
71 #endif@H_987_404@

三路快排:

#ifndef INC_06_HEAP_SORT_QUICKSORT3WAYS_H
#define INC_06_HEAP_SORT_QUICKSORT3WAYS_H
 递归的三路快速排序算法
void __quickSort3Ways(T arr[],1)">17         21     swap( arr[l],1)">l ] );
22 
23     T v =int lt = l;      arr[l+1...lt] < v
26     int gt = r + 1;  arr[gt...r] > v
1;     arr[lt+1...i) == v
28     while( i < gt ){
30             swap( arr[i],arr[lt+]);
31             i ++32             lt ++if( arr[i] >35             swap( arr[i],arr[gt-36             gt --38         else{  arr[i] == v
39             i ++40 45     __quickSort3Ways(arr,lt-    __quickSort3Ways(arr,gt,1)">47 48 
void quickSort3Ways(T arr[],1)">53     __quickSort3Ways( arr,1)">#endif@H_987_404@

测试用例:

#ifndef INC_06_HEAP_SORT_SORTTESTHELPER_H
#define INC_06_HEAP_SORT_SORTTESTHELPER_H
 5 #include <string>
 6 #include <ctime>
 7 #include <cassert>
 8 #include < SortTestHelper {
 生成有n个元素的随机数组,每个元素的随机范围为[rangeL,rangeR]
int *generateRandomArray(int n,1)">int range_l,1)"> range_r) {
int *arr = new [n];
        srand(time(NULL));
15         for (0; i < n; i++16             arr[i] = rand() % (range_r - range_l + 1) + range_l;
 arr;
 生成一个近乎有序的数组
 首先生成一个含有[0...n-1]的完全有序数组,之后随机交换swapTimes对数据
 swapTimes定义了数组的无序程度
int *generateNearlyOrderedArray( swapTimes){
23         for(25             arr[i] = i;
0 ; i < swapTimes ; i ++29             int posx = rand()%n;
30             int posy = rand()%31             swap( arr[posx],arr[posy] );
37      拷贝整型数组a中的所有元素到一个新的数组,并返回新的数组
int *copyIntArray(int a[],1)">40         41         * 在VS中,copy函数被认为是不安全的,请大家手动写一遍for循环:)
42         copy(a,a+n,arr);
 打印arr数组的所有内容
47     template<typename T>
48     void printArray(T arr[],1)"> n) {
49 
50         51             cout << arr[i] << " "52         cout << endl;
54         55 56 
 判断arr数组是否有序
58     template<typename T>
bool isSorted(T arr[],1)">60 
0; i < n - 1; i++62             if (arr[i] > arr[i + ])
63                 return false64 
65         66 67 
68      测试sort排序算法排序arr数组所得到结果的正确性和算法运行时间
69      将算法的运行时间打印在控制台上
70     template<typename T>
71     void testSort(const string &sortName,1)">void (*sort)(T[],1)">int),T arr[],1)">72 
73         clock_t startTime = clock();
74         sort(arr,1)">75         clock_t endTime =76         cout << sortName <<  : " << double(endTime - startTime) / CLOCKS_PER_SEC <<  s"<<endl;
77 
78         assert(isSorted(arr,n));
79 
80         81 82 
83     84      将算法的运行时间以double类型返回,单位为秒(s)
85     template<typename T>
86     double testSort(87 
88         clock_t startTime =90         clock_t endTime =91 
92 93 
94         double(endTime - startTime) / CLOCKS_PER_SEC;
95 96 
97 98 
99 #endif @H_987_404@

测试结果:

  平均时间复杂度 是否是原地排序 需要额外空间 稳定排序
插入排序 O(n^2) O(1)
归并排序 O(nlogn) O(n)
快速排序 O(nlogn) O(logn)
堆排序 O(nlogn) O(1)

稳定性解释:排序后的元素相同元素的顺序依然是排序之前的顺序。

 堆排序的最坏时间复杂度为O(N*logN),其平均性能较接近于最坏性能。由于初始建堆所需比较的次数较多,所以堆排序不适合记录数较少的文件,其空间复杂度是O(1),它是一种不稳定的排序算法.

猜你在找的算法相关文章