前言✨笔者也仅是大一萌新,写博客为了记录和巩固知识✨
赠人玫瑰,手留余香,欢迎各位读者进行交流和建议
能与大家一起学习,一起进步是我的荣幸
如果这篇文章有帮助到您,还请留个赞支持一下哦
前情提要
二叉树第一章——初识二叉树
二叉树第二章——二叉树堆排序,TopK问题
在前文中,我们学习了二叉树的顺序结构,但是顺序结构只能处理完全二叉树,如果要处理一棵普通的二叉树,那就要用到链式结构
二叉树会有两种情况:空数(一个值都没有) 非空树(由根结点,根节点的左子树、根节点的右子树组成),并且二叉树是递归式的,这也是我们遍历二叉树的重要知识点
二叉树遍历是按照某种特定的规则,依次对二叉树的结点进行对应操作,并且每个结点只操作一次,遍历是二叉树进行其它运算的基础。
二叉树遍历具有三种方法:
- 前序——根,左子树,右子树
- 中序——左子树,根,右子树
- 后序——左子树,右子树,根
以下面的图为例子,每一个数据它可以是根、左子树、右子树的任意一种
如:5可以作为根,它的左子树为NULL,右子树为NULL,6也可以作为根,它的左子树为5,右子树为3,所以一定要完全遵守方法进行遍历
前序的遍历顺序是:根,左子树,右子树
以上图数据(12 10 11 6 4 0 1 5 3 3)来看,它**前序遍历后的打印顺序为:12 10 6 5 NULL NULL 3 NULL NULL 4 NULL 3 NULL NULL 11 0 NULL NULL 1 NULL NULL(NULL是为了更好理解它的遍历过程,实际并不需要打印出来)**
前序的遍历顺序是:左子树,根,右子树
以数据(12 10 11 6 4 0 1 5 3 3)来看,它**中序遍历后的打印顺序为:NULL 5 NULL 6 NULL 3 NULL 10 NULL 4 NULL 3 NULL 12 NULL 0 NULL 11 NULL 1 NULL**
前序的遍历顺序是:左子树,右子树,根
以数据(12 10 11 6 4 0 1 5 3 3)来看,它**后序遍历后的打印顺序为:NULL NULL 5 NULL NULL 3 6 NULL NULL NULL 3 4 10 NULL NULL 0 NULL NULL 1 11 12**
这里引用了队列的头文件是方便之后进行层序遍历
#include "Queue.h" typedef struct BinaryTreeNode { struct BinaryTreeNode* left; //左孩子指针 struct BinaryTreeNode* right; //右孩子指针 QDataType data; }BTNode; BTNode* BuyBTNode(QDataType x) //扩容 { BTNode* node = (BTNode*)malloc(sizeof(BTNode)); if (node == NULL) { printf("malloc fail!"); exit(-1); } node->data = x; node->left = node->right = NULL; return node; } BTNode* CreatBinaryTree() //录入数据 { BTNode* node1 = BuyBTNode(12); BTNode* node2 = BuyBTNode(10); BTNode* node3 = BuyBTNode(11); BTNode* node4 = BuyBTNode(6); BTNode* node5 = BuyBTNode(4); BTNode* node6 = BuyBTNode(0); BTNode* node7 = BuyBTNode(1); BTNode* node8 = BuyBTNode(5); BTNode* node9 = BuyBTNode(3); BTNode* node10 = BuyBTNode(3); node1->left = node2; node1->right = node3; node2->left = node4; node2->right = node5; node3->left = node6; node3->right = node7; node4->left = node8; node4->right = node9; node5->right = node10; return node1; }
递归图(这玩意害得自己画,真要画出来可能除了自己别人也看不懂,笔者水平有限只能画成这样了,也只是给大家看下大概的思路,后面递归也不再画了,毕竟画了大家也看不懂,真有问题的话可以来问我哦):
void PrevOrder(BTNode* root) { if (root == NULL) { printf("NULL "); return; } printf("%d ", root->data); PrevOrder(root->left); PrevOrder(root->right); }
void InOrder(BTNode* root) { if (root == NULL) { printf("NULL "); return; } InOrder(root->left); printf("%d ", root->data); InOrder(root->right); }
void TailOrder(BTNode* root) { if (root == NULL) { printf("NULL "); return; } TailOrder(root->left); TailOrder(root->right); printf("%d ", root->data); }
这里可以使用全局变量进行存储count,但是会存在线程安全问题,当在linux多线程中,别人不小心用了这个全局变量,就会出错,并且多次调用函数时,全局或者静态变量会继续上次的值进行运算
int BTreeSize(BTNode* root/*, int* pCount*/) //思路:每当遍历到了一个不为空的结点,就加一(遍历+计数) { /*if (root == NULL) return; ++(*pCount); BTreeSize(root->left, pCount); BTreeSize(root->right, pCount);*/ //后序 最优 return root == NULL ? 0 : BTreeSize(root->left) + BTreeSize(root->right) + 1; }
int BTreeLeafSize(BTNode* root) //思路:叶子大小就是度为0的结点,那么我们就判断该结点是否具有左子树和右子树 { if (root == NULL) { return 0; } if (root->left == NULL && root->right == NULL) { return 1; } return BTreeLeafSize(root->left) + BTreeLeafSize(root->right); }
思路:求第K层大小,实际找到K-1层然后向下遍历一层即可,如求第4层,那么我们到第三层时开始遍历,此时第四层k等于1,如果该节点不为空,那么就会返回1回去
int BTreeKLevelSize(BTNode* root, int k) { assert(k >= 1); if (root == NULL) { return 0; } if (k == 1) { return 1; } else //转换成左子树k-1层节点个数 + 右子树k-1层节点个数 return BTreeKLevelSize(root->left, k - 1) + BTreeKLevelSize(root->right, k - 1); }
int BTreeDepth(BTNode* root)//找最高的+1 { if (root == NULL) { return 0; } int leftDepth = BTreeDepth(root->left); int rightDepth = BTreeDepth(root->right); return leftDepth > rightDepth ? leftDepth + 1 : rightDepth + 1; }
类似于前序递归,根–>左树–>右树的方式查找,并且返回指针,如果没有找到就返回空。
BTNode* BTreeFind(BTNode* root, QDataType x) { if (root == NULL) return NULL; if (root->data == x) return root; BTNode* ret1 = BTreeFind(root->left, x); if (ret1) { return ret1; } BTNode* ret2 = BTreeFind(root->right, x); if (ret2) { return ret2; } return NULL; }
bool BTreeComplete(BTNode* root) { Queue q; QueueInit(&q); if (root) 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)) { BTNode* front = QueueFront(&q); QueuePop(&q); if (front) { QueueDestory(&q); return false; } } QueueDestory(&q); return true; }
void LevelOreder(BTNode* root) { Queue q; QueueInit(&q); if (root) { QueuePush(&q, root); } 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"); QueueDestory(&q); }
void BTreeDestory(BTNode* root) //root只是一个形参 { if (root == NULL) { return; } BTreeDestory(root->left); BTreeDestory(root->right); free(root); }
BTNode* tree = CreatBinaryTree(); PrevOrder(tree); printf("\n"); InOrder(tree); printf("\n"); TailOrder(tree); printf("\n"); /*int count = 0; //每次使用都要先置0一次 BTreeSize(tree, &count); printf("%d\n", count);*/ int count1 = BTreeSize(tree); printf("size = %d\n", count1); int count2 = BTreeLeafSize(tree); printf("leaf size = %d\n", count2); int count3 = BTreeKLevelSize(tree, 3); printf("No.2 size = %d\n", count3); int count4 = BTreeDepth(tree); printf("depth = %d\n", count4); for (int i = 1; i <= 10; i++) { printf("Find:%d,%p\n",i,BTreeFind(tree, i)); } BTNode* ret = BTreeFind(tree, 5); printf("Find:%p\n", BTreeFind(tree, 50)); if (ret) { ret->data = 50; } printf("Find:%p\n", BTreeFind(tree, 50)); LevelOrder(tree); printf("完全二叉树:%d\n", BTreeComplete(tree)); BTreeDestory(tree); //传一级没法完全置空,出来后还需要置空一次 tree = NULL; //这里置空才起作用 return 0;