树 非空树

  • 有一个(root)根节点r
  • 其余节点可分为m个互不相交的有限集(子树)T1....Tm

具有n个节点的树,具有(n-1)条连接(指针域),需要构成结构体,尽可能减少空间域的浪费,使用儿子兄弟结构体,每个结构体包含 数据 儿子 及 儿子的兄弟

typedef struct Tree{
ElemType data;
Tree* First_Child; ## 儿子
Tree* Next_Sibling; ## 兄弟
}

/* 二叉树结构体 */
typedef int Elementtype; typedef struct TreeNode{
Elementtype data;
TreeNode* left;
TreeNode* right;
}TreeNode,*PTREE;
  • 第i层最多可以有2^(i-1) 每个节点可以延伸2子节点 222...*2
  • 深度为k的二叉树 最大节点数 2^k -1
  • 非空二叉树,n0 = n2 + 1 边数证明:
1. 节点数-1 = 边长数
2. n0+n1+n2-1=0*n0+1*n1+2*n2

主要操作:判空 遍历 创建 删除 查找 平衡

  • 先序 根-左-右 (递归 or 堆栈)
  • 中序 左-根-右
  • 后序 左-右-根 根的遍历次序
  • 层次遍历 从上到下 从左到右 (队列)

顺序存储结构 注意 左右孩子和父节点的索引关系

根据两个遍历确定一棵树

根据先序/中序 或者 后序/中序 判断二叉树

  • 先序: a b c d e f g h i j
  • 中序: c b e d a h g i j f

我们知道先序遍历 第一个元素为根(a),所以利用将中序遍历分成左右子树(cbed)(hgijf),再找根节点(b),分成(c)(ed)两个子树,逐渐递归:

构建二叉树

参考二叉树的递归遍历,可以同样适用递归进行二叉树的构建,先申请根节点的空间,然后赋值,然后分别递归建立其左子树和右子树


//输入 abc***de**fg*** 建立先序遍历为abcdefg的普通树
//需要修改指向 树结构的 指针,所以形参为 指针的引用(指针的指针)
void CreatTree(PTREE& Root){
char a=0;
a = getchar();
if(a=='*'){
Root = NULL;
}
else{
Root = (TreeNode *)malloc(sizeof(TreeNode));
if(Root==NULL)
printf("Failed");
else{
Root->data = a;
CreatTree(Root->left);
CreatTree(Root->right);
}
}
}

二叉树的遍历

二叉树的遍历分为 先序 中序 后序遍历,可以使用递归遍历 和 非递归遍历

递归遍历


//先序**
void fsearch(PTREE Root){
if (Root == NULL) return;
else{
printf("%d ",Root->data);
fsearch(Root->left);
fsearch(Root->right);
}
} // 中序
void msearch(PTREE Root){
if (Root == NULL) return;
else{
msearch(Root->left);
printf("%d ",Root->data);
msearch(Root->right);
}
} // 后序
void psearch(PTREE Root){
if (Root == NULL) return;
else{
psearch(Root->left);
psearch(Root->right);
printf("%d ",Root->data);
}
}

非递归遍历

需要借助栈(先进后去)的特性实现非递归

  1. 先序(根左右)

void f_search(PTREE Root){
std::stack<PTREE> my_s;
PTREE cur=Root; while(cur!=NULL || !my_s.empty()){ while(cur!=NULL){
printf("%d ",cur->val);
my_s.push(cur);
cur = cur->left;
} if(!my_s.empty()){
cur = my_s.top();
my_s.pop();
}
cur = cur->right;
}
}
  1. 中序(左根右)
void m_search(PTREE Root){
std::stack<PTREE> my_s;
PTREE cur=Root; while(cur!=NULL || !my_s.empty()){ while(cur!=NULL){
my_s.push(cur);
cur = cur->left;
} cur = my_s.top();
my_s.pop();
printf("%d ",cur->val);
cur = cur->right;
}
}
  1. 后序(左右根)
void d_search(PTREE Root){
std::stack<PTREE> my_s;
PTREE cur=Root,Pre= nullptr; while(cur || !my_s.empty()){
while(cur)
{
my_s.push(cur);
cur = cur->left;
} PTREE top_node = my_s.top();
if( Pre == top_node->right || top_node->right==NULL ){
printf("%d ",top_node->val);
my_s.pop();
Pre = top_node;
}
else
cur = top_node->right;
}
}

实现2

void d_search3(PTREE Root){
std::stack<PTREE> my_s,my_s2; PTREE cur = Root;
my_s.push(Root);
while(!my_s.empty())
{
cur = my_s.top();
my_s.pop();
my_s2.push(cur);
if(cur->left) my_s.push(cur->left);
if(cur->right) my_s.push(cur->right);
}
while(!my_s2.empty())
{
cur = my_s2.top();
my_s2.pop();
std::cout << cur->val << " ";
}
}

二叉树的插入


void Insert_Node(PTREE& Root,ElemType a){ if(Root==NULL){
Root = (PTREE)malloc(sizeof(TreeNode));
if(!Root)
printf("malloc failed");
else{
Root->val = a;
Root->left = NULL;
Root->right = NULL;
}
}else{
if(a>Root->val){
Insert_Node(Root->right,a);
}else if(a<Root->val){
Insert_Node(Root->left,a);
}else{
printf("dup node");
}
}
}

二叉树的删除

void Free_Tree(PTREE& Root){

    if(Root == nullptr ) return ;

    Free_Tree(Root->left);
Free_Tree(Root->right);
free(Root);
Root = nullptr;
}

普通二叉树的查找


int Find_Node(PTREE Root,int target){
if(Root==NULL) return -1; if(target == Root->val)
return target; if(target > Root->val)
return Find_Node(Root->right,target);
else if(target < Root->val){
return Find_Node(Root->left,target);
}
}

104. Maximum Depth of Binary Tree

Tree高度延伸的一类题目

最基础的递归,先递归到底,当Leaf Node的左右两个Children Node都分别触及Base Case,也就是None的时候,向上返回。然后之后对应当前node,左右两边的递归都操作结束以后,返回的过程中对左右高度进行对比,取两个中间最大值,然后这里记住要加1,也就是当前的层数。


class Solution(object):
def maxDepth_gd(self, root):
if not root: return 0 left = self.maxDepth(root.left)
right = self.maxDepth(root.right)
return max(left, right) + 1

110. Balanced Binary Tree

有了104的基础,我们在延伸下看看110这道题,其实就是基于高度计算,然后判断一下。

但由于嵌套的Recursion调用,整体的时间复杂度是:O(nlogn) , 在每一层调用get_height的平均时间复杂度是O(N),然后基于二叉树的性质,调用了的高度是logn,所以n * logn 的时间复杂。

时间复杂度为什么是nlogn搞不清楚的看 时间复杂度图解

class Solution(object):
def isBalanced(self, root):
if not root: return True
left = self.get_height(root.left)
right = self.get_height(root.right)
if abs(left - right) > 1:
return False
return self.isBalanced(root.left) and self.isBalanced(root.right) def get_height(self, root):
if not root: return 0
left = self.get_height(root.left)
right = self.get_height(root.right)
return max(left, right) + 1

上面这种Brute Froce的方法,整棵树有很多冗余无意义的遍历,其实我们在处理完get_height这个高度的时候,我们完全可以在检查每个节点高度并且返回的同时,记录左右差是否已经超过1,只要有一个节点超过1,那么直接返回False即可,因此我们只需要在外围设立一个全球变量记录True和False,在调用get_height的时候,内置代码里加入对左右高度的判定即可,代码如下

时间复杂度: O(N)

Recursive Rules:

索取:Node的左孩子是不是全部是Balanced,Node的右孩子是不是全部是Balanced的,返回:如果都是Balanced的,返回True,不然返回False

class Solution(object):
def isBalanced(self, root):
self.flag = False
self.getHeight(root)
return not self.flag def getHeight(self, root):
if not root: return 0
left = self.getHeight(root.left)
right = self.getHeight(root.right)
if abs(left - right) > 1:
self.flag = True
return max(left, right) + 1

最后Leetcode上有一种-1的方法,其实就是上面这种方法的一种延伸。如果左右两边出现了高度差高于1的情况,直接返回-1,这个-1怎么来的?因为高度不可能为负数,-1其实就是一种True/False的表达。

那么在实现上,我们只要对get_height每次返回前做一个判定即可,具体实现看下方:

时间复杂度: O(N)

class Solution(object):
def isBalanced(self, root):
height = self.get_height(root)
return height != -1 def get_height(self, root):
if not root: return 0
left = self.get_height(root.left)
right = self.get_height(root.right)
if left == -1 or right == -1 : return -1
if abs(left - right) > 1: return -1
return max(left, right) + 1

111. Minimum Depth of Binary Tree

返回叶子 (无左右孩子的节点) 节点的最小深度

int minDepth(TreeNode* root) {
if(root==NULL) return 0;
int dep =1;
queue<TreeNode*> q_cur,q_next;
q_cur.push(root); while(!q_cur.empty()){
TreeNode* a = q_cur.front();
q_cur.pop(); if(a->left==NULL && a->right==NULL)
return dep;
if(a->left!=NULL)
q_next.push(a->left);
if(a->right!=NULL)
q_next.push(a->right); if(q_cur.empty()){
swap(q_cur,q_next);
dep+=1; }
}
return dep;
} # 或者使用一个队列 每一次层次遍历 都提前计算出当前层的size 既当前层的元素数量 // while(!q.empty()){
// int size = q.size();
// while(size>0){
// TreeNode* a = q.front();
// q.pop(); // if(a->left==NULL && a->right==NULL)
// return dep;
// if(a->left!=NULL)
// q.push(a->left);
// if(a->right!=NULL)
// q.push(a->right);
// size--;
// } // dep+=1;
// }

112. Path Sum 路径数值和

普通的dfs 递归

bool hasPathSum(TreeNode* root, int sum) {
if(root == NULL) return false; if(root->left == NULL && root->right == NULL && sum-root->val==0) return true; return hasPathSum(root->left,(sum-root->val)) || hasPathSum(root->right,(sum-root->val));
}

利用栈实现的非递归dfs

bool hasPathSum(TreeNode* root, int sum) {
if(root==NULL) return false; stack<TreeNode*> s;
stack<int> s_n; s.push(root);
s_n.push(root->val); while (!s.empty()){
TreeNode* a = s.top();
int b = s_n.top();
s.pop(); s_n.pop();
if(a->left==NULL && a->right==NULL && b==sum)
return true; if(a->left!=NULL){
s.push(a->left);
s_n.push(b+a->left->val);
} if(a->right!=NULL){
s.push(a->right);
s_n.push(b+a->right->val);
} } return false;
}

输出Tree的路径value 257. Binary Tree Paths

递归dfs 实现

 void dfs(TreeNode* root,string str,vector<string>& res){

        if(root->left==NULL && root->right==NULL){
res.push_back(str);
}
if(root->left!=NULL){
dfs(root->left,str+"->"+to_string(root->left->val),res);
}
if(root->right!=NULL){
dfs(root->right,str+"->"+to_string(root->right->val),res);
}
} vector<string> binaryTreePaths(TreeNode* root) {
vector<string> res;
if(root == NULL) return res;
string str = to_string(root->val);
dfs(root,str,res);
return res; }

使用Stack 进行的非递归遍历

    vector<string> binaryTreePaths(TreeNode* root) {
vector<string> res;
if(root == NULL) return res;
stack<TreeNode*> s;
stack<string> str_s;
s.push(root);
str_s.push(to_string(root->val));
string str;
while(!s.empty()){
TreeNode* a = s.top();
str = str_s.top();
s.pop(); str_s.pop(); if(a->left==NULL && a->right==NULL)
res.push_back(str); if(a->left!=NULL){
s.push(a->left);
str_s.push(str+"->"+to_string(a->left->val));
}
if(a->right!=NULL){
s.push(a->right);
str_s.push(str+"->"+to_string(a->right->val));
} }
reverse(res.begin(),res.end());
return res;

翻转二叉树 226. Invert Binary Tree

dfs 遍历每一个节点 进行交换

    TreeNode* invertTree(TreeNode* root)
{
dfs_invert(root);
return root;
}
void dfs_invert(TreeNode* root)
{
if(root == NULL) return; invertTree(root -> left);
invertTree(root -> right);
// swap after the left subtree and right subtree has been done
swap(root -> left, root -> right);
}

queue_BFS

   TreeNode* invertTree(TreeNode* root)
{
if(!root) return NULL;
queue<TreeNode*> q;
q.push(root);
while(!q.empty()) {
TreeNode* node = q.front();
q.pop();
#只需要交换 非叶子节点的左右孩子即可
if (node->left != NULL || node->right != NULL) {
TreeNode* temp = node->left;
node->left = node->right;
node->right = temp;
}
if (node->left)
q.push(node->left);
if (node->right)
q.push(node->right);
}
return root;
}

二叉树 c++的更多相关文章

  1. [数据结构]——二叉树(Binary Tree)、二叉搜索树(Binary Search Tree)及其衍生算法

    二叉树(Binary Tree)是最简单的树形数据结构,然而却十分精妙.其衍生出各种算法,以致于占据了数据结构的半壁江山.STL中大名顶顶的关联容器--集合(set).映射(map)便是使用二叉树实现 ...

  2. 二叉树的递归实现(java)

    这里演示的二叉树为3层. 递归实现,先构造出一个root节点,先判断左子节点是否为空,为空则构造左子节点,否则进入下一步判断右子节点是否为空,为空则构造右子节点. 利用层数控制迭代次数. 依次递归第二 ...

  3. c 二叉树的使用

    简单的通过一个寻找嫌疑人的小程序 来演示二叉树的使用 #include <stdio.h> #include <stdlib.h> #include <string.h& ...

  4. Java 二叉树遍历右视图-LeetCode199

    题目如下: 题目给出的例子不太好,容易让人误解成不断顺着右节点访问就好了,但是题目意思并不是这样. 换成通俗的意思:按层遍历二叉树,输出每层的最右端结点. 这就明白时一道二叉树层序遍历的问题,用一个队 ...

  5. 数据结构:二叉树 基于list实现(python版)

    基于python的list实现二叉树 #!/usr/bin/env python # -*- coding:utf-8 -*- class BinTreeValueError(ValueError): ...

  6. [LeetCode] Path Sum III 二叉树的路径和之三

    You are given a binary tree in which each node contains an integer value. Find the number of paths t ...

  7. [LeetCode] Find Leaves of Binary Tree 找二叉树的叶节点

    Given a binary tree, find all leaves and then remove those leaves. Then repeat the previous steps un ...

  8. [LeetCode] Verify Preorder Serialization of a Binary Tree 验证二叉树的先序序列化

    One way to serialize a binary tree is to use pre-oder traversal. When we encounter a non-null node, ...

  9. [LeetCode] Binary Tree Vertical Order Traversal 二叉树的竖直遍历

    Given a binary tree, return the vertical order traversal of its nodes' values. (ie, from top to bott ...

  10. [LeetCode] Binary Tree Longest Consecutive Sequence 二叉树最长连续序列

    Given a binary tree, find the length of the longest consecutive sequence path. The path refers to an ...

随机推荐

  1. mysql5.7 误删管理员root账户

    1.停止数据库,并在mysql配置文件my.cnf中添加skip-grant-tables参数到[mysqld]配置块中 2. 执行 systemctl start mysqld 3. 执行 mysq ...

  2. SDN2017 第四次实验作业

    实验目的 1.使用图形化界面搭建拓扑如下并连接控制器 2.使用python脚本搭建拓扑如下并通过命令行连接控制器 3.使用任一种方法搭建拓扑连接控制器后下发流表 实验步骤 建立以下拓扑,并连接上ODL ...

  3. BZOJ4810:[YNOI2017]由乃的玉米田(莫队,bitset)

    Description 由乃在自己的农田边散步,她突然发现田里的一排玉米非常的不美.这排玉米一共有N株,它们的高度参差不齐. 由乃认为玉米田不美,所以她决定出个数据结构题   这个题是这样的: 给你一 ...

  4. 6、JVM--类文件结构(上)

    6.1.概述 写的程序需要经编译器翻译成由0和1构成的二进制格式才能由计算机执行 6.2.无关性基石 Java在刚刚诞生之时曾经提出过一个非常著名的宣传口号:“一次编写,到处运行(Write Once ...

  5. maven项目中,lib目录下有自己私有的包,则需要配置一下代码,然后进行打包

    <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-compi ...

  6. JS编写日历控件(支持单日历 双日历 甚至多日历等)

    前言: 最近几天都在研究日历控件编写,当然前提我要说明下,当然看过别人写的源码 所以脑子一热 就想用自己的编码方式 来写一套可扩展性 可维护性 性能高点的代码控件出来,就算练习练习下,所以前几天晚上下 ...

  7. python在输出一段话中插入多个变量,每日作业补充

    %s用来插入用户输入的值name=input('请输入您的姓名:')age=input('请输入您的年龄:')sex=input('你的性别:')print('-------您好!%s------\n ...

  8. Hibernae

    开始尝试挺java ee的课程,马士兵老师的 1.ssh的整个框架体系 spring会贯穿在整个过程之中 2.Hibernate的整个框架体系 3. 4. 5. 6.

  9. 用pyinstaller把python代码打包成exe可执行文件

    优点: 1. pyinstaller 是跨平台的可以用在linux和windows系统上 2. 操作非常简单,几个命令就搞定了,这个比py2exe容易用多了 缺点: 1. 打包后的体积过大,因为要带p ...

  10. go语言之行--基础部分

    一.数据类型 布尔型 布尔类型 - 由两个预定义常量组成:true.false,默认值为false package main import "fmt" func main() { ...