数据结构_二叉树_属性-构建-遍历

本文介绍了二叉树的基本属性,如层节点数量、完全二叉树的节点计算,并通过实例展示了如何根据节点编号构建完全二叉树。此外,还提供了C++和Python两种语言实现的二叉树节点结构及树的遍历方法。内容涵盖了二叉树的前序、中序和后序遍历。
摘要由CSDN通过智能技术生成

一、二叉树的属性

  • 1)二叉树的第i层至多有 2 i − 1 2^{i-1} 2i1个节点
  • 2)深度为h的二叉树至多有 2 h − 1 2^h-1 2h1个节点
    • 基于等比序列和计算: s n = a 1 ( 1 − q n ) 1 − q s_n = \frac{a_1(1-q^n)}{1-q} sn=1qa1(1qn)
  • 3)对于任何一颗二叉树,若它含有n0个叶子节点, n2个度为2的节点,则存在两者的关系 n 0 = n 2 + 1 n_0=n_2+1 n0=n2+1
    • 节点数 n = n0+n1+n2
    • 边数 2 n 2 + 1 n 1 = n − 1 2n_2+1n_1 = n - 1 2n2+1n1=n1
      => 2 n 2 + n 1 = n 0 + n 1 + n 2 − 1 2n_2+n_1 = n_0 + n_1 + n_2 - 1 2n2+n1=n0+n1+n21
      => n 0 = n 2 + 1 n_0=n_2+1 n0=n2+1
  • 4)具有n个节点的完全二叉树的深度为floor(log2(n)) + 1
  • 5)若对含n个节点的完全二叉树从上到下且从左到右进行1到n编码,则对完全二叉树中
    任意一个编号为i的节点:
    • 若i=1,则节点是二叉树的跟, floor(i/2)为其父节点
    • 2 ∗ i > n 2*i>n 2i>n, 则该节点无左孩子, 否则编码2i的节点为其左孩子节点
    • 2 ∗ i + 1 > n 2*i+1>n 2i+1>n, 则该节点为右孩子, 否则编码2i+1的节点为其右孩子节点

例题

若一个完全二叉树 n=1450 个结点数,
则度为1的节点数n1?
度为2的节点数n2?
叶子节点数n0?
有多少左孩子,多少右孩子,树的高度是多少?

import math
n=1450
# 树高
tree_depth = math.floor(math.log2(n)) + 1
# 倒数第二层全都是n2- n2个数最大值
n2_max = math.pow(2, tree_depth-1) - 1
# 完全二叉树总n
compelet_n = math.pow(2, tree_depth) - 1

# 最后层
n0_part1 = n - n2_max
# 倒数第二层
n0_part2 = math.floor((compelet_n - n)/2)
n0 = n0_part1 + n0_part2
n1 = 1
n2 = n - n0 - n1
left_ = 1 + n2
right_ = n2
print(f"n0={n0}, n1={n1}, n2={n2}, left_={left_}, right_={right_}")

"""
n0=725.0, n1=1, n2=724.0, left_=725.0, right_=724.0
"""

二、二叉树的树节点

主要就是一个节点,存在两个指针可以指向左右子树。

2.1 cpp

#include <iostream>
using namespace std;

template<typename T>
struct  treeNode{
    T data;
    treeNode *left;
    treeNode *right;

    treeNode(T const & data) : data(data){
        left = nullptr;
        right = nullptr;
    }
};


int main(){
    treeNode<int> *tree = new treeNode<int>(10);
    treeNode<int> *left_ = new treeNode<int>(20);
    treeNode<int> *right_ = new treeNode<int>(30);
    tree->left = left_;
    tree->right = right_;
    cout << "tree->data " << tree->data << endl;
    cout << "tree->left->data " << tree->left->data << endl;
    cout << "tree->right->data " << tree->right->data << endl;
}

2.2 python


class treeNode:
    def __init__(self, data, left=None, right=None):
        self.data = data
        self.left = left
        self.right = right

    def __str__(self):
        return f'<data: {self.data} (left:{self.left}; right:{self.right};)>'

    def __repr__(self):
        return str(self)

tree = treeNode(
    "A",
    left=treeNode('B', 
              left=treeNode('D', right=treeNode('G'))),
    right=treeNode('C', left=treeNode('E', right=treeNode('H')), right=treeNode('F'))
)
tree
"""
<data: A (left:<data: B (left:<data: D (left:None; right:<data: G (left:None; right:None;)>;)>; right:None;)>; right:<data: C (left:<data: E 
(left:None; right:<data: H (left:None; right:None;)>;)>; right:<data: F (left:None; right:None;)>;)>;)>
"""

三、二叉树构建与遍历

3.1 树构建

基于完全二叉树的编号顺序输入节点,构建二叉树,空节点名称为^
输入如下:
string name_q[7] = {"aa", "bb", "cc", "dd", "ee", "ff", "^"};
int value_q[7] = {1, 2, 3, 4, 5, 6, 0};
可以直接看书中的构建方法
在这里插入图片描述

cpp

cpp实现思路是和python一样的
因为我们在一个节点存储的信息会很多,所以我们构建了一个数据结构treeNodeData
为了保持输入的数据的一致性,所以我们构建了数据结构levelData


#include <iostream>
#include <string>
#include <typeinfo>
#include <queue>
#include <cmath>

using namespace std;

struct treeNodeData{
string name;
int value;
treeNodeData(const string _name, int _value){
    name=_name;
    value=_value;
};
void String(){
    cout << "name: " << name << " value: " << value << endl;
}
};

template<typename T>
struct treeNode{
T data;
treeNode<T>* left;
treeNode<T>* right;
treeNode(T const data=NULL):data(data){
    left=nullptr;
    right=nullptr;
}
};

template<typename T>
void LDR(treeNode<T> *tree);
template<typename T>
void LRD(treeNode<T> *tree);
template<typename T>
void DLR(treeNode<T> *tree);

/*
基于层次输入构建树
*/
struct levelData{
    int max_depth;
    string* name_q;
    int* value_q;
};

treeNode<treeNodeData>* createBiTree(levelData d, treeNode<treeNodeData> *Q[]){
    int last_idx = pow(2, d.max_depth-1) - 1;
    int final_idx = pow(2, d.max_depth) - 1;
    int maxDepth = d.max_depth;
    bool vis_q[final_idx];
    cout << "max_depth: "<< d.max_depth << ", last_idx: " << last_idx << endl;
    cout << final_idx << endl;
    
    fill(vis_q, vis_q+final_idx, false);
    for (int i = 1; i <= last_idx; i++){
        int root_idx = i-1;
        if (d.name_q[root_idx] == "^"){continue;}
        if(!vis_q[root_idx]){// 对于访问过的节点不重新赋值
            treeNodeData *root_data = new treeNodeData(d.name_q[root_idx], d.value_q[root_idx]);
            Q[root_idx] = new treeNode<treeNodeData>(*root_data);
            vis_q[root_idx] = true;
        }

        int left_idx = 2*i-1;
        if (d.name_q[left_idx] == "^"){continue;}
        if(!vis_q[left_idx]){// 对于访问过的节点不重新赋值
            treeNodeData *left_data = new treeNodeData(d.name_q[left_idx], d.value_q[left_idx]);
            Q[left_idx] = new treeNode<treeNodeData>(*left_data);
            vis_q[left_idx] = true;
        }
        Q[root_idx] -> left = Q[left_idx];

        cout << Q[root_idx] -> data.name<< " left->" << Q[left_idx] -> data.name<< endl;

        int right_idx = 2*i+1-1;
        if (d.name_q[right_idx] == "^"){continue;}
        if(!vis_q[right_idx]){
            treeNodeData *right_data = new treeNodeData(d.name_q[right_idx], d.value_q[right_idx]);
            Q[right_idx] = new treeNode<treeNodeData>(*right_data);
            vis_q[right_idx] = true;
        }
        Q[root_idx] -> right = Q[right_idx];

    }
    return Q[0];
}



int main(){
    string name_q[7] = {"aa", "bb", "cc", "dd", "ee", "ff", "^"};
    int value_q[7] = {1, 2, 3, 4, 5, 6, 0};
    int max_depth=floor(log2(7)) +1;
    levelData d = levelData();
    d.max_depth = max_depth;
    d.name_q = name_q;
    d.value_q = value_q;
    treeNode<treeNodeData> *Q[7];
    treeNode<treeNodeData> *bi_tree = createBiTree(d, Q);
    LDR(bi_tree);
    int *qq[5];
    for (int i=1; i<5;i++){
        *qq[i] = i;
        int left_idx = 2 * i;
        if (left_idx > 5){continue;}
        *qq[left_idx] = left_idx;
        *qq[i] = *qq[left_idx];
    }
}




template<typename T>
void LDR(treeNode<T> *tree){
    if(tree->left){
        LDR(tree->left);
    }
    cout << "name: " << tree->data.name << " Value: " << tree->data.value << endl;
    if(tree->right){
        LDR(tree->right);
    }
}
max_depth: 3, last_idx: 3
7
aa left->bb
bb left->dd
cc left->ff
name: bb Value: 2
name: aa Value: 1
name: cc Value: 3

python

class treeNodeData:
    def __init__(self, name, value):
        self.name = name
        self.value = value

    def __str__(self):
        return f'Data[name: {self.name}, value:{self.value}]'

    def __repr__(self):
        return str(self)
    

def creatBiTree(name_list, value_list):
    final_idx = len(name_list)
    Q = [0] * final_idx
    max_depth = math.floor(math.log2(final_idx)) + 1
    last_idx = int(math.pow(2, max_depth-1))
    vis_q = [False] * final_idx
    print(f'max_depth={max_depth}, final_idx={final_idx},last_idx={last_idx} ')
    for idx in range(1, last_idx):
        root_idx = idx - 1
        if name_list[root_idx] == '^': continue;
        if not vis_q[root_idx]:
            root_data = treeNode(data=treeNodeData(
                name_list[root_idx], value_list[root_idx], 
            ))
            Q[root_idx] = root_data
            vis_q[root_idx] = True

        left_idx = 2 * idx - 1
        if name_list[left_idx] == '^': continue;
        if not vis_q[left_idx]:
            left_data = treeNode(data=treeNodeData(
                name_list[left_idx], value_list[left_idx], 
            ))
            Q[left_idx] = left_data
            vis_q[left_idx] = True
    
        Q[root_idx].left = Q[left_idx]

        right_idx = 2 * idx + 1 - 1
        if name_list[right_idx] == '^': continue;
        if not vis_q[right_idx]:
            right_data = treeNode(data=treeNodeData(
                name_list[right_idx], value_list[right_idx], 
            ))
            Q[right_idx] = right_data
            vis_q[right_idx] = True
    
        Q[root_idx].right = Q[right_idx]
    return Q[0]


def ldr_view(tree):
    if tree.left:
        ldr_view(tree.left)
    print(tree.data)
    if tree.right:
        ldr_view(tree.right)
    return None

name_q = "aa bb cc dd ee ff ^".split(' ')
value_q = [1, 2, 3, 4, 5, 6, 0]
tree =  creatBiTree(name_q, value_q)
ldr_view(tree)
>>> tree =  creatBiTree(name_q, value_q)       
max_depth=3, final_idx=7,last_idx=4
>>> ldr_view(tree)
Data[name: dd, value:4]
Data[name: bb, value:2]
Data[name: ee, value:5]
Data[name: aa, value:1]
Data[name: ff, value:6]
Data[name: cc, value:3]

3.2 树遍历

这个就是一个非常典型的递归问题了

cpp

template<typename T>
void LDR(TreeNode<T> *tree){
    if(tree->left){
        LDR(tree->left);
    }
    cout << "name: " << tree->data.name << " Value: " << tree->data.value << endl;
    if(tree->right){
        LDR(tree->right);
    }
}

template<typename T>
void LRD(TreeNode<T> *tree){
    if(tree->left){
        LRD(tree->left);
    }
    if(tree->right){
        LRD(tree->right);
    }
    cout << "name: " << tree->data.name << " Value: " << tree->data.value << endl;
}

template<typename T>
void DLR(TreeNode<T> *tree){
    cout << "name: " << tree->data.name << " Value: " << tree->data.value << endl;
    if(tree->left){
        DLR(tree->left);
    }
    if(tree->right){
        DLR(tree->right);
    }
}

python

# 中序
def ldr_view(tree, out=[]):
    print(out)
    if tree.left:
        ldr_view(tree.left, out)
    
    out.append(tree.data)
    if tree.right:
        ldr_view(tree.right, out)
    return out

ldr_view(tree, out=[])

# 前序
def dlr_view(tree, out=[]):
    out.append(tree.data)
    print(out)
    if tree.left:
        dlr_view(tree.left, out)
    
    if tree.right:
        dlr_view(tree.right, out)
    return out

dlr_view(tree, out=[])


# 后序
def lrd_view(tree, out=[]):
    print(out)
    if tree.left:
        lrd_view(tree.left, out)
    
    if tree.right:
        lrd_view(tree.right, out)
    
    out.append(tree.data)
    return out

lrd_view(tree, out=[])

参考

  • 《数据结构与算法分析新视角》
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Scc_hy

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值