1.二叉树的遍历
2.二叉树链式结构的实现
3.解决单值二叉树题
1.二叉树的遍历
1.1前序,中序以及后序遍历
二叉树的遍历是按照某种特定的规则,依次对二叉树的结点进行相应的操作,并且每个结点只操作一次。
二叉树的遍历有这些规则:前序/中序/后序/的递归结构遍历
1.前序遍历:先访问根结点,再访问左子树,最后访问右子树
2.中序遍历:先访问左子树,再访问根结点,最后访问右子树
3.后序遍历:先访问左子树,再访问右子树,最后根结点
以前序的规则来遍历二叉树,进入1结点(值为1的结点),1结点为根结点所以先访问,然后是左子树也就是2结点,此时进入2结点后,2结点是根结点,先访问2结点再访问左子树3结点,进入3结点后,以3结点作为根结点,先访问3结点,后访问3结点的左子树,此时就到最后一行都是NULL结点,访问完NULL结点后(3结点的左子树),就访问3结点的右子树(NULL结点),这时候3结点已访问完,而3结点又是2结点的左子树,也就是说2结点的左子树访问完,就访问2结点的右子树(NULL结点),2结点也访问完了,2结点又是1结点的左子树,既1结点的左子树访问完,开始访问1结点的右子树,后续过程也是一样的,每进入一个结点都是新的根结点,按照前序的遍历,总的看就是一个递归过程了,只有3结点的左右子树以及自身被访问完后才去访问上面结点的右子树。
访问的顺序不同也会带来不同的特点,前序访问则第一个是最上面的结点,中序访问最上面的结点会把其的左右子数分成俩边,后序则最上面的结点在最后面,中序和后序的过程与前序差不多,就是根结点的访问有所不同。
2.二叉树链式结构的实现
2.1代码实现遍历二叉树数
下面是以前序为规则遍历的代码实现:
#include<stdio.h>
#include<stdlib.h>
typedef int BTDataType;
typedef struct BinaryTreeNode
{BTDataType data;BinaryTreeNode* left;BinaryTreeNode* right;
}BTNode;
BTNode* BuyNode(int x)
{BTNode* a = (BTNode*)malloc(sizeof(BTNode));if (a == NULL){perror("malloc fail:");return NULL;}a->data = x;a->left = NULL;a->right = NULL;return a;
}
BTNode* CreatNode()
{BTNode* node1 = BuyNode(1);BTNode* node2 = BuyNode(2);BTNode* node3 = BuyNode(3);BTNode* node4 = BuyNode(4);BTNode* node5 = BuyNode(5);BTNode* node6 = BuyNode(6);node1->left = node2;node1->right = node4;node2->left = node3;node4->left = node5;node4->right = node6;return node1;
}
void PrevOver(BTNode* root)
{if (root == NULL){printf("N ");return;}printf("%d ", root->data);PrevOver(root->left);PrevOver(root->right);
}
int main()
{BTNode* root = CreatNode();PrevOver(root);return 0;
}
结果如上面分析的一样。
只需要改变 PrevOver函数中代码的顺序就可以切换成其他的规则的遍历,把左子树放在根的访问前就是中序的遍历规则。
2.2实现计算二叉树中的结点的个数
错误代码1:
int TreeNode(BTNode* root)
{int size = 0;if (root == NULL){return 0;}++size;TreeNode(root->left);TreeNode(root->right);return size;
}
首先size是在栈区上开辟的空间,当出了这个函数,栈区就会被销毁,函数只会传size的值,size变量是被销毁了,但是没有接受返回的值,等于是去银行取钱,结果忘记拿钱了,这样是找不到累计的效果的,需要理解的是不是名字一样就是同一个变量,在递归的过程中,虽然都是size但是这些size是在不同栈区上开辟的不同变量,只是名字相同,所以最后返回的size是第一次调用的函数的size,最后会返回1.
错误代码2:
通过static可以是局部变量在出函数时不会被销毁掉,保留size的值,使得size能达到计数的效果,
但是也有缺陷,就是在主函数中多次调用计算结点数量的函数会出问题
int TreeNode(BTNode* root)
{static int size = 0;if (root == NULL){return 0;}++size;TreeNode(root->left);TreeNode(root->right);return size;
}
多次调用计算函数:
会导致一直积累
错误代码3:
int size = 0;
int TreeNode(BTNode* root)
{if (root == NULL){return 0;}++size;TreeNode(root->left);TreeNode(root->right);return size;
}
只需要把size从局部变量变成全局变量就行,这样递归过程中的每个函数的size都是同一个size了,全局变量的生命周期是程序结束,同static一样,但是相对static的方法安全一些,使用static需要谨慎。
合理代码:
int TreeNode(BTNode* root)
{return root == NULL ? 0 : TreeNode(root->left) + TreeNode(root->right) + 1;
}
把根结点分成左子树和右子树在加1(计入一个结点数量),每次进入一个结点都会有左子树和右子树,每进入一个结点就计入1,直到root=NULL时返回0,就把关于根和子树所计入的数返回到上一个结点去,通过这样会一直返回到最上面的结点,就把所有结点的数量都计入进去了,不会出现多次调用函数而发生数量的变化。
2.3计算二叉树的高度
因为根结点有左右俩个子树,树的高度是由最长的决定的,所有要对比左右俩个子树的长度,找到长度长的子树,再加上一(根结点算一个高度),这样就计算出树的高度了。
错误代码:
int TreeHeight(BTNode* root)
{if (root == NULL){return 0;}return TreeHeight(root->left) > TreeHeight(root->right) ? TreeHeight(root->left)+1 : TreeHeight(root->right)+1;
}
这样虽然可以计算出树的高度,但是存在一个问题,函数会在树的最下面开始计入,比我后在返回值那里又要计算一次,因为这个是没有变量来存储计入的值,所有每次对比和返回值时都要在通过递归来得到计入的值,如果树的高度是大的,那么当递归到较为上面时,在往下递归就会导致程序运行慢,一直重复计算,计算最底下的结点的度会因为结点的上走而暴增,倒数第二层计算最后一层结点的度会执行俩次,倒数第三层则会计算最后一层结点四次,所以这个代码是不合适的。
改进代码:
分析:
通过递归会找到最下面的结点,既叶子结点,开始往上走,返回的地方就是比较那边大,就往那边加一(这里的一是根的计入),直到走到最上面结点,这时比较就是总的左子树和右子树的高度(不是最上面结点)。
int TreeHeight(BTNode* root)
{if (root == NULL){return 0;}int heightleft = TreeHeight(root->left);int heightright = TreeHeight(root->right);return heightleft > heightright ? heightleft +1 : heightright +1;
}
创建临时变量来存储函数返的值,就不用重复计算,可以提高运行速度,减少消耗。
2.4计算树的叶子结点个数
叶子结点的左右子数都是NULL,可以以此为特点找。
错误代码:
int LeafNode(BTNode* root)
{if (root->left == NULL && root->right == NULL)return 1;return LeafNode(root->left) + LeafNode(root->right);
}
因为二叉树不是所有结点都有左右子树,有些子树是NULL,如果为NULL则是不能用->符号引用的,所以代码是不合理的。
改进代码:
int LeafNode(BTNode* root)
{if (root == NULL)return 0;if (root->left == NULL && root->right == NULL)return 1;return LeafNode(root->left) + LeafNode(root->right);
}
先在最前面判断是否为空,在进行下面的操作,就可以避免遇到NULL而去解引。
3.解决单值二叉树题
题意可以知道单值二叉树是结点的值是一样的,需要去遍历二叉树去判断是否所有的值都相等,
通过判断根和左右子树是否相等,如果每个根和左右子树都相等,则整体的全部值也会相等,就像a=b,b=c,所以a=b=c。
代码:
bool isUnivalTree(struct TreeNode* root) {if(root==NULL)return true;if(root->left&&root->left->val!=root->val)return false;if(root->right&&root->right->val!=root->val)return false;return isUnivalTree(root->left)&&isUnivalTree(root->right);}
分析:
之所用如果不相等就返回false是因为后面的子结点是否相等还不知道,判断相等就返回true太早了,可能后面的值与前面的不相等,只有全部相等才能返回true。