二叉树定义
struct TreeNode {
int val;
TreeNode *left;
TreeNode *right;
TreeNode(int x) : val(x), left(NULL), right(NULL) {}
};
遍历结果
int main(){
/*
* 1
* / \
* 2 3
* / \
* 4 5
* /
* 6
*/
TreeNode *root = new TreeNode(1);
TreeNode *node1 = new TreeNode(2);
TreeNode *node2 = new TreeNode(3);
TreeNode *node3 = new TreeNode(4);
TreeNode *node4 = new TreeNode(5);
TreeNode *node5 = new TreeNode(6);
root->left = node1,root->right = node2;
node2->left = node3,node2->right = node4;
node3->left = node5;
cout<<"seqOrder\n"; // 层序遍历
seqOrder(root);
cout<<"\n\npreOrder\n"; // 先序遍历
preOrder(root);
cout<<"\n\npreOrderII\n"; // 先序遍历(非递归)
preOrderII(root);
cout<<"\n\npreOrderIII\n"; // 先序遍历(非递归)
preOrderIII(root);
cout<<"\n\ninOrder\n"; // 中序遍历
inOrder(root);
cout<<"\n\ninOrderII\n"; // 中序遍历(非递归)
inOrderII(root);
cout<<"\n\nposOrder\n"; // 后序遍历
posOrder(root);
cout<<"\n\nposOrderII\n"; // 后序遍历(非递归)
posOrderII(root);
cout<<"\n\nprintTree\n"; // 分层打印
printTree(root);
return 0;
}
seqOrder
1 2 3 4 5 6
preOrder
1 2 3 4 6 5
preOrderII
1 2 3 4 6 5
preOrderIII
1 2 3 4 6 5
inOrder
2 1 6 4 3 5
inOrderII
2 1 6 4 3 5
posOrder
2 6 4 5 3 1
posOrderII
2 6 4 5 3 1
printTree
1
2 3
4 5
6
递归实现遍历
先序遍历
// 先序遍历二叉树(递归)
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 posOrder(TreeNode *root){
if(root == nullptr) return;
posOrder(root->left);
posOrder(root->right);
cout<<root->val<<" ";
}
非递归实现遍历
非递归实现的模板1 用于先序、后序
...
stack<TreeNode*> s;
vector<int> ret;
s.push(root);
while(!s.empty()){
root = s.top();
s.pop();
ret.push_back(root->val);
if(root->right) // 后序是先访问left 最后输出序列反转
s.push(root->right);
if(root->left)
s.push(root->left);
}
...
非递归实现的模板2 用于中序、后序
...
stack<TreeNode*> s;
vector<int> ret;
while(root || !s.empty()){
while (root){
s.push(root);
root = root->left;
}
root = s.top();
...
}
...
先序遍历
【1】利用栈实现
(1)输出当前节点P的值,如果右孩子不为空,将右孩子入栈。判断P的左孩子是否为空
(2)如果P的左孩子不为空,则置P的左孩子为当前节点,重复(1)的操作
(3)如果P的左孩子为空,并且栈非空,则将栈顶元素出栈,将该元素置为当前节点,重复(1)的操作;若栈空,结束遍历
(4)直到当前节点P为null并且栈空,遍历结束
// 先序遍历(非递归)
void preOrderII(TreeNode *root){
if(root == nullptr) return;
stack<TreeNode*> s;
while (root || !s.empty()){
cout<<root->val<<" ";
if(root->right) // 右子节点压栈
s.push(root->right);
if(root->left) // 沿着左子节点遍历
root = root->left;
else{
// 没有左子节点,弹栈
if(!s.empty()){
root = s.top();
s.pop();
}
else return; // 最后一个节点访问完
}
}
}
【2】利用栈实现方法2,更简洁
访问顺序为根节点=》左孩子=》右孩子,所以入栈顺序为右孩子=》左孩子
(1)先将根节点入栈
(2)将栈顶元素出栈,访问该节点,如果该节点的右孩子非空,则将右孩子入栈;如果左孩子非空,则将左孩子入栈
(3)重复(2)操作,直到栈为空结束循环
// 先序遍历(非递归)方法2
void preOrderIII(TreeNode *root){
if(root == nullptr) return;
stack<TreeNode*> s;
s.push(root);
while (!s.empty()){
root = s.top();
s.pop();
cout<<root->val<<" ";
if(root->right)
s.push(root->right);
if(root->left)
s.push(root->left);
}
}
中序遍历
(1)将当前节点P入栈,如果P的左孩子非空,则将P的左孩子置为当前节点,重复(1)操作
(2)如果栈非空,将栈顶元素出栈,并置为当前节点,打印该节点的值;将该节点的右孩子置为当前节点
(3)直到当前节点P为null并且栈空,遍历结束
// 中序遍历(非递归)
void inOrderII(TreeNode *root){
if(root == nullptr) return;
stack<TreeNode*> s;
// 右子树为空,栈也为空时结束遍历
while (root || !s.empty()){
// 沿着左孩子将元素入栈
while(root != nullptr){
s.push(root);
root = root->left;
}
root = s.top();
s.pop();
cout << root->val <<" ";
root = root->right;
}
}
后序遍历
(1)将当前节点P入栈,如果P的左孩子非空,则将P的左孩子置为当前节点,重复(1)操作
(2)将栈顶元素弹出,打印该节点值
(3)判断当前节点是否为栈顶的左孩子,如果是的话那么还需要先访问右子树才能访问根节点;否则将当前节点置为NULL
(4)直到当前节点P为NULL并且栈空,遍历结束
void posOrderII(TreeNode *root){
if(root == nullptr) return;
stack<TreeNode*> s;
TreeNode *last = nullptr; // 存储上一次访问的节点
// 栈为空时结束遍历
while (root!= nullptr || !s.empty()){
while (root != nullptr){
s.push(root);
root = root->left;
}
root = s.top();
if(root->right == nullptr || root->right == last){ // 右子树已经访问过
cout << root->val <<" ";
s.pop();
last = root;
root = nullptr;
}
else
root = root->right; // 右子树未访问,需要先访问有子树
}
}
方法2:利用非递归先序遍历的变形
原本是非递归先序遍历是 根->左->右,变形为根->右->左,然后输出反转
/*
* 非递归后序遍历(方法2)
* 将简洁的非递归先序遍历修改为 根->右->左 顺序遍历,然后将最后得到的递归序列翻转
* 得到后序遍历 左->右->根
*/
void posOrderIII(TreeNode *root){
vector<int> ret;
if(!root) return ret;
stack<TreeNode *> s;
s.push(root);
while ( !s.empty()){
root = s.top();
s.pop();
ret.push_back(root->val);
if (root->left)
s.push(root->left);
if (root->right)
s.push(root->right);
}
for(int i = ret.size()-1; i>=0; --i)
cout << ret[i] << endl;
}
层序遍历
队列实现
(1)将根节点入队
(2)弹出队列首元素,打印该节点
(3)如果节点左孩子非空,将左孩子入队列;如果节点右孩子非空,将右孩子入队列,返回步骤(2)
(4)当队列为空时结束循环
// 层序遍历二叉树
void seqOrder(TreeNode *root){
if(root == nullptr) return;
queue<TreeNode*> q;
TreeNode *node;
q.push(root);
while(!q.empty()){
node = q.front();
q.pop();
cout<<node->val<<" ";
if(node->left) q.push(node->left);
if(node->right) q.push(node->right);
}
return;
}
分层打印
分层打印其实就是层序遍历的基础上,每一层访问完后需要直接打印该层的所有值,需要标记当前层是否都打印完了。
// 分层打印二叉树
void printTree(TreeNode *root){
if(root == nullptr) return;
queue<TreeNode*> q;
TreeNode *node;
int nodesToPrint = 1; // 当前层节点总数
int nextLayerNodes = 0; // 下一层节点总数
int cnt = 0; // 当前层已打印节点数
q.push(root);
while(!q.empty()){
cnt = 0;
nextLayerNodes = 0;
while (cnt < nodesToPrint){
node = q.front();
q.pop();
cout<<node->val<<" ";
++cnt;
if(node->left) {
q.push(node->left);
++nextLayerNodes;
}
if(node->right) {
q.push(node->right);
++nextLayerNodes;
}
}
cout<<endl;
nodesToPrint = nextLayerNodes;
}
}