一、二叉树概念

1. 二叉树的定义

二叉树是有序树有左右之分,可为空。每个结点至多有俩子树。
**在这里插入图片描述**

  • 满二叉树:高度 h h h,有2h- 1个结点,
    • 每层含有最多的结点,即:每行都是满的
    • 结点i的双亲若存在,则为 ⌊ i 2 ⌋ \left\lfloor \frac { i }{ 2 } \right\rfloor 2i
  • 完全二叉树:高度 h h h,有 n n n个结点,
    • 前n-1行都满,最后一行从左开始排,可不满
    • i ≤ ⌊ n 2 ⌋ i\le \left\lfloor \frac { n }{ 2 } \right\rfloor i2n ,则结点 i i i为分支结点,否则为叶结点。
  • 二叉排序树 - BST(又名:二叉搜索树、二叉查找树):
    • 空二叉树 or 每个“根节点”的左子树所有结点 < 该根节点 < 右子树所有结点的二叉树。
  • 平衡二叉树 - AVL:
    • 树上任一结点左右子树深度之差<1。(树的深度:树中最大的结点层)

2. 二叉树的存储结构

  • 顺序存储结构:一维数组下标从1开始存,适合存储满2叉or完全2叉。
  • 链式存储结构:含有n个结点的二叉链表中,有n+1个空链域。
typedef struct Tree{
	int value;
	Tree* pLeft;
	Tree* pRight;
}Tree,*binaryTree;

二、二叉树的遍历

前序、中序、后序以及层次遍历(递归与非递归)

1. 如何遍历一棵树

有两种通用的遍历树的策略:

  • 深度优先搜索(DFS)
    • 在这个策略中,我们采用深度作为优先级,以便从跟开始一直到达某个确定的叶子,然后再返回根到达另一个分支。
    • 深度优先搜索策略又可以根据根节点、左孩子和右孩子的相对顺序被细分为:先序遍历,中序遍历和后序遍历
  • 广度优先搜索(BFS)
    • 我们按照高度顺序,一层一层的访问整棵树,高层次的节点将会比低层次的节点先被访问到。

2. 二叉树遍历的递归实现

二叉树的先序遍历NLR

/* 递归实现前序遍历 */
void preOrderRecur(binaryTree T){
	if(T == NULL) 
         return;
	printf("%d ",T->value);
	preOrderRecur(T->pLeft);
	preOrderRecur(T->pRight);
}

二叉树的中序遍历LNR

/* 递归实现中序遍历 */
void inOrderRecur(binaryTree T){
	if(T == NULL)
		return;
	inOrderRecur(T->pLeft);
	printf("%d ", T->value);
 	inOrderRecur(T->pRight);
}

二叉树的后序遍历LRN

/* 递归实现后续遍历 */
void posOrderRecur(binaryTree T){
	if(T == NULL)
		return;
	posOrderRecur(T->pLeft); 
 	posOrderRecur(T->pRight);
	printf("%d ", T->value);
}

3. 二叉树遍历的非递归实现

二叉树的先序遍历NLR

(1)将根节点压入栈;
(2)弹出栈顶元素,打印栈顶元素的值,如果该元素的右子树不为空,则先将右子数压入栈,如果该元素的左子树也不为空,则将该元素的左子树也压入栈;
(3)重复步骤2直到栈为空。
注:假设有结点root-L(LL、LR)-R(RL、RR)

弹出根节点rootLLLLRRRLRR
栈顶根节点root根节点的左子树 LL的左子树 LLLRRRLRR
根节点的右子树 RL的右子树 LRRRR
R
/* 非递归实现先序遍历,用栈,LeetCode144 */
void preOrderUnRecur(binaryTree T){
	if(T == NULL)
		return;
	stack<binaryTree> S;   //定义一个栈
	S.push(T); //压入根节点	
	while(!S.empty()){
		//弹出栈顶元素
		binaryTree tempNode = S.top();
		S.pop();
		printf("%d ", tempNode->value); 
		//先压入右子树,后压入左子树
		if(tempTree != NULL)
			S.push(tempNode->pRight); 
		if(tempTree != NULL)
			s.push(tempNode->pLeft);
	}
}

二叉树的中序遍历LNR

(1)定义一个当前节点,将根节点指针赋给当前节点;
(2)如果当前节点不为空,就将当前节点压入栈,并将当前节点更新为其左子树;
(3)如果当前节点为空,就弹出栈顶元素,并打印该元素值。然后将当前节点更新为该元素值的右子树;
(4)一直重复(2)或(3)直到栈为空而且当前元素值也为空。
例如:root - L(LL, LR) - R

弹出LLLLRrootR
当前节点rootLLL空(LLL)空(LLR)LR空(LRL)空(LRR)R空(RL)
栈顶rootLLLLrootLRrootR
rootLrootroot
root
/* 非递归实现中序遍历 LeetCode94*/
void inOrderUnrecur(binaryTree T){
	if(T == NULL)
		return;
	stack<binaryTree> S;
	binaryTree tempNode = T;  // tempNode是遍历指针,定义一个当前节点,将根节点指针赋给当前节点;
	while(!S.empty() || tempNode != NULL)  // 栈不空或temp不空时循环
	{   // 先把temp压入栈中,依次把左边界压入栈中,即不停的令cur=cur.left,重复步骤2
		if(tempNode != NULL) 
		{
			S.push(tempNode);
			tempNode = tempNode->pLeft;
		}
		else//不断重复2直到为null,从栈中弹出栈顶节点,打印它的值,并令temp=node.right,重复步骤2
		{
			tempNode = S.top();
			printf("%d ",tempNode->value);//第二次遇见的时候输出
			S.pop();
			tempNode = tempNode->pRight;
		}
	}
}

二叉树的后序遍历LRN

迭代 莫里斯遍历
/*非递归实现后序遍历 LeetCode145*/
/*** Definition for a binary tree node.
 * struct TreeNode {
 *     int val;
 *     TreeNode *left;
 *     TreeNode *right;
 *     TreeNode(int x) : val(x), left(NULL), right(NULL) {}
 * };
*/

//迭代,利用莫里斯遍历,核心模板如下:利用一个pre指针保存前一个结点的情况来判断是否重复计数
class Solution {
public:
    vector<int> postorderTraversal(TreeNode* root) {
        stack<TreeNode*> mystack;
        vector<int> ans;
        TreeNode* curr = root;
        TreeNode* pre = NULL;        
        while(curr || !mystack.empty()){
            while(curr){
                mystack.push(curr);
                curr = curr->left;
            }
            curr = mystack.top();  
                      
            //若右节点已经访问过或者没有右节点  则输出该节点值
            if(!curr->right || pre == curr->right){
                mystack.pop();
                ans.push_back(curr->val);    
                pre = curr;
                curr = NULL;
            }else{ // 右节点存在且没有访问过
                curr = curr->right;
                pre = NULL;
            }
        }
        return ans;
    }
};

在这里插入图片描述

双栈法
/* 双栈法,栈1从左至右依次层次遍历,栈2作为记录栈,最后将栈2中结点值逆序输出(正常出栈)即可 */
// 出栈2:左右根,那么入2:根右左,那么1出:根右左,1入就是先左后右
class Solution {
public:
    vector<int> postorderTraversal(TreeNode* root) {
        vector<int> res;
        stack<TreeNode*> stk1,stk2;
        if(root==NULL)
            return res;
        stk1.push(root);
        TreeNode* temp;
        while(!stk1.empty()){
            temp=stk1.top();
            stk1.pop();
            stk2.push(temp);
            if(temp->left!=NULL)
                stk1.push(temp->left);
            if(temp->right!=NULL)
                stk1.push(temp->right);
        }
        while(!stk2.empty()){
            res.push_back(stk2.top()->val);
            stk2.pop();
        }
        return res;
    }
} ;

4. 二叉树的层序遍历

按层遍历就是二叉树的广度优先搜索
(1)将二叉树的根从头部压入队列
(2)从队列尾部弹出一个元素,打印该元素值,然后将如果该元素左子树不为空,则将该元素的左子树从头部压入队列,右子树同左子树的操作;
(3)重复过程2直到队列为空。

/* 按层遍历二叉树 */
//用一个队列保存数据
void floorOrder(binaryTree T){
	if(T == NULL)
		return;
	deque<int> Q; //辅助队列Q
	Q.push_back(T); //根节点入队列
	while(!Q.empty()){ // 队列不空循环
		binaryTree tempNode = Q.front();// 队列头
		printf("%d ", tempNode->value); // 访问当前节点,打印该元素值
		if(tempNode->pLeft != NULL)
			Q.push_back(tempNode.pLeft); // 左子树不空,左子树入队列
		if(tempNode->pRight != NULL)
			Q.push_back(tempNode.pRight);
		Q.pop_front(); // 队列头出队
	}
}

递归是解决二叉树相关问题的神级方法;

三、二叉树的各种常见算法题

二叉树的构建

二叉树的镜像

重建二叉树,依据前序遍历结果和中序遍历结果

判断二叉搜索树的后序遍历是否合法

二叉树的子结构

二叉树中和为某一值的路径

将二叉搜索树转化为双向链表

求二叉树的深度

判断一棵二叉树是否是平衡二叉树

判断一棵二叉树是否为完全二叉树

求二叉树第K层节点个数

求二叉树中两个节点的最低公共祖先节点

求二叉树中两个节点的最大距离

四、其他二叉树

二叉排序树

线索二叉树

平衡二叉树-AVL树

红黑树-R-B Tree

红黑树与AVL树的区别

五、多路查找树

B树、B+树

2-3树、2-3-4树

六、哈夫曼树

七、Trie树(字典树)

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值