二叉树进阶
1. 二叉搜索树
1.1 二叉搜索树概念
二叉搜索树又称二叉排序树,它或者是一棵空树,或者是具有以下性质的二叉树:
- 若它的左子树不为空,则左子树上所有节点的值都小于根节点的值
- 若它的右子树不为空,则右子树上所有节点的值都大于根节点的值
- 它的左右子树也分别为二叉搜索树
二叉搜索树(BST,Binary Search Tree),也称二叉排序或二叉查找树。
1.2 二叉搜索树操作
int a[] = {8, 3, 1, 10, 6, 4, 7, 14, 13};
-
二叉搜索树的查找
a、从根开始比较,查找,比根大则往右边走查找,比根小则往左边走查找。
b、最多查找高度次,走到到空,还没找到,这个值不存在。
代码:
非递归版本:
bool Find(const K& key) { Node* cur = _root; while (cur) { if (cur->_key > key) { cur = cur->_left; } else if (cur->_key < key) { cur = cur->_right; } else { return true; } } return false; }
递归版本:
bool _FindR(Node* root, const K& key) { if (root == nullptr) return false; if (root->_key < key) { return _FindR(root->_right, key); } else if (root->_key > key) { return _FindR(root->_left, key); } else { return true; } } bool FindR(const K& key) { return _FindR(_root, key); }
-
二叉搜索树的插入
插入的具体过程如下:
a. 树为空,则直接新增节点,赋值给root指针
b. 树不空,按二叉搜索树性质查找插入位置,插入新节点
代码:
非递归版本:
bool Insert(const K& key) { if (_root == nullptr) { _root = new Node(key); return true; } Node* cur = _root; Node* parant = nullptr; while (cur) { if (cur->_key > key) { parant = cur; cur = cur->_left; } else if (cur->_key < key) { parant = cur; cur = cur->_right; } else { return false; } } cur = new Node(key); if (key < parant->_key) { parant->_left = cur; } else { parant->_right = cur; } return true; }
递归版本:
引用实现(更好一些):
bool _InsertR(Node*& root, const K& key) { if (root == nullptr) { root = new Node(key); return true; } if (key > root->_key) { return _InsertR(root->_right, key); } else if (key < root->_key) { return _InsertR(root->_left, key); } else { return false; } } bool InsertR(const K& key) { return _InsertR(_root, key); }
添加一个参数实现:
bool _InsertR(Node* root, const K& key, Node* parent) { if (root == nullptr) { if (parent == nullptr) { _root = new Node(key); return true; } if (key < parent->_key) { parent->_left = new Node(key); } else { parent->_right = new Node(key); } return true; } parent = root; if (key > root->_key) { return _InsertR(root->_right, key, parent); } else if (key < root->_key) { return _InsertR(root->_left, key, parent); } else { return false; } } bool InsertR(const K& key) { return _InsertR(_root, key, nullptr); }
-
二叉树的删除
首先查找元素是否在二叉搜索树中,如果不存在,则返回, 否则要删除的结点可能分下面四种情况:
a. 要删除的结点无孩子结点
b. 要删除的结点只有左孩子结点
c. 要删除的结点只有右孩子结点
d. 要删除的结点有左、右孩子结点
-
情况b:删除该结点且使被删除节点的双亲结点指向被删除节点的左孩子结点–直接删除
-
情况c:删除该结点且使被删除节点的双亲结点指向被删除结点的右孩子结点–直接删除情况
-
情况d:在它的右子树中寻找中序下的第一个结点(关键码最小),用它的值填补到被删除节点 中,再来处理该结点的删除问题–替换法删除(寻找左子树的最大值节点或者右子树的最小值节点)
代码:
非递归版本:
bool Erase(const K& key) { Node* cur = _root; Node* parent = nullptr; while (cur) { //下面是查找 if (cur->_key > key) { parent = cur; cur = cur->_left; } else if (cur->_key < key) { parent = cur; cur = cur->_right; } else//正式删除 { //一个孩子左为空或者右为空 if (cur->_left == nullptr)//要删除的节点左子节点为空 { if (_root == cur)//要删除的节点为根节点 { _root = cur->_right; } else { if (cur == parent->_left)//要删除的节点在父节点的左边 { parent->_left = cur->_right; } else//要删除的节点在父节点的右边 { parent->_right = cur->_right; } } delete cur; } else if (cur->_right == nullptr)//要删除的节点右子节点为空 { if (_root == cur)//要删除的节点刚好为根节点 { _root = cur->_left; } else { if (cur == parent->_left)//要删除的节点在父节点的左边 { parent->_left = cur->_left; } else//要删除的节点在父节点的右边 { parent->_right = cur->_left; } } delete cur; } else//两个孩子都不为空的情况 { Node* minParent = cur; Node* minRight = cur->_right;//此处是选的右子树的最小节点 while (minRight->_left) { minParent = minRight; minRight = minRight->_left; } swap(cur->_key, minRight->_key); //Erase(key); //问:为什么不能像上面这样写?答:因为交换之后,二叉搜索树就不符合二叉搜索树的规则了,递归会找不到key节点了 if (minParent->_left == minRight) { minParent->_left = minRight->_right; } else { minParent->_right = minRight->_right; } delete minRight; } return true; } } return false; }
从3和8两个节点中抽取处两种情况:
递归版本:
bool _EraseR(Node*& root, const K& key) { if (root == nullptr) { return false; } if (key < root->_key) { return _EraseR(root->_left, key); } else if (key > root->_key) { return _EraseR(root->_right, key); } else { //删除 //注意:此时的root是上一个节点的左节点或者右节点的引用 Node* del = root; if (root->_left == nullptr) { root = root->_right; } else if (root->_right == nullptr) { root = root->_left; } else { Node* minRight = root->_right; while (minRight->_left) { minRight = minRight->_left; } swap(minRight->_key, root->_key); return _EraseR(root->_right, key); } delete del; return true; } } bool EraseR(const K& key) { return _EraseR(_root, key); }
-
-
搜索二叉树的构造函数、拷贝析构函数/析构函数和赋值运算符重载
void DestoryTree(Node* root) { if (root == nullptr) return; DestoryTree(root->_left); DestoryTree(root->_right); delete root; } Node* CopyTree(Node* root) { if (root == nullptr) return nullptr; Node* copyNode = new Node(root->_key); copyNode->_left = CopyTree(root->_left); copyNode->_right = CopyTree(root->_right); return copyNode; } //构造函数 BSTree() :_root(nullptr) {} //拷贝构造函数:深拷贝 BSTree(const BSTree<K>& b) { _root = CopyTree(b._root); } //析构函数 ~BSTree() { DestoryTree(_root); _root = nullptr; } //赋值运算符重载 BSTree<K>& operator=(BSTree<K> b) { swap(_root, b._root); return (*this); } //t1 = t2 //问:为什么此处的参数要使用值拷贝? //答:此处调用了构造函数(BSTree<K> b(t2)),即使用t2来构造了一个b对象,然后通过交换b和t1的根节点即可实现我们的目标
注意:如果我们不想写构造函数,那么我们可以使用下面的这条指令来强制编译器自己生成构造:
BSTree() = default;//C++11才支持的,C++98不支持
1.3 二叉搜索树的模拟实现
代码:
#include<iostream>
using namespace std;
template<class K>
//struct BinarySearchTreeNode
struct BSTreeNode
{
BSTreeNode(const K& key)
:_left(nullptr)
,_right(nullptr)
,_key(key)
{}
BSTreeNode<K>* _left;
BSTreeNode<K>* _right;
K _key;
};
template<class K>
class BSTree
{
typedef BSTreeNode<K> Node;
private:
void DestoryTree(Node* root)
{
if (root == nullptr)
return;
DestoryTree(root->_left);
DestoryTree(root->_right);
delete root;
}
Node* CopyTree(Node* root)
{
if (root == nullptr)
return nullptr;
Node* copyNode = new Node(root->_key);
copyNode->_left = CopyTree(root->_left);
copyNode->_right = CopyTree(root->_right);
return copyNode;
}
//二叉搜索树的查找:递归版本子函数
bool _FindR(Node* root, const K& key)
{
if (root == nullptr)
return false;
if (root->_key < key)
{
return _FindR(root->_right, key);
}
else if (root->_key > key)
{
return _FindR(root->_left, key);
}
else
{
return true;
}
}
//二叉搜索树的插入:递归版本子函数
bool _InsertR(Node*& root, const K& key)
{
if (root == nullptr)
{
root = new Node(key);
return true;
}
if (key > root->_key)
{
return _InsertR(root->_right, key);
}
else if (key < root->_key)
{
return _InsertR(root->_left, key);
}
else
{
return false;
}
}
//二叉搜索树的删除:递归版本子函数
bool _EraseR(Node*& root, const K& key)
{
if (root == nullptr)
{
return false;
}
if (key < root->_key)
{
return _EraseR(root->_left, key);
}
else if (key > root->_key)
{
return _EraseR(root->_right, key);
}
else
{
//删除
//注意:此时的root是上一个节点的左节点或者右节点的引用
Node* del = root;
if (root->_left == nullptr)
{
root = root->_right;
}
else if (root->_right == nullptr)
{
root = root->_left;
}
else
{
Node* minRight = root->_right;
while (minRight->_left)
{
minRight = minRight->_left;
}
swap(minRight->_key, root->_key);
return _EraseR(root->_right, key);
}
delete del;
return true;
}
}
public:
//构造函数
BSTree()
:_root(nullptr)
{}
//拷贝构造函数:深拷贝
BSTree(const BSTree<K> & b)
{
_root = CopyTree(b._root);
}
//析构函数
~BSTree()
{
DestoryTree(_root);
_root = nullptr;
}
//t2 = t1
//赋值运算符重载
BSTree<K>& operator=(BSTree<K> b)
{
swap(_root, b._root);
return (*this);
}
//二叉搜索树的插入:非递归
bool Insert(const K& key)
{
if (_root == nullptr)
{
_root = new Node(key);
return true;
}
Node* cur = _root;
Node* parant = nullptr;
while (cur)
{
if (cur->_key > key)
{
parant = cur;
cur = cur->_left;
}
else if (cur->_key < key)
{
parant = cur;
cur = cur->_right;
}
else
{
return false;
}
}
cur = new Node(key);
if (key < parant->_key)
{
parant->_left = cur;
}
else
{
parant->_right = cur;
}
return true;
}
//二叉搜索树的插入:递归
bool InsertR(const K& key)
{
return _InsertR(_root, key);
}
//中序遍历
void InOrder()
{
_InOrder(_root);
}
//二叉搜索树的查找:非递归
bool Find(const K& key)
{
Node* cur = _root;
while (cur)
{
if (cur->_key > key)
{
cur = cur->_left;
}
else if (cur->_key < key)
{
cur = cur->_right;
}
else
{
return true;
}
}
return false;
}
//二叉搜索树的查找:递归
bool FindR(const K& key)
{
return _FindR(_root, key);
}
//二叉搜索树的删除:非递归
bool Erase(const K& key)
{
Node* cur = _root;
Node* parent = nullptr;
while (cur)
{
//下面是查找
if (cur->_key > key)
{
parent = cur;
cur = cur->_left;
}
else if (cur->_key < key)
{
parent = cur;
cur = cur->_right;
}
else//正式删除
{
//一个孩子左为空或者右为空
if (cur->_left == nullptr)//要删除的节点左子节点为空
{
if (_root == cur)//要删除的节点为根节点
{
_root = cur->_right;
}
else
{
if (cur == parent->_left)//要删除的节点在父节点的左边
{
parent->_left = cur->_right;
}
else//要删除的节点在父节点的右边
{
parent->_right = cur->_right;
}
}
delete cur;
}
else if (cur->_right == nullptr)//要删除的节点右子节点为空
{
if (_root == cur)//要删除的节点刚好为根节点
{
_root = cur->_left;
}
else
{
if (cur == parent->_left)//要删除的节点在父节点的左边
{
parent->_left = cur->_left;
}
else//要删除的节点在父节点的右边
{
parent->_right = cur->_left;
}
}
delete cur;
}
else//两个孩子都不为空的情况
{
Node* minParent = cur;
Node* minRight = cur->_right;//此处是选的右子树的最小节点
while (minRight->_left)
{
minParent = minRight;
minRight = minRight->_left;
}
swap(cur->_key, minRight->_key);
//Erase(key);
//问:为什么不能像上面这样写?答:因为交换之后,二叉搜索树就不符合二叉搜索树的规则了,递归会找不到key节点了
if (minParent->_left == minRight)
{
minParent->_left = minRight->_right;
}
else
{
minParent->_right = minRight->_right;
}
delete minRight;
}
return true;
}
}
return false;
}
//二叉搜索树的删除:递归
bool EraseR(const K& key)
{
return _EraseR(_root, key);
}
private:
void _InOrder(Node* root)
{
if (root == nullptr)
{
return;
}
_InOrder(root->_left);
cout << root->_key << " ";
_InOrder(root->_right);
}
Node* _root = nullptr;
};
1.4 二叉搜索树的应用
- K模型:K模型即只有key作为关键码,结构中只需要存储Key即可,关键码即为需要搜索到的值。 比如:给一个单词word,判断该单词是否拼写正确,具体方式如下:
- 以词库中所有单词集合中的每个单词作为key,构建一棵二叉搜索树
- 在二叉搜索树中检索该单词是否存在,存在则拼写正确,不存在则拼写错误。
- KV模型:每一个关键码key,都有与之对应的值Value,即键值对。该种方式在现实生活中非常常见:
- 比如英汉词典就是英文与中文的对应关系,通过英文可以快速找到与其对应的中文,英文单词与其对应的中文就构成一种键值对;
- 再比如统计单词次数,统计成功后,给定单词就可快速找到其出现的次数,单词与其出现次数就是就构成一种键值对。
template<class K, class V>
struct BSTreeNode
{
BSTreeNode(const K& key, const V& value)
:_left(nullptr)
, _right(nullptr)
, _key(key)
, _value(value)
{}
BSTreeNode<K, V>* _left;
BSTreeNode<K, V>* _right;
K _key;
V _value;
};
template<class K, class V>
class BSTree
{
typedef BSTreeNode<K, V> Node;
private:
public:
//二叉搜索树的插入:非递归
bool Insert(const K& key, const V& value)
{
if (_root == nullptr)
{
_root = new Node(key, value);
return true;
}
Node* cur = _root;
Node* parant = nullptr;
while (cur)
{
if (cur->_key > key)
{
parant = cur;
cur = cur->_left;
}
else if (cur->_key < key)
{
parant = cur;
cur = cur->_right;
}
else
{
return false;
}
}
cur = new Node(key, value);
if (key < parant->_key)
{
parant->_left = cur;
}
else
{
parant->_right = cur;
}
return true;
}
//中序遍历
void InOrder()
{
_InOrder(_root);
}
//二叉搜索树的查找:非递归
Node* Find(const K& key)
{
Node* cur = _root;
while (cur)
{
if (cur->_key > key)
{
cur = cur->_left;
}
else if (cur->_key < key)
{
cur = cur->_right;
}
else
{
return cur;
}
}
return nullptr;
}
//二叉搜索树的删除:非递归
bool Erase(const K& key)
{
Node* cur = _root;
Node* parent = nullptr;
while (cur)
{
//下面是查找
if (cur->_key > key)
{
parent = cur;
cur = cur->_left;
}
else if (cur->_key < key)
{
parent = cur;
cur = cur->_right;
}
else//正式删除
{
//一个孩子左为空或者右为空
if (cur->_left == nullptr)//要删除的节点左子节点为空
{
if (_root == cur)//要删除的节点为根节点
{
_root = cur->_right;
}
else
{
if (cur == parent->_left)//要删除的节点在父节点的左边
{
parent->_left = cur->_right;
}
else//要删除的节点在父节点的右边
{
parent->_right = cur->_right;
}
}
delete cur;
}
else if (cur->_right == nullptr)//要删除的节点右子节点为空
{
if (_root == cur)//要删除的节点刚好为根节点
{
_root = cur->_left;
}
else
{
if (cur == parent->_left)//要删除的节点在父节点的左边
{
parent->_left = cur->_left;
}
else//要删除的节点在父节点的右边
{
parent->_right = cur->_left;
}
}
delete cur;
}
else//两个孩子都不为空的情况
{
Node* minParent = cur;
Node* minRight = cur->_right;//此处是选的右子树的最小节点
while (minRight->_left)
{
minParent = minRight;
minRight = minRight->_left;
}
swap(cur->_key, minRight->_key);
//Erase(key);
//问:为什么不能像上面这样写?答:因为交换之后,二叉搜索树就不符合二叉搜索树的规则了,递归会找不到key节点了
if (minParent->_left == minRight)
{
minParent->_left = minRight->_right;
}
else
{
minParent->_right = minRight->_right;
}
delete minRight;
}
return true;
}
}
return false;
}
private:
void _InOrder(Node* root)
{
if (root == nullptr)
{
return;
}
_InOrder(root->_left);
cout << root->_key << " ";
_InOrder(root->_right);
}
Node* _root = nullptr;
};
void TestBSTree1()
{
// 输入单词,查找单词对应的中文翻译
key_value::BSTree<string, string> dict;
dict.Insert("string", "字符串");
dict.Insert("tree", "树");
dict.Insert("left", "左边、剩余");
dict.Insert("right", "右边");
dict.Insert("sort", "排序");
// 插入词库中所有单词
string str;
while (cin >> str)
{
key_value::BSTreeNode<string, string>* ret = dict.Find(str);
if (ret == nullptr)
{
cout << "单词拼写错误,词库中没有这个单词:" << str << endl;
}
else
{
cout << str << "中文翻译:" << ret->_value << endl;
}
}
}
void TestBSTree2()
{
// 统计水果出现的次数
string arr[] = { "苹果", "西瓜", "苹果", "西瓜", "苹果", "苹果", "西瓜",
"苹果", "香蕉", "苹果", "香蕉" };
key_value::BSTree<string, int> countTree;
for (const auto& str : arr)
{
// 先查找水果在不在搜索树中
// 1、不在,说明水果第一次出现,则插入<水果, 1>
// 2、在,则查找到的节点中水果对应的次数++
//BSTreeNode<string, int>* ret = countTree.Find(str);
auto ret = countTree.Find(str);
if (ret == NULL)
{
countTree.Insert(str, 1);
}
else
{
ret->_value++;
}
}
countTree.InOrder();
}
问:while(cin >> str)
代码中的cin >> str
为什么能当判断条件?
答:
上面的两个函数的意思是istream类型的对象可以转换为void*或者bool,即上面的代码能够作为判断条件的原因就是调用了这个函数。
cin.operator bool();
我们也可以使用上面的方式对自定义类进行类似的函数重载:
class A
{
public:
operator bool()const
{
return true;
}
};
int main()
{
A a;
if (a)
{
cout << "hello " << endl;
}
return 0;
}
运行结果:
问:为什么不用()作为强制类型转换运算符重载?
答:因为()已经被仿函数使用了。
问:explict是什么意思?
答:是为了防止隐式类型转换发生的。例如我们这样定义A类:class A { public: explict operator bool()const { return true; } };
就不能这样写代码:
A a; bool ret = a;//此处无法发生从类型A到bool类型的隐式类型的转换。
1.5 二叉搜索树的性能分析
插入和删除操作都必须先查找,查找效率代表了二叉搜索树中各个操作的性能。
对有n个结点的二叉搜索树,若每个元素查找的概率相等,则二叉搜索树平均查找长度是结点在二 叉搜索树的深度的函数,即结点越深,则比较次数越多。
但对于同一个关键码集合,如果各关键码插入的次序不同,可能得到不同结构的二叉搜索树:
最优情况下,二叉搜索树为完全二叉树(或者接近完全二叉树),其平均比较次数为:log2N
最差情况下,二叉搜索树退化为单支树(或者类似单支),其平均比较次数为:N
2. 二叉树进阶面试题
-
二叉树创建字符串。
代码:
//方法一:存在多处string拷贝构造,所以并不是特别好 class Solution { public: string tree2str(TreeNode* root) { if(root == nullptr) return ""; string str; str += to_string(root->val); //左不为空 if(root->left) { str += "("; str += tree2str(root->left); str += ")"; } //右不为空 if(root->right) { if(root->left == nullptr)//左为空且右不为空的情况,此时不能省略左子树的() str += "()"; str += "("; str += tree2str(root->right); str += ")"; } return str; } }; //方法二: class Solution { public: void _tree2str(TreeNode* root, string& str) { if(root == nullptr) return; str += to_string(root->val); //左不为空 if(root->left) { str += "("; _tree2str(root->left, str); str += ")"; } //右不为空 if(root->right) { if(root->left == nullptr)//左为空右不为空的情况 str += "()"; str += "("; _tree2str(root->right, str); str += ")"; } } string tree2str(TreeNode* root) { string ret; _tree2str(root, ret); return ret; } };
-
二叉树的分层遍历1
class Solution { public: vector<vector<int>> levelOrder(TreeNode* root) { vector<vector<int>> vv; if(root == nullptr) return vv; queue<TreeNode*> q; q.push(root); int levelSize = 1;//levelSize控制每层节点的个数 while(!q.empty()) { vector<int> levelV;//存储每一层的节点存储的值 while(levelSize--)//当levelSize为0时就说明当前这一层已经没有节点了 { TreeNode* front = q.front(); levelV.push_back(front->val); if(front->left != nullptr) { q.push(front->left); } if(front->right != nullptr) { q.push(front->right); } q.pop(); } levelSize = q.size(); vv.push_back(levelV); } return vv; } };
-
二叉树的分层遍历2
代码:
class Solution { public: vector<vector<int>> levelOrder(TreeNode* root) { vector<vector<int>> vv; if(root == nullptr) return vv; queue<TreeNode*> q; q.push(root); int levelSize = 1;//levelSize控制每层节点的个数 while(!q.empty()) { vector<int> levelV;//存储每一层的节点存储的值 while(levelSize--)//当levelSize为0时就说明当前这一层已经没有节点了 { TreeNode* front = q.front(); levelV.push_back(front->val); if(front->left != nullptr) { q.push(front->left); } if(front->right != nullptr) { q.push(front->right); } q.pop(); } levelSize = q.size(); vv.push_back(levelV); } return vv; } };
-
给定一个二叉树, 找到该树中两个指定节点的最近公共祖先
代码:
class Solution { public: //判断x节点是否在root这颗树中 bool IsInSubtree(TreeNode* root, TreeNode* x) { if(root == nullptr) return false; if(root == x) return true; return IsInSubtree(root->left, x) || IsInSubtree(root->right, x); } TreeNode* lowestCommonAncestor(TreeNode* root, TreeNode* p, TreeNode* q) { if(root == nullptr) return nullptr; if(root == p || root == q)//判断的是某个节点就是它们的最近公共祖先的情况 return root; bool pInLeft = IsInSubtree(root->left, p); bool pInRight = !pInLeft; bool qInLeft = IsInSubtree(root->left, q); bool qInRight = !qInLeft; if((pInLeft && qInRight) || (pInRight && qInLeft))//是最近公共祖先的情况 return root; else if(pInLeft && qInLeft)//两个节点都在左子树的情况 { return lowestCommonAncestor(root->left, p, q); } else if(pInRight && qInRight)//两个节点都在右子树的情况 { return lowestCommonAncestor(root->right, p, q); } else { return nullptr; } } };
总共有两种情况:
情况一(正常情况):
情况二(非正常情况):
上面代码的时间复杂度非常高,能达到O(n2),
下面是能够让时间复杂度能够到达O(n)的方法:
-
该树为搜索树
-
该树为三叉树
-
方法如下:
图示:
代码:
class Solution { public: bool FindPath(TreeNode* root, TreeNode* x, stack<TreeNode*>& path) { if(root == nullptr) { return false; } path.push(root); if(root == x) { return true; } if(FindPath(root->left, x, path)) return true; if(FindPath(root->right, x, path)) return true; //root不是要找的节点,左子树和右子树都没有找到,那么root不是x的路径中的节点,要出栈 path.pop();//对应前面的push return false; } TreeNode* lowestCommonAncestor(TreeNode* root, TreeNode* p, TreeNode* q) { stack<TreeNode*> pathP; stack<TreeNode*> pathQ; FindPath(root, p, pathP); FindPath(root, q, pathQ); //路径长的先走 while(pathP.size() > pathQ.size()) { pathP.pop(); } while(pathP.size() < pathQ.size()) { pathQ.pop(); } while(pathQ.top() != pathP.top()) { pathQ.pop(); pathP.pop(); } return pathP.top(); } };
-
-
二叉树搜索树转换成排序双向链表
图示:
代码:
class Solution {
public:
void InOrderConvert(TreeNode* cur, TreeNode*& prev)//通过中序遍历的方式进行转换
{
if(cur == nullptr)
return;
InOrderConvert(cur->left, prev);//左子树
cur->left = prev;
if(prev)
prev->right = cur;
prev = cur;//根节点
InOrderConvert(cur->right, prev);//右子树
}
TreeNode* Convert(TreeNode* pRootOfTree) {
if(pRootOfTree == nullptr)//当所给的树为空的时候
return nullptr;
TreeNode* prev = nullptr;
InOrderConvert(pRootOfTree, prev);
TreeNode* head = pRootOfTree;
while(head->left)//找到链表的起始节点
{
head = head->left;
}
return head;
}
};
-
根据一棵树的前序遍历与中序遍历构造二叉树
代码:
class Solution { public: TreeNode* _buildTree(vector<int>& preorder, vector<int>& inorder, int& prei, int inBegin, int inEnd) { //中序区间不存在,说明递归创建的子树是空 if(inBegin > inEnd) return nullptr; TreeNode* root = new TreeNode(preorder[prei]); ++prei; size_t rooti = inBegin; while(rooti <= inEnd) { if(root->val == inorder[rooti]) break; else rooti++; } //[inBegin, rooti - 1]rooti[rooti + 1, inEnd] root->left = _buildTree(preorder, inorder, prei, inBegin, rooti - 1); root->right = _buildTree(preorder, inorder, prei, rooti + 1, inEnd); return root; } TreeNode* buildTree(vector<int>& preorder, vector<int>& inorder) { int prei = 0; TreeNode* root = _buildTree(preorder, inorder, prei, 0, inorder.size() - 1); return root; } };
-
根据一棵树的中序遍历与后序遍历构造二叉树
class Solution { private: TreeNode* build(vector<int>& inorder, vector<int>& postorder,int root,int start,int end){ if(start>end){ return nullptr; } //创建根节点 TreeNode* newTree = new TreeNode(postorder[root]); int i = start; while((i < end) && inorder[i] != postorder[root]) i++; newTree->left = build(inorder,postorder,root - 1 - (end - i),start,i-1); newTree->right = build(inorder,postorder,root-1,i+1,end); return newTree; } public: TreeNode* buildTree(vector<int>& inorder, vector<int>& postorder) { int n = postorder.size(); return build(inorder,postorder,n-1,0,n-1); } };
-
二叉树的前序遍历,非递归迭代实现
图示:
代码:
class Solution { public: vector<int> preorderTraversal(TreeNode* root) { vector<int> v; stack<TreeNode*> s; TreeNode* cur = root; while(cur || !s.empty()) { //遍历左路节点,左路节点入栈 -- 访问一棵树的开始 while(cur) { v.push_back(cur->val); s.push(cur); cur = cur->left; } //此时cur为空 //依此取左路节点的右子树访问 TreeNode* top = s.top(); s.pop(); //访问左路节点的右子树:子问题 cur = top->right; } return v; } };
-
二叉树中序遍历 ,非递归迭代实现
思路和上一个题类似,改变的只是访问根节点的位置。
代码:
class Solution { public: vector<int> inorderTraversal(TreeNode* root) { vector<int> v; stack<TreeNode*> s; TreeNode* cur = root; while(cur || !s.empty()) { //遍历左路节点,左路节点入栈 -- 访问一棵树的开始 while(cur) { s.push(cur); cur = cur->left; } //此时cur为空 //依此取左路节点的右子树访问 TreeNode* top = s.top(); s.pop(); v.push_back(top->val); //访问左路节点的右子树:子问题 cur = top->right; } return v; } };
-
二叉树的后序遍历 ,非递归迭代实现
图示:
代码:
class Solution { public: vector<int> postorderTraversal(TreeNode* root) { vector<int> v; stack<TreeNode*> s; TreeNode* cur = root; TreeNode* prev = nullptr;//记录上一个访问过的节点 while(cur || !s.empty()) { while(cur) { s.push(cur); cur = cur->left; } TreeNode* top = s.top(); //1、左子树已经访问过了,如果top的右子树为空或者右子树已经被访问 //2、top->right != nullptr且右子树还没有访问,子问题迭代访问 if(top->right == nullptr || top->right == prev) { v.push_back(top->val); s.pop(); prev = top; } else { cur = top->right; } } return v; } };