蹦点儿一下

导航

C++堆排序

---恢复内容开始---

本文通过C++实现了堆排序,通过构建最大堆对数组进行从小到大的排序,通过构建最小堆进行从大到小的排序

堆排序定义:

  是指利用这种数据结构所设计的一种排序算法。堆是一个近似完全二叉树的结构,并同时满足。

堆排序性质:

  即子结点的键值或索引总是小于(或者大于)它的父节点。

最大堆和最小堆:是二叉堆的两种形式

  最大堆:根结点的键值是所有堆结点键值中最大者,且每个结点的值都比其孩子的值大。

  最小堆:根结点的键值是所有堆结点键值中最小者,且每个结点的值都比其孩子的值小。

堆排序相关的具体理论请参照其他博文,下面给出具体代码和思想:

 

1. 构建最大堆:

  通过该函数对节点i构成的子树构建最大堆

// arr[]为输入的数组,n为数组长度,i为节点的序号
void
heapify(int arr[], int n, int i) { int largest = i; // Initialize largest as root int l = 2*i + 1; // left = 2*i + 1 int r = 2*i + 2; // right = 2*i + 2 // If left child is larger than root if (l < n && arr[l] > arr[largest]) largest = l; // If right child is larger than largest so far if (r < n && arr[r] > arr[largest]) largest = r; // If largest is not root if (largest != i) { std::swap(arr[i], arr[largest]); // Recursively heapify the affected sub-tree heapify(arr, n, largest); } }

例如,通过下面的测试测试程序可以实现最大堆的构建

#include <iostream>
void heapify(int arr[], int n, int i);
void printArray(int arr[], int n);
int main(int argc, char **argv) {
    const int n = 5;
    int a[n] = {4,10,3,5,1};
    std::cout << "Before: ";
    printArray(a,n);
    heapify(a,n,0);
    std::cout << " After: ";
    printArray(a,n);
    return 0;
}
void heapify(int arr[], int n, int i) 
{ 
    int largest = i; // Initialize largest as root 
    int l = 2*i + 1; // left = 2*i + 1 
    int r = 2*i + 2; // right = 2*i + 2 
  
    // If left child is larger than root 
    if (l < n && arr[l] > arr[largest]) 
        largest = l; 
  
    // If right child is larger than largest so far 
    if (r < n && arr[r] > arr[largest]) 
        largest = r; 
  
    // If largest is not root 
    if (largest != i) 
    { 
        std::swap(arr[i], arr[largest]); 
  
        // Recursively heapify the affected sub-tree 
        heapify(arr, n, largest); 
    } 
} 
void printArray(int arr[], int n)
{
    for(int i = 0; i < n; i++)
    {
        if(arr[i] != 0)
            std::cout << arr[i] << " ";
        else
            std::cout << "Null" << " ";
    }
    std::cout << std::endl;
}

输出:

Before: 4 10 3 5 1 
 After: 10 5 3 4 1

即实现了最大堆排列。还有一种可以减少计算次数的方法实现堆排列,思想是先对较小的子树进行堆排列,再对较大的子树进行堆排列,可以写为下列函数

void MaxHeap(int arr[], int n)
{
    for(int i = n / 2 - 1; i >= 0; i--)
    {
        heapify(arr,n,i);
    }
}

通过下面的测试测试程序同样可以实现最大堆的构建

#include <iostream>
void heapify(int arr[], int n, int i);
void MaxHeap(int arr[], int n);
void printArray(int arr[], int n);
int main(int argc, char **argv) {
    const int n = 5;
    int a[n] = {4,10,3,5,1};
    std::cout << "Before: ";
    printArray(a,n);
    MaxHeap(a,n);
    std::cout << " After: ";
    printArray(a,n);
    return 0;
}
void heapify(int arr[], int n, int i) 
{ 
    int largest = i; // Initialize largest as root 
    int l = 2*i + 1; // left = 2*i + 1 
    int r = 2*i + 2; // right = 2*i + 2 
  
    // If left child is larger than root 
    if (l < n && arr[l] > arr[largest]) 
        largest = l; 
  
    // If right child is larger than largest so far 
    if (r < n && arr[r] > arr[largest]) 
        largest = r; 
  
    // If largest is not root 
    if (largest != i) 
    { 
        std::swap(arr[i], arr[largest]); 
  
        // Recursively heapify the affected sub-tree 
        heapify(arr, n, largest); 
    } 
} 
void MaxHeap(int arr[], int n)
{
    for(int i = n / 2 - 1; i >= 0; i--)
    {
        heapify(arr,n,i);
    }
}
void printArray(int arr[], int n)
{
    for(int i = 0; i < n; i++)
    {
        if(arr[i] != 0)
            std::cout << arr[i] << " ";
        else
            std::cout << "Null" << " ";
    }
    std::cout << std::endl;
}

2. 堆排序(升序排列):

思想:1. 根据输入数据构建最大堆

     2. 此时,最大的项在最大堆的顶端,将最大项与堆的最后一项交换,将其从堆中删除作为已排列好的数

     3. 将堆的大小减一,再次构建最大堆,重复上一步的操作,即将每次构建最大堆后的根往后放,最终实现排序

通过下面的函数实现堆排序

void HeapSort(int arr[], int n)
{
    MaxHeap(arr,n);
    for(int i = n - 1; i >= 0; i--)
    {
        std::swap(arr[0],arr[i]);
        heapify(arr,i,0);
    }
}

测试代码:

#include <iostream>
void heapify(int arr[], int n, int i);
void MaxHeap(int arr[], int n);
void HeapSort(int arr[], int n);
void printArray(int arr[], int n);
int main(int argc, char **argv) {
    const int n = 5;
    int a[n] = {4,10,3,5,1};
    std::cout << "Before: ";
    printArray(a,n);
    HeapSort(a,n);
    std::cout << " After: ";
    printArray(a,n);
    return 0;
}
void heapify(int arr[], int n, int i) 
{ 
    int largest = i; // Initialize largest as root 
    int l = 2*i + 1; // left = 2*i + 1 
    int r = 2*i + 2; // right = 2*i + 2 
  
    // If left child is larger than root 
    if (l < n && arr[l] > arr[largest]) 
        largest = l; 
  
    // If right child is larger than largest so far 
    if (r < n && arr[r] > arr[largest]) 
        largest = r; 
  
    // If largest is not root 
    if (largest != i) 
    { 
        std::swap(arr[i], arr[largest]); 
  
        // Recursively heapify the affected sub-tree 
        heapify(arr, n, largest); 
    } 
} 
void MaxHeap(int arr[], int n)
{
    for(int i = n / 2 - 1; i >= 0; i--)
    {
        heapify(arr,n,i);
    }
}
void HeapSort(int arr[], int n)
{
    MaxHeap(arr,n);
    for(int i = n - 1; i >= 0; i--)
    {
        std::swap(arr[0],arr[i]);
        heapify(arr,i,0);
    }
}
void printArray(int arr[], int n)
{
    for(int i = 0; i < n; i++)
    {
        if(arr[i] != 0)
            std::cout << arr[i] << " ";
        else
            std::cout << "Null" << " ";
    }
    std::cout << std::endl;
}

输出:

Before: 4 10 3 5 1 
 After: 1 3 4 5 10

可见实现了对数组的升序排列

3. 构建最小堆

构建最小堆的思想和构建最大堆基本相同,程序上也没什么区别,只是条件判断上有点不同,下面看代码

void MinHeapify(int arr[], int n, int i)
{
    int minimum = i;
    int left  = 2 * i + 1;
    int right = 2 * i + 2;
    if (left < n && arr[left] < arr[minimum])
    {
        minimum = left;
    }
    if(right < n && arr[right] < arr[minimum])
    {
        minimum = right;
    }
    if(minimum != i)
    {
        std::swap(arr[minimum],arr[i]);
        MinHeapify(arr,n,minimum);
    }
}

4. 堆排序(降序排列)

程序思想和利用最大堆进行升序排列一样,这里我们只是用了最小堆就可以实现降序排列,程序如下

void HeapSort(int arr[], int n)
{
    MinHeapify(arr,n,0);
    for(int i = n - 1; i >= 0; i--)
    {
        std::swap(arr[0],arr[i]);
        MinHeapify(arr,i,0);
    }
}

5. 既能升序也能进行降序的堆排列

我们进一步完善代码,通过函数指针的方式,将构建最大堆和最小堆的函数作为参数传入排列函数,当传入构建最大堆的函数时,完成升序排列,当传入构建最小堆的函数时,完成降序排列,下面直接给出完整代码

#include <iostream>
void printArray(int arr[], int n);
void MaxHeapify(int arr[], int n, int i);
void MinHeapify(int arr[], int n, int i);
void HeapSort(int arr[], int n, void (*pf)(int [], int, int));
int main()
{
    const int n = 6;
    int a[n] = {9,79,46,30,58,49};
    int b[n] = {9,79,46,30,58,49};
    std::cout << "升序排列:\n";
    std::cout << "\tBefore: ";
    printArray(a,n);
    HeapSort(a,n,MaxHeapify);
    std::cout << "\t After: ";
    printArray(a,n);
    std::cout << "降序排列:\n";
    std::cout << "\tBefore: ";
    printArray(b,n);
    HeapSort(b,n,MinHeapify);
    std::cout << "\t After: ";
    printArray(b,n);
    return 0;
}
//函数功能: 构建最大堆
void MaxHeapify(int arr[], int n, int i)
{
    int largest = i;
    int left  = 2 * i + 1;
    int right = 2 * i + 2;
    if(left < n && arr[left] > arr[largest])
    {
        largest = left;
    }
    if(right < n && arr[right] > arr[largest])
    {
        largest = right;
    }
    if(largest != i)
    {
        std::swap(arr[largest],arr[i]);
        MaxHeapify(arr,n,largest);
    }
}
//函数功能: 构建最小堆
void MinHeapify(int arr[], int n, int i)
{
    int minimum = i;
    int left  = 2 * i + 1;
    int right = 2 * i + 2;
    if (left < n && arr[left] < arr[minimum])
    {
        minimum = left;
    }
    if(right < n && arr[right] < arr[minimum])
    {
        minimum = right;
    }
    if(minimum != i)
    {
        std::swap(arr[minimum],arr[i]);
        MinHeapify(arr,n,minimum);
    }
}
//函数功能: 堆排列函数,可完成升序排列(MaxHeapify)和降序排列(MinHeapify)
void HeapSort(int arr[], int n, void (*pf)(int [], int, int))
{
    (*pf)(arr,n,0);
    for(int i = n - 1; i >= 0; i--)
    {
        std::swap(arr[0],arr[i]);
        (*pf)(arr,i,0);
    }
}
//函数功能: 打印数组
void printArray(int arr[], int n)
{
    for(int i = 0; i < n; i++)
    {
        if(arr[i] != 0)
            std::cout << arr[i] << " ";
        else
            std::cout << "Null" << " ";
    }
    std::cout << std::endl;
}

输出:

升序排列:
    Before: 9 79 46 30 58 49 
     After: 9 30 46 49 58 79 
降序排列:
    Before: 9 79 46 30 58 49 
     After: 79 58 30 49 46 9 

 

---恢复内容结束---

posted on 2019-03-30 14:15  蹦点儿一下  阅读(1270)  评论(0编辑  收藏  举报