##0、什么是树?(了解)
树:树(tree)是n(n >= 0)个节点的有限集,当n=0时为空树。在任意一个非空树中满足如下特点:
1、有且仅有一个特定的称为根的节点(通俗讲,从最初的节点引出到其他节点的节点为根节点)
2、当n>1时,其余节点可分为m(m>0)个互不相交的有限集,每个集合本身也是一个树,并称为根的子树。
树的例图如下:
孩子节点:从当前节点引出来的节点(当前节点的后继节点)。
父节点:被引出来节点的节点(后继节点的前一个节点)。
叶子节点:没有“孩子”的节点(没有后继节点)
树的高度/深度:树的最大层级。(例图中的树高度为3)
n叉树:每个节点最多只能有n个孩子的树。
二叉树(binary tree):树的每个节点最多有2个节点。(注意:树节点可以没有孩子节点,可以有一个孩子节点)
左孩子(left child): 二叉树一个节点左边的孩子。
右孩子(right child): 二叉树一个节点右边的孩子。
满二叉树:一个二叉树的所有非叶子节点都存在左右孩子,并且所有的叶子节点都在同一级上。
如下图高度为3的满二叉树:
完全二叉树:对一个有n个节点的二叉树,按层级顺序编号,则所有节点的编号为从1到n。如果这个树所有的节点和同深度的满二叉树的编号为从1到n的节点位置相同,则这个二叉树为完全二叉树。
如下图高度为3的完全二叉树:
注意:满二叉树要求所有分支都是满的,完全二叉树只需保证最后一个节点之前的节点都齐全。
数组存储:使用数组存储时,会按照层级顺序把二叉树的节点放到数组中的对应位置,如果某一个节点的左孩子或右孩子空缺,则数组的相应位置也要空出来。
定位二叉树的父节点和孩子节点:
1、(假设父节点下标为parent)则:
左孩子下标:leftChild = 2*parent +1;
右孩子下标:rightChild = 2*parent+2;
2、(假设左孩子下标为leftChild)则:
父节点下标:parent = (leftChild - 1)/2;
3、(假设右孩子下标为rightChild)则:
父节点下标:parent = (rightChild - 2)/2;
数组存储如下:
1、二叉树形式:
2、数组存储形式:
0 | 1 | 2 | 3 | 4 | 6 | 7 |
---|
数组存储:我们从上述的数组中不难看出,在树不满足完全二叉树的形式时,会浪费掉许多空间。在实际开发中,我们很难保证我们所创建的二叉树都是完全二叉树。故多以链表的方式建立、存储。
链表存储:一个节点最多可以指向左右两个孩子节点,故二叉树的每一个节点包含三部分:
故结构体形式为:
typedef struct treeNode {
char data;
struct treeNode* leftChild;
struct treeNode* rightChild;
}binaryTreeNode, * binaryTreePoint;
创建步骤:(递归创建)
//主要代码:
binaryTreePoint create_binary_tree() {
char ch;
binaryTreePoint treeNode;
cin >> ch;
if (ch == '#') { //表示子节点为空,停止递归调用并返回空,
return NULL;
}
else {
treeNode = (binaryTreePoint)malloc(sizeof(binaryTreeNode));
treeNode->data = ch;
cout << "请输入" << ch << "左孩子节点(输入数据(A~Z), 无则输入#) :";
treeNode->leftChild = create_binary_tree();
cout << "请输入" << ch << "右孩子节点(输入数据(A~Z), 无则输入#) :";
treeNode->rightChild = create_binary_tree();
return treeNode;
}
}
下图为创建一颗二叉树的图,其中序号表示递归创建的顺序:
a、从节点位置关系的角度考虑,二叉树遍历分为以下4种:
b、从更宏观的角度,二叉树的遍历分为以下2种:
前序遍历的输出顺序为:根节点、左节点、右节点(常称为:中左右)
下图为创建一颗二叉树的图,其中序号表示前序遍历输出顺序:
详细步骤如下:
//主要代码:
void preorder_traversal(binaryTreePoint treeNode) {
if (treeNode == NULL) {
return;
}
else {
cout << treeNode->data;
preorder_traversal(treeNode->leftChild);
preorder_traversal(treeNode->rightChild);
}
}
中序遍历的输出顺序为:左节点、根节点、右节点(常称为:左中右)
下图为创建一颗二叉树的图,其中序号表示中序遍历输出顺序:
详细步骤如下:
//主要代码:
void inorder_traversal(binaryTreePoint treeNode) {
if (treeNode == NULL) {
return;
}
else {
inorder_traversal(treeNode->leftChild);
cout << treeNode->data;
inorder_traversal(treeNode->rightChild);
}
}
后序遍历的输出顺序为:左节点、右节点、根节点(常称为:左右中)
下图为创建一颗二叉树的图,其中序号表示后序遍历输出顺序:
后序遍历与前、中序遍历几乎一样,在此不再叙述。
//主要代码:
void postorder_traversal(binaryTreePoint treeNode) {
if (treeNode == NULL) {
return;
}
else {
postorder_traversal(treeNode->leftChild);
postorder_traversal(treeNode->rightChild);
cout << treeNode->data;
}
}
前序遍历的输出顺序为:根节点、左节点、右节点(常称为:中左右)
前面介绍了递归实现的方法,其实递归操作和栈存储操作再一定程度上是相同的,下面以一个例子说明。
下图为创建一颗二叉树的图,其中序号表示前序遍历输出顺序:
具体实现步骤:
首先遍历根节点1,输出根节点1,并压栈;
1 |
---|
遍历根节点1的左孩子节点2,输出节点2,并压栈;
1 | 2 |
---|
遍历节点2的左孩子节点3,输出节点3,并压栈;
1 | 2 | 3 |
---|
节点3既没有左孩子节点,也没有右孩子节点,故此时让栈顶元素3弹栈,再访问栈顶元素2,得到节点2的右孩子节点4;
1 | 2 |
---|
节点2的左、右孩子已近访问过,故节点2弹栈,遍历输出节点4,节点4压栈;
1 | 4 |
---|
节点4既没有左孩子节点,也没有右孩子节点,故此时让栈顶元素4弹栈,再访问栈顶元素1,得到节点1的右孩子节点5;
1 |
---|
节点1的左、右孩子已近访问过,故节点1弹栈,遍历输出节点5,节点5压栈;
5 |
---|
节点5没有左孩子,故访问节点5的右孩子节点6,此时节点5弹栈,节点6压栈;
6 |
---|
节点6既没有左孩子节点,也没有右孩子节点,故此时栈空,遍历完成。
其中运用了STL栈操作,详情请看栈与队列基础操作(含STL)从函数具体实现到STL运用
//主要代码:
void preorder_stack_traversal(binaryTreePoint treeRootNode) {
stack<binaryTreePoint> s; //栈元素类型为树节点指针
binaryTreePoint treeNode = treeRootNode;
while (treeNode != NULL || !s.empty()) {
while (treeNode != NULL) { //访问节点的左孩子,并进栈
cout << treeNode->data;
s.push(treeNode);
treeNode = treeNode->leftChild;
}
if (!s.empty()) { //如果节点没有左孩子,则弹栈顶定节点,访问节点右孩子
treeNode = s.top();
s.pop();
treeNode = treeNode->rightChild;
}
}
}
层序遍历:二叉树按照从根节点到叶节点的层序关系,一层一层地横向遍历各个节点。
以下层序遍历借助队列进行操作。
下图为创建一颗二叉树的图,其中序号表示层序遍历输出顺序:
具体实现步骤:
根节点1入队;
1 |
---|
节点1出队,输出节点1,并得到节点1的左孩子节点2、右孩子节点3,让节点2、3依次入队;
2 | 3 |
---|
节点2出队,输出节点2,得到节点2的左孩子节点4、右孩子节点5,让节点4、5依次入队;
3 | 4 | 5 |
---|
节点3出队,输出节点3,得到节点3的右孩子节点6,让节点6入队;
4 | 5 | 6 |
---|
节点4出队,输出节点4,节点4没有左右孩子节点,故没有新节点入队;
5 | 6 |
---|
节点5出队,输出节点5,节点5没有左右孩子节点,故没有新节点入队;
6 |
---|
节点6出队,输出节点6,节点6没有左右孩子节点,故没有新节点入队;
由于此时队列为空,故所有节点遍历完成。
其中运用了STL队列操作,详情请看栈与队列基础操作(含STL)从函数具体实现到STL运用
//主要代码
void sequence_traversal(binaryTreePoint treeRootNode) {
queue<binaryTreePoint> s; 队列元素类型为树节点指针
s.push(treeRootNode);
while (!s.empty()) {
binaryTreePoint treeNode = s.front();
s.pop();
cout << treeNode->data;
if (treeNode->leftChild != NULL) {
s.push(treeNode->leftChild);
}
if (treeNode->rightChild != NULL) {
s.push(treeNode->rightChild);
}
}
}
代码编译器:VS2019
//主要代码 建立BinaryTree类进行封装 BinaryTree.h
#pragma once
#include
#include
#include
using namespace std;
typedef struct treeNode {
char data;
struct treeNode* leftChild;
struct treeNode* rightChild;
}binaryTreeNode, * binaryTreePoint;
class BinaryTree{
public:
binaryTreePoint create_binary_tree();
void preorder_traversal(binaryTreePoint treeNode);
void preorder_stack_traversal(binaryTreePoint treeRootNode);
void inorder_traversal(binaryTreePoint treeRNode);
void postorder_traversal(binaryTreePoint treeNode);
void sequence_traversal(binaryTreePoint treeRootNode);
};
//主要代码 BinaryTree.cpp
#include "BinaryTree.h"
binaryTreePoint BinaryTree::create_binary_tree() {
char ch;
binaryTreePoint treeNode;
cin >> ch;
if (ch == '#') { //表示子节点为空,停止递归调用并返回空,
return NULL;
}
else {
treeNode = (binaryTreePoint)malloc(sizeof(binaryTreeNode));
treeNode->data = ch;
cout << "请输入" << ch << "左孩子节点(输入数据(A~Z), 无则输入#) :";
treeNode->leftChild = create_binary_tree();
cout << "请输入" << ch << "右孩子节点(输入数据(A~Z), 无则输入#) :";
treeNode->rightChild = create_binary_tree();
return treeNode;
}
}
void BinaryTree::preorder_traversal(binaryTreePoint treeNode) {
if (treeNode == NULL) {
return;
}
else {
cout << treeNode->data;
preorder_traversal(treeNode->leftChild);
preorder_traversal(treeNode->rightChild);
}
}
void BinaryTree::inorder_traversal(binaryTreePoint treeNode) {
if (treeNode == NULL) {
return;
}
else {
inorder_traversal(treeNode->leftChild);
cout << treeNode->data;
inorder_traversal(treeNode->rightChild);
}
}
void BinaryTree::postorder_traversal(binaryTreePoint treeNode) {
if (treeNode == NULL) {
return;
}
else {
postorder_traversal(treeNode->leftChild);
postorder_traversal(treeNode->rightChild);
cout << treeNode->data;
}
}
void BinaryTree::preorder_stack_traversal(binaryTreePoint treeRootNode) {
stack<binaryTreePoint> s; 栈元素类型为树节点指针
binaryTreePoint treeNode = treeRootNode;
while (treeNode != NULL || !s.empty()) {
while (treeNode != NULL) { //访问节点的左孩子,并进栈
cout << treeNode->data;
s.push(treeNode);
treeNode = treeNode->leftChild;
}
if (!s.empty()) { //如果节点没有左孩子,则弹栈顶定节点,访问节点右孩子
treeNode = s.top();
s.pop();
treeNode = treeNode->rightChild;
}
}
}
void BinaryTree::sequence_traversal(binaryTreePoint treeRootNode) {
queue<binaryTreePoint> s; 队列元素类型为树节点指针
s.push(treeRootNode);
while (!s.empty()) {
binaryTreePoint treeNode = s.front();
s.pop();
cout << treeNode->data;
if (treeNode->leftChild != NULL) {
s.push(treeNode->leftChild);
}
if (treeNode->rightChild != NULL) {
s.push(treeNode->rightChild);
}
}
}
//示例代码运行 main.cpp
#include
#include "BinaryTree.h"
using namespace std;
int main() {
binaryTreePoint root;
BinaryTree binaryTree;
cout << "请输入头节点(A~Z):";
root = binaryTree.create_binary_tree(); //建立二叉树
cout << "二叉树前序遍历:";
binaryTree.preorder_traversal(root); //二叉树前序遍历
cout << endl;
cout << "非递归前序遍历:";
binaryTree.preorder_stack_traversal(root); //非递归前序遍历
cout << endl;
cout << "二叉树中序遍历:";
binaryTree.inorder_traversal(root); //二叉树中序遍历
cout << endl;
cout << "二叉树后续遍历:";
binaryTree.postorder_traversal(root); //二叉树后续遍历
cout << endl;
cout << "二叉树层序遍历:";
binaryTree.sequence_traversal(root); //二叉树层序遍历
cout << endl;
return 0;
}
示例截图:
二叉堆:二叉堆本质上是一种完全二叉树(由于是完全二叉树,利用数组存储的效率最大,事实上也是利用数组存储)。
二叉堆的分类:大根堆、小根堆。
大根堆:大根堆的任何一个父节点的值都大于或等于它左、右孩子节点的值。
下图为一个大根堆的示例:
小根堆:小根堆的任何一个父节点的值都小于或等于它左、右孩子节点的值。
下图为一个小根堆的示例:
堆顶:二叉堆的根节点。
大根堆的特点:大根堆的堆顶是整个堆中的最大元素。
小根堆的特点:小根堆的堆顶是整个堆中的最小元素。
以创建小根堆为例,进行讲解。
构建小根堆:就是把一个无序的的完全二叉树调整为二叉堆,本质上就是让所有非叶子节点依次下沉。(如果是大根堆则是让所有非叶子节点依次上浮)
下图为一个无序二叉树:
示例步骤:
首先,从最后一个非叶子节点开始,也就是从节点10开始。如果节点10大于它左、右孩子节点中的最小一个;则节点10”下沉“,与6进行交换;
然后是节点3,如果节点3大于它左、右孩子节点中最小的一个,则节点3”下沉“,与2进行交换;
然后是节点1,如果节点1大于它左、右孩子节点中最小的一个,则节点1”下沉“,但节点1小于左、右孩子节点,故不用改变;
然后是节点7,如果节点7大于它左、右孩子节点中最小的一个,则节点7”下沉“,与1进行交换;
节点7继续比较,继续下沉,与5进行交换;
经过比较后,最终每个节点都小于它的左、右孩子节点,小根堆创建完成。
//主要代码: 利用模板接受参数,使接受的参数根据有多样性。
template<class iterator>
void build_heap(iterator start, iterator end) {
int arrayLength = end - start;
for (int i = (arrayLength - 2) / 2; i >= 0; i--) { //从最后一个非叶子节点开始,依次下沉调整
down_adjustment(start, end, i, arrayLength);
}
}
template <class iterator>
void down_adjustment(iterator start, iterator end, int parentIndex, int length) {
int temp = *(start + parentIndex); //temp保存父节点值,用于最后的赋值
int childIndex = 2 * parentIndex + 1;
while (childIndex < length) {
if (childIndex + 1 < length && *(start + childIndex + 1) < *(start + childIndex)) {
childIndex++;
}
if (temp <= *(start + childIndex))
break;
*(start + parentIndex) = *(start + childIndex); //*解引用,取值
parentIndex = childIndex;
childIndex = 2 * childIndex + 1;
}
*(start + parentIndex) = temp;
}
插入节点的位置:当二叉堆插入节点时,插入的位置时完全二叉树的最后一个位置。
下图以一个小根堆为例。(其中0为插入到末尾的元素,为节点5的左孩子)
示例步骤:
新插入的节点0比父节点5小,于是让新节点上浮,和父节点5交换;
继续用节点0与父节点3作比较,因为节点3大于节点0,则让新节点继续上浮,和父节点3交换;
继续比较,最终新节点0上浮到堆顶位置.
//主要代码: 利用模板接受参数,使接受的参数根据有多样性。
//插入到数组末尾的函数就省略,本质上就是再数组后再添加一个元素,模板函数为上浮元素
template <class iterator>
void up_adjustment(iterator start, iterator end) {
int arrayLength = end - start;
int childIndex = arrayLength - 1;
int parentIndex = (childIndex - 1) / 2;
int temp = *(start + childIndex);
while (childIndex > 0 && temp < *(start + parentIndex)) {
*(start + childIndex) = *(start + parentIndex);
childIndex = parentIndex;
parentIndex = (parentIndex - 1) / 2;
}
*(start + childIndex) = temp;
}
删除元素:二叉堆删除节点的过程和插入节点的过程正好相反,所删除的元素为堆顶元素。(实际中删除元素并非真的删除,只是将其位置进行调整,并不再访问该位置)
下图以一个小根堆为例。删除节点1;
示例步骤:
删除节点1,把堆最后一个节点10临时补到原来的堆顶位置;
接下来,让处于堆顶的节点10与左、右孩子节点进行比较,如果左、右孩子节点中的最小一个比节点10小,那么让节点10下沉,故与节点2进行交换;
继续让处于节点10与左、右孩子节点进行比较,如果左、右孩子节点中的最小一个比节点10小,那么让节点10下沉,故与节点7进行交换;
最终,二叉堆调整完成。
//主要代码:删除堆顶节点1,在此函数中,只是将堆顶节点1放在数组最后一个,并不再访问。
template<class iterator>
void delete_top(iterator start, iterator end) {
swap(*start, *(end - 1));
int arrayLength = end - start;
auto temp = *(start); //auto自动匹配数据类型
int parentIndex = 0;
int childIndex = parentIndex * 2 + 1;
while (childIndex < arrayLength - 1) {
if (childIndex + 1 < arrayLength - 1 && *(start + childIndex + 1) < *(start + childIndex)) {
childIndex++;
}
*(start + parentIndex) = *(start + childIndex);
parentIndex = childIndex;
childIndex = 2 * childIndex + 1;
}
*(start + parentIndex) = temp;
}
代码编译器:VS2019
/*主要代码 建立类进行封装 Heap.h 注意:写类进行封装时,不能把类写在.cpp中,模板是不能拆分的,在.h中声明,就要在.h中定义,否则会报错,在不同的编译器中,报错可能不同*/
#pragma once
#include
using namespace std;
class Heap{
public:
template<class iterator>
void build_heap(iterator start, iterator end) {
int arrayLength = end - start;
for (int i = (arrayLength - 2) / 2; i >= 0; i--) { //从最后一个非叶子节点开始,依次下沉调整
down_adjustment(start, end, i, arrayLength);
}
}
template <class iterator>
void up_adjustment(iterator start, iterator end) {
int arrayLength = end - start;
int childIndex = arrayLength - 1;
int parentIndex = (childIndex - 1) / 2;
auto temp = *(start + childIndex); //auto自动匹配数据类型
while (childIndex > 0 && temp < *(start + parentIndex)) {
*(start + childIndex) = *(start + parentIndex);
childIndex = parentIndex;
parentIndex = (parentIndex - 1) / 2;
}
*(start + childIndex) = temp;
}
template <class iterator>
void down_adjustment(iterator start, iterator end, int parentIndex, int length) {
int temp = *(start + parentIndex); //temp保存父节点值,用于最后的赋值
int childIndex = 2 * parentIndex + 1;
while (childIndex < length) {
if (childIndex + 1 < length && *(start + childIndex + 1) < *(start + childIndex)) {
childIndex++;
}
if (temp <= *(start + childIndex))
break;
*(start + parentIndex) = *(start + childIndex);
parentIndex = childIndex;
childIndex = 2 * childIndex + 1;
}
*(start + parentIndex) = temp;
}
template<class iterator>
void display(iterator start, iterator end) {
for (auto it = start; it < end; it++) {
cout << *(it) << " ";
}
}
template<class iterator>
void delete_top(iterator start, iterator end) {
swap(*start, *(end - 1));
int arrayLength = end - start;
auto temp = *(start);
int parentIndex = 0;
int childIndex = parentIndex * 2 + 1;
while (childIndex < arrayLength - 1) {
if (childIndex + 1 < arrayLength - 1 && *(start + childIndex + 1) < *(start + childIndex)) {
childIndex++;
}
*(start + parentIndex) = *(start + childIndex);
parentIndex = childIndex;
childIndex = 2 * childIndex + 1;
}
*(start + parentIndex) = temp;
}
};
//示例代码运行 main.cpp
#include
#include "Heap.h"
using namespace std;
int main() {
int a[] = { 7,1,2,6,8,5,4,4,6};
Heap heap;
heap.build_heap(a, a + 9);
heap.display(a,a + 9);
cout << endl;
int b[] = { 1,3,2,6,5,7,8,9,10,0 };
heap.up_adjustment(b, b + 10);
heap.display(b, b + 10);
cout << endl;
int c[] = { 1,3,2,6,5,7,8,9,10};
heap.delete_top(c, c + 9);
heap.display(c, c + 9);
cout << endl;
return 0;
}
优先队列:优先队列不再遵循先进先出的原则,而是分为一下两种:
最大优先队列:无论入队顺序如何,都是当前最大的元素优先出队;
最小优先队列:无论入队顺序如何,都是当前最小的元素优先出队;
利用线性结构实现优先队列,时间复杂度较高,因此使用二叉堆实现。
以最大优先为例
入队操作:可以利用大根堆实现最大优先队列,每一次入队就是堆的插入操作(小根堆实现最小优先队列)。
下图以最大优先插入为例,插入5;
示例步骤:
插入新节点5;
新节点5上浮到合适的位置,到此入队操作结束。(提示:具体上浮操作请看前面的二叉堆)
//主要代码:
void push(int valua) {
if (priority_queue.index >= priority_queue.arrayLength) {
cout << "priority_queue full!" << endl;
return;
}
priority_queue.array[++priority_queue.index] = valua;
up_adjustment();
}
void up_adjustment() {
int childIndex = priority_queue.index;
int parentIndex = (childIndex - 1) / 2;
int temp = priority_queue.array[childIndex];
while (childIndex > 0 && temp > priority_queue.array[parentIndex]) {
priority_queue.array[childIndex] = priority_queue.array[parentIndex];
childIndex = parentIndex;
parentIndex = parentIndex / 2;
}
priority_queue.array[childIndex] = temp;
}
以最大优先为例
出队操作:可以利用大根堆实现最大优先队列,每一次出队就是堆的删除操作(小根堆实现最小优先队列)。
下图以最大优先删除为例,删除节点10;
示例步骤:
让节点10出队,把最后一个节点1替换到堆顶位置。
然后让节点1下沉,节点9成为新的堆顶节点。(提示:具体下沉操作请看前面的二叉堆)
//主要代码:
int pop() {
if (priority_queue.index < 0) {
cout << "priority_queue empty!" << endl;
system("pause");
return 0;
}
int top = priority_queue.array[0];
priority_queue.array[0] = priority_queue.array[priority_queue.index--]; //让最后一个元素移动到堆顶
down_adjustment();
return top;
}
void down_adjustment() {
int parentIndex = 0;
int temp = priority_queue.array[parentIndex];
int childIndex = 1;
while (childIndex <= priority_queue.index) {
if (childIndex + 1 <= priority_queue.index &&
priority_queue.array[childIndex + 1] > priority_queue.array[childIndex]) {
childIndex++;
}
priority_queue.array[parentIndex] = priority_queue.array[childIndex];
parentIndex = childIndex;
childIndex = childIndex * 2 + 1;
}
priority_queue.array[parentIndex] = temp;
}
代码编译器:VS2019
//主要代码: 写PriorityQueue类进行封装 PriorityQueue.h
#pragma once
#include
using namespace std;
class PriorityQueue {
public:
PriorityQueue() {
priority_queue.index = -1;
priority_queue.arrayLength = 10;
}
int pop();
void push(int valua);
void display() {
for (int i = 0; i <= priority_queue.index; i++) {
cout << priority_queue.array[i];
}
cout << endl;
}
private:
struct priority_queue {
int array[10];
int arrayLength;
int index; //标记当前现有元素末尾
};
struct priority_queue priority_queue;
void up_adjustment();
void down_adjustment();
};
//主要代码: PriorityQueue.cpp
#include "PriorityQueue.h"
void PriorityQueue::push(int valua) {
if (priority_queue.index >= priority_queue.arrayLength) {
cout << "priority_queue full!" << endl;
return;
}
priority_queue.array[++priority_queue.index] = valua;
up_adjustment();
}
void PriorityQueue::up_adjustment() {
int childIndex = priority_queue.index;
int parentIndex = (childIndex - 1) / 2;
int temp = priority_queue.array[childIndex];
while (childIndex > 0 && temp > priority_queue.array[parentIndex]) {
priority_queue.array[childIndex] = priority_queue.array[parentIndex];
childIndex = parentIndex;
parentIndex = parentIndex / 2;
}
priority_queue.array[childIndex] = temp;
}
int PriorityQueue::pop() {
if (priority_queue.index < 0) {
cout << "priority_queue empty!" << endl;
system("pause");
return 0;
}
int top = priority_queue.array[0];
priority_queue.array[0] = priority_queue.array[priority_queue.index--]; //让最后一个元素移动到堆顶
down_adjustment();
return top;
}
void PriorityQueue::down_adjustment() {
int parentIndex = 0;
int temp = priority_queue.array[parentIndex];
int childIndex = 1;
while (childIndex <= priority_queue.index) {
if (childIndex + 1 <= priority_queue.index &&
priority_queue.array[childIndex + 1] > priority_queue.array[childIndex]) {
childIndex++;
}
priority_queue.array[parentIndex] = priority_queue.array[childIndex];
parentIndex = childIndex;
childIndex = childIndex * 2 + 1;
}
priority_queue.array[parentIndex] = temp;
}
//示例代码运行 main.cpp
#include
#include "PriorityQueue.h"
using namespace std;
int main() {
PriorityQueue priorityQueue;
priorityQueue.push(3);
priorityQueue.display();
priorityQueue.push(2);
priorityQueue.display();
priorityQueue.push(7);
priorityQueue.display();
priorityQueue.push(1);
priorityQueue.display();
priorityQueue.push(4);
priorityQueue.display();
priorityQueue.push(6);
priorityQueue.display();
priorityQueue.display();
cout << "第一个出队元素:" << priorityQueue.pop() << endl;
priorityQueue.display();
cout << "第二个出队元素:" << priorityQueue.pop() << endl;
priorityQueue.display();
cout << "第三个出队元素:" << priorityQueue.pop() << endl;
priorityQueue.display();
cout << "第四个出队元素:" << priorityQueue.pop() << endl;
priorityQueue.display();
cout << "第五个出队元素:" << priorityQueue.pop() << endl;
priorityQueue.display();
cout << "第六个出队元素:" << priorityQueue.pop() << endl;
return 0;
}
在实际开发中,优先队列的操作具有一定的通用性,且代码在本质上也相同,最多只是优先队列中存储的元素类型、大小不一。因此STL优先队列就孕育而生,其中STL优先队列具有更强的通用性。
#include
priority_queue<元素类型,容器类型,排序方式> 创建对象名
其中:1、容器类型可以用vector,deque(双向队列)等来实现,其中list不能用来实现,因为list的迭代器不是任意取iterator.
容器可以默认,如果容器默认,则排序方式也要默认(VS2019是这样的的,否则会报错)。
2、排序方式有:利用STL自带的比较大小
1)greater<元素类型> : 从小到大顺序出队,即为最小优先队列
2)less<元素类型> : 从大到小顺序出队,即为最大优先队列
3、不加排序方式默认为最大优先队列
push() :添加一个元素。
pop(): 删除堆顶元素,本质上删除第一个元素,且不返回值(前提是已经形成二叉堆)。
empty():如果优先队列为空,返回true。否则,返回false。
size():返回优先队列中的元素个数
top():返回堆顶元素,本质上就是返回对一个元素(前提是已经形成二叉堆)。
代码编译器:VS2019
//主要代码: 写模板可接受更多不同参数类型
#include
#include
#include
#include
using namespace std;
template<class T>
void display(T s, string name) {
if (s.empty()) {
cout << name << " empty!" << endl;
}
else {
cout << name << " no empty!" << endl;
}
s.push(3);
s.push(7);
s.push(6);
s.push(9);
s.push(10);
if (s.empty()) {
cout << name << " empty!" << endl;
}
else {
cout << name << " no empty!" << endl;
}
for (int i = 0; i < 5; i++) {
cout << name << " size:" << s.size() << endl;
cout << name << " top:" << s.top() << endl;
s.pop();
}
if (s.empty()) {
cout << name << " empty!" << endl;
}
else {
cout << name << " no empty!" << endl;
}
}
int main() {
priority_queue<int> s;
priority_queue<int,vector<int>, greater<int>> s1;
priority_queue<int,vector<int>, less<int>> s2;
display(s, "s");
cout << endl;
cout << "s1" << endl;
display(s1, "s1");
cout << endl;
cout << "s2" << endl;
display(s2, "s2");
return 0;
}
树的学习就先告一段落,当然还有二叉搜索树(又称二叉排序树、二叉查找树),平衡二叉树,红黑树,B树B+树。我们会在后续陆续学到。希望这些分享能给你帮助。