目录
几个关于性质的题目
1. 某二叉树共有 399 个结点,其中有 199 个度为 2 的结点,则该二叉树中的叶子结点数为( )A 不存在这样的二叉树B 200C 198D 1992. 下列数据结构中,不适合采用顺序存储结构的是( )A 非完全二叉树B 堆C 队列D 栈3. 在具有 2n 个结点的完全二叉树中,叶子结点个数为( )A nB n+1C n-1D n/24. 一棵完全二叉树的节点数位为 531 个,那么这棵树的高度为( )A 11B 10C 8D 125. 一个具有 767 个节点的完全二叉树,其叶子节点个数为()A 383B 384C 385D 386
1.B -- 根据性质第3条很容易得出 -- 叶子指的是度为0结点2.A -- 这里的队列其实也不应该使用顺序存储,但是相比A来说就离谱了 -- 看补图3.A -- 2n就一定是非满二叉树了,所以有一个度为1的结点,再利用性质3可以得出4.B -- 对于高度h的计算公式:2^(h-1) <= N <= 2^h-15.B -- 根据二叉树的性质这个一定是满二叉树,所以没有度为1的结点,再利用性质3可以得出
对于2的补图


基于上述才可以引入 二叉树的遍历 这一概论
学习二叉树结构,最简单的方式就是遍历。所谓二叉树遍历(Traversal)是按照某种特定的规则,依次对二叉树中的节点进行相应的操作,并且每个节点只操作一次。访问结点所做的操作依赖于具体的应用问题。 遍历是二叉树上最重要的运算之一,也是二叉树上进行其它运算的基础。
根 左子树 右子树 -- 1概述
左子树 根 右子树 -- 2概述
左子树 右子树 根 -- 3概述

为了后续更好的理解二叉树的实现,下面写一个简单的只遍历的测试用例
- #include "Queue.h"
-
- //初始化 -- 到现在有三种不用二级指针来初始化结构体了 返回值、头节点
- void QueueInit(Queue* pq)
- {
- assert(pq); //因为这里是自己创立的节点是不可能为空的,为空就是出问题了
- pq->head = pq->tail = NULL;
- pq->size = 0;
- }
-
- //销毁
- void QueueDestroy(Queue* pq)
- {
- assert(pq);
- QNode* cur = pq->head;
- while (cur)
- {
- QNode* del = cur;
- cur = cur->next;
- free(del);
- }
-
- pq->head = pq->tail = NULL; //要改变什么就需要什么的指针,这里置空就可以了
- }
-
- //入队列 -- 根据队列的性质 只尾插
- void QueuePush(Queue* pq, QDataType x)
- {
- assert(pq);
- QNode* newnode = (QNode*)malloc(sizeof(QNode));
- if (newnode == NULL)
- {
- perror("malloc fail");
- exit(-1);
- }
- else
- {
- newnode->data = x;
- newnode->next = NULL; //这里置空,下面就不需要置空了
- }
-
- if (pq->tail == NULL)
- {
- pq->head = pq->tail = newnode;
- }
- else
- {
- pq->tail->next = newnode;
- pq->tail = newnode;
- }
-
- pq->size++;
- }
-
- //出队列 -- 根据队列的性质 只头删
- void QueuePop(Queue* pq)
- {
- assert(pq);
- assert(!QueueEmpty(pq));
-
- if (pq->head->next == NULL) //防止只剩下一个节点时候再删,就会导致野指针
- {
- free(pq->head);
- pq->tail = pq->head = NULL;
- }
- else
- {
- QNode* del = pq->head;
- pq->head = pq->head->next;
-
- free(del);
- del = NULL;
- }
-
- pq->size--;
- }
-
- //查看头
- QDataType QueueFront(Queue* pq)
- {
- assert(pq);
- assert(!QueueEmpty(pq));
-
- return pq->head->data;
- }
-
- //查看尾
- QDataType QueueBack(Queue* pq)
- {
- assert(pq);
- assert(!QueueEmpty(pq));
-
- return pq->tail->data;
- }
-
- //判断是否为空
- bool QueueEmpty(Queue* pq)
- {
- assert(pq);
-
- return pq->head == NULL && pq->tail == NULL; //理论上两个都应该为空才为空,一般一个为空就都会为空,不是就出问题了
- }
-
- //查看节点长度
- int QueueSize(Queue* pq)
- {
- assert(pq);
-
- //这样写就变为O(N)级别了 为此需要稍作修改
- //QNode* cur = pq->head;
- //int n = 0;
- //while (cur)
- //{
- // ++n;
- // cur = cur->next;
- //}
-
- //return n;
-
- return pq->size;
- }
- #pragma once
-
- #include
- #include
- #include
- #include
-
-
-
-
- typedef int BTDataType;
- typedef struct BinaryTreeNode
- {
- BTDataType data;
- struct BinaryTreeNode* left;
- struct BinaryTreeNode* right;
- }BTNode;
-
-
- //能用单链表解决问题就可以用单链表 -- 没必要使用双向循环链表,这属于有点小题大做了
-
- //用C写的话就得把 -- 之前写的队列用上
- typedef BTNode* QDataType;
- typedef struct QueueNode
- {
- struct QueueNode* next;
- QDataType data;
- }QNode;
-
- //再定义一个节点来存放两个指针,方便后面操作
- typedef struct Queue
- {
- //尾指针
- QNode* tail;
- //头指针
- QNode* head;
- int size;
- }Queue;
-
- //初始化 -- 到现在有三种不用二级指针来初始化结构体了 返回值、哨兵位的头节点和这里的创立一个新的结构体放俩这种
- void QueueInit(Queue* pq);
-
- //销毁
- void QueueDestroy(Queue* pq);
-
- //入队列 -- 根据队列的性质 只尾插
- void QueuePush(Queue* pq,QDataType x);
-
- //出队列 -- 根据队列的性质 只头删
- void QueuePop(Queue* pq);
-
- //查看头
- QDataType QueueFront(Queue* pq);
-
- //查看尾
- QDataType QueueBack(Queue* pq);
-
- //判断是否为空
- bool QueueEmpty(Queue* pq);
-
- //查看节点长度
- int QueueSize(Queue* pq);
- #include
- #include
- #include
- #include "Queue.h"
-
- //typedef int BTDataType;
- //typedef struct BinaryTreeNode
- //{
- // BTDataType data;
- // struct BinaryTreeNode* left;
- // struct BinaryTreeNode* right;
- //}BTNode;
-
-
- // 二叉树前序遍历
- void PreOrder(BTNode* root)
- {
- if (root == NULL)
- {
- printf("NULL ");
- return;
- }
- printf("%d ", root->data);
- PreOrder(root->left);
- PreOrder(root->right);
- }
- // 二叉树中序遍历
- void InOrder(BTNode* root)
- {
- if (root == NULL)
- {
- printf("NULL ");
- return;
- }
- InOrder(root->left);
- printf("%d ", root->data);
- InOrder(root->right);
- }
- // 二叉树后序遍历
- void PostOrder(BTNode* root)
- {
- if (root == NULL)
- {
- printf("NULL ");
- return;
- }
- PostOrder(root->left);
- printf("%d ", root->data);
- PostOrder(root->right);
- }
- //会发现其实遍历的代码很简单,只是理解起来要复杂
-
-
- //遍历计算的思路
- //int count = 0;
- //void TreeSize_1(BTNode* root)
- //{
- // if (root == NULL)
- // return;
- //
- // ++count;
- // TreeSize_1(root->left);
- // TreeSize_1(root->right);
- //
- // return;
- //}
-
- //层序遍历 -- 用C的方法就得用到队列 -- 解析二
- void TreeLevelOrder(BTNode* root)
- {
- Queue q;
- QueueInit(&q);
-
- if (root)
- QueuePush(&q, root); //QueueNode* 里面包含一个BTNode*的指针
-
- while (!QueueEmpty(&q))
- {
- BTNode* front = QueueFront(&q);
- QueuePop(&q);
- printf("%d ", front->data);
-
- //下一层入队列
- if(front->left)
- QueuePush(&q, front->left);
- if (front->right)
- QueuePush(&q, front->right);
- }
- printf("\n");
-
- QueueDestroy(&q);
- }
-
-
-
- //二叉树的结点数 -- 切割子问题的思路
- int TreeSize(BTNode* root)
- {
- return root == NULL ? 0 : TreeSize(root->left) + TreeSize(root->right) + 1;
- //解释起来就是:左子树 + 右子树 + 根
- }
-
- //二叉树的叶子结点数
- int TreeLeafSize(BTNode* root)
- {
- if (root == NULL)
- return 0;
- if (root->left == NULL && root->right == NULL)
- return 1;
-
- return TreeLeafSize(root->left) + TreeLeafSize(root->right);
- }
-
- //二叉树的高度 -- 解析一
- int TreeHeight(BTNode* root)
- {
- if (root == NULL)
- return 0;
-
- int lh = TreeHeight(root->left);
- int rh = TreeHeight(root->right);
-
- return lh > rh ? lh + 1 : rh + 1;
- }
-
- //第k层节点个数
- int TreeKlevel(BTNode* root, int k)
- {
- assert(k > 0);
-
- if (root == NULL)
- return 0;
- if (k == 1)
- return 1;
-
- //转化成求子树第k-1层
- return TreeKlevel(root->left, k-1) + TreeKlevel(root->right, k-1);
- }
-
- //返回x所在的节点
- BTNode* TreeFind(BTNode* root, BTDataType x)
- {
- if (root == NULL)
- return NULL;
- if (root->data == x)
- return root;
-
- BTNode* lret = TreeFind(root->left, x);
- if (lret)
- return lret;
- BTNode* rret = TreeFind(root->right, x);
- if (rret)
- return rret;
-
- return NULL;
- }
-
-
- //判断是否为完全二叉树 -- 利用队列的方法
- int BinaryTreeComplete(BTNode* root)
- {
- Queue q;
- QueueInit(&q);
-
- if (root)
- QueuePush(&q, root); //QueueNode* 里面包含一个BTNode*的指针
-
- while (!QueueEmpty(&q))
- {
- BTNode* front = QueueFront(&q);
- QueuePop(&q);
-
- //遇到空就跳出循环 -- 解析3
- if (front == NULL)
- break;
-
- QueuePush(&q, front->left);
- QueuePush(&q, front->right);
- }
-
- //遇到空以后,后面全是空,则是完全二叉树
- //遇到空以后,后面存在非空,则不是完全二叉树
- while (!QueueEmpty(&q))
- {
- BTNode* front = QueueFront(&q);
- QueuePop(&q);
-
- //遇到空就跳出循环 -- 解析3
- if (front != NULL)
- {
- QueueDestroy(&q);
- return false;
- }
-
- }
-
- QueueDestroy(&q);
- return true;
- }
-
-
- BTNode* CreateTree()
- {
- BTNode* n1 = (BTNode*)malloc(sizeof(BTNode));
- assert(n1);
- BTNode* n2 = (BTNode*)malloc(sizeof(BTNode));
- assert(n2);
- BTNode* n3 = (BTNode*)malloc(sizeof(BTNode));
- assert(n3);
- BTNode* n4 = (BTNode*)malloc(sizeof(BTNode));
- assert(n4);
- BTNode* n5 = (BTNode*)malloc(sizeof(BTNode));
- assert(n5);
- BTNode* n6 = (BTNode*)malloc(sizeof(BTNode));
- assert(n6);
- BTNode* n7 = (BTNode*)malloc(sizeof(BTNode));
- assert(n7);
-
- n1->data = 1;
- n2->data = 2;
- n3->data = 3;
- n4->data = 4;
- n5->data = 5;
- n6->data = 6;
-
- //为测试高度添加的一个结点
- n7->data = 7;
-
- n1->left = n2;
- n1->right = n4;
- n2->left = n3;
- n2->right = NULL;
- n4->left = n5;
- n4->right = n6;
- n3->left = NULL;
- n3->right = NULL;
- n5->left = NULL;
- n5->right = NULL;
- n6->left = NULL;
- n6->right = NULL;
-
- //为测试高度添加的一个结点
- n3->right = n7;
- n7->left = NULL;
- n7->right = NULL;
-
- return n1;
- }
-
-
- //二叉树的销毁 -- 后序销毁
- void BinaryTreeDestroy(BTNode* root)
- {
- //当左右子树都为空时就释放
- if (root == NULL)
- return;
-
- BinaryTreeDestroy(root->left);
- BinaryTreeDestroy(root->right);
- free(root);
- }
-
- int main()
- {
- BTNode* root = CreateTree();
- //前序遍历
- PreOrder(root);
- printf("\n");
-
- //中序遍历
- InOrder(root);
- printf("\n");
-
- //后序遍历
- PostOrder(root);
- printf("\n");
-
- //遍历计算
- //先归零再计算,因为是全局变量
- //count = 0;
- //TreeSize_1(root);
- //printf("Tree size:%d\n", count);
-
- //结点数
- printf("Tree size:%d\n", TreeSize(root));
-
- //叶子数
- printf("Tree Leaf size:%d\n", TreeLeafSize(root));
-
- //高度
- printf("Tree Height size:%d\n", TreeHeight(root));
-
- //第k层节点个数
- printf("Tree K Level:%d\n", TreeKlevel(root, 4));
-
- //返回x所在的节点
- printf("Tree Find:%p\n", TreeFind(root, 7));
- //有了节点的地址可以改该节点的值
- BTNode* p = TreeFind(root, 7);
- p->data *= 10;
- //前序遍历
- PreOrder(root);
- printf("\n");
-
- //层序遍历
- TreeLevelOrder(root);
-
- //判断是否为完全二叉树 -- true 与 false 本质上就是1与0
- printf("Tree BinaryTreeComplete:%d\n", BinaryTreeComplete(root));
-
- //因为里面没有实现置空,所以在外部需要置空 -- 要实现置空得使用二级指针
- BinaryTreeDestroy(root);
- root = NULL;
-
- return 0;
- }

结果与前面分析是一致的
分析高度的递归路线

其他的递归路线大同小异,画图便自然理解
分析层序遍历 -- 利用队列的方法去实现

完全二叉树与非完全二叉树的解析

DFS -- 深度优先遍历
前序遍历 -- 遍历顺序和访问数据都符合
中序遍历 -- 遍历符合
后序遍历 -- 遍历符合
递归先往深走,遇到不满足条件再往回退,走其他分支路径 -- 往一个方向走

BFS -- 广度优先遍历
层序遍历

965. 单值二叉树 -- leetcode链接
递归解决 -- 让子树去比较
- bool isUnivalTree(struct TreeNode* root){
- if(root == NULL)
- return true;
- if(root->left && root->val != root->left->val)
- return false;
- if(root->right && root->val != root->right->val)
- return false;
-
- return isUnivalTree(root->left) && isUnivalTree(root->right);
- }
100. 相同的树 -- -- leetcode链接
- bool 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);
- }
572. 另一棵树的子树 -- leetcode链接
上面一题刚好可以为下面一题服务
- bool 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))
- return true;
-
- return isSubtree(root->left,subRoot) || isSubtree(root->right,subRoot);
-
- }
144. 二叉树的前序遍历 -- -- leetcode链接
- //查看节点数
- int TreeSize(struct TreeNode* root)
- {
- if(root == NULL)
- return 0;
- return TreeSize(root->left) + TreeSize(root->right) + 1;
- }
-
- //前序遍历
- void preorder(struct TreeNode* root, int* a, int* pi)
- {
- if(root == NULL)
- return;
-
- a[*pi] = root->val;
- (*pi)++;
-
- preorder(root->left, a, pi);
- preorder(root->right, a, pi);
- }
-
- int* preorderTraversal(struct TreeNode* root, int* returnSize){
-
- int n = TreeSize(root);
- int* a = (int*)malloc(sizeof(int)*n);
-
- int i = 0;
- preorder(root, a, &i);
- //这里是返回数组的大小,因为C不支持一次返回两个值,利用输出型参数可以解决
- *returnSize = n;
-
- return a;
- }
94. 二叉树的中序遍历 -- leetcode链接
- //查看节点数
- int TreeSize(struct TreeNode* root)
- {
- if(root == NULL)
- return 0;
- return TreeSize(root->left) + TreeSize(root->right) + 1;
- }
-
- //中序遍历
- void preorder(struct TreeNode* root, int* a, int* pi)
- {
- if(root == NULL)
- return;
-
- preorder(root->left, a, pi);
- a[*pi] = root->val;
- (*pi)++;
- preorder(root->right, a, pi);
- }
-
- int* inorderTraversal(struct TreeNode* root, int* returnSize){
-
- int n = TreeSize(root);
- int* a = (int*)malloc(sizeof(int)*n);
-
- int i = 0;
- preorder(root, a, &i);
- //这里是返回数组的大小,因为C不支持一次返回两个值,利用输出型参数可以解决
- *returnSize = n;
-
- return a;
- }
145. 二叉树的后序遍历 -- leetcode链接
- //查看节点数
- int TreeSize(struct TreeNode* root)
- {
- if(root == NULL)
- return 0;
- return TreeSize(root->left) + TreeSize(root->right) + 1;
- }
-
- //后序遍历
- void preorder(struct TreeNode* root, int* a, int* pi)
- {
- if(root == NULL)
- return;
-
- preorder(root->left, a, pi);
- preorder(root->right, a, pi);
- a[*pi] = root->val;
- (*pi)++;
- }
-
- int* postorderTraversal(struct TreeNode* root, int* returnSize){
-
- int n = TreeSize(root);
- int* a = (int*)malloc(sizeof(int)*n);
-
- int i = 0;
- preorder(root, a, &i);
- //这里是返回数组的大小,因为C不支持一次返回两个值,利用输出型参数可以解决
- *returnSize = n;
-
- return a;
- }
二叉树遍历_牛客题霸_牛客网 (nowcoder.com) -- 题目链接
- #include
- #include
- #include
- typedef char BTDataType;
- typedef struct BinaryTreeNode
- {
- BTDataType data;
- struct BinaryTreeNode* left;
- struct BinaryTreeNode* right;
- }BTNode;
-
- //中序遍历
- void InOrder(BTNode* root)
- {
- if (root == NULL)
- {
- return;
- }
- InOrder(root->left);
- printf("%c ", root->data);
- InOrder(root->right);
- }
-
- // 通过前序遍历的数组"abc##de#g##f###"构建二叉树
- BTNode* BinaryTreeCreate(BTDataType* a, int* pi)
- {
- if(a[*pi] == '#')
- {
- (*pi)++;
- return NULL;
- }
-
-
- BTNode* root = (BTNode*)malloc(sizeof(BTNode));
-
- root->data = a[*pi];
- (*pi)++;
-
- root->left = BinaryTreeCreate(a, pi);
- root->right = BinaryTreeCreate(a, pi);
-
- return root;
- }
-
-
- int main()
- {
- char str[100] = {0};
- scanf("%s",str);
-
- int i = 0;
- BTNode* root = BinaryTreeCreate(str, &i);
- assert(root);
-
- InOrder(root);
-
- return 0;
- }
101. 对称二叉树 -- leetcode链接
- bool 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){
- if(root == NULL)
- return true;
-
- return isSameTree(root->left,root->right);
-
- }
根据另一棵树的子树改编过来
兔子不吃窝边草,窝边有草何必满山跑
——俗语