zoukankan      html  css  js  c++  java
  • 第50课 堆排序和桶排序

    1. 堆排序(Heap Sort)

    (1)堆:这里所指的堆,一般是一颗完全二叉树,同时满足以下两个性质(以大顶堆为例)

     

      ①每个父结点的键值总是大于等于其左右孩子的键值

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

    (2)基本思想(以大顶堆为例):

     

      将待排序的序列构成一个大顶堆(如图①)。此时,整个序列的最大值就是堆顶的根结点。并将它与堆数组的堆末尾元素交换在堆末尾形成有序区(最大值)(如图②)然后将剩余的n-1个序列重新构造成一个堆,这样就会得到n个元素中的次小值(如图③)。如此反复执行,便能得到一个有序序列。

    (3)实例分析

     

    2. 桶排序

    (1)基本思想:将要排列的序列分成n组每组分别进行排序然后在合并到一起,这里面有分而治之的思想。

     

    (2)基本流程:

      ①建立一定数量的桶(buckets)

      ②遍历原始数组,并按一定的算法将数据放入到各自的buckets中(类似于哈希函数)

      ③对非空的buckets进行排序;(可能使用别的排序算法)

      ④按照顺序遍历这些buckets并放回到原始数组中即可构成排序后的数组。

    (3)实例分析

     

    【编程实验】堆排序(HeapSort)和桶排序算法(BucketSort)

    //Sort.h

    #ifndef SORT_H
    #define SORT_H
    
    #include "Object.h"
    
    namespace DTLib
    {
    
    //结点(for BucketSort)
    template <typename T>
    class BNode : public Object
    {
    public:
        T data;
        BNode<T>* next;
    public:
        BNode(){next = NULL;}
    };
    
    class Sort : public Object
    {  
    private:
        //私有化构造函数、赋值、拷贝构造等函数
        Sort();
        Sort(const Sort&);
        Sort& operator=(const Sort&);
    
        template <typename T>
        static void Swap(T& a, T& b)
        {
            T temp(a);
            a = b;
            b = temp;
        }
    
        //两路合并算法中的合并过程
        template <typename T> //将两个有序序列array[low...mid]和array[mid+1...high]
        static void Merge(T src[], T helper[], int low, int mid, int high, bool min2max)
        {
            int i = low;
            int j = mid +1;
            int k = 0;
    
            //按顺序将两个有序序列中较小的依次放入helper中
            while( (i<=mid) && (j<=high) ){
                if(min2max ? (src[i] < src[j]) : (src[i] > src[j])){ //取较小(大)者放入helper中
                    helper[k++] = src[i++];
                }else{
                    helper[k++] = src[j++];
                }
            }
    
            //将左子序列中剩余部分直接拷贝到helper中
            while(i<=mid){
                helper[k++] = src[i++];
            }
    
            //将右子序列中剩余部分直接拷贝到helper中
            while(j<=high){
                helper[k++] = src[j++];
            }
    
            //将数据拷贝回array中
            for(int i=0; i< k; i++){
                src[low+i] = helper[i];
            }
        }
    
        //两路合并算法中的分解过程
        template <typename T>
        static void MSort(T src[], T helper[], int low, int high, bool min2max)
        {
            int mid = (low + high) /2;
    
            if(low  < high){ //递归结束条件,只要high>low说明还可以再分解。
    
                //分解为两路
                MSort(src, helper, low, mid, min2max);     //函数返回后左子序列为有序序列
                MSort(src, helper, mid + 1, high, min2max);//函数返回后右子序列为有序序列
    
                //合并
                Merge(src, helper, low, mid, high, min2max);
            }
        }
    
        template <typename T>
        static int Partition(T array[], int low, int high, bool min2max)
        {
            int pivot = low; //基准元素,将数组分为左右两个分区,并将基准放于正确的位置
    
            while(low < high){
    
                //high指针的移动,从右向左找到小于等于pivot的元素
                while((low < high) && (min2max ? (array[high] > array[pivot]):(array[high] < array[pivot]))){
                    high--;
                }
    
                //low指针的移动,从左向右找到大于pivot的元素
                while((low < high) && (min2max ? (array[low] <= array[pivot]) : (array[low] >= array[pivot]))){
                    low++;
                }
    
                Swap(array[low], array[high]);
            }
    
            //将基准元素放入low和high相遇的地方
            Swap(array[low], array[pivot]);
    
            return low; //返回当前基准的位置
        }
    
        template <typename T>
        static void Quick(T array[], int low, int high, bool min2max)
        {
            if(low < high){  //递归结束条件
                int pvIndex = Partition(array, low, high, min2max); //依基准,分成两个区
    
                Quick(array, low, pvIndex-1, min2max);  //左区快排(递归)
                Quick(array, pvIndex+1, high, min2max);  //右区快排(递归)
            }
        }
    
       //桶排序的映射函数: 计算桶号和散列函数相似
       template<typename T>
       static int getBucketIndex(T value, T min, T max, int len)
       {
           int ret = (max == min) ? 0 : (value-min) * len / (max - min);
           if (value == max)
               ret = len - 1;
    
           return  ret;
       }
    
       //桶内的插入排序
       template<typename T>
       static void putIntoBucket(BNode<T>*& bucket, BNode<T>* node,bool min2max)
       {
           BNode<T>* tmp = bucket;
           BNode<T>* prev = bucket;
    
           while( tmp && (min2max ? (node->data > tmp->data) : (node->data < tmp->data))){
                 prev = tmp;
                 tmp = tmp->next;
           }
    
           if(prev == tmp){
               bucket = node;
           }else{
               prev->next = node;
           }
    
           node->next = tmp;
       }
    
       //堆排序:返回左孩子结点
       static int left(int i)
       {
           return (2*i + 1);
       }
    
       //堆排序:返回右孩子
       static int right(int i)
       {
           return (2 * i + 2);
       }
    
       //堆排序(以某一结点为根的子树做堆调整(保证最大堆性质)
       template <typename T>
       static void HeapAdjust(T array[], int i, int heapSize, bool min2max)
       {
            int lf = left(i);
            int rt = right(i);
    
            int largest;
            T tmp;
    
            //比较父结点和左孩子(并将较大者的索引记录下来)
            if(lf < heapSize && (min2max ? (array[lf] > array[i]) : (array[lf] < array[i]))){
                largest = lf;
            }else{
                largest = i;
            }
    
            //比较父结点和右孩子(并将较大者的索引记录下来)
            if(rt < heapSize && (min2max ? (array[rt] > array[largest]) :(array[rt] < array[largest]) )){
                largest = rt;
            }
    
            //找到父结点与左右孩子三个结点中的最大/小值
            if(largest != i){
                //将最大/小值与父结点交换
                tmp = array[i];
                array[i] = array[largest];
                array[largest] = tmp;
                //交换后导致左右孩子的数值发生变化,对有变化结点(如左孩子)的子树进一步做堆调整。
                HeapAdjust(array, largest, heapSize, min2max);
            }
       }
    
       //堆排序:创建最大堆
       template <typename T>
       static void BuildHeap(T array[], int len, bool min2max)
       {
           //根据结定的数据创建最大堆(不断进行堆调整)
           for(int i=(len-2)/2; i>=0; i--){ //注意:从后向前创建堆。
                                            //叶子结点无左右孩子,不必调整。所以
                                            //i=(len-2)/2开始,跳过所有叶子结点。
               HeapAdjust(array, i, len, min2max); //对以结点i的子树进行堆调整。
           }
       }
    
    public:
        //选择排序(O(n*n),不稳定)
        template <typename T>
        static void Select(T array[], int len, bool min2max = true)
        {
            for(int i=0; i<len-1; i++){
    
                //第i趟。有序区为[0,i-1],无序区为[i, len-1]
                //1.找到无序区的首元素: array[i]
    
                //2.从无序区中找到最小值
                int min = i; //假设:无序区中最小元素的为首元素
                for(int j=i+1; j<len; j++){
                    if (min2max ? (array[j] < array[min]) : (array[j] > array[min])){
                        min = j;
                    }
                }
    
                //3. 交换无序区中首元素和最小值的位置,将最小值放在无序区的首部
                if(i != min){
                    Swap(array[i], array[min]);
                }
            }
        }
    
        //插入排序(O(n*n), 稳定)
        template <typename T>
        static void Insert(T array[], int len, bool min2max = true)
        {
            //1. 初始状态有序区为[0],无序区为[1..n-1]
            for(int i=1; i<len; i++){ //注意,从1开始,因为要将无序区的每个元素插入到有序区中
    
                //有序区[0, i-1],无序区[i, n-1]
                //2. 取出无序区首元素,从有序区后面往前面比较依次与其比较
                T e = array[i]; //无序区首元素
                int k = i; //k记录着e最终应该放置的位置
                for(int j=i-1; (j>=0) && (min2max ? (e<array[j]) : (e>array[j]));j--){
                    //3. 边比较边移动元素,e最终的位置记录在k中
                    array[j+1] = array[j]; //将a[j]往后移一位
                    k = j;
                }
    
                //4. 将e放置下来
                if(k != i){
                    array[k] = e;
                }
            }
        }
    
        //冒泡排序: O(n*n), 稳定
        template <typename T>
        static void Bubble(T array[], int len, bool min2max = true)
        {
            bool exchange = true; //如果无序区元素己排好序,则exchange为false,
                                  //否则当发生元素交换时,说明仍是无序的
            //1. 初始状态:有序区[],无序区[0..n-1]
            for(int i=0; (i<len) && exchange; i++){
                exchange = false;
                //2. 从无序的区最后面开始,让小的元素向上冒
                for(int j=len-1; j>i; j--){
                    if(min2max ? (array[j]<array[j-1]) : (array[j]>array[j-1])){ //发生逆序,则交换
                        Swap(array[j], array[j-1]);
                        exchange = true;
                    }
                }
            }
        }
    
        //希尔排序
        template <typename T>
        static void Shell(T array[], int len, bool min2max = true)
        {
            int d = len; //增量
    
            do
            {
                d = d/3 + 1; //增量序列,要保证每次d增量是递减的,且最终为1
    
                //分成d个组(子序列),再对各个子序列进行插入排序:
                //1. 注意插入排序会将每个序列内各自的第1个元素视为有序区。各个子序列详情如下:
                //{R[0], R[0+d], R[0+2d],…,R[0+kd]} //第0个序列:有序区[0],无序区[0+d, 0+kd]
                //{R[1], R[1+d], R[1+2d],…,R[1+kd]} //第1个序列:有序区[1],无序区[1+d, 1+kd]
                //{R[2], R[2+d], R[2+2d],…,R[2+kd]} //第2个序列:有序区[2],无序区[2+d, 2+kd]
                //...
                //{R[d-1], R[(d-1)+d], ,…,R[(d-1)+kd]} //第d个序列:有序区[d-1],无序区[(d-1)+d, (d-1)+kd]
                //2. 以下是经过优化的代码,其思路是交替处理每个组,而不是处理完一组后再处理下一组。
    
                //i的意义:表示第(i % d)个子序列无序区首元素的位置。
                for(int i=d; i<len; i++){  //选择无序区中的第1个元素,第1个被选中的是第0个序列的无序区首元素array[d]
                    //(1)i每次自增,会取出第(i % d)个序列中无序区的首元素,然后对该组再进行组内插入排序。
                    //(注意,经过组内排序后该组的有序区扩大,因此交替处理分组后,当重新轮到该组时其无序区的缩小了,当然
                    //首元素位置也发生了变化)
    
                    //(2)由于i表示的是该组无序区首元素,所以i-d即有该组有序区的最后一个元素,从这元素开始向0方向依次比较
                    //该组内的各个元素并进行插入操作
                    int k = i;  //应插入的位置
                    T e = array[i]; //取出该组无序区首元素
                    for(int j=i-d; (j>=0) && (min2max ? (array[j] > e) : (array[j] < e)); j-=d){ //i为无序区首元素,i-d为有序区的最后一个元素
                       array[j+d] = array[j]; //元素后移
                       k = j;
                    }
    
                    if(k != i){
                        array[k] = e;
                    }
                }
    
            }while (d>1);
        }
    
        //归并排序
        template <typename T>
        static void Merge(T array[], int len, bool min2max = true)
        {
            //申请一片与待排数据大小一样的空间
            T* helper = new T[len];
    
            if(helper != NULL){
                MSort(array, helper, 0, len-1, min2max);
            }
    
            delete[] helper;
        }
    
        //快速排序
        template <typename T>
        static void Quick(T array[], int len, bool min2max = true)
        {
            Quick(array, 0, len-1, min2max);
        }
    
        //桶排序
        template <typename T>
        static void Bucket(T array[], int len, bool min2max = true)
        {
            //创建桶(Buckets)
            BNode<T>** buckets = new BNode<T>*[len];
    
            //初始化Buckets,同时找到数组中的最大/小值
            T min = array[0];
            T max = array[0];
            for(int i=0; i<len; i++){
                buckets[i] = NULL;
    
                if(array[i] > max)
                    max = array[i];
    
                if(array[i] < min)
                    min = array[i];
            }
    
            //将数据放入桶中
            for(int i=0; i<len; i++){
                int pos = getBucketIndex(array[i], min, max, len);
                if(0<=pos && pos <len){
                    BNode<T>* curr = new BNode<T>();
                    curr->data = array[i];
                    curr->next = NULL;
                    putIntoBucket(buckets[pos], curr, min2max); //放入相应的桶中并做插入排序
                }
            }
    
            //将桶中的数据放回原数组中去
            if(min2max){
                for(int i=0, j = 0; i<len; i++){
                    BNode<T>* node = buckets[i];
    
                    while(node){
                        array[j++] = node->data;
                        node = node->next;
                    }
                }
            }else{
                for(int i=len-1, j = 0; i>=0; i--){
                    BNode<T>* node = buckets[i];
    
                    while(node){
                        array[j++] = node->data;
                        node = node->next;
                    }
                }
            }
    
            //删除各个桶
            for(int i=0; i<len; i++){
                BNode<T>* node = buckets[i];
    
                while(node){
                    BNode<T>* tmp = node->next;
    
                    delete node;
    
                    node = tmp;
                }
            }
    
            delete[] buckets;
        }
    
        //堆排序
        template <typename T>
        static void Heap(T array[], int len, bool min2max = true)
        {
            BuildHeap(array, len, min2max);
            T tmp;
    
            for(int i=len-1; i>=0; i--){
                tmp = array[0];
                array[0] = array[i];
                array[i] = tmp;
    
                HeapAdjust(array, 0, i, min2max);
            }
        }
    
    };
    
    }
    
    #endif // SORT_H

    //main.cpp

    #include <iostream>
    #include "Sort.h"
    
    using namespace std;
    using namespace DTLib;
    
    int main()
    {
    
        //int array[]={49,38,65,97,76,13,49,27};
        int array[]={78,17,39,26,72,94,21,12,23,68};
        int len = sizeof(array)/sizeof(*array);
    
        Sort::Heap(array, len, false);//降序排序
    
        for(int i=0; i<len; i++){
            cout << array[i] << " ";
        }
        cout << endl;
    
        return 0;
    }
    /*测试结果
    94 78 72 68 39 26 23 21 17 12
    */
  • 相关阅读:
    HTML超链接标签—链接QQ在线聊天
    超链接标签-QQ邮箱链接经验分享
    数据类型转换的事项和注释
    关键字、标识符、常量、变量的(定义)
    WendosiDOS命令的一些使用命令
    Map集合
    Set集合 HashSet集合 LInkHathSet集合
    增强for循环
    22_迭代器
    包装类
  • 原文地址:https://www.cnblogs.com/5iedu/p/7748413.html
Copyright © 2011-2022 走看看