二叉树性质和有关操作汇总

二叉树是一种重要的数据结构.

二叉树是n(n>=0)个结点的有限集合,该集合或为空集,或由一个根结点和两棵互不相交的,分别称为根结点的左子树和右子树的二叉树组成(递归定义)

满二叉树:对于这样的一棵二叉树,如果所有分支结点都存在左右子树,且所有叶子节点都在同一层上,称这样的二叉树为满二叉树。

完全二叉树:如果一棵具有n个结点的二叉树的结构与满二叉树的前n个结点完全相同,称之为完全二叉树。

二叉树的性质:

性质1 二叉树的第i层结点数目最多为2^(i-1) (i>=1)

性质2 深度为k的二叉树至多有2^(k-1)

性质3 对于任意一棵二叉树,叶子结点(度为0)数为n0,度为2的结点个数为n2,则有n0=n2+1.

证明:n0+n1+n2=n1+2*n2+1 即结点数=分支数+1

二叉树存储结构

1.顺序存储结构 (可用数组表示)适用于满二叉树,完全二叉树,否则会有大量空间浪费

2链式存储结构 数据域+左右孩子指针表示 数据域+左右孩子指针表示+双亲指针表示

二叉树的数据域+左右孩子指针表示
还可以推广到每个结点的孩子数至多为常数k的任意类型的树,但实际中若不是所有结点都有k个孩子,则会造成空间浪费。一种巧妙的方法用来表示孩子数任意的树-----左孩子右兄弟表示法,该方法对于任意n个结点的树,只需要O(n)的存储空间。下面以二叉树的链式存储结构: 数据域+左右孩子指针表示法为例 进行二叉树的相关操作。

#include<stdio.h>
#include<malloc.h>
#include<stdlib.h>
#define maximum 20

typedef struct BiTNode
{
	char data;
	struct BiTNode *lchild,*rchild;
}BTNode ;
typedef BTNode *BTree;

struct
{
	int ln;//节点的层次编号
	BTree p;//节点指针
}Qu[maximum];

//输入先序序列,创建二叉树的二叉链表
BTree CreateBTree()
{
	char ch;
	BTree T;
	if((ch=getchar())=='#')
		return(NULL);
	else
	{
		T=(BTNode *)malloc(sizeof(BTNode));
		T->data=ch;
		T->lchild=CreateBTree(T->lchild);//构造左子树
		T->rchild=CreateBTree(T->rchild);//构造右子树
		return (T);
	}
}

//根据前序遍历和中序遍历构建二叉树
BTree CreateTreeByPreIn(char *preorder,char *inorder,int length)
{
	BTree root;
	int rootIndex=0;
	if(0==length)
		return NULL;
	root = (BTNode *)malloc(sizeof(BTNode));
	root->data = *preorder;
	for(;rootIndex<length;rootIndex++)
	{
		if(inorder[rootIndex]==*preorder)
			break;
	}
	root->lchild = CreateTreeByPreIn(preorder+1,inorder,rootIndex);
	root->rchild = CreateTreeByPreIn(preorder+rootIndex+1,inorder+rootIndex+1,length-rootIndex-1);
	return root;
}

//先序遍历
void preorder(BTree root)
{
	if(root!=NULL)
	{
		printf("%c",root->data);
		preorder(root->lchild);
		preorder(root->rchild);
	}
}

//先序遍历的非递归算法
void preorder1(BTree root)
{
    //BTNode *St[MaxSize],*p;
	BTree St[maximum],p;
    int top=-1;
    if(root!=NULL)
    {
        top++;//根结点入栈
        St[top]=root;
        while(top>-1)//桟不为空时循环
        {
            p=St[top];//退桟并访问该结点
            top--;
            printf("%c",p->data);
            if(p->rchild!=NULL)//右孩子入桟----顺序:先右再左入栈,出栈相反
            {
                top++;
                St[top]=p->rchild;
            }
            if(p->lchild!=NULL)//左孩子入桟
            {
                top++;
                St[top]=p->lchild;
            }
        }
        printf("\n");
    }
}

//中序遍历
void inorder(BTree root)
{
	if(root)
	{
		inorder(root->lchild);
		printf("%c",root->data);
		inorder(root->rchild);
	}
}

//中序遍历的非递归算法
void inorder1(BTree root)
{
    //BTNode *St[MaxSize],*p;
	BTree St[maximum],p;
    int top=-1;
    if(root!=NULL)
    {
		p=root;
        while(top>-1||p!=NULL)//桟不为空时循环
        {

            while(p!=NULL)//入桟
            {
                top++;
                St[top]=p;
				p=p->lchild;
            }
			if(top>-1)
			{
				p=St[top];
				top--;
			printf("%c",p->data);
			p=p->rchild;
			}
        }
        printf("\n");
    }
}

//后序遍历
void postorder(BTree root)
{
	if(root)
	{
	postorder(root->lchild);
	postorder(root->rchild);
	printf("%c",root->data);
	}
}

//后序遍历非递归算法
void postorder1(BTree root)
{
	BTree St[maximum],p;
    int flag,top=-1;
    if(root!=NULL)
    {
		do
		{
			while(root!=NULL)
			{
				top++;
				St[top]=root;
				root=root->lchild;
			}
			p=NULL;//p指向当前节点的前一个访问的节点
			flag=1;//设置b的访问标记为已访问过
			while(top!=-1&&flag)
			{
				root=St[top];//取出当前的栈顶元素
				if(root->rchild==p)//右子树不存在或已被访问,访问之
				{
					printf("%c",root->data);
					top--;
					p=root;//p指向刚被访问的节点
				}
				else
				{
					root=root->rchild;
					flag=0;//设置未被访问的标记
				}
			}
		}while(top!=-1);
        printf("\n");
    }
}

//层次遍历
void travelevel(BTree root)
{
	BTree Qu[maximum];
	int front,rear;
	front=rear=0;
	if(root!=NULL)
		printf("%c",root->data);
	rear++;
	Qu[rear]=root;
	while(rear!=front)
	{
		front=(front+1)%maximum;
		root=Qu[front];
		if(root->lchild!=NULL)
		{
			printf("%c",root->lchild->data);
			rear=(rear+1)%maximum;
			Qu[rear]=root->lchild;
		}
		if(root->rchild!=NULL)
		{
			printf("%c",root->rchild->data);
			rear=(rear+1)%maximum;
			Qu[rear]=root->rchild;
		}
	}
}

//统计二叉树叶子节点个数
int leaf_num(BTree root)
{
	int L,R;
	if(root==NULL)
		return 0;
	if(root->lchild==NULL&&root->rchild==NULL)
		return 1;
	L=leaf_num(root->lchild);
	R=leaf_num(root->rchild);
	return L+R;
}

//统计二叉树深度
int BTheight(BTree root)
{
	int l_height,r_height;
	if(root==NULL)
		return 0;
	else
	{
		l_height=BTheight(root->lchild);//左子树深度+1(根节点)
		r_height=BTheight(root->rchild);
		return l_height>r_height?(l_height+1):(r_height+1);
	}
}

//统计二叉树宽度
int BTwidth(BTree root)
{
	int front,rear;
	int num,max,i,n;
	front=rear=0;
	if(root!=NULL)
	{
		rear++;
		Qu[rear].p=root;//根节点入队
		Qu[rear].ln=1;//根节点层次编号1
		while(front!=rear)
		{
			front++;
			root=Qu[front].p;//队头出队
			num=Qu[front].ln;
			if(root->lchild!=NULL)
			{
				rear++;
				Qu[rear].p=root->lchild;
				Qu[rear].ln=num+1;
			}
			if(root->rchild!=NULL)
			{
				rear++;
				Qu[rear].p=root->rchild;
				Qu[rear].ln=num+1;
			}
		}
			max=0;num=1;i=1;
			while(i<=rear)//通过比较相同层次的节点数求数的宽度
			{
				n=0;
				while(i<=rear&&Qu[i].ln==num)
				{n++;i++;}
				num=Qu[i].ln;
				if(n>max)
					max=n;
			}
			return max;
	}
	else
		return 0;

}

//输出每个从根节点到叶子节点的路径
void printArray(char a[],int len)
{
	int i;
	for(i=0;i<len;i++)
		printf("%c",a[i]);
	printf("\n");
}

void printAllPath(BTree root,char path[],int pathLen)
{
	if(root == NULL)
		return;
	path[pathLen] = root->data;
	pathLen++;
	if(root->lchild == NULL && root->rchild == NULL)
		printArray(path,pathLen);
	else
	{
		printAllPath(root->lchild,path,pathLen);
		printAllPath(root->rchild,path,pathLen);
	}
} 

void printPaths(BTree root)
{
	char path[100];
	printAllPath(root,path,0);
}

//二叉树镜像--每个节点的左右子树都交换了的。
BTree exchange2(BTree T)
{
	BTree temp;
	if(T)
	{
		temp=T->lchild;
		T->lchild=T->rchild;
		T->rchild=temp;
		exchange2(T->lchild);
		exchange2(T->rchild);
	}
	return T;
}
//比较两棵二叉树是否相等
int CompareBTree(BTree root1,BTree root2)
{
	if(root1==NULL && root2==NULL)
		return 1;
	else if((root1==NULL&&root2!=NULL)||(root2==NULL&&root1!=NULL))
		return 0;
	else if(root1->data == root2->data)
	{
		if(CompareBTree(root1->lchild,root2->lchild) == 1)
			return CompareBTree(root1->rchild,root2->rchild);
	}
}

void main()
{
	BTree root1,root2;
	//int height,width;
	char pre_order[6]={'A','B','D','C','E','F'};
	char in_order[6]={'B','D','A','E','F','C'};
	printf("******************************************");
	printf("\n按先序遍历和空节点#建立二叉树:");
	root1 = CreateBTree();

	printf("\n先序遍历-递归root1:");
	preorder(root1);

	printf("\n先序遍历-非递归root1:");
	preorder1(root1);

	printf("\n中序遍历-递归root1:");
	inorder(root1);

	printf("\n中序遍历-非递归root1:");
	inorder1(root1);

	printf("\n后序遍历-递归root1:");
	postorder(root1);

	printf("\n后序遍历-非递归root1:");
	postorder1(root1);

	printf("\n层次遍历");
	travelevel(root1);

	printf("\n二叉树高度和宽度和叶子节点个数:");
	printf("%d,%d,%d",BTheight(root1),BTwidth(root1),leaf_num(root1));

	printf("\n输出二叉树所有路径:\n");
	printPaths(root1);
	printf("\n******************************************");
	printf("\n按先序遍历结果和中序遍历结果建立二叉树");
	root2 = CreateTreeByPreIn(pre_order,in_order,6);

	printf("\nroot1和root2是否相等:");
	if(CompareBTree(root1,root2)==1)
		printf("root1 == root2\n");
	else
		printf("root1 != root2\n");
}

参考:http://blog.csdn.net/sunmenggmail/article/details/7466635

时间: 2024-08-05 23:11:50

二叉树性质和有关操作汇总的相关文章

二叉树的性质和常用操作代码集合

二叉树的性质和常用操作代码集合 性质: 二叉树的性质和常用代码操作集合 性质1:在二叉树的第i层上至多有2^i-1个结点 性质2:深度为k的二叉树至多有2^k - 1个结点 性质3:对任意一棵二叉树T,若终端结点数为n0,而其度数为2的结点数为n2,则n0 = n2 + 1 满二叉树:深度为k且有2^-1个结点的树 完全二叉树:深度为k,结点数为n的二叉树,如果其结点1~n的位置序号分别与等高的满二叉树的结 点1~n的位置序号一一对应,则为完全二叉树. 性质4:具有n的结点的完全二叉树深度为lo

二叉树性质盘点

========================================================================================= 基础部分 ========================================================================================= 图论中的定义: 二叉树在图论中是这样定义的:二叉树是一个连通的无环图,并且每一个顶点的度不大于3.有根二叉树还要满足根结点的度不大

二叉树的实现与操作(C语言实现)

 二叉树的定义:     上一篇的树的通用表示法太过于复杂,由此这里采用了孩子兄弟表示法来构建二叉树. 孩子兄弟表示法: 每个结点包含一个数据指针和两个结点指针 --->数据指针:指向保存于树中的数据 --->孩子结点指针:指向第一个孩子 --->兄弟结点指针:指向第一个右兄弟 二叉树是由 n( n>=0 ) 个结点组成的有限集合,该集合或者为空,或者是由一个根结点加上两棵分别称为左子树和右子树的.互不相交的二叉树组成. 特殊的二叉树: 定义1:满二叉树(Full Binary T

SQL字符串操作汇总

--将字符串中从某个字符开始截取一段字符,然后将另外一个字符串插入此处 select stuff('hello,world!',4,4,'****') --返回值hel****orld! --返回从指定位置开始指定长度的字符串 select substring('Hello,World!',2,10) --返回值ello,World --将字符串中某段字符替换为指定的字符串 select replace('hello,world!','ll','aa') --返回值heaao,world! --

提升效率的Linux终端快捷操作汇总

很多普通 Linux 桌面用户都对使用终端感到排斥和恐惧,其实它没大家想的那么复杂,很多常见操作都可以直接在终端中进行,如:安装软件.升级系统等. 无论你是新手还是 Linux 终端使用的老鸟,系统极客在此为大家总结了提升终端命令执行效率的快捷操作汇总,希望能帮助你学习和提升效率. 移动定位光标 在终端中移动光标和定位似乎非常不便,其实不是你想的那样,有很多种方式可以让键盘成为你的好朋友,只是需要掌握正确的方法而已. 定位单词 在长段的命令中,使用 Ctrl + ← 和 Ctrl + → 可快速

JS数组(Array)操作汇总

1.去掉重复的数组元素.2.获取一个数组中的重复项.3.求一个字符串的字节长度,一个英文字符占用一个字节,一个中文字符占用两个字节.4.判断一个字符串中出现次数最多的字符,统计这个次数.5.数组排序. 6.快排. 7.删除/添加数组项. 8.数组随机顺序输出. 9.数组求和.最大值. 10.判断是否为数组. 11.冒泡排序. 1.去掉重复的数组元素. Array.prototype.unique = function() { var ret = []; var o = {}; for(var i

二叉树的8种操作

1.为什么会有树?因为当有大量的输入数据时,链表的线性访问时间就显得略长了.而树结构,其大部分操作的运行时间平均为O(logN). 2.树的实现并不难,几行代码就搞定了. struct TreeNode { Object element; TreeNode *firstChild; TreeNode *nextSibling; } 3.遍历形式: // 中序遍历二叉树 void inorder(tree_pointer ptr) { if(ptr) { inorder(ptr->left_chi

Scala中List、Map、Set各类型操作汇总

1.Scala中List.Map.Set等各类型函数操作汇总 package com.scala.study import scala.collection.immutable.{Queue, TreeMap}import scala.collection.mutable /**  * Created by HP-PC on 2016/5/26.  */ object ScalaCaseDemo {  def main(args: Array[String]): Unit = {    prin

【转】C#路径/文件/目录/I/O常见操作汇总

文件操作是程序中非常基础和重要的内容,而路径.文件.目录以及I/O都是在进行文件操作时的常见主题,这里想把这些常见的问题作个总结,对于每个问题,尽量提供一些解决方案,即使没有你想要的答案,也希望能提供给你一点有益的思路,如果你有好的建议,恳请能够留言,使这些内容更加完善. 主要内容: 一.路径的相关操作, 如判断路径是否合法,路径类型,路径的特定部分,合并路径,系统文件夹路径等内容: 二.相关通用文件对话框,这些对话框可以帮助我们操作文件系统中的文件和目录: 三.文件.目录.驱动器的操作,如获取