由于二叉树是数据结构中偏难的一块,这里我们先熟悉二叉树的结构,再具体来实现一颗二叉树,采用手动构建二叉树的方式,帮助大家进一步理解
呈现的是一个树型结构
BTNode *BinaryTreeCreate(char ch)
{
BTNode *newNode = (BTNode *)malloc(sizeof(BTNode));
newNode->data = ch;
newNode->left = NULL;
newNode->right = NULL;
return newNode;
}
void func()
{
BTNode* A = BinaryTreeCreate('A');
BTNode* B = BinaryTreeCreate('B');
BTNode* C = BinaryTreeCreate('C');
BTNode* D = BinaryTreeCreate('D');
BTNode* E = BinaryTreeCreate('E');
BTNode* F = BinaryTreeCreate('F');
A->left = B;
A->right = C;
B->left = D;
C->left = E;
C->right = F;
}
构建了结点与结点之间的父子关系,从代码中可以看到A的左右子树是B和C,B的左子树是D,C的左右子树是E和F,剩余的默认给NULL值,在创建结点的时候就已经初始化好了,那如果想要呈现出遍历顺序呢。
#pragma once
#include<memory.h>
#include<stdbool.h>
#include<stdlib.h>
#include<stdio.h>
#include"Queue.h"
typedef char BTDataType;
typedef struct BTNode
{
struct BTNode *left;
struct BTNode *right;
BTDataType data;
}BTNode;
//创建结点
BTNode *BinaryTreeCreate(char ch);
//后序遍历
void PrevOrder(BTNode *root);
//中序遍历
void Inorder(BTNode *root);
//后序遍历
void Postorder(BTNode *root);
//树结点的个数
int BinaryTreeSize(BTNode *root);
//求叶子结点的个数
int BinaryleafSize(BTNode* root);
//求二叉树的第k层结点个数
int BinaryKSize(BTNode* root,int k);
//查找值为val的结点
BTNode* BinaryFind(BTNode* root,char ch);
//广度优先遍历二叉树
void TreeLevelorder(BTNode* root);
//二叉树的销毁
void BinaryTreeDestroy(BTNode* root);
//判断一颗树是不是完全二叉树
bool BinaryTreecomp(BTNode* root);
#include"BinaryTree.h"
BTNode *BinaryTreeCreate(char ch)
{
BTNode *newNode = (BTNode *)malloc(sizeof(BTNode));
assert(newNode);
newNode->data = ch;
newNode->left = NULL;
newNode->right = NULL;
return newNode;
}
//后序遍历
void PrevOrder(BTNode *root)
{
if (root == NULL)
{
printf("NULL ");
return;
}
else
{
PrevOrder(root->left);
PrevOrder(root->right);
printf("%c ",root->data);
}
}
//中序遍历
void Inorder(BTNode *root)
{
if (root == NULL)
{
printf("NULL ");
return;
}
else
{
PrevOrder(root->left);
printf("%c ", root->data);
PrevOrder(root->right);
}
}
//后序遍历
void Postorder(BTNode *root)
{
if (root == NULL)
{
printf("NULL ");
return;
}
else
{
printf("%c ", root->data);
PrevOrder(root->left);
PrevOrder(root->right);
}
}
//树结点的个数
int BinaryTreeSize(BTNode *root)
{
return root == NULL ? 0 : BinaryTreeSize(root->left) + BinaryTreeSize(root->right) + 1;
}
//求叶子结点的个数
int BinaryleafSize(BTNode *root)
{
if (root == NULL)
{
return 0;
}
else if(!root->left && !root->right)
{
return 1;
}
else
{
return BinaryleafSize(root->left) + BinaryleafSize(root->right);
}
}
//求二叉树的第k层结点个数
int BinaryKSize(BTNode* root,int k)
{
if (!root)
{
return 0;
}
else if (k == 1)
{
return 1;
}
else
{
return BinaryKSize(root->left, k - 1)
+ BinaryKSize(root->right, k - 1);
}
}
//查找值为val的结点
BTNode* BinaryFind(BTNode* root,char ch)
{
if (!root)
{
return NULL;
}
if (root->data == ch)
{
return root;
}
BTNode* leftNode = BinaryFind(root->left,ch);
BTNode* rightNode = BinaryFind(root->right, ch);
if (leftNode )
{
return leftNode;
}
if(rightNode)
{
return rightNode;
}
return NULL;
}
void TreeLevelorder(BTNode* root)
{
assert(root);
Queue q;
QueueInit(&q);
QueuePush(&q,root);
while (!QueueEmpty(&q))
{
//取出队头的数据
BTNode* front = QueueFront(&q);
QueuePop(&q);
printf("%c ",front->data);
//接着入,出一层父亲结点,带进去子节点
if (front->left != NULL)
{
QueuePush(&q, front->left);
}
if (front->right != NULL)
{
QueuePush(&q, front->right);
}
}
printf("\n");
QueueDestroy(&q);
}
//二叉树的销毁
void BinaryTreeDestroy(BTNode* root)
{
if (!root)
{
return;
}
BinaryTreeDestroy(root->left);
BinaryTreeDestroy(root->right);
free(root);
}
//判断一颗树是不是完全二叉树
bool BinaryTreecomp(BTNode* root)
{
Queue q;
QueueInit(&q);
QueuePush(&q, root);
while (!QueueEmpty(&q))
{
BTNode* front = QueueFront(&q);
QueuePop(&q);
if (!front)
{
break;
}
QueuePush(&q, front->left);
QueuePush(&q, front->right);
}
while (!QueueEmpty(&q))
{
BTNode* front = QueueFront(&q);
if (front)
{
return false;
}
}
return true;
}
我们知道二叉树有三种遍历方式,为了遍历前面构建出来的树型结构这三种遍历方式我们都采用
按照规则,二叉树的遍历有:前序/中序/后序的递归结构遍历:
总结:
//后序遍历
void PrevOrder(BTNode *root)
{
if (root == NULL)
{
printf("NULL ");
return;
}
else
{
PrevOrder(root->left);
PrevOrder(root->right);
printf("%d ",root->data);
}
}
//中序遍历
void Inorder(BTNode *root)
{
if (root == NULL)
{
printf("NULL ");
return;
}
else
{
PrevOrder(root->left);
printf("%d ", root->data);
PrevOrder(root->right);
}
}
//前序遍历
void Postorder(BTNode *root)
{
if (root == NULL)
{
printf("NULL ");
return;
}
else
{
printf("%d ", root->data);
PrevOrder(root->left);
PrevOrder(root->right);
}
}
int BinaryTreeSize(BTNode *root)
{
return root == NULL ? 0 : BinaryTreeSize(root->left)
+ BinaryTreeSize(root->right) + 1;
}
后序思想,递归左右子树,如果该结点为空就返回0,不为空就返回左右子树的结点个数相加的和值 + 1,这里的+ 1 操作是用作计数根结点的(每一个真实结点),其实看作是一个二叉树的后序遍历也不为过
//求叶子结点的个数
int BinaryleafSize(BTNode *root)
{
if (root == NULL)
{
return 0;
}
else if(!root->left && !root->right)
{
return 1;
}
else
{
return BinaryleafSize(root->left)
+ BinaryleafSize(root->right);
}
}
叶子结点表示的是没有孩子的结点,当遍历一颗树不断的往下递归,总会遇到度为0的结点,而这个结点的就是作为这颗树的叶子结点,在这里可以将一颗大树看成是多颗小树,计算出多颗小树的叶子结点个数就是整个大树的叶子结点
//求二叉树的第k层结点个数
int BinaryKSize(BTNode* root,int k)
{
if (!root)
{
return 0;
}
else if (k == 1)
{
return 1;
}
else
{
return BinaryKSize(root->left, k - 1)
+ BinaryKSize(root->right, k - 1);
}
}
想求出第k层的结点的个数只需要将这一层的结点相加得到的结果就是这一层的结点个数
BTNode* BinaryFind(BTNode* root,char ch)
{
if (!root)
{
return NULL;
}
if (root->data == ch)
{
return root;
}
BTNode* leftNode = BinaryFind(root->left,ch);
BTNode* rightNode = BinaryFind(root->right, ch);
if (leftNode )
{
return leftNode;
}
if(rightNode)
{
return rightNode;
}
return NULL;
}
到树的左右子树中去查找该结点如果找到就返回该节点,否则继续查找,直到走到NULL的位置,那就返回NULL
英文缩写为BFS即Breadth FirstSearch。其过程检验来说是对每一层节点依次访问,访问完一层进入下一层,而且每个节点只能访问一次。这里用队列来实现这个遍历方式,还是由于队列先进先出的特性,
实现思路:
先将根入队列,将父亲pop出去后再入孩子,打印父亲的值,那么这一层就已经遍历完了,紧接着就可以继续入下一层,父亲每次pop出去的时让父亲的孩子入队,
void TreeLevelorder(BTNode* root)
{
assert(root);
Queue q;
QueueInit(&q);
QueuePush(&q,root);
while (!QueueEmpty(&q))
{
//取出队头的数据
BTNode* front = QueueFront(&q);
QueuePop(&q);
printf("%c ",front->data);
//接着入,出一层父亲结点,带进去子节点
if (front->left != NULL)
{
QueuePush(&q, front->left);
}
if (front->right != NULL)
{
QueuePush(&q, front->right);
}
}
QueueDestroy(&q);
}
实现思路:后序遍历,从最后一个NULL位置开始边回退,返回它的上一层,释放这个结点
//二叉树的销毁
void BinaryTreeDestroy(BTNode* root)
{
if (!root)
{
return;
}
BinaryTreeDestroy(root->left);
BinaryTreeDestroy(root->right);
free(root);
}
首先我们得有完全二叉树的概念,完全二叉树的最后一层结点个数可以不满,但是必须是从左到右连续的,那么看上面的图,你觉得他会是完全二叉树吗,左边是,右边不是,原因不是连续的,如果接着采用层序遍历的思路搞定这个,还是比较简单的,
//判断一颗树是不是完全二叉树
/**
* struct TreeNode {
* int val;
* struct TreeNode *left;
* struct TreeNode *right;
* TreeNode(int x) : val(x), left(nullptr), right(nullptr) {}
* };
*/
class Solution {
public:
/**
* 代码中的类名、方法名、参数名已经指定,请勿修改,直接返回方法规定的值即可
*
*
* @param root TreeNode类
* @return bool布尔型
*/
bool isCompleteTree(TreeNode* root) {
// write code here
if(!root) return false;
queue<TreeNode *> q;
q.push(root);
while(!q.empty()){
auto node = q.front();
q.pop();
if(!node) break;
if(node->left) q.push(node->left);
else q.push(nullptr);
if(node->right) q.push(node->right);
else q.push(nullptr);
}
while(!q.empty()){
auto node = q.front();
if(node != nullptr) return false;
q.pop();
}
return true;
}
};
如果二叉树每个节点都具有相同的值,那么该二叉树就是单值二叉树。只有给定的树是单值二叉树时,才返回 true;否则返回 false
实现思路:
如果左孩子跟右孩子的值都与父亲的值相等,那么他就是单值二叉树,再往下递归的过程中只需要将值不等就直接返回false,否则就继续递归下去直到整个树遍历完了,那么就是单值二叉树
bool isUnivalTree(struct TreeNode* root){
if(!root)
{
return true;
}
//判断左孩子跟父亲是否相等,不等返回false
if(root->left && root->left->val != root->val)
{
return false;
}
//判断右孩子是否和父亲相等,不等返回false
else if(root->right && root->right->val != root->val)
{
return false;
}
//相等继续往下递归,
else
{
return isUnivalTree(root->left)
&& isUnivalTree(root->right);
}
}
给你二叉树的根节点 root ,返回它节点值的 前序 遍历。
实现接口
int* preorderTraversal(struct TreeNode* root,
int* returnSize)
{
}
实现思路:
前序遍历这颗二叉树,将二叉树每个结点的值存放进数组中,最后返回该数组,值得注意的是这个接口的参数,*returnsize,到底需要开辟多大的空间来存放二叉树的值,可以通过遍历二叉树求出它的结点个数,malloc出等大的数组出来,存放结点的值
//计算结点的个数
int TreeSize(struct TreeNode* root)
{
return root == NULL ? 0 : TreeSize(root->left) + TreeSize(root->right) + 1;
}
//以前序遍历的方式,将树的值存放到数组中
void _preoder(struct TreeNode* root,int *retArr, int *pi)
{
if(!root)
{
return;
}
else
{
retArr[(*pi)++] = root->val;
_preoder(root->left,retArr,pi);
_preoder(root->right,retArr,pi);
}
}
int* preorderTraversal(struct TreeNode* root, int* returnSize){
*returnSize = TreeSize(root);
int *retArr = (int *)malloc(sizeof(int) * (* returnSize));
int i = 0;
_preoder(root,retArr,&i);
return retArr;
}
给你两棵二叉树的根节点 p 和 q ,编写一个函数来检验这两棵树是否相同。如果两个树在结构上相同,并且节点具有相同的值,则认为它们是相同的。
实现思路:
能先想到的就是这两颗树都是空树,那么他们就是相同的,还有一种情况就是一个树的结点多,一个树的结点少,少的先被遍历完,所以他们肯定不相同,剩下的就是判断值了,比较两颗树的左右子树的值是否是相等的
bool isSameTree(struct TreeNode* p, struct TreeNode* q){
if(p == NULL && q == NULL)
{
return true;
}
if(p == NULL || q == NULL)
{
return false;
}
else if(p->val != q->val)
{
return false;
}
else
{
return isSameTree(p->left,q->left) && isSameTree(p->right,q->right);
}
}
从树的第二层开始,将每一层的根划分出两颗左右子树,比较根再分别将两颗子树的左孩子和右孩子比较,右孩子和左孩子比较,如果相同就返回true
bool _issymmetry(struct TreeNode *Treeleft, struct TreeNode* Treeright)
{
if(!Treeleft && !Treeright)
{
return true;
}
if(!Treeleft || !Treeright)
{
return false;
}
else if(Treeleft->val != Treeright->val)
{
return false;
}
return _issymmetry(Treeleft->left,Treeright->right)
&& _issymmetry(Treeleft->right,Treeright->left) ;
}
bool isSymmetric(struct TreeNode* root){
if(!root)
{
return true;
}
return _issymmetry(root->left,root->right);
}
题目描述:
翻转一棵二叉树。
实现思路:
递归到最后一层开始再往回返的过程,备份根的左右孩子,回退到根的时候交换左右孩子的位置
struct TreeNode* invertTree(struct TreeNode* root){
if(!root)
return NULL;
struct TreeNode* left = invertTree(root->left);
struct TreeNode* right = invertTree(root->right);
root->left = right;
root->right = left;
return root;
}
给你两棵二叉树 root 和 subRoot 。检验 root 中是否包含和 subRoot 具有相同结构和节点值的子树。如果存在,返回 true ;否则,返回 false。
二叉树 tree 的一棵子树包括 tree 的某个节点和这个节点的所有后代节点。tree 也可以看做它自身的一棵子树。
实现思路:
从root中选出每一个根看作一颗子树去和subroot这颗子树比较,如果他们,左右子树都相等了就返回true,如果不相等继续从root中找下一个根
bool _issymmetry(struct TreeNode *root,
struct TreeNode* subRoot)
{
if(!root && !subRoot)
return true;
if(!root || !subRoot)
return false;
else if(root->val != subRoot->val)
return false;
return _issymmetry(root->left,subRoot->left)
&& _issymmetry(root->right,subRoot->right);
}
bool isSubtree(struct TreeNode* root,
struct TreeNode* subRoot){
if(!root)
return false;
if(_issymmetry(root,subRoot))
return true;
//寻找下一个根比较是否与subRoot相等
return isSubtree(root->left,subRoot)
|| isSubtree(root->right,subRoot);
}
题目描述:
本题中,一棵高度平衡二叉树定义为:一个二叉树每个节点 的左右两个子树的高度差的绝对值不超过 1 。
实现思路:
大问题化成小问题的思路,要想判断是不是平衡二叉树,就得把整个树拆分成多颗子树,去判断左右子树的高度差是否 < 2,分别求出n颗左右子树的深度,如果他们的差距 < 2 就满足,直到把整个树遍历完就返回true,这里采用的遍历方式是后序遍历
bool isbalance(struct TreeNode* root, int *pi)
{
if(!root)
{
*pi = 0;
return true;
}
int leftheight = 0;
if(isbalance(root->left,&leftheight) == false)
return false;
int rightheight = 0;
if(isbalance(root->right,&rightheight) == false)
return false;
*pi = fmax(leftheight,rightheight) + 1;
return abs(leftheight - rightheight) < 2;
}
bool isBalanced(struct TreeNode* root){
if(!root)
return true;
int i = 0;
return isbalance(root,&i);
}
KY11 二叉树遍历
链接: link.
原题描述:
编一个程序,读入用户输入的一串先序遍历字符串,根据此字符串建立一个二叉树(以指针方式存储)。 例如如下的先序遍历字符串: ABC##DE#G##F### 其中“#”表示的是空格,空格字符代表空树。建立起此二叉树以后,再对二叉树进行中序遍历,输出遍历结果。
根据题意手动还原出来的二叉树应该是这样的,满足先序遍历的结构
实现思路:
把字符串存放进一个数组中,每次遍历这个数组,取出一个字符创建结点,从上往下递归,不断创建左右孩子,当遇到#的时候就表示这个结点是叶子,那就返回它的上一层,它的上一层就是根,把叶子当作根的孩子,往上返回,不断创建父子关系,当左子树递归完了就去递归右子树,直到整个树创建出来。
#include<stdio.h>
typedef char BTNodeType;
typedef struct BTNode
{
struct BTNode *left;
struct BTNode *right;
BTNodeType data;
}BTNode;
BTNode *BTNodecreate(char *str,int *pi)
{
if(str[*pi] == '#')
{
(*pi)++;
return NULL;
}
//取字符创建结点
BTNode* root = (BTNode *)malloc(sizeof(BTNode));
root->data = str[(*pi)++];
//创建左右孩子
root->left = BTNodecreate(str, pi);
root->right = BTNodecreate(str, pi);
return root;
}
//中序遍历
void inorder(BTNode *root)
{
if(!root)
{
return ;
}
inorder(root->left);
printf("%c ",root->data);
inorder(root->right);
}
int main()
{
int i = 0;
char arr[100] = {
0};
scanf("%s",arr);
BTNode *root = BTNodecreate(arr,&i);
inorder(root);
return 0;
}
文章浏览阅读3.8k次,点赞9次,收藏28次。直接上一个工作中碰到的问题,另外一个系统开启多线程调用我这边的接口,然后我这边会开启多线程批量查询第三方接口并且返回给调用方。使用的是两三年前别人遗留下来的方法,放到线上后发现确实是可以正常取到结果,但是一旦调用,CPU占用就直接100%(部署环境是win server服务器)。因此查看了下相关的老代码并使用JProfiler查看发现是在某个while循环的时候有问题。具体项目代码就不贴了,类似于下面这段代码。while(flag) {//your code;}这里的flag._main函数使用while(1)循环cpu占用99
文章浏览阅读347次。idea shift f6 快捷键无效_idea shift +f6快捷键不生效
文章浏览阅读135次。Ecmacript 中没有DOM 和 BOM核心模块Node为JavaScript提供了很多服务器级别,这些API绝大多数都被包装到了一个具名和核心模块中了,例如文件操作的 fs 核心模块 ,http服务构建的http 模块 path 路径操作模块 os 操作系统信息模块// 用来获取机器信息的var os = require('os')// 用来操作路径的var path = require('path')// 获取当前机器的 CPU 信息console.log(os.cpus._node模块中有很多核心模块,以下不属于核心模块,使用时需下载的是
文章浏览阅读10w+次,点赞435次,收藏3.4k次。SPSS 22 下载安装过程7.6 方差分析与回归分析的SPSS实现7.6.1 SPSS软件概述1 SPSS版本与安装2 SPSS界面3 SPSS特点4 SPSS数据7.6.2 SPSS与方差分析1 单因素方差分析2 双因素方差分析7.6.3 SPSS与回归分析SPSS回归分析过程牙膏价格问题的回归分析_化工数学模型数据回归软件
文章浏览阅读7.5k次。如何利用hutool工具包实现邮件发送功能呢?1、首先引入hutool依赖<dependency> <groupId>cn.hutool</groupId> <artifactId>hutool-all</artifactId> <version>5.7.19</version></dependency>2、编写邮件发送工具类package com.pc.c..._hutool发送邮件
文章浏览阅读867次,点赞2次,收藏2次。docker安装elasticsearch,elasticsearch-head,kibana,ik分词器安装方式基本有两种,一种是pull的方式,一种是Dockerfile的方式,由于pull的方式pull下来后还需配置许多东西且不便于复用,个人比较喜欢使用Dockerfile的方式所有docker支持的镜像基本都在https://hub.docker.com/docker的官网上能找到合..._docker安装kibana连接elasticsearch并且elasticsearch有密码
文章浏览阅读1.3w次,点赞57次,收藏92次。整理 | 郑丽媛出品 | CSDN(ID:CSDNnews)近年来,随着机器学习的兴起,有一门编程语言逐渐变得火热——Python。得益于其针对机器学习提供了大量开源框架和第三方模块,内置..._beeware
文章浏览阅读7.9k次。//// ViewController.swift// Day_10_Timer//// Created by dongqiangfei on 2018/10/15.// Copyright 2018年 飞飞. All rights reserved.//import UIKitclass ViewController: UIViewController { ..._swift timer 暂停
文章浏览阅读986次,点赞2次,收藏2次。1.硬性等待让当前线程暂停执行,应用场景:代码执行速度太快了,但是UI元素没有立马加载出来,造成两者不同步,这时候就可以让代码等待一下,再去执行找元素的动作线程休眠,强制等待 Thread.sleep(long mills)package com.example.demo;import org.junit.jupiter.api.Test;import org.openqa.selenium.By;import org.openqa.selenium.firefox.Firefox.._元素三大等待
文章浏览阅读3k次,点赞4次,收藏14次。Java软件工程师职位分析_java岗位分析
文章浏览阅读2k次。Java:Unreachable code的解决方法_java unreachable code
文章浏览阅读1w次。1、html中设置标签data-*的值 标题 11111 222222、点击获取当前标签的data-url的值$('dd').on('click', function() { var urlVal = $(this).data('ur_如何根据data-*属性获取对应的标签对象