《数据结构与算法分析(c 描述)》—— 第六章笔记

一、优先队列

优先队列是允许至少两种操作的数据结构:Insert(插入),DeleteMin(删除最小者)

简单的实现方式是使用二叉堆。

堆是一种非常实用的数据结构,其中以二叉堆最为常用。二叉堆可以看作一棵完全二叉树,每个节点的键值都大于(小于)其子节点,但左右孩子之间不需要有序。我们关心的通常只有堆顶的元素,而整个堆则被封装起来,保存在一个数组中。

下面分别介绍优先队列基本操作的实现

二、入队 (push)

基本实现步骤如下:

  • 在下一个空闲处建立一个空穴
  • 若空穴的父节点大于空穴的值,则将空穴向上行一步

流程图如下:

《数据结构与算法分析(c 描述)》—— 第六章笔记_第1张图片

c 代码实现:

void Insert(int x, int *array, int num)
{
    int i;
    // i 表示空穴所在位置, i/2 表示空穴的父节点所在位置
    // array[0] = 0 作为哨兵防止溢出
    for (i = ++num; array[i/2] > x; i /= 2)
        array[i] = array[i/2];
    array[i] = x;
}

注意: 始终保持队列第一个元素为 0 是为了防止空穴上滤过程溢出

二、出队(pop)

需要考虑的是,由于堆现在少了一个元素,因此堆中最后一个元素 x 必须移动到该堆的某个地方。

基本实现步骤如下:

  • 根处建立一个空穴(默认将最后一个元素放入空穴)
  • 如果有比自己小的儿子,则将空穴的较小的儿子移入空穴
  • 否则将最后一个元素放入空穴

流程图如下:

《数据结构与算法分析(c 描述)》—— 第六章笔记_第2张图片

c 代码实现:

void DeleteMin(int *array, int *size)
{   
    int num = *size;
    int lastVal = array[num--];
    int child;
    for (int i = 1; 2 * i < num; i *= 2)
    {
        child = 2 * i;
        if (child != num && array[child + 1] < array[child])
            child++;
        if (lastVal > array[child])
            array[i] = array[child];
        else 
            break;
    }
    array[i] = lastVal;
}

三、建堆(buildHeap)

将任意数组调整成堆的过程如下

void buildHeap(int *array, int num)
{
    for (int i = num/2; i > 0; i--)
        minHeapfiy(i);
}

算法运行时间的界为 O(N) ,即虚线条数的界,可以通过计算堆中所有节点的高度的和来得到

流程图如下:

调整保持堆序结构

当遇到以节点i的左右儿子为根的两棵二叉树都是最大堆,而以节点i为根的二叉树可能不是最大堆情况的时候

我们需要一个辅助函数 minHeapfiy(int i) 来调整,过程如下

  • 从元素 arr[i], arr[left(i)], arr[right(i)] 中找出最小的元素,将下标存在 small 中;
  • 如果 arr[i] 是最小的,说明以节点 i 为根的二叉树是最小堆,无须调整,程序结束;
  • 否则将最小元交换到节点 i ,然后递归调用 minHeapfiy(int small)

c 代码实现:

void minHeapfiy(int *array, int num, int i)
{
    int l = i * 2;
    int r = l + 1; 
    int small = i;
    int temp;
    if (l < num && array[i] < array[small])
        small = l;
    if (r < num && array[i] < array[small])
        small =r;
    if (small != i)
    {
        temp = array[small];
        array[small] = array[i];
        array[i] = temp;
        minHeapfiy(array, num, small);
    }
}

四、所有优先队列的 c++ 实现以及简单测试

#include<iostream>
#include<vector>
using namespace std;

template <class T>
class PriorityQueue {
    public:
        // deleteMin
        void pop() {
            T val = m_data[m_data.size() - 1];
            int i;
            for (i  = 1; i * 2 < m_data.size(); i *= 2) {
                int child = i * 2;
                // 将空穴儿子的较小者移入空穴
                if (child != m_data.size() && m_data[child] > m_data[child + 1])
                    child++;
                if (val > m_data[child])
                    m_data[i] = m_data[child];
                else 
                    break;
            }
            m_data[i] = val;
            m_data.pop_back();
        }

        // insert
        void push(const T &val) {
            m_data.push_back(val);
            int i = m_data.size() - 1;

            // m_data[0] == 0 作为哨兵防止溢出
            while (m_data[i/2] > val) {
                m_data[i] = m_data[i/2];
                i = i/2;
            }
            m_data[i] = val;
        }

        void buildHeap(const int *array, int num) {
            m_data.push_back(0);
            size_t i;
            for (i = 1; i <= num; i++)
                m_data.push_back(array[i - 1]);

            cout << "befort : ";
            for (i = 0; i < m_data.size(); i++)
                cout << m_data[i] << ' ';
            cout << endl;

            for (i = m_data.size()/2; i > 0; i--)
                minHeapfiy(i);

            cout << "after : ";
            for (i = 0; i < m_data.size(); i++)
                cout << m_data[i] << ' ';
            cout << endl;
        }

        void print() {
            size_t i;
            cout << "m_data : ";
            for (i = 0; i < m_data.size(); i++)
                cout << m_data[i] << ' ';
            cout << endl;
        }

    private:
        vector<T> m_data;
        // 假设节点 i 的左右子树都为最小堆
        void minHeapfiy(int i) {
            int size = m_data.size();
            int l = i * 2;
            int r = l + 1;
            int small = i;
            if (l < size && m_data[l] < m_data[small])
                small = l;
            if (r < size && m_data[r] < m_data[small])
                small = r;
            if (small != i) {
                T temp = m_data[small];
                m_data[small] = m_data[i];
                m_data[i] = temp;
                minHeapfiy(small);
            }
        }

};


int main() {
    int array[15] = {150,80,40,30,10,70,110,100,20,90,60,50,120,140,130};
    PriorityQueue<int> pq;
    pq.buildHeap(array, 15);
    pq.pop();
    pq.print();
    pq.push(10);
    pq.print();
    return 0;
}

测试结果如下所示:

《数据结构与算法分析(c 描述)》—— 第六章笔记_第3张图片

你可能感兴趣的:(数据结构,优先队列,二叉堆)