数据结构—二叉树

文章目录

  • 二叉树的概念
    • 1. 二叉树的定义
    • 2. 几个特殊的二叉树
    • 3. 二叉树的性质
    • 4. 二叉树的存储结构
  • 二叉树的遍历
    • 1. 先序遍历(根左右)
    • 2. 中序遍历(左根右)
    • 3. 后序遍历(左右根)
    • 4. 层次遍历
    • 5. 总结

二叉树的概念

1. 二叉树的定义

与树的定义相似,二叉树也以递归的形式定义。二叉树是n(n>=0)个结点的有限结合:

  • n=0,空二叉树
  • 根结点+左子树(是二叉树)+右子树(是二叉树)

二叉树与度为2的有序树的区别有:

  1. 度为2的有序树至少要有3个结点,而二叉树可以为空。
  2. 度为2的有序树的孩子次序是相对于另一孩子而言的,只有一个孩子就无需区分次序;二叉树孩子次序始终固定。

2. 几个特殊的二叉树

  1. 满二叉树:每层都有最多的节点,若高度为h,结点数为2h-1。
  2. 完全二叉树:结点与满二叉树一一对应,空缺的只会是层次遍历中的最后几个节点。

题型:一个完全二叉树有x个叶子结点,求这个二叉树最多和最少有多少个节点。
注意:编号为i的节点的双亲结点为 ⌊ i / 2 ⌋ \lfloor i/2\rfloor i/2

  1. 二叉排序树:左子树上所有关键字均小于根结点的关键字;右子树上的所有节点的关键字均大于根结点的关键字。
  2. 平衡二叉树:任一节点的左子树和右子树深度之差不超过1。

3. 二叉树的性质

  1. 非空二叉树上的叶子结点数等于度为2的结点数加1,即n0=n2+1(设度为0,1和2的结点个数分别为n0,n1和n2)。

n0+n1+n2=n1+2*n2+1

  1. 对于完全二叉树,结点i所在层次深度为 ⌊ l o g 2 i + 1 ⌋ \lfloor log_2i+1\rfloor log2i+1
  2. 具有n个结点的完全二叉树深度为 ⌈ l o g 2 ( n + 1 ) ⌉ \lceil log_2(n+1)\rceil log2(n+1)

4. 二叉树的存储结构

主要用链式存储结构,顺序存储结构则堆排序的时候用的多。二叉树的链式存储结构描述如下:

struct TreeNode {
     
	int val;
    TreeNode *left;
    TreeNode *right;
    TreeNode(int x) : val(x), left(NULL), right(NULL) {
     }
};

二叉树的遍历

1. 先序遍历(根左右)

递归实现

vector<int> ans;   //存放遍历结点的值
void PreOrder(TreeNode *root)
{
     
	if(root!=nullptr)
	{
     
		ans.push_back(root->val); 
		PreOrder(root->left);
		PreOrder(root->right);
	}
}
  • 时间复杂度:每个结点都访问一次且只访问一次,时间复杂度为O(n),n为树的节点个数。
  • 空间复杂度:递归工作栈深正好为树的深度,空间复杂度也为O(n)。

非递归实现

vector<int> ans;   //存放遍历结点的值
void PreOrder(TreeNode *root)
{
     
	stack<TreeNode*> s; //定义一个栈 
    TreeNode* p=root;   
    while(p||!s.empty())
    {
     
        while(p)
        {
     
            ans.push_back(p->val);  //遍历
            s.push(p);
            p=p->left;   //一直向左
        }
        p=s.top();     //回溯
        s.pop();      //出栈
        
        p=p->right;   //右子树
    } 	
}
  • 时间复杂度:O(n)
  • 空间复杂度:O(n)

Morris 遍历:

待补充

  • 时间复杂度:O(n)
  • 空间复杂度:O(1)

2. 中序遍历(左根右)

递归实现

vector<int> ans;   //存放遍历结点的值
void InOrder(TreeNode *root)
{
     
	if(root!=nullptr)
	{
     
		InOrder(root->left);
		ans.push_back(root->val); 
		InOrder(root->right);
	}
}

非递归实现

vector<int> ans;   //存放遍历结点的值
void InOrder(TreeNode *root)
{
     
	stack<TreeNode*> s;
	TreeNode* p=root;
	while(p||!s.empty())
	{
     
		while(p)
		{
     
			s.push(p);
			p=p->left;
		}
		p=s.top();
		s.pop();
		ans.push_back(p->val);
		p=p->right;
	}
}

3. 后序遍历(左右根)

递归实现

vector<int> ans;   //存放遍历结点的值
void PostOrder(TreeNode *root)
{
     
	if(root!=nullptr)
	{
     
		PostOrder(root->left);
		PostOrder(root->right);
		ans.push_back(root->val); 
	}
}

非递归实现

vector<int> ans;   //存放遍历结点的值
void PostOrder(TreeNode *root)
{
     
	stack<TreeNode*> s;
	TreeNode* p=root,pre=nullptr; 
	while(p||!s.empty())
	{
     
		while(p)
		{
     
			s.push(p);
			p=p->left;
			pre=p;   //指向前一个遍历的指针			
		}
		p=s.top();
		if(p->right&&pre!=p->right)//右子树还未遍历
		{
     
			p=p->right;
		}
		else  
		{
     
			s.pop();
			ans.push_back(p->val);
			pre=p;
			p=nullptr;
		}
	}
}

4. 层次遍历

vector<int> ans;
void LevelOrder(TreeNode* root) //需要用到队列
{
     
	if(root==nullptr) return;
	queue<TreeNode*> q;
	q.push(root);
	TreeNode* p;
	while(!q.empty())
	{
     
		p=q.front();
		q.pop();
		ans.push_back(p->val);  //存放值
		
		if(p->left) q.push(p->left);
		if(p->right) q.push(p->right);
		
	}
}
  • 时间复杂度:O(n)
  • 空间复杂度:O(n)

5. 总结

上面的代码可以作为模板来记忆,很多题目都是以上遍历算法的变形。Morris 遍历算法在空间上有优化,但博主几乎没见到相关的题目,而且这也不是重点考察的知识,大家可以根据兴趣自行查找了解。

你可能感兴趣的:(数据结构与算法,数据结构,二叉树)