C++中哈夫曼树的定义、构造及其遍历
哈夫曼树是一种特殊的二叉树,它的叶子节点代表一个字符或符号,并且每个叶子节点都有一个权值。哈夫曼树的构造基于贪心算法,它的目标是通过选择最小的两个节点来合并节点,直到最终形成一个根节点的二叉树,同时保证该二叉树的带权路径长度最小。
构造哈夫曼树的具体步骤如下:
首先将所有节点按照权值从小到大排序,并构造一颗只包含所有叶子节点的二叉树。
选择权值最小的两个节点作为左右子节点构造一个新的二叉树,将它的根节点权值设为左右子节点权值之和。
重复上述过程,直到所有节点都被合并到一颗哈夫曼树中。
哈夫曼树的遍历方式和普通二叉树类似,主要有前序遍历、中序遍历和后序遍历。具体的实现方式也与普通二叉树类似,只不过需要注意的是,由于哈夫曼树不一定是平衡二叉树,因此它的遍历时间复杂度可能不是 O(n),而可能是 O(nlogn)。
下面是C++中哈夫曼树的构造和遍历代码示例:
#include <iostream> #include <queue> #include <vector> using namespace std; // 哈夫曼树节点 struct HuffmanNode { int weight; // 权值 int parent, left, right; // 父节点、左子节点、右子节点 HuffmanNode() : weight(0), parent(-1), left(-1), right(-1) {} HuffmanNode(int w) : weight(w), parent(-1), left(-1), right(-1) {} }; // 构造哈夫曼树 void buildHuffmanTree(vector<int> weights, vector<HuffmanNode>& nodes) { priority_queue<int, vector<int>, greater<int>> pq; // 小根堆 // 初始化叶子节点 int n = weights.size(); for (int i = 0; i < n; i++) { nodes[i] = HuffmanNode(weights[i]); pq.push(i); } // 构造哈夫曼树 while (pq.size() > 1) { int i = pq.top(); pq.pop(); int j = pq.top(); pq.pop(); nodes[i].parent = nodes[j].parent = pq.size(); nodes[pq.size()] = HuffmanNode(nodes[i].weight + nodes[j].weight); nodes[pq.size()].left = i; nodes[pq.size()].right = j; pq.push(pq.size()); } } // 前序遍历哈夫曼树
哈夫曼树遍历
哈夫曼树的遍历同样可以使用递归的方式来实现,遍历的顺序与二叉树一样有前序、中序和后序遍历。
前序遍历
前序遍历的步骤与二叉树的前序遍历类似,先访问根节点,再遍历左子树和右子树。
void preOrder(TreeNode* root) { if (root == nullptr) { return; } cout << root->val << " "; preOrder(root->left); preOrder(root->right); }
中序遍历
中序遍历同样与二叉树的中序遍历类似,先遍历左子树,再访问根节点,最后遍历右子树。
void inOrder(TreeNode* root) { if (root == nullptr) { return; } inOrder(root->left); cout << root->val << " "; inOrder(root->right); }
后序遍历
后序遍历同样与二叉树的后序遍历类似,先遍历左子树,再遍历右子树,最后访问根节点。
void postOrder(TreeNode* root) { if (root == nullptr) { return; } postOrder(root->left); postOrder(root->right); cout << root->val << " "; }
需要注意的是,由于哈夫曼树是一棵完全二叉树,因此可以通过数组来表示。此时遍历的顺序就需要变换一下,具体来说,可以使用堆的性质进行遍历,具体实现可以参考以下代码。
void heapOrder(vector<TreeNode*>& tree) { if (tree.empty()) { return; } queue<TreeNode*> q; q.push(tree[0]); while (!q.empty()) { int n = q.size(); for (int i = 0; i < n; ++i) { TreeNode* node = q.front(); q.pop(); cout << node->val << " "; if (node->left != nullptr) { q.push(node->left); } if (node->right != nullptr) { q.push(node->right); } } } }
其中,tree
表示哈夫曼树,第一个节点为根节点,之后按照从上到下、从左到右的顺序存储其他节点。该函数中使用了一个队列 q
来实现遍历,每次取出队列中的第一个节点,输出该节点的值,然后将其左右孩子加入队列中。由于哈夫曼树是一棵完全二叉树,因此堆的性质也被满足,可以使用堆的遍历方式来实现哈夫曼树的遍历。
哈夫曼树是一种特殊的二叉树,它的构造方法是将权值最小的节点两两合并,形成一个新的节点,新节点的权值为两个被合并节点的权值之和。重复这个过程,直到所有节点都被合并成一个节点,这个节点就是哈夫曼树的根节点。
哈夫曼树的形态可以用类似下面的图形来表示:
[50] / \\ / \\ / \\ [20] [30] / \\ / \\ / \\ / \\ [10] [10] [15] [15] / \\ / \\ / \\ / \\ [5] [5] [5] [5] [7] [8] [7] [8]
在这个例子中,数字表示节点的权值,方括号中的数字表示这个节点所代表的子树中所有节点的权值之和。最后合并成的根节点[50]表示整棵哈夫曼树的权值和。

- 上一篇:C++中完全二叉树的数组表示法
- 下一篇:C++中二叉排序树的定义、构造及其遍历