普通二叉树

1.什么是二叉树

树是一种比较重要的数据结构,它可以是二叉树,三叉树,可以说多叉树,但是我们今天是来看二叉树。二叉树是使用最广的树,二叉树是n个结点的有限集合,该集合为空集时候,就是我们空二叉树。或者有一个根结点有两棵互不相交的树分别称为左子树和右子树(或左孩子和右孩子),其次序不能任意颠倒。

画张图看一下

 

根结点:                           结点A

分支结点:                       结点A  B   F   D

叶子结点:                       没有分支的结点C  E  G(这里可以看出叶子结点  +   分支结点   =    整棵树 )

孩子结点:                        结点B是结点A 的左孩子          结点F是结点A的右孩子                                      

父结点:                           结点A  是结点B   C的父结点

兄弟结点:                        结点B  F        结点  C   2.

  

2.二叉树的五种形态

1.空二叉树       2.只有一个根节点    3.根节点只有左子树    4.根节点只有右之树       5.根节点既有左子树又有右子树

 

3.二叉树的特殊类型

(1)斜树

一条线性的二叉树,要么左斜要么右斜

 

(2)满二叉树

这个是指一棵二叉树所有的分支节点都存在左子树和右子树,且所有的叶子结点都在同一层上,这样的二叉树是满二叉树。

 

(3)完全二叉树

这个画图吧,感觉解释会绕

 

4.二叉树性质:

1.二叉树第i层至多2^(i-1)个结点(i>1)

2.深度k二叉树至多(2^k)-1个结点

3.终端结点为n0,度为2的结点数为n2,则n0 = n2 +1

什么是度

(没有分叉的二叉树节点的度就是0度。如果一个节点只有一个分叉就是1度。两个分叉就是2度的子树。)

推理步骤:

二叉树T总结点数n = n0+n1+n2

连接线总数(结点之间的连线) = n-1 = n2*2+n1  

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

n0 = n2+1

4.具有n 个结点的完全二叉树的深度为(log2 n)取下限 +1              n = 2^k-1

5.完全二叉树序号

左子树序号 = 2*父结点序号+1 

右子树序号 = 2*(父节点序号+1)

父节点序号 = (子树序号+1/2)-1

 

二叉树的前序遍历

void PerOrder(BtNode *head)
{
	if(head != NULL)
	{
	cout<<head->data<<" ";
	PerOrder(head->leftchild);   //递归先左后右
	PerOrder(head->rightchild);   
	}
}

我们看一下这个逻辑

 

1.就是一进函数只要不为NULL 就打印当前结点的数据

2.从左子树开始往下递归,只要不为NULL就打印当前结点,当打印完ABC遇到NULL就执行完函数回退栈帧

3.回退回去之后C结点又遍历其右子树,遇见NULL执行完函数又回退栈帧

…………这般按蓝箭头的顺序递归

 

二叉树中序遍历

void InOrder(BtNode *head)
{
	if(head != NULL)
	{
	InOrder(head->leftchild);
	cout<<head->data<<" ";
	InOrder(head->rightchild);
	}
}

二叉树的后序遍历

void PastOrder(BtNode *head)
{
	if(head != NULL)
	{
	PastOrder(head->leftchild);
	PastOrder(head->rightchild);
	cout<<head->data<<" ";
	}
}

三者相比就是打印时机不同而已,递归形式还是一样的

 

三者非递归的遍历

前序非递归

void PerOrder1(BtNode *head)
{
	stack<BtNode*> st;               
	BtNode *p = head;
	while(p != NULL || !st.empty())      //这个判断是这个的关键,我们要明白当栈空了不一定就意味着遍历完整个二叉树了,
//还有关键核心就是这是前序遍历 ,最最右边那个结点就是出口,
	{
		while(p != NULL)               //这个while就是压栈加打印
		{
			st.push(p);
			cout<<p->data<<" ";
			p = p->leftchild;
		}

		if(!st.empty())   //这个就是让出栈的第一个结点变成右子树,然后不NULL继续大循环
		{
			p = st.top();
			st.pop();
			p = p->rightchild;
		}
	}
}

我们分析一下这个代码

1.首先我们要使用非递归来前序遍历,那么我要用栈数据结构

2.前序压栈应该压入的是每次打印完的结点

3.都是左孩子跑到底画个图

 

中序非递归

void InOrder1(BtNode *head)
{
	stack<BtNode*> st;
	BtNode *p = head;
	while(p != NULL || !st.empty())
	{
		while(p != NULL)
		{
			st.push(p);
			
			p = p->leftchild;
		}

		if(!st.empty())
		{
			p = st.top();
			st.pop();
			cout<<p->data<<" ";
			p = p->rightchild;
		}
	}
}

这个就是先让左孩子走到底但是不打印,出栈再打印之后再往右子树走

 

后序非递归

后序需要设置标记结点flag,我们知道的是后序的遍历顺序是什么样的左     右     中

代码就做了三件事

1.用if(p!=NULL)把左孩子跑到最底,每次压入栈中,不打印

2.如果右孩子遍历过了(用个flag来判断)或者右孩子为NULL时候,就打印,出栈,做标记flag,p=NULL,

flag用于标记已经处理到哪个结点了。其次p=NULL是为了再取栈顶元素做个判断

3.如果有右孩子,跑去右孩子,前提这个右孩子没被遍历过

void PostOrder1(BtNode *head)
{
	stack<BtNode *> st;
	BtNode *p = head;
	BtNode *flag = NULL;                 //设置的标记判断右结点是否被访问过
	while(p != NULL || !st.empty())
	{
		if(p != NULL)
		{
			st.push(p);              //if就可以让左孩子跑到底
			p = p->leftchild;        //很容易让人产生误会写成while            
		}
		else
		{
			p = st.top();             
			if(p->rightchild == NULL || p->rightchild == flag)
			{
				st.pop();             //右边被访问过或者为空
				cout<<p->data<<" ";  //右边没有或访问过就代表可以打印中序了
				flag = p;
				p = NULL;       //置于空是为了接着出栈
			}
			else
			{
				p = p->rightchild;    //没被访问就进循环
			}
		}
	}
}

 

创建二叉树

这个注意一下传入的指针的问题,如果传一级指针就出问题,因为没有解引用操作,那么栈帧回退的时候,指向是也会退回去的,要么传二级指针要么用引用。

//ABC##DE##F##G#H##    利用这种字符串创建一个二叉树   #代表空
#define END #
BtNode *CreateTree2(ElemType **arr)
{
	BtNode * p =NULL;
	if( arr != NULL&& *arr!= NULL && **arr != END)
	{
		p = Buynode();    //在堆区创建一个结点
		p->data = **arr;
		p->leftchild = CreateTree2(&(++*arr));
		p->rightchild = CreateTree2(&(++*arr));
	}

	return p;
}

二叉树的搜索

这个代码很好理解,先找到递归的出口,p == NULL 没找到或者找到了val就出栈。

遍历方式

1.先跑完左孩子跑到底

2.然后再进右孩子,为空就return出去了,不为空,就把右孩子的左孩子给遍历一次,直到为空。

就这种递归方式递归下去就遍历完整棵树了

 

BtNode *Findval(BtNode *p,ElemType val)
{
	if(p == NULL || p->data == val)
	{
		return p;
	}
	else
	{
		BtNode *q = Findval(p->leftchild,val);
		if(q == NULL)
		{
			q = Findval(p->rightchild,val);
		}
		return q;
	}
}
已标记关键词 清除标记
©️2020 CSDN 皮肤主题: 鲸 设计师:meimeiellie 返回首页