目录
1. 二叉搜索树 1.1 二叉搜索树的结构 1.2 二叉搜索树的接口及其优点与不足 1.3 二叉搜索树自实现 1.3.1 二叉树结点结构 1.3.2 查找 1.3.3 插入 1.3.4 删除 1.3.5 中序遍历
2. 二叉树进阶相关练习 2.1 根据二叉树创建字符串 2.2 二叉树的层序遍历I 2.3 二叉树层序遍历II 2.4 二叉树最近公共祖先结点 2.5 二叉树搜索与双向链表 2.6 从前序与中序遍历序列构造二叉树 2.7 从中序与后序遍历序列构造二叉树 2.8 二叉树前序遍历(非递归) 2.9 二叉树中序遍历(非递归) !!!3.10 二叉树后序遍历(非递归)
1. 二叉搜索树
1.1 二叉搜索树的结构
搜索二叉树中的所有结点都满足 <1> 若左子树不为空,则其所有结点的键值都小于父结点 <2> 若右子树不为空,则其所有结点的键值都大于父节点 <3> 左右子树都为二叉搜索树
1.2 二叉搜索树的接口及其优点与不足
搜索二叉树的功能接口 <1> 数据插入(Insert) <2> 数据删除(Erase) <3> 数据查寻(Find) <4> 中序遍历(InOrder)
二叉搜索树的优点: <1> 正如其名,此种数据存储结构,尤其擅长数据搜索,其进行数据搜索的效率极高。 <2> 在其结构为近似完全二叉树或满二叉树的情况时,其的搜索效率可以达到O(
l
o
g
N
logN
l o g N ) <3> 当以中序遍历的方式遍历整棵搜索树时,得到的数据即为升序序列
二叉搜索树的不足: <1> 根据二叉搜索树的插入逻辑,当数据以完全升序或降序插入时,会使得二叉树退化为单枝结构,此种结构下其搜索效率也退化为O(N)
1.3 二叉搜索树自实现
1.3.1 二叉树结点结构
搜索树结点:由左右孩子结点指针与key组成 搜索树:由根节点与类方法构成
template < class T >
struct BinarySearchNode
{
typedef BinarySearchNode< T> BSNode;
BSNode* _left;
BSNode* _right;
T _key;
BinarySearchNode ( T key)
: _left ( nullptr )
, _right ( nullptr )
, _key ( key)
{ }
} ;
template < class T >
class BinarySearchTree
{
public :
typedef BinarySearchNode< T> BSNode;
bool Find ( T key) ;
bool Insert ( T key) ;
bool Erase ( T key) ;
void InOrder ( ) ;
private :
BSNode* _root = nullptr ;
} ;
1.3.2 查找
查找实现:在一颗搜索树中搜寻一个数据是否存在 查找逻辑: <1> 若查找值小于当前结点的key值,向左搜寻,向左孩子查找 <2> 若查找值大于当前结点的key值,向右搜寻,向右孩子查找 <3> 查找遍历至空结点,则证明搜索树中不存在此值
bool Find ( T key)
{
BSNode* cur = _root;
while ( cur)
{
if ( key < cur-> _key)
{
cur = cur-> _left;
}
else if ( key > cur-> _key)
{
cur = cur-> _right;
}
else
{
return true ;
}
}
return false ;
}
bool _FindR ( BSNode* cur, T key)
{
if ( cur == nullptr )
{
return false ;
}
if ( key < cur-> _key)
{
return _FindR ( cur-> _left, key) ;
}
else if ( key > cur-> _key)
{
return _FindR ( cur-> _right, key) ;
}
else
{
return true ;
}
assert ( true ) ;
return - 1 ;
}
bool FindR ( T key)
{
return _FindR ( _root, key) ;
}
1.3.3 插入
搜寻插入位置: <1> key小向左 <2> key大向右 <3> 直至遍历到空 <1> 若遇key相同的结点,停止插入,返回false <2> 遍历至空,创建结点,链接结点
bool Insert ( T key)
{
if ( _root == nullptr )
{
_root = new BSNode ( key) ;
return true ;
}
BSNode* cur = _root;
BSNode* parent = nullptr ;
while ( cur)
{
if ( key < cur-> _key)
{
parent = cur;
cur = cur-> _left
}
else if ( key > cur-> _key)
{
parent = cur;
cur = cur-> _right;
}
else
{
return false ;
}
}
if ( key < parent-> _key)
{
parent-> _left = new BSNode ( key) ;
}
else
{
parent-> _right = new BSNode ( key) ;
}
return true ;
}
bool _InsertR ( BSNode* & cur, T key)
{
if ( cur == nullptr )
{
cur = new BSNode ( key) ;
return true ;
}
if ( key < cur-> _key)
{
return _InsertR ( cur-> _left, key) ;
}
else if ( key > cur-> _key)
{
return _InsertR ( cur-> _right, key) ;
}
else
{
return false ;
}
assert ( true ) ;
return false ;
}
bool InsertR ( T key)
{
if ( _root == nullptr )
{
_root = new BSNode ( key) ;
return true ;
}
return _InsertR ( _root, key) ;
}
1.3.4 删除
删除结点后,不能破坏搜索树的结构 删除不同类型结点的场景,删除逻辑: <1> 叶子结点,直接删除,父节点链接指针置空 <2> 单子树结点,托孤,即,将自己的子树链接给父结点 <3> 双子树结点,寻找key值最接近的结点(左子树的最右结点,右子树的最左结点),值替换被删除的结点,而后删除被替换的结点
叶子结点 单子树结点 双子树结点
bool Erase ( T key)
{
BSNode* cur = _root;
BSNode* parent = nullptr ;
while ( cur)
{
if ( key < cur-> _key)
{
parent = cur;
cur = cur-> _left;
}
else if ( key > cur-> _key)
{
parent = cur;
cur = cur-> _right;
}
else
{
if ( cur-> _left == nullptr || cur-> _right == nullptr )
{
if ( cur == _root)
{
if ( cur-> _left)
{
_root = cur-> _left;
}
else
{
_root = cur-> _right;
}
delete cur;
}
else
{
if ( cur-> _left)
{
if ( cur == parent-> _left)
{
parent-> _left = cur-> _left;
}
else
{
parent-> _right = cur-> _left;
}
delete cur;
}
else
{
if ( cur == parent-> _left)
{
parent-> _left = cur-> _right;
}
else
{
parent-> _right = cur-> _right;
}
delete cur;
}
}
return true ;
}
else
{
BSNode* RightParent = cur;
BSNode* RightMin = cur-> _right;
while ( RightMin-> _left)
{
RightParent = RightMin;
RightMin = RightMin-> _left;
}
cur-> _key = RightMin-> _key;
if ( RightMin == RightParent-> _left)
{
RightParent-> _left = RightMin-> _right;
}
else
{
RightParent-> _right = RightMin-> _right;
}
delete RightMin;
return true ;
}
}
}
return false ;
}
bool _EraseR ( BSNode* & cur, T key)
{
if ( cur == nullptr )
{
return false ;
}
if ( key < cur-> _key)
{
return _EraseR ( cur-> _left, key) ;
}
else if ( key > cur-> _key)
{
return _EraseR ( cur-> _right, key) ;
}
else
{
if ( cur-> _left == nullptr || cur-> _right == nullptr )
{
BSNode* del = cur;
if ( cur-> _left)
{
cur = cur-> _left;
}
else
{
cur = cur-> _right;
}
delete del;
return true ;
}
else
{
BSNode* RightMin = cur-> _right;
while ( RightMin-> _left)
{
RightMin = RightMin-> _left;
}
cur-> _key = RightMin-> _key;
_EraseR ( cur-> _right, cur-> _key) ;
}
}
assert ( true ) ;
return false ;
}
1.3.5 中序遍历
void _InOrder ( BSNode* cur)
{
if ( cur == nullptr )
{
return ;
}
_InOrder ( cur-> _left) ;
cout << cur-> _key << " " ;
_InOrder ( cur-> _right) ;
}
void InOrder ( )
{
_InOrder ( _root) ;
cout << endl;
}
2. 二叉树进阶相关练习
2.1 根据二叉树创建字符串
题目信息: 题目连接: 根据二叉树创建字符串
class Solution
{
public :
void _tree2str ( string& str, TreeNode* cur)
{
if ( cur == nullptr )
{
return ;
}
str += to_string ( cur-> val) ;
if ( cur-> left || cur-> right)
{
str += "(" ;
_tree2str ( str, cur-> left) ;
str += ")" ;
}
if ( cur-> right)
{
str += "(" ;
_tree2str ( str, cur-> right) ;
str += ")" ;
}
}
string tree2str ( TreeNode* root)
{
string ret;
_tree2str ( ret, root) ;
return ret;
}
} ;
2.2 二叉树的层序遍历I
题目信息: 题目连接: 二叉树的层序遍历I 思路:levelsize计数
class Solution
{
public :
vector< vector< int >> levelOrder ( TreeNode* root)
{
vector< vector< int >> ret;
queue< TreeNode* > q;
int levelsize = 1 ;
q. push ( root) ;
if ( root == nullptr )
{
return ret;
}
while ( ! q. empty ( ) )
{
vector< int > count;
while ( levelsize-- )
{
TreeNode* top = q. front ( ) ;
q. pop ( ) ;
count. push_back ( top-> val) ;
if ( top-> left)
q. push ( top-> left) ;
if ( top-> right)
q. push ( top-> right) ;
}
levelsize = q. size ( ) ;
ret. push_back ( count) ;
}
return ret;
}
} ;
2.3 二叉树层序遍历II
题目信息: 题目连接: 二叉树层序遍历II 思路:反转自上至下的层序遍历
class Solution
{
public :
vector< vector< int >> levelOrderBottom ( TreeNode* root)
{
vector< vector< int >> ret;
if ( root == nullptr )
{
return ret;
}
queue< TreeNode* > q;
int levelsize = 1 ;
q. push ( root) ;
while ( ! q. empty ( ) )
{
vector< int > count;
while ( levelsize-- )
{
TreeNode* cur = q. front ( ) ;
q. pop ( ) ;
count. push_back ( cur-> val) ;
if ( cur-> left)
q. push ( cur-> left) ;
if ( cur-> right)
q. push ( cur-> right) ;
}
levelsize = q. size ( ) ;
ret. push_back ( count) ;
}
reverse ( ret. begin ( ) , ret. end ( ) ) ;
return ret;
}
} ;
2.4 二叉树最近公共祖先结点
题目信息: 二叉树最近公共祖先结点 思路: <1> 方法1:p与q一定分别在祖先结点的左侧与右侧,祖先结点可能是自己 <2> 方法2:栈记录遍历路径,路径上的所有祖先结点
class Solution
{
public :
bool SearchNode ( TreeNode* cur, TreeNode* search)
{
if ( cur == nullptr )
{
return false ;
}
if ( cur == search)
{
return true ;
}
return SearchNode ( cur-> left, search) || SearchNode ( cur-> right, search) ;
}
TreeNode* lowestCommonAncestor ( TreeNode* root, TreeNode* p, TreeNode* q)
{
bool qInleft = false ;
bool qInright = false ;
bool pInleft = false ;
bool pInright = false ;
TreeNode* cur = root;
while ( cur)
{
if ( cur == p)
{
pInleft = pInright = true ;
}
else if ( SearchNode ( cur-> left, p) )
{
pInleft = true ;
pInright = false ;
}
else
{
pInright = true ;
pInleft = false ;
}
if ( cur == q)
{
qInleft = qInright = true ;
}
else if ( SearchNode ( cur-> left, q) )
{
qInleft = true ;
qInright = false ;
}
else
{
qInright = true ;
qInleft = false ;
}
if ( ( pInleft && qInright) || ( pInright && qInleft) )
break ;
else if ( pInleft && qInleft)
cur = cur-> left;
else
cur = cur-> right;
}
return cur;
}
} ;
class Solution
{
public :
bool PreOrder ( TreeNode* cur, TreeNode* search, stack< TreeNode* > & count)
{
if ( cur == nullptr )
{
return false ;
}
count. push ( cur) ;
if ( cur == search)
{
return true ;
}
if ( ! PreOrder ( cur-> left, search, count) && ! PreOrder ( cur-> right, search, count) )
{
count. pop ( ) ;
}
else
{
return true ;
}
assert ( true ) ;
return false ;
}
TreeNode* lowestCommonAncestor ( TreeNode* root, TreeNode* p, TreeNode* q)
{
stack< TreeNode* > p_st;
stack< TreeNode* > q_st;
PreOrder ( root, p, p_st) ;
PreOrder ( root, q, q_st) ;
while ( p_st. size ( ) != q_st. size ( ) )
{
if ( p_st. size ( ) > q_st. size ( ) )
p_st. pop ( ) ;
else
q_st. pop ( ) ;
}
while ( p_st. top ( ) != q_st. top ( ) )
{
p_st. pop ( ) ;
q_st. pop ( ) ;
}
return p_st. top ( ) ;
}
} ;
2.5 二叉树搜索与双向链表
题目信息: 题目链接: 二叉搜索树与双向链表 思路:记录前置结点,中序遍历,注意调整指针链接的时机
class Solution
{
public :
void InOrder ( TreeNode* cur, TreeNode* & pre)
{
if ( cur == nullptr )
{
return ;
}
InOrder ( cur-> left, pre) ;
cur-> left = pre;
if ( pre)
pre-> right = cur;
pre = cur;
InOrder ( cur-> right, pre) ;
}
TreeNode* Convert ( TreeNode* pRootOfTree)
{
if ( pRootOfTree == nullptr )
{
return nullptr ;
}
TreeNode* pre = nullptr ;
InOrder ( pRootOfTree, pre) ;
while ( pRootOfTree-> left)
{
pRootOfTree = pRootOfTree-> left;
}
return pRootOfTree;
}
} ;
2.6 从前序与中序遍历序列构造二叉树
题目信息: 题目链接: 从前序与中序遍历序列构造二叉树 思路:根据根分割出左右子树,区域分割,引用
class Solution
{
public :
TreeNode* buildNode ( vector< int > & preorder, vector< int > & inorder, int & i, int left, int right)
{
if ( left > right)
{
return nullptr ;
}
int j = 0 ;
while ( inorder[ j] != preorder[ i] )
{
j++ ;
}
TreeNode* newnode = new TreeNode ( preorder[ i++ ] ) ;
newnode-> left = buildNode ( preorder, inorder, i, left, j - 1 ) ;
newnode-> right = buildNode ( preorder, inorder, i, j + 1 , right) ;
return newnode;
}
TreeNode* buildTree ( vector< int > & preorder, vector< int > & inorder)
{
int i = 0 ;
return buildNode ( preorder, inorder, i, 0 , inorder. size ( ) - 1 ) ;
}
} ;
2.7 从中序与后序遍历序列构造二叉树
题目信息: 题目链接: 从中序与后序遍历序列构造二叉树 思路:区域分割判断,引用,逆向遍历,根右左
class Solution
{
public :
TreeNode* buildNode ( vector< int > & inorder, vector< int > & postorder, int & i, int left, int right)
{
if ( left > right)
{
return nullptr ;
}
int j = 0 ;
while ( inorder[ j] != postorder[ i] )
{
j++ ;
}
TreeNode* newnode = new TreeNode ( postorder[ i-- ] ) ;
newnode-> right = buildNode ( inorder, postorder, i, j + 1 , right) ;
newnode-> left = buildNode ( inorder, postorder, i, left, j - 1 ) ;
return newnode;
}
TreeNode* buildTree ( vector< int > & inorder, vector< int > & postorder)
{
int i = postorder. size ( ) - 1 ;
return buildNode ( inorder, postorder, i, 0 , postorder. size ( ) - 1 ) ;
}
} ;
2.8 二叉树前序遍历(非递归)
题目信息: 题目链接: 二叉树前序遍历 思路:根左右,插入右子树时就将根删除,栈记录
class Solution
{
public :
vector< int > preorderTraversal ( TreeNode* root)
{
vector< int > ret;
TreeNode* cur = root;
stack< TreeNode* > st;
while ( cur || ! st. empty ( ) )
{
while ( cur)
{
st. push ( cur) ;
ret. push_back ( cur-> val) ;
cur = cur-> left;
}
TreeNode* top = st. top ( ) ;
st. pop ( ) ;
cur = top-> right;
}
return ret;
}
} ;
2.9 二叉树中序遍历(非递归)
题目信息: 题目链接: 二叉树中序遍历 思路:插入时机,删除时插入,左右根
class Solution {
public :
vector< int > inorderTraversal ( TreeNode* root)
{
vector< int > ret;
stack< TreeNode* > st;
TreeNode* cur = root;
while ( cur || ! st. empty ( ) )
{
while ( cur)
{
st. push ( cur) ;
cur = cur-> left;
}
TreeNode* top = st. top ( ) ;
st. pop ( ) ;
ret. push_back ( top-> val) ;
cur = top-> right;
}
return ret;
}
} ;
!!!3.10 二叉树后序遍历(非递归)
题目信息: 题目链接: 二叉树后续遍历 思路:二次遍历时删除,删除时插入,何时删除
class Solution
{
public :
vector< int > postorderTraversal ( TreeNode* root)
{
vector< int > ret;
stack< TreeNode* > st;
TreeNode* cur = root;
TreeNode* pre = nullptr ;
while ( cur || ! st. empty ( ) )
{
while ( cur)
{
st. push ( cur) ;
cur = cur-> left;
}
TreeNode* top = st. top ( ) ;
if ( top-> right == nullptr || pre == top-> right)
{
st. pop ( ) ;
pre = top;
ret. push_back ( top-> val) ;
}
else
{
cur = top-> right;
}
}
return ret;
}
} ;