二叉树的基本操作实现(递归和非递归)
来源:互联网 发布:2017淘宝年货节时间 编辑:程序博客网 时间:2024/06/05 21:04
二叉树:二叉树是一棵特殊的树,二叉树每个节点最多有两个孩子结点,分别称为左孩子和右孩子。
满二叉树:高度为N的满二叉树有2^N- 1个节点的二叉树。
完全二叉树: 若设二叉树的深度为h,除第h 层外,其它各层(1~h-1) 的结点数都达到最大个数,第h 层所有的结点都连续集中在最左边,这就是完全二叉树
二叉树的基本操作:构造二叉树,前序遍历,中序遍历,后序遍历,求树的叶子结点,树的深度,树中第K层的结点个数,树中结点的个数,以及在树中查找一个结点。
前序遍历(先根遍历):(1):先访问根节点; (2):前序访问左子树;(3):前序访问右子树; 【1 2 3 4 5 6】
中序遍历: (1):中序访问左子树;(2):访问根节点; (3):中序访问右子树; 【3 2 4 1 6 5】
后序遍历(后根遍历):(1):后序访问左子树;(2):后序访问右子树;(3):访问根节点; 【3 4 2 6 5 1】
层序遍历: (1):一层层节点依次遍历。 【1 2 5 3 4 6】
注意
我们二叉树的前中后序三种遍历可以用递归和非递归来实现,用递归实现简单但是他也是有缺陷的,它的缺陷是递归每次需要压栈,而栈的空间相对来说不是很大,在多线程中只有十几兆,如果在极端情况下这个二叉树深度很大,则会有栈溢出的问题产生,所以本文给大家介绍递归与非递归两种方法
我们先定义一个二叉树的结点
二叉树的结点
template<class T>struct BinaryTreeNode{ BinaryTreeNode<T>* _left; BinaryTreeNode<T>* _right; T _data; BinaryTreeNode(const T& x) :_left(NULL) , _right(NULL) , _data(x) {}};
构造二叉树:
在这里invalid就是所谓的非法值,而index就是数组的下标。
比如:int array[10] = { 1, 2, 3, ‘#’, ‘#’, 4, ‘#’, ‘#’, 5, 6 }
这里的invalid就是‘#’.
BinaryTree() :_root(NULL) {} BinaryTree(T* a, size_t n, const T& invalid) { size_t index = 0; _root = CreateTree(a, n, invalid, index); } //构建当前树或子树 //这里需要注意的是index要使用& //若没有使用则index在上一层递归中的++index,对之后的是没有影响的 Node* CreateTree(T* a, size_t n, const T& invalid, size_t &index) { Node*root = NULL; if (index<n&&a[index]!=invalid) { root = new Node(a[index]); root->_left = CreateTree(a, n, invalid, ++index); root->_right = CreateTree(a,n, invalid, ++index); } return root; }
前序遍历(先根遍历):
(1):先访问根节点;
(2):前序访问左子树;
(3):前序访问右子树; 【1 2 3 4 5 6】
这里需要注意的是下面所有代码使用到递归的我们都会把它分为两个函数
如PrevOrder和_PrevOrder
原因是因为若将_root传入,外部对象是访问不到的。
//前序遍历(递归) void PrevOrder() { _PrevOrder(_root); cout << endl; } void _PrevOrder(Node* root) { if (root == NULL)//递归结束的条件 return; cout << root->_data << " "; _PrevOrder(root->_left); _PrevOrder(root->_right); }
前序遍历非递归:
我们前序遍历的非递归要借助栈的后进先出的特性来完成。
我们将1,2,3分别压栈,3的左为空则停止压栈,取栈顶元素3存放在变量top中,并删除栈顶元素,访问top的右子树,它的右子树也为空,则取栈顶元素2并访问它的右子树,如此重复便是(中序遍历同)
//前序遍历(非递归) void PrevOrderNonR() { stack<Node*> s; Node* cur = _root; while (cur ||! s.empty()) { while (cur) { s.push(cur); cout << cur->_data << " "; cur = cur->_left; } // top从栈取出来表示这个节点的左子树访问过了 // 剩余右子树还没访问,循环子问题访问右树 Node* top = s.top(); s.pop(); //子问题的方式访问右树 cur = top->_right; } cout << endl; }
中序遍历:
(1):中序访问左子树;
(2):访问根节点;
(3):中序访问右子树; 【3 2 4 1 6 5】
//中序遍历(递归) void InOrder() { _InOrder(_root); cout << endl; } void _InOrder(Node* root) { if (root == NULL) return; _InOrder(root->_left); cout << root->_data << " "; _InOrder(root->_right); }
中序遍历非递归实现:
这里中序遍历非递归的实现道理与前序遍历非递归的道理相同,如果你掌握了前序遍历则中序遍历一定也没有问题。
//中序遍历(非递归) void InOrderNonR() { stack<Node*> s; Node* cur = _root; while (cur || !s.empty()) { while (cur) { s.push(cur); cur = cur->_left; } Node*top = s.top(); s.pop(); cout << top->_data << " "; cur = top->_right; } cout << endl; }
后序遍历: (后根遍历)
(1):后序访问左子树;
(2):后序访问右子树;
(3):访问根节点; 【3 4 2 6 5 1】
//后序遍历(递归) void PostOrder() { _PostOrder(_root); cout << endl; } void _PostOrder(Node* root) { if (root == NULL) return; _PostOrder(root->_left); _PostOrder(root->_right); cout << root->_data << " "; }
后序遍历(非递归):
相比较前序遍历与中序遍历,后序遍历是偏难的,它的难点在于根是最后访问的,我们要先访问了左子树和右子树才能访问根。
所以我们在这里增加了一个变量prev,它用于记录上一个走过的结点
举个例子,
2这个结点,我们把3这个结点访问结束后会返回2 这个结点,但是这是我们不能读取2 这个结点,我们要先访问2的右子树,我们将2 的右子树访问结束后才会访问2这个结点,那么这样的话我们会经过两次2这个结点,如果我们不加一个变量去记录上一个走过的节点的话,程序就会发生死循环,从而导致程序的崩溃。
下面我们来分析一下加了prev这个变量以后的情况,我们访问完3这个结点之后退回到2这个结点,这时prev是3,它不等于2的右子树,那么我们访问2的右子树,当2的右子树访问结束后退回到2这个结点时,prev现在是4,这个2的右子树等于prev说明2的右子树已经访问过了,这时就可以安心的访问2这个结点的,后面的情况和这个都一样。
//后序遍历(非递归) void PostOrderNonR() { stack<Node*> s; Node* cur = _root; Node* prev = NULL; while (cur || !s.empty()) { while (cur) { s.push(cur); cur = cur->_left; } Node* top = s.top(); if (top->_right == NULL||top->_right==prev) { cout << top->_data << " "; prev = top; s.pop(); } else { cur = top->_right; } } cout << endl; }
层序遍历:
(1):一层层节点依次遍历。 【1 2 5 3 4 6】
思想:
层序遍历我们需要借助队列的先进先出的特性,所以在这里我们创建了队列q。我们先将根放入队列中,然后每次取队列里面的结点时,我们把该结点的左子树的根和右子树的根放进去,直到队列为空,则访问结束。
图解:
void LevelOrder() { queue<Node*> q; if (_root) q.push(_root); while (!q.empty()) { Node* front = q.front(); q.pop(); cout << front->_data << " "; if (front->_left) q.push(front->_left); if (front->_right) q.push(front->_right); } cout << endl; }
树中结点的个数:
这里也是把主问题分解成无数个子问题:
1.对于根节点来说它的总节点个数,就是左孩子和右孩子结点的个数加上自己的总和也就是 左孩子总结点数 + 1 + 右孩子总结点
2.当你使用递归时也就是每一个结点都可以看成根节点,所以就是每个结点的左孩子+1+右孩子总结点个数.
size_t Size() { return _Size(_root); } size_t _Size(Node* root) { if (root == NULL) return 0; return _Size(root->_left)+_Size(root->_right)+1; }
求树的叶子节点的个数
size_t LeafSize() { return _LeafSize(_root); } size_t _LeafSize(Node* root) { if (root == NULL) return 0; if (root->_left == NULL&&root->_right == NULL) return 1; return(_LeafSize(root->_left) + _LeafSize(root->_right)); }
求树的深度
size_t Depth() { return _Depth(_root); } size_t _Depth(Node* root) { if (root == NULL) return 0; if (root->_left == NULL&&root->_right == NULL) return 1; size_t left = _Depth(root->_left); size_t right = _Depth(root->_right); return left > right ? left + 1 : right + 1; }
树中第K层的节点个数
size_t GetKLevel(size_t k) { assert(k > 0); return _GetKLevel(_root, k); } size_t _GetKLevel(Node* root, size_t k) { if (root == NULL) return 0; if (k == 1) return 1; return _GetKLevel(root->_left, k - 1) + _GetKLevel(root->_right, k - 1); }
在树中查找一个结点
Node* Find(const T&x) { return _Find(_root, x); } Node* _Find(Node* root, const T&x) { if (root == NULL) return NULL; if (root->_data == x) return _root; Node*ret = _Find(root->_left, x); if (ret) return ret; return _Find(root->_right, x); }
二叉树的析构函数
~BinaryTree() { Destroy(_root); } void Destroy(Node*root) { if (root == NULL) return; Destroy(root->_left); Destroy(root->_right); delete root; }
二叉树的拷贝构造函数
BinaryTree(const BinaryTree<T>& t) { _root = _Copy(t._root); } Node* _Copy(Node* root) { if (root == NULL) return NULL; Node* newRoot = new Node(root->_data); newRoot->_left = _Copy(root->_left); newRoot->_right = _Copy(root->_right); return newRoot; }
二叉树的复制运算符重载
// t1 = t2(传统写法)/*BinaryTree<T>& operator=(const BinaryTree<T>& t){if (this != &t){Destroy(_root);_root = _Copy(t._root);}return *this}*///现代写法BinaryTree<T>& operator=(BinaryTree<T> t){swap(_root, t._root);return *this;}
二叉树复制运算符的现代写法很简单,我们这里用的是值传递,当你传参数进来的时候会创建一个和你一模一样的局部变量,而这个局部变量 会随着函数的结束而销毁,我们现在让局部变量的根节点和我们的根节点进行交换,我们的根节点指向我们所赋值的那棵树,而局部变量的根节点指向了我们以前的内容,当他出了这个作用于以后,这个局部变量也会随之销毁(释放)。现在的根节点就指向了我们想要的内容。
完整的代码实现:
#pragma once#include<iostream>#include <queue>#include <stack>#include<assert.h>using namespace std;template<class T>struct BinaryTreeNode{ BinaryTreeNode<T>* _left; BinaryTreeNode<T>* _right; T _data; BinaryTreeNode(const T& x) :_left(NULL) , _right(NULL) , _data(x) {}};template<class T>class BinaryTree{ typedef BinaryTreeNode<T> Node;public: BinaryTree() :_root(NULL) {} BinaryTree(T* a, size_t n, const T& invalid) { size_t index = 0; _root = CreateTree(a, n, invalid, index); } //构建当前树或子树 Node* CreateTree(T* a, size_t n, const T& invalid, size_t &index) { Node*root = NULL; if (index<n&&a[index]!=invalid) { root = new Node(a[index]); root->_left = CreateTree(a, n, invalid, ++index); root->_right = CreateTree(a,n, invalid, ++index); } return root; } Node* _Copy(Node* root) { if (root == NULL) return NULL; Node* newRoot = new Node(root->_data); newRoot->_left = _Copy(root->_left); newRoot->_right = _Copy(root->_right); return newRoot; } BinaryTree(const BinaryTree<T>& t) { _root = _Copy(t._root); } // t1 = t2(传统写法) /*BinaryTree<T>& operator=(const BinaryTree<T>& t) { if (this != &t) { Destroy(_root); _root = _Copy(t._root); } return *this }*/ //现代写法 BinaryTree<T>& operator=(BinaryTree<T> t) { swap(_root, t._root); return *this; } ~BinaryTree() { Destroy(_root); } void Destroy(Node*root) { if (root == NULL) return; Destroy(root->_left); Destroy(root->_right); delete root; } //前序遍历(递归) void PrevOrder() { _PrevOrder(_root); cout << endl; } void _PrevOrder(Node* root) { if (root == NULL)//递归结束条件 return; cout << root->_data << " "; _PrevOrder(root->_left); _PrevOrder(root->_right); } //前序遍历(非递归) void PrevOrderNonR() { stack<Node*> s; Node* cur = _root; while (cur ||! s.empty()) { while (cur) { s.push(cur); cout << cur->_data << " "; cur = cur->_left; } // top从栈取出来表示这个节点的左子树访问过了 // 剩余右子树还没访问,循环子问题访问右树 Node* top = s.top(); s.pop(); //子问题的方式访问右树 cur = top->_right; } cout << endl; } //中序遍历(递归) void InOrder() { _InOrder(_root); cout << endl; } void _InOrder(Node* root) { if (root == NULL) return; _InOrder(root->_left); cout << root->_data << " "; _InOrder(root->_right); } //中序遍历(非递归) void InOrderNonR() { stack<Node*> s; Node* cur = _root; while (cur || !s.empty()) { while (cur) { s.push(cur); cur = cur->_left; } Node*top = s.top(); s.pop(); cout << top->_data << " "; cur = top->_right; } cout << endl; } //后序遍历(递归) void PostOrder() { _PostOrder(_root); cout << endl; } void _PostOrder(Node* root) { if (root == NULL) return; _PostOrder(root->_left); _PostOrder(root->_right); cout << root->_data << " "; } //后序遍历(非递归) void PostOrderNonR() { stack<Node*> s; Node* cur = _root; Node* prev = NULL; while (cur || !s.empty()) { while (cur) { s.push(cur); cur = cur->_left; } Node* top = s.top(); if (top->_right == NULL||top->_right==prev) { cout << top->_data << " "; prev = top; s.pop(); } else { cur = top->_right; } } cout << endl; } void LevelOrder() { queue<Node*> q; if (_root) q.push(_root); while (!q.empty()) { Node* front = q.front(); q.pop(); cout << front->_data << " "; if (front->_left) q.push(front->_left); if (front->_right) q.push(front->_right); } cout << endl; } size_t Size() { return _Size(_root); } size_t _Size(Node* root) { if (root == NULL) return 0; return _Size(root->_left)+_Size(root->_right)+1; } size_t LeafSize() { return _LeafSize(_root); } size_t _LeafSize(Node* root) { if (root == NULL) return 0; if (root->_left == NULL&&root->_right == NULL) return 1; return(_LeafSize(root->_left) + _LeafSize(root->_right)); } size_t Depth() { return _Depth(_root); } size_t _Depth(Node* root) { if (root == NULL) return 0; if (root->_left == NULL&&root->_right == NULL) return 1; size_t left = _Depth(root->_left); size_t right = _Depth(root->_right); return left > right ? left + 1 : right + 1; } size_t GetKLevel(size_t k) { assert(k > 0); return _GetKLevel(_root, k); } size_t _GetKLevel(Node* root, size_t k) { if (root == NULL) return 0; if (k == 1) return 1; return _GetKLevel(root->_left, k - 1) + _GetKLevel(root->_right, k - 1); } Node* Find(const T&x) { return _Find(_root, x); } Node* _Find(Node* root, const T&x) { if (root == NULL) return NULL; if (root->_data == x) return _root; Node*ret = _Find(root->_left, x); if (ret) return ret; return _Find(root->_right, x); }protected: Node* _root;};void TestBinaryTree(){ int array[10] = { 1, 2, 3, '#', '#', 4, '#', '#', 5, 6 }; BinaryTree<int> t1(array, sizeof(array) / sizeof(array[0]), '#'); t1.PrevOrder(); t1.InOrder(); t1.PostOrder(); BinaryTree<int> t2(t1); t2.PrevOrderNonR(); t1.PrevOrderNonR(); t1.InOrderNonR(); t1.PostOrderNonR(); //t1.InOrder(); t1.LevelOrder(); cout<<"Size?"<<t1.Size()<<endl; cout<<"K Level?"<<t1.GetKLevel(3)<<endl; //cout<<"K Level?"<<t1.GetKLevel(4)<<endl; cout<<"Leaf Size?"<<t1.LeafSize()<<endl; cout<<"Depth?"<<t1.Depth()<<endl; cout << "Find" << t1.Find(2) << endl;}
- 二叉树的基本操作实现(递归和非递归)
- C语言实现二叉树的递归和非递归算法的基本操作
- 二叉树的创建和基本操作(递归和非递归)
- C++实现二叉树的基本操作(递归+非递归)
- 二叉树基本操作递归和非递归方法
- 二叉树的建立和基本操作(递归实现)
- 二叉树的遍历(非递归和递归实现)
- 二叉树的基本操作(非递归)(修改)
- 二叉树的基本操作(非递归)
- 二叉搜索树递归&&非递归的基本实现
- C++实现二叉搜索树基本操作(递归+非递归+应用)
- 数据结构——排序/搜索二叉树(非递归)的基本操作实现
- 二叉树的基本操作-递归实现
- 二叉树的反转,递归实现和非递归实现。
- 二叉树的基本操作(创建、递归和非递归遍历、求深度、求叶子数)
- 二叉树的遍历:递归和非递归实现
- 二叉树遍历的递归和非递归实现
- 二叉树创建、遍历的递归和非递归实现
- 关于setInterval和setTImeout中的this指向问题
- Java多线程-线程的状态
- 通过选择器改变字体颜色
- JAVA编程获取音频时长
- sql之left join、right join、inner join的区别
- 二叉树的基本操作实现(递归和非递归)
- nginx命令详解
- centos7安装tomcat和jdk
- jmeter插件使用
- DOTween-Ease缓动函数
- HDOJ 2095 find your present (2)
- 机器学习技法课程学习笔记9 -- Decision Tree
- PostgreSQL将日志发送到系统日志中
- centos7 安装mysql5.7(源码安装)