目录
[1. 二叉树链式结构的概念](#1. 二叉树链式结构的概念)
[2. 二叉树的遍历](#2. 二叉树的遍历)
[2.1 前序遍历](#2.1 前序遍历)
[2.2 中序遍历](#2.2 中序遍历)
[2.3 后序遍历](#2.3 后序遍历)
[2.4 层序遍历](#2.4 层序遍历)
[3. 二叉树的节点个数以及高度](#3. 二叉树的节点个数以及高度)
[3.1 二叉树节点个数](#3.1 二叉树节点个数)
[3.2 二叉树叶子节点个数](#3.2 二叉树叶子节点个数)
[3.3 二叉树的高度](#3.3 二叉树的高度)
[3.4 二叉树第k层节点个数](#3.4 二叉树第k层节点个数)
[3.5 二叉树查找值为x的节点](#3.5 二叉树查找值为x的节点)
[4. 二叉树的创建和销毁](#4. 二叉树的创建和销毁)
[4.1 二叉树的销毁](#4.1 二叉树的销毁)
[4.2 通过前序遍历的数组构建二叉树](#4.2 通过前序遍历的数组构建二叉树)
[4.3 判断是否是完全二叉树](#4.3 判断是否是完全二叉树)
[5. 编程题](#5. 编程题)
[5.1 相同的树](#5.1 相同的树)
[5.2 单值二叉树](#5.2 单值二叉树)
[5.3 对称二叉树](#5.3 对称二叉树)
[5.4 二叉树的前序遍历](#5.4 二叉树的前序遍历)
[5.5 另一棵树的子树](#5.5 另一棵树的子树)
[6. 选择题](#6. 选择题)
1. 二叉树链式结构的概念
由空树,根节点,根的左子树,根的右子树组成。
2. 二叉树的遍历
2.1 前序遍历
访问根结点的操作发生在遍历其左右子树之前(根左右)。当根节点为NULL时返回。
1 2 3 N N N 4 5 N N 6 N N(空可以省略)
先访问根节点,然后访问根的左子树。
如果根的左子树不为空就把左子树的第一个节点作为根继续往下。
左子树访问完后就访问右子树。
cppvoid PreOrder(BTNode* root) { if (root == NULL) return; printf("%d ", root->data); PreOrder(root->left); PreOrder(root->right); }
2.2 中序遍历
访问根结点的操作发生在遍历其左右子树之中(左根右)。当根节点为NULL时返回。
N 3 N 2 N 1 N 5 N 4 N 6 N(N可以省略)
先访问根节点的左子树。
将左子树的第一个节点作为根节点继续访问根的左子树。
当左子树为空就返回访问根,然后访问根的右子树。
cppvoid InOrder(BTNode* root) { if (root == NULL) return; InOrder(root->left); printf("%d ", root->data); InOrder(root->right); }
2.3 后序遍历
访问根结点的操作发生在遍历其左右子树之后
N N 3 N 2 N N 5 N N 6 4 1
cppvoid PostOrder(BTNode* root) { if (root == NULL) return; PostOrder(root->left); PostOrder(root->right); printf("%d ", root->data); }
2.4 层序遍历
要借助队列。
每个队列节点里面的数据是二叉树节点的指针。
第一步,一开始,当二叉树根节点不为空就入队列。
第二步,当队列不为空就不断出队列,并带入队头数据的左右节点,前提是他们不为空。
cppvoid LevelOrder(BTNode* root) { Queue q; QueueInit(&q); //一开始,当二叉树根节点不为空就入队列。 if (root != NULL) QueuePush(&q, root); while (!QueueEmpty(&q)) { BTNode* front = QueueFront(&q); QueuePop(&q); printf("%d ", front->data); if(front->left != NULL) QueuePush(&q, front->left); if(front->right != NULL) QueuePush(&q, front->right); } printf("\n"); QueueDestroy(&q); }
3. 二叉树的节点个数以及高度
3.1 二叉树节点个数
返回右子树的节点个数和左子树的节点个数并加上自己。
遇到空节点返回0。
cppint BinaryTreeSize(BTNode* root) { //遇到空节点返回0。 if (root == NULL) return 0; //返回右子树的节点个数和左子树的节点个数并加上自己。 return BinaryTreeSize(root->left) + BinaryTreeSize(root->right) + 1; }
3.2 二叉树叶子节点个数
找自己的左子树和右子树。
找到叶子就返回1。
cppint BinaryTreeLeafSize(BTNode* root) { //空节点返回0 if (root == NULL) return 0; //找到叶子就返回1。 if (root->left == NULL && root->right == NULL) return 1; //找自己的左子树和右子树。 return BinaryTreeLeafSize(root->left) + BinaryTreeLeafSize(root->right); }
3.3 二叉树的高度
遇到空节点返回0。
返回左右子树较大值,并加1也就是加上自己。
cppint BTHeight(BTNode* root) { //遇到空节点返回0。 if (root == NULL) return 0; //返回左右子树较大值,并加1也就是加上自己。 int lefthigh = BTHeight(root->left); int righthigh = BTHeight(root->right); return max(lefthigh, righthigh) + 1; }
3.4 二叉树第k层节点个数
思路:对于第一层是找第k层,对于第二层是找k-1层,对于第k层就是找当前层,不断让子树去找,每下去一层k就减一,当k==1时就是第k层。
k必须大于0。
遇到空节点返回0。
当k == 1返回1。
继续找左右子树。
cppint BinaryTreeLevelKSize(BTNode* root, int k) { //1. k必须大于0。 assert(k > 0); //2. 遇到空节点返回0。 if (root == NULL) return 0; //3. 当k == 1返回1。 if (k == 1) return 1; //4. 继续找左右子树。 return BinaryTreeLevelKSize(root->left, k-1) + BinaryTreeLevelKSize(root->right, k-1); }
3.5 二叉树查找值为x的节点
遇到值相等就返回该节点,遇到空节点就返回空。
先判断根节点的值是否等于x,如果等于就返回根节点,不等于就找自己的左右子树。
记录左右子树的结果,谁找到了就返回,都没找到就返回空。
cppBTNode* BinaryTreeFind(BTNode* root, BTDataType x) { if (root == NULL) return NULL; if (root->data == x) return root; BTNode* left = BinaryTreeFind(root->left, x); if (left != NULL) return left; BTNode* right = BinaryTreeFind(root->right, x); if (right != NULL) return right; return NULL; }
4. 二叉树的创建和销毁
4.1 二叉树的销毁
利用后序的思想,先释放左子树再释放右子树最后释放自己。
在函数外置空,调用的人自己置空。
cppvoid BinaryTreeDestory(BTNode* root) { if (root == NULL) return; BinaryTreeDestory(root->left); BinaryTreeDestory(root->right); free(root); }
4.2 通过前序遍历的数组构建二叉树
用%s接收字符串。
利用前序思想构建二叉树,将当前数组下标对应的值作为根的值构建节点,然后数组下标加一,再然后就去构建他的左子树和右子树。
遇到#代表是空,下标加一然后返回空。
cpp#include <stdio.h> typedef char BTDataType; typedef struct BTNode { BTDataType data; struct BTNode* left; struct BTNode* right; }BTNode; BTNode* BuyNode(BTDataType x) { BTNode* node = (BTNode*)malloc(sizeof(BTNode)); if (node == NULL) { perror("malloc"); return NULL; } node->data = x; node->left = node->right = NULL; return node; } BTNode* Insert(char* arr, int* pi) { if(arr[*pi] == '#') { (*pi)++; return NULL; } BTNode* root = BuyNode(arr[*pi]); (*pi)++; root->left = Insert(arr, pi); root->right = Insert(arr, pi); return root; } void InOrder(BTNode* root) { if(root == NULL) return; InOrder(root->left); printf("%c ", root->data); InOrder(root->right); } int main() { char arr[100]; scanf("%s", arr); int i = 0; BTNode* root = Insert(arr, &i); InOrder(root); return 0; }
4.3 判断是否是完全二叉树
完全二叉树每一层都是连续的
利用层序遍历的思想,将二叉树节点作为数据插入队列,遇到数据为空就结束遍历。
判断队列剩下的数据有无非空,全都是空证明是连续的,有非空证明不连续。
cppbool BinaryTreeComplete(BTNode* root) { Queue q; QueueInit(&q); if(root != NULL) QueuePush(&q, root); while (!QueueEmpty(&q)) { BTNode* front = QueueFront(&q); QueuePop(&q); if (front == NULL) break; QueuePush(&q, front->left); QueuePush(&q, front->right); } while (!QueueEmpty(&q)) { if (QueueFront(&q) != NULL) { QueueDestroy(&q); return false; } QueuePop(&q); } QueueDestroy(&q); return true; }
5. 编程题
5.1 相同的树
思路:分治子问题。
根节点值相等就比较他们的左子树和右子树。
根节点值不相等直接返回false。
接下来比结构,同时走到空意味着前面的结构和值是相等的,一个为空一个不为空证明结构不相等。
cppbool isSameTree(struct TreeNode* p, struct TreeNode* q) { if(p == NULL && q == NULL) return true; if(p == NULL || q == NULL) return false; if(p->val != q->val) return false; return isSameTree(p->left, q->left) && isSameTree(p->right, q->right); }
5.2 单值二叉树
思路:
将根节点和自己的左右孩子比较,注意左右孩子可能为空。
相等则往下交给自己的左右子树。
cppbool isUnivalTree(struct TreeNode* root) { if(root == NULL) return true; if(root->left != NULL && root->val != root->left->val) return false; if(root->right != NULL && root->val != root->right->val) return false; return isUnivalTree(root->left) && isUnivalTree(root->right); }
5.3 对称二叉树
链接: . - 力扣(LeetCode)
思路:
根节点的左右子树要形成镜像对称。
需要写一个子函数去比较左右子树是否对称。
左子树的左边和右子树的右边对应,左子树的右边和右子树的左边对应。
cppbool isSameTree(struct TreeNode* p, struct TreeNode* q) { if(p == NULL && q == NULL) return true; if(p == NULL || q == NULL) return false; if(p->val != q->val) return false; return isSameTree(p->left, q->right) && isSameTree(p->right, q->left); } bool isSymmetric(struct TreeNode* root) { return isSameTree(root->left, root->right); }
5.4 二叉树的前序遍历
链接: . - 力扣(LeetCode)
思路:
题目给个returnSize代表节点的个数。需要自己求。
利用求的节点个数开空间。
再写一个子函数进行前序遍历,将根值放入数组中,下标要传址。
cppvoid _preorderTraversal(struct TreeNode* root, int* ret, int* i) { if(root == NULL) return; ret[(*i)++] = root->val; _preorderTraversal(root->left, ret, i); _preorderTraversal(root->right, ret, i); } int Size(struct TreeNode* root) { if(root == NULL) return 0; else return 1 + Size(root->left) + Size(root->right); } int* preorderTraversal(struct TreeNode* root, int* returnSize) { *returnSize = Size(root); int* tmp = (int*)malloc(sizeof(int)*(*returnSize)); int i = 0; _preorderTraversal(root, tmp, &i); return tmp; }
5.5 另一棵树的子树
链接: . - 力扣(LeetCode)
思路:
遍历root,将root每个节点和subroot比较。
注意,遍历到空意味着前面的比较都不相等。subroot也不可能为空。
cppbool isSameTree(struct TreeNode* p, struct TreeNode* q) { if(p == NULL && q == NULL) return true; if(p == NULL || q == NULL) return false; if(p->val != q->val) return false; return isSameTree(p->left, q->left) && isSameTree(p->right, q->right); } bool isSubtree(struct TreeNode* root, struct TreeNode* subRoot) { if(root == NULL) return false; if(isSameTree(root, subRoot) == true) return true; return isSubtree(root->left, subRoot) || isSubtree(root->right, subRoot); }
6. 选择题
1.某完全二叉树按层次输出(同一层从左到右)的序列为 ABCDEFGH 。则该完全二叉树的前序序列为()
A. ABDHECFG
B. ABCDEFGH
C. HDBEAFCG
D. HDEBFGCA
答:A
2.二叉树的先序遍历和中序遍历如下:先序遍历:EFHIGJK; 中序遍历:HFIEJKG.则二叉树根结点为()A. E
B. F
C. G
D. H
答:A
3.设一课二叉树的中序遍历序列:badce,后序遍历序列:bdeca,则二叉树前序遍历序列为____。A. adbce
B. decab
C. debac
D. abcde
答:D
4.某二叉树的后序遍历序列与中序遍历序列相同,均为 ABCDEF ,则按层次输出(同一层从左到右)的序列为A. FEDCBA
B. CBAFED
C. DEFCBA
D. ABCDEF
答:A