数据结构与算法C++之原地堆排序

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/majinlei121/article/details/84067393

上一篇博客中的堆排序需要先将数组中的元素放入堆中,然后再从堆中取出来,整个操作开辟了n个额外元素的堆,其实也可以将数组看做是一个堆,如下图
(1)最大堆的第一个元素 v v 是最大值,根据排序需要将最大元素放入数组中的最后一个位置 w w 处,因此交换 v v w w 的位置
在这里插入图片描述
(2)此时已经将 v v 放到排好序的位置,但是左边的橙色部分就不是个最大堆了在这里插入图片描述
(3)需要将其恢复为最大堆,使用上篇的ShiftDown操作就可将左边部分恢复为最大堆,此时最左边的元素又是最大值,因此将其与倒数第二个元素交换位置,以此类推
在这里插入图片描述
此时整个算法的空间复杂度是O(1),由于使用数组进行堆排序,数组一般索引从0开始,所以就需要将上篇博客中从1开始设定的一些操作改为从0开始,如下图
在这里插入图片描述
在heapify中最后一个非叶子节点的索引变为
在这里插入图片描述
程序实现如下

//heapSorting.cpp
#include <iostream>

#ifndef _SORTINGHELP_H_
#define _SORTINGHELP_H_
#include "SortingHelp.h"
#endif // _SORTINGHELP_H_

#ifndef _HEAP_H_
#define _HEAP_H_
#include "Heap.h"
#endif // _HEAP_H_

#include "MergeSorting.h"
#include "quickSorting.h"
#include "heapSorting12.h"

using namespace std;

template<typename T>
void __shiftDown(T arr[], int n, int k){
    while (2*k + 1 < n){
        int j = 2*k + 1;
        if (arr[j] < arr[j+1] && j + 1 < n)
            j += 1;
        if (arr[k] >= arr[j])
            break;

        swap(arr[j], arr[k]);
        k = j;
    }
}

//原地堆排序
template<typename T>
void heapSorting(T arr[], int n){

    for (int i = (n-1)/2; i >=0; i--)
        __shiftDown(arr, n, i);

    for (int i = n - 1; i > 0; i --){
        swap(arr[0], arr[i]);
        __shiftDown(arr, i, 0);
    }
}

int main()
{
    //对普通的随机数组进行排序
    int n = 500000;
    int *arr = generateRandomArray(n, 0, n);
    int *arr2 = copyIntArray(arr, n);
    int *arr3 = copyIntArray(arr, n);
    int *arr4 = copyIntArray(arr, n);
    int *arr5 = copyIntArray(arr, n);
    cout<<"普通数组进行排序"<<endl;
    testSorting("MergeSorting", MergeSorting, arr, n);
    testSorting("quickSorting2", quickSorting2, arr2, n);
    testSorting("heapSorting1", heapSorting1, arr3, n);
    testSorting("heapSorting2", heapSorting2, arr4, n);
    testSorting("heapSorting", heapSorting, arr5, n);
    delete[] arr;//最后删除数组开辟的空间
    delete[] arr2;
    delete[] arr3;
    delete[] arr4;
    delete[] arr5;

    //对近乎有序的数组进行排序
    arr = generateNearlyOrderedArray(n, 100);//生成只有200个无序元素的数组
    arr2 = copyIntArray(arr, n);
    arr3 = copyIntArray(arr, n);
    arr4 = copyIntArray(arr, n);
    arr5 = copyIntArray(arr, n);
    cout<<"近乎有序的数组进行排序"<<endl;
    testSorting("MergeSorting", MergeSorting, arr, n);
    testSorting("quickSorting2", quickSorting2, arr2, n);
    testSorting("heapSorting1", heapSorting1, arr3, n);
    testSorting("heapSorting2", heapSorting2, arr4, n);
    testSorting("heapSorting", heapSorting, arr5, n);
    delete[] arr;//最后删除数组开辟的空间
    delete[] arr2;
    delete[] arr3;
    delete[] arr4;
    delete[] arr5;

    //对有大量重复元素的数组进行排序
    arr = generateRandomArray(n, 0, 10);
    arr2 = copyIntArray(arr, n);
    arr3 = copyIntArray(arr, n);
    arr4 = copyIntArray(arr, n);
    arr5 = copyIntArray(arr, n);
    cout<<"有大量重复元素的数组进行排序"<<endl;
    testSorting("MergeSorting", MergeSorting, arr, n);
    testSorting("quickSorting2", quickSorting2, arr2, n);
    testSorting("heapSorting1", heapSorting1, arr3, n);
    testSorting("heapSorting2", heapSorting2, arr4, n);
    testSorting("heapSorting", heapSorting, arr5, n);
    delete[] arr;//最后删除数组开辟的空间
    delete[] arr2;
    delete[] arr3;
    delete[] arr4;
    delete[] arr5;
    return 0;
}

输出为
在这里插入图片描述
可以看出运行时间要比前两个堆排序要段
程序中调用的
"SortingHelp.h"
“Heap.h”
“heapSorting12.h”
“MergeSorting.h”
"quickSorting.h"

分别为
也可见博客 SortingHelp.h

//SortingHelp.h
#include <iostream>
#include <ctime>  //time()函数
#include <cstdlib> //rand()函数
#include <cassert> //assert()函数


using namespace std;

int* generateRandomArray(int n, int rangeL, int rangeR){//生成随机数组
    assert(rangeL < rangeR);
    int *arr = new int[n];
    srand(time(NULL));
    for (int i = 0; i < n; i++){
        arr[i] = rand() % (rangeR - rangeL + 1) + rangeL;
    }
    return arr;
}

int* generateNearlyOrderedArray(int n, int swapTimes){//生成近乎有序的数组
    int *arr = new int[n];
    for (int i = 0; i < n; i++){
        arr[i] = i;
    }
    srand(time(NULL));
    for (int i = 0; i < swapTimes; i++){
        int posx = rand() % n;
        int posy = rand() % n;
        swap(arr[posx], arr[posy]);
    }
    return arr;
}


template<typename T>
void printArray(T arr[], int n){//打印数组元素
    for (int i = 0; i < n; i ++){
        cout<<arr[i]<<" ";
    }
    cout<<endl; //换行
    return;
}


template<typename T>
bool isSorted(T arr[], int n){//测试排序算法是否正确
    for (int i = 0; i < n - 1; i++){
        if (arr[i] > arr[i + 1])
            return false;
    }
    return true;
}

template<typename T>
void testSorting(string sortName, void(*sorting)(T[], int), T arr[], int n){
    //第二个参数是传入排序函数的指针

    clock_t startClock = clock();
    sorting(arr, n);
    clock_t endClock = clock();
    assert(isSorted(arr, n));
    cout<<sortName<<" : "<<double(endClock-startClock)/CLOCKS_PER_SEC<<" s"<<endl;
    return;
}

int* copyIntArray(int arr[], int n){
    int* arr2 = new int[n];
    copy(arr, arr+n, arr2);
    return arr2;
}

template<typename T> //定义模板类型,使对各种数据类型都适用,如double,float,string
void SelectionSorting(T a[], int n){//选择排序算法
    for (int i = 0; i < n; i++){
        int minIndex = i;
        for (int j = i + 1; j < n; j++){
            if (a[j] < a[minIndex])
                minIndex = j;
        }
        swap(a[i], a[minIndex]);
    }
}

template<typename T>
void InsertionSortingImproved(T arr[], int n){
    for (int i = 0; i < n - 1; i++){
        T temp = arr[i+1];
        int j;
        for (j = i + 1; j > 0; j--){
            if (arr[j-1] > temp){
                arr[j] = arr[j-1];
            }
            else{
                break;
            }
        }
        arr[j] = temp;
    }
    return;
}

也可见博客Heap.h

//Heap.h
#include <iostream>
#include <algorithm>
#include <string>
#include <ctime>
#include <cmath>
#include <cassert>
#include <typeinfo>

//#ifndef _SORTINGHELP_H_
//#define _SORTINGHELP_H_
//#include "SortingHelp.h"
//#endif // _SORTINGHELP_H_
//
//#include "MergeSorting.h"
//#include "quickSorting.h"

using namespace std;

template<typename Item>
class MaxHeap{
private:
    Item *data;
    int count;
    int capacity;

    void ShiftUp(int k){
        while (data[k/2] < data[k] && k > 1){
            swap(data[k/2], data[k]);
            k /= 2;
        }
    }

    void ShiftDown(int k){
        while (k <= count/2){
            int j = 2*k; //此轮循环中,data[k]和data[j]交换位置
            if (data[j] < data[j+1] && j + 1 <= count)
                j += 1;
            if (data[k] >= data[j])
                break;

            swap(data[j], data[k]);
            k = j;
        }
    }

public:
    MaxHeap(int capacity){
        data = new Item[capacity + 1];
        count = 0;
        this->capacity = capacity;
    }

    MaxHeap(Item arr[], int n){
        data = new Item[n+1];
        capacity = n;
        for (int i = 0; i < n; i ++)
            data[i+1] = arr[i];
        count = n;
        for (int i = count / 2; i >= 1; i--)
            ShiftDown(i);
    }

    ~MaxHeap(){
        delete[] data;
    }

    int size(){
        return count;
    }

    bool isEmpty(){
        return count == 0;
    }

    void insert(Item item){

        assert( count + 1 <= capacity );
        data[count+1] = item;
        count ++;
        ShiftUp(count);
    }

    Item extractMax(){
        assert( count > 0);

        Item ret = data[1];

        swap(data[1], data[count]);
        count --;
        ShiftDown(1);

        return ret;
    }

public:
    void testPrint(){

        if( size() >= 100 ){
            cout<<"Fancy print can only work for less than 100 int";
            return;
        }

        if( typeid(Item) != typeid(int) ){
            cout <<"Fancy print can only work for int item";
            return;
        }

        cout<<"The Heap size is: "<<size()<<endl;
        cout<<"data in heap: ";
        for( int i = 1 ; i <= size() ; i ++ )
            cout<<data[i]<<" ";
        cout<<endl;
        cout<<endl;

        int n = size();
        int max_level = 0;
        int number_per_level = 1;
        while( n > 0 ) {
            max_level += 1;
            n -= number_per_level;
            number_per_level *= 2;
        }

        int max_level_number = int(pow(2, max_level-1));
        int cur_tree_max_level_number = max_level_number;
        int index = 1;
        for( int level = 0 ; level < max_level ; level ++ ){
            string line1 = string(max_level_number*3-1, ' ');

            int cur_level_number = min(count-int(pow(2,level))+1,int(pow(2,level)));
            bool isLeft = true;
            for( int index_cur_level = 0 ; index_cur_level < cur_level_number ; index ++ , index_cur_level ++ ){
                putNumberInLine( data[index] , line1 , index_cur_level , cur_tree_max_level_number*3-1 , isLeft );
                isLeft = !isLeft;
            }
            cout<<line1<<endl;

            if( level == max_level - 1 )
                break;

            string line2 = string(max_level_number*3-1, ' ');
            for( int index_cur_level = 0 ; index_cur_level < cur_level_number ; index_cur_level ++ )
                putBranchInLine( line2 , index_cur_level , cur_tree_max_level_number*3-1 );
            cout<<line2<<endl;

            cur_tree_max_level_number /= 2;
        }
    }

private:
    void putNumberInLine( int num, string &line, int index_cur_level, int cur_tree_width, bool isLeft){

        int sub_tree_width = (cur_tree_width - 1) / 2;
        int offset = index_cur_level * (cur_tree_width+1) + sub_tree_width;
        assert(offset + 1 < line.size());
        if( num >= 10 ) {
            line[offset + 0] = '0' + num / 10;
            line[offset + 1] = '0' + num % 10;
        }
        else{
            if( isLeft)
                line[offset + 0] = '0' + num;
            else
                line[offset + 1] = '0' + num;
        }
    }

    void putBranchInLine( string &line, int index_cur_level, int cur_tree_width){

        int sub_tree_width = (cur_tree_width - 1) / 2;
        int sub_sub_tree_width = (sub_tree_width - 1) / 2;
        int offset_left = index_cur_level * (cur_tree_width+1) + sub_sub_tree_width;
        assert( offset_left + 1 < line.size() );
        int offset_right = index_cur_level * (cur_tree_width+1) + sub_tree_width + 1 + sub_sub_tree_width;
        assert( offset_right < line.size() );

        line[offset_left + 1] = '/';
        line[offset_right + 0] = '\\';
    }
};

也可见博客heapSorting12.h

//heapSorting12.h
#include <iostream>

#ifndef _SORTINGHELP_H_
#define _SORTINGHELP_H_
#include "SortingHelp.h"
#endif // _SORTINGHELP_H_

//#include "MergeSorting.h"
//#include "quickSorting.h"
//#include "Heap.h"

#ifndef _HEAP_H_
#define _HEAP_H_
#include "Heap.h"
#endif // _HEAP_H_

using namespace std;


template<typename T>
void heapSorting1(T arr[], int n){
    MaxHeap<T> maxheap = MaxHeap<T>(n);
    for (int i = 0; i < n; i ++)
        maxheap.insert(arr[i]);

    for (int i = n - 1; i >=0; i--)
        arr[i] = maxheap.extractMax();
}

//Heapify
template<typename T>
void heapSorting2(T arr[], int n){

    MaxHeap<T> maxheap = MaxHeap<T>(arr, n);

    for (int i = n - 1; i >=0; i--)
        arr[i] = maxheap.extractMax();
}

也可见博客MergeSorting.h

//MergeSorting.h
#include <iostream>

#ifndef _SORTINGHELP_H_
#define _SORTINGHELP_H_
#include "SortingHelp.h"
#endif // _SORTINGHELP_H_

using namespace std;

//将arr[l...mid]和arr[mid+1...r]两部分进行归并
template<typename T>
void __merge(T arr[], int l, int mid, int r){
    T aux[r-l+1];
    for (int i = l; i <= r; i++){
        aux[i-l] = arr[i];
    }

    int i = l, j = mid + 1;
    for (int k = l; k <= r; k++){
        if (i > mid){
            arr[k] = aux[j-l];
            j++;
        }
        else if (j > r){
            arr[k] = aux[i-l];
            i++;
        }

        else if (aux[i-l] < aux[j-l]){
            arr[k] = aux[i-l];
            i++;
        }
        else{
            arr[k] = aux[j-l];
            j++;
        }
    }
}

//递归使用归并排序,对arr[l...r]的范围进行排序
template<typename T>
void __mergeSorting(T arr[], int l, int r){
    if (l >= r)
        return;

    int mid = (l + r)/2;
    __mergeSorting(arr, l, mid);
    __mergeSorting(arr, mid+1, r);
    if (arr[mid] > arr[mid+1])//如果左边的序列已经小于右边的序列,就不用合并了
        __merge(arr, l, mid, r);

}

template<typename T>
void MergeSorting(T arr[], int n){

    __mergeSorting(arr, 0, n-1);
}

也可见博客quickSorting.h

//quickSorting.h
#include <iostream>

#ifndef _SORTINGHELP_H_
#define _SORTINGHELP_H_
#include "SortingHelp.h"
#endif // _SORTINGHELP_H_

using namespace std;

//对arr[l...r]进行partition操作
//返回p,使得arr[l...p-1] < arr[p]; arr[p+1...r] > arr[p]
template<typename T>
int __partition2(T arr[], int l, int r){

    //随机找一个元素与最左边元素进行交换位置
    swap(arr[l], arr[rand()%(r-l+1)+l]);
    T v = arr[l];

    //arr[l+1...i)<=v; arr(j...r]>=v
    int i = l + 1;
    int j = r;
    while(true){
        while(arr[i] < v && i <= r) i++;
        while(arr[j] > v && j >= l + 1) j--;
        if (i > j) break;
        swap(arr[i], arr[j]);
        i ++;
        j --;
    }
    swap(arr[l], arr[j]);
    return j;
}

//对arr[l...r]部分进行排序
template<typename T>
void __quickSorting2(T arr[], int l, int r){
    if (l >= r)
        return;

    srand(time(NULL));
    int p = __partition2(arr, l, r);
    __quickSorting2(arr, l, p - 1);
    __quickSorting2(arr, p + 1, r);

}

template<typename T>
void quickSorting2(T arr[], int n){

    __quickSorting2(arr, 0, n-1);

}

猜你喜欢

转载自blog.csdn.net/majinlei121/article/details/84067393