vlambda博客
学习文章列表

一文学会树及二叉树的操作

  • 目录

  • 树的概念及相关术语

  • 二叉树及性质

  • 二叉树的操作

  • 创建二叉树

  • 二叉树的结点个数

  • 叶子结点个数

  • 二叉树的深度

  • 二叉树第k层结点个数

  • 判断一个结点是否在二叉树中

  • 获取一个结点的左孩子,右孩子结点

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

  • 给定一棵二叉树,判断它是否是BST


一、树的定义

一棵树是由n(n>0)个元素组成的有限集合,其中:

  (1)每个元素称为结点(node);

  (2)有一个特定的结点,称为根结点或树根(root);
  (3)除根结点外,其余结点能分成m(m>=0)个互不相交的有限集合T0,T1,T2,……Tm-1。其中的每个子集又都是一棵树,这些集合称为这棵树的子树。
   (4)一棵树中的任意两个结点有且仅有唯一的一条路径连通。
   (5)一棵树如果有n个结点,那么它一定恰好有n-1条边。
  如下图是一棵典型的树:

二叉树

二叉树(binarytree,简写成BT)是一种特殊的树型结构,它的度数为2的树。即二叉树的每个结点最多有两个子结点。每个结点的子结点分别称为左孩子、右孩子,它的两棵子树分别称为左子树、右子树。二叉树有5中基本形态:


一文学会树及二叉树的操作


满二叉树:除了叶结点外每一个结点都有左右子女且叶结点都处在最底层的二叉树。
完全二叉树:在满足满二叉树的性质后,最后一层的叶子节点均需在最左边。完全二叉树中度数为1的结点至多有1个。


一文学会树及二叉树的操作

二叉树性质

1.在二叉树的第i层上最多有2i-1个结点(i>=1)。

2.深度为k的二叉树至多有2k–1个结点(k>=1)。

3.对任意一棵二叉树,如果其叶结点数为n0,度为2的结点数为n2,则一定满足:n0=n2+1。
证明:
因为二叉树中所有结点的度数分为三种情况,度数为0的点,度数为1的点,度数为2的点。不妨设n0表示度为0的结点个数,n1表示度为1的结点个数, n2表示度为2的结点个数。三类结点加起来为总结点个数,于是便可得到:n=n0+n1+n2
又因为一棵n个点的二叉树由n-1条边构成。
度数为2的点会产生2条边,那么,现在有n2个度数为2的点,是不是会产生2*n2条边呢?同理,度数为1的点会产生1条边,那么,现在有n1个度数为1的点,是不是会产生1*n1条边呢?即总共会产生2*n2+1*n1条边。
根据顶点数与边的关系, 顶点数=边数+1。
得到:n0+n1+n2=2*n2+1*n1+1,推导得出:n0=n2+1


4.具有n个结点的完全二叉树的深度k=[log2n]+1

5.对于一棵n个结点的完全二叉树,对任一个结点(编号为i),有:

 ①如果i=1,则结点i为根,无父结点;如果i>1,则其父结点编号为i/2。

  如果2*i>n,则结点i无左孩子(当然也无右孩子,即结点i为叶结点);否则左孩子编号为2*i。

  ②如果2*i+1>n,则结点i无右孩子;否则右孩子编号为2*i+1。


二叉树的存储

1.顺序存储结构

char a[100];


一文学会树及二叉树的操作


1

2

3

4

5

6

7

8

9

10

A

B

C

D

E

F

G

H

I

J

优点:方便获取父节点,左结点,右结点
缺点:对于非完全二叉树,较浪费空间

2.链式存储结构


typedefstructnode{ intdata; struct node *lchild; struct node * rchild;}BTNode,*tree;tree root=NULL;



一文学会树及二叉树的操作


优点:节约空间


二叉树的操作
  • 建立一棵二叉树


 void pre_crt(tree &bt)//按先序次序输入二叉树中结点的值,生成 { charch; ch= getchar( ); //二叉树的单链表存储结构,bt为指向根结点的指针,'$'表示空树 if(ch!= '$') { bt= new node; //建根结点 bt->data= ch;    pre_crt(bt->lchild); //建左子树    pre_crt(bt->rchild); //建右子树 } else bt= NULL; }


  • 二叉树结点个数


intBTNodeSize(treeroot){if (root == NULL) return 0;return BTNodeSize(root->lchild) + BTNodeSize(root->rchild) + 1;}


  • 二叉树叶子结点个数


intBTNodeLeaf(treeroot){if (root == NULL)return 0; if ((root->lchild==NULL) && (root->rchild==NULL)) return 1;return BTNodeLeaf(root->lchild) +BTNodeLeaf(root->rchild);}


  • 二叉树深度


intBTDepth(pBTNoderoot){if (root == NULL) return 0;intleft = BTDepth(root->lchild);intright = BTDepth(root->rchild);return (left>right)? (left+1) : (right+1);}


  • 判断一个结点是否在二叉树中


tree BTFind(treeroot,intx){tree ret;if (root == NULL || root->data ==x) return root;ret = BTFind(root->lchild,x);if (ret) return ret;ret = BTFind(root->rchild,x);if (ret) return ret;return NULL;}
  • k层结点的个数


intBTNodeKLevel(treeroot, int k){  if(root == NULL) return0; if(k == 1) return1; returnBTNodeKLevel(root->lchild, k -1) +BTNodeKLevel(root->rchild, k -1);}


  • 获取一个结点的左孩子结点


tree GetBTNodeLeftChild(treeroot,intx) {tree ret =NULL;if (root == NULL){ return NULL; } ret = BTFind(root,x);if (ret->lchild)return ret->lchild;}
  • 获取一个结点的右孩子结点 


tree GetBTNodeRightChild(treeroot,intx){tree ret = NULL;if (root == NULL) return NULL; ret = BTFind(root, x); if (ret->rchild) return ret->rchild;}
  • 二叉树某结点的层数


void getNodeLayer(node *root,intx,int layer){ if(root ==NULL) return; if(root->data == x) { printf(“%d\n”,layer); return; } getNodeLayer(root->lchild,x,layer+1); getNodeLayer(root->rchild,x,layer+1);}


  • 判断二叉树棵树是否为搜索二叉树
二叉查找树(BinarySearch Tree)(二叉搜索树,二叉排序树)
它或者是一棵空树,或者是具有下列性质的二叉树:
   若它的左子树不空,则左子树上所有结点的值均小于它的根结点的值;
   若它的右子树不空,则右子树上所有结点的值均大于它的根结点的值;
   它的左、右子树也分别为二叉排序树。

一文学会树及二叉树的操作

方法:
利用搜索二叉树的特性,中序遍历结果为一个无重复数据的升序序列
则先将这棵二叉树采用中序遍历结果保存在vector
然后遍历vector,判断是否满足是升序并且无重复数据,不满足则不是搜索二叉树,反之则是搜索二叉树。
时间复杂度为O(N)
空间复杂度为O(N)


//中序遍历二叉树,采用递归方法void mid_order(treeroot,vector<char>& v1){ if(root == NULL) return; mid_order(root->lchild,v1);//先遍历左子树 v1.push_back(root->data); //然后遍历根节点,将值存储在vector中 mid_order(root->rchild,v1);//再遍历右子树}boolisValidBST(treeroot){ if(root == NULL) returntrue; vector<char> v; mid_order(root,v); int size = v.size(); for(inti = 0; i <size - 1 ; ++i) { if(v[i] >= v[i +1])//不符合升序,或者存在重复,返回false return false; } //经中序遍历后为无重复的升序序列,则是二叉搜索树 return true;}


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


借助queue,将各个结点,以层序遍历序列进行入队。

如果发现frontNULL时,表示读取到了一个空结点,若它后面的结点都是NULL,则表示为完全二叉树;

若它后面的结点出现不为NULL的情况,表示为非完全二叉树。





bool BTComplete(tree root) //判断是否是完全二叉树 {
  queue<tree> q;;   if (root == NULL) return true;
q.push(root); while (!q.empty()) { tree f = q.front();; q.pop(); if (f == NULL) { while (!q.empty()) { q.pop(); if (q.front()) { return false; } } return true; } else { q.push(f->lchild); q.push(f->rchild); } }}


参考书籍《信息学奥赛一本通》



往期精彩回顾