二叉树是常见的数据结构,常见的有三种遍历方式:前序遍历(根→左→右)、中序遍历(左→根→右)和后序遍历(左→右→根)。使用递归的方式实现遍历比较简单,书上看到一个不使用递归的方法比较复杂,记录一下。
二叉树节点定义
typedef struct node {
int value;
struct node *left;
struct node *right;
}Node;
前序遍历(递归)
递归方式实现前序遍历,先打印根节点,然后依次把左节点和右节点当做根节点来打印。
void Tree::preOrderRecur(Node *tree) {
if (!tree) return;
std::cout << tree->value << " ";
preOrderRecur(tree->left);
preOrderRecur(tree->right);
}
前序遍历(非递归)
非递归遍历使用栈结构(先进先出)来暂存节点,先打印根节点,同时依次把右节点和左节点压到栈中。接着取栈顶节点作为根节点进行打印,然后再依次把右节点和左节点压到栈中。。。这样可以始终保持先打印的是根节点,且左节点比右节点先打印。
void Tree::preOrderUnRecur(Node *tree) {
if (!tree) return;
std::stack<Node*> nodeStack;
nodeStack.push(tree);
Node *root = nullptr;
while (!nodeStack.empty()) {
root = nodeStack.top();
nodeStack.pop();
std::cout << root->value << " ";
if (root->right)
nodeStack.push(root->right);
if (root->left)
nodeStack.push(root->left);
}
}
中序遍历(递归)
递归方式依次打印左、根、右节点。
void Tree::inOrderRecur(Node *tree) {
if (!tree) return;
inOrderRecur(tree->left);
std::cout << tree->value << " ";
inOrderRecur(tree->right);
}
中序遍历(非递归)
先依次把最左边的节点压到栈中,(这样弹出节点打印的时候可以保证先打印左节点再打印根节点)。一旦检测到左节点为空,就弹出栈顶的节点并打印该节点(此时可以理解为根节点)。接着把其右节点作为根节点循环处理。
void Tree::inOrderUnRecur(Node *tree) {
if (!tree) return;
std::stack<Node*> nodeStack;
Node *root = tree;
while (!nodeStack.empty() || root) {
if (root) {
nodeStack.push(root);
root = root->left;
} else {
root = nodeStack.top();
std::cout << root->value << " ";
nodeStack.pop();
root = root->right;
}
}
}
后序遍历(递归)
依次处理左右节点再打印根节点。
void Tree::posOrderRecur(Node *tree) {
if (!tree) return;
posOrderRecur(tree->left);
posOrderRecur(tree->right);
std::cout << tree->value << " ";
}
后序遍历(非递归)
把根节点压入栈中,记录栈顶元素为current当前节点。如果左节点不为空则压入栈中,否则把右节点压入栈中(不为空时)。如果当前节点的左右节点都为空则打印当前节点,(可以保证根节点最后打印)。
压入右节点的时候要保证上一次没有打印过右节点,避免根节点和右节点打印后再压栈变成死循环。压入左节点的时候也要保证上次打印不是做节点,且右节点不存在或者已经处理过了,(可以保证左节点始终在右节点的前面被打印)。
void Tree::posOrderUnRecur(Node *tree) {
if (!tree) return;
std::stack<Node*> nodeStack;
nodeStack.push(tree);
Node *last = nullptr;
Node *current = nullptr;
while (!nodeStack.empty()) {
current = nodeStack.top();
if ((current->left) && (last != current->left)
&& ((last != current->right) || !current->right)) {
nodeStack.push(current->left);
} else if(current->right && (last != current->right)) {
nodeStack.push(current->right);
} else {
std::cout << current->value << " ";
last = current;
nodeStack.pop();
}
}
}
二叉树的创建和销毁
这里我使用一个类似二叉树层序遍历的数组的形式创建二叉树(避免歧义使用-1代表空节点),销毁二叉树要释放所有申请空间的节点。
// 创建
Node *Tree::createBiTree(const int *arr, int size, int index) {
if (index >= size || arr[index] == -1){
return nullptr;
}
Node *node = new Node;
if (index != size) {
node->value = arr[index];
node->left = createBiTree(arr, size, 2*(index+1)-1);
node->right = createBiTree(arr, size, 2*(index+1));
} else {
node->left = NULL;
node->right = NULL;
}
return node;
}
// 销毁
void Tree::destoryBiTree(Node *tree) {
if (tree == NULL)
return;
destoryBiTree(tree->left);
if (tree->left)
tree->left = NULL;
destoryBiTree(tree->right);
if (tree->right)
tree->right = NULL;
delete tree;
}
测试
int main()
{
/*
* 使用如下二叉树
* 1
* 2 3
* 4 5 6 7
* 8 x x x x x 9 10
* /
int array[15] = {1,2,3,4,5,6,7,8,-1,-1,-1,-1,-1,9,10};
Tree *ins = new Tree;
Node *tree = ins->createBiTree(array, 15, 0);
std::cout << "\n前序遍历(递归):" << std::endl;
ins->preOrderRecur(tree);
std::cout << "\n前序遍历(非递归):" << std::endl;
ins->preOrderUnRecur(tree);
std::cout << "\n中序遍历(递归):" << std::endl;
ins->inOrderRecur(tree);
std::cout << "\n中序遍历(非递归):" << std::endl;
ins->inOrderUnRecur(tree);
std::cout << "\n后序遍历(递归):" << std::endl;
ins->posOrderRecur(tree);
std::cout << "\n后序遍历(非递归):" << std::endl;
ins->posOrderUnRecur(tree);
ins->destoryBiTree(tree);
}
程序输出:
前序遍历(递归):
1 2 4 8 5 3 6 7 9 10
前序遍历(非递归):
1 2 4 8 5 3 6 7 9 10
中序遍历(递归):
8 4 2 5 1 6 3 9 7 10
中序遍历(非递归):
8 4 2 5 1 6 3 9 7 10
后序遍历(递归):
8 4 5 2 6 9 10 7 3 1
后序遍历(非递归):
8 4 5 2 6 9 10 7 3 1