构造一个二叉树

1. 需求

构造一个二叉树,所有数据按照升序排列。

2. 原型版本

先考虑一个最简单的原型。代码如下:

/**
 * tree.cc
 *
 * A example to show how to construct a binary tree.
 *
 * Command:
 * g++ tree.cc
 * valgrind --tool=memcheck ./a.out
 * 
 */
#include <stdio.h>
#include <stdlib.h>

class Data {
public:
    explicit Data(int id):_id(id){}
    Data(const Data& data);
    Data(): _id(-1) {}
    
    int getId() const {
        return _id;
    }
    
private:
    int _id;
};

Data::Data(const Data& data):_id(data._id)
{
}

class Tree
{
public:
    explicit Tree();
    
    ~Tree();
    
    void insert(const Data &data);
    void dump() const;
    
private:
    void insert_left(const Data &data);
    void insert_right(const Data &data);
    
private:
    Data* _data;
    Tree* left_child;
    Tree* right_child;
};

Tree::Tree():_data(NULL), left_child(NULL), right_child(NULL) {}

Tree::~Tree()
{
    if (_data != NULL) {
        delete _data;
    }
    
    if (left_child != NULL) {
        delete left_child;
        left_child = NULL;
    }
    
    if (right_child != NULL) {
        delete right_child;
        right_child = NULL;
    }
}

void Tree::insert(const Data &data)
{
    if (NULL == _data) {
        _data = new Data(data);
        return;
    }
    
    if (data.getId() <= _data->getId()) {
        insert_left(data);
    } else {
        insert_right(data);
    }
}

void Tree::insert_left(const Data &data)
{
    if (NULL == left_child) {
        left_child = new Tree();
        left_child->insert(data);
        return;
    }
    
    left_child->insert(data);
}

void Tree::insert_right(const Data &data)
{
    if (NULL == right_child) {
        right_child = new Tree();
        right_child->insert(data);
        return;
    }
    
    right_child->insert(data);
}

void Tree::dump() const
{
    if (left_child != NULL) {
        left_child->dump();
    }
    
    printf("%d\t", _data->getId());
    
    if (right_child != NULL) {
        right_child->dump();
    }
}

int main()
{
    Tree tree;
    
    int data[] = {1, 3, 5, 7, 2, 4, 6, 8};
    int i;
    
    for (i = 0; i < sizeof(data) / sizeof(int); i++) {
        tree.insert(Data(data[i]));
    }
    
    tree.dump();
    printf("\n");
    
    return 0;
}

运行结果:

flying-bird@flyingbird:~/examples/cpp/tree$ g++ tree.cc
flying-bird@flyingbird:~/examples/cpp/tree$ ./a.out 
1	2	3	4	5	6	7	8	
flying-bird@flyingbird:~/examples/cpp/tree$ valgrind --tool=memcheck ./a.out 
==7673== Memcheck, a memory error detector
==7673== Copyright (C) 2002-2013, and GNU GPL'd, by Julian Seward et al.
==7673== Using Valgrind-3.10.0.SVN and LibVEX; rerun with -h for copyright info
==7673== Command: ./a.out
==7673== 
1	2	3	4	5	6	7	8	
==7673== 
==7673== HEAP SUMMARY:
==7673==     in use at exit: 0 bytes in 0 blocks
==7673==   total heap usage: 15 allocs, 15 frees, 116 bytes allocated
==7673== 
==7673== All heap blocks were freed -- no leaks are possible
==7673== 
==7673== For counts of detected and suppressed errors, rerun with: -v
==7673== ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 0 from 0)
flying-bird@flyingbird:~/examples/cpp/tree$ 

3. 重构

我们通常会搭建一个可以运行的、满足基本功能的代码,然后再快速重构、逐步演进。

3.1 operator <<

在C++中,用printf总是不如ostream正式。所以,首先对dump()进行重构,为Tree定义一个operator<<()操作符。

代码如下://同时头文件风格进行优化:用cstdio代替stdio.h (参考Effective C++, 2nd, Item 49)

/**
 * tree2.cc
 *
 * A example to show how to construct a binary tree.
 *
 * Command:
 * g++ tree2.cc
 * valgrind --tool=memcheck ./a.out
 * 
 */
#include <cstdlib>
#include <iostream>

class Data {
public:
    explicit Data(int id):_id(id){}
    Data(const Data& data);
    Data(): _id(-1) {}
    
    int getId() const {
        return _id;
    }
    
private:
    int _id;
};

Data::Data(const Data& data):_id(data._id)
{
}

class Tree
{
public:
    explicit Tree();
    
    ~Tree();
    
    void insert(const Data &data);

private:
    void insert_left(const Data &data);
    void insert_right(const Data &data);
    
private:
    Data* _data;
    Tree* left_child;
    Tree* right_child;

    friend std::ostream& operator<<(std::ostream &os, const Tree& tree);
};

Tree::Tree():_data(NULL), left_child(NULL), right_child(NULL) {}

Tree::~Tree()
{
    if (_data != NULL) {
        delete _data;
    }
    
    if (left_child != NULL) {
        delete left_child;
        left_child = NULL;
    }
    
    if (right_child != NULL) {
        delete right_child;
        right_child = NULL;
    }
}

void Tree::insert(const Data &data)
{
    if (NULL == _data) {
        _data = new Data(data);
        return;
    }
    
    if (data.getId() <= _data->getId()) {
        insert_left(data);
    } else {
        insert_right(data);
    }
}

void Tree::insert_left(const Data &data)
{
    if (NULL == left_child) {
        left_child = new Tree();
        left_child->insert(data);
        return;
    }
    
    left_child->insert(data);
}

void Tree::insert_right(const Data &data)
{
    if (NULL == right_child) {
        right_child = new Tree();
        right_child->insert(data);
        return;
    }
    
    right_child->insert(data);
}

std::ostream& operator<<(std::ostream &os, const Tree& tree)
{
    if (tree.left_child != NULL) {
        os<<*(tree.left_child);
    }
    
    os<<tree._data->getId()<<'\t';
    
    if (tree.right_child != NULL) {
        os<<*(tree.right_child);
    }

    return os;
}


int main()
{
    Tree tree;
    
    int data[] = {1, 3, 5, 7, 2, 4, 6, 8};
    int i;
    
    for (i = 0; i < sizeof(data) / sizeof(int); i++) {
        tree.insert(Data(data[i]));
    }
    
    std::cout<<tree<<std::endl;
    
    return 0;
}

3.2 Data抽象

从逻辑上来讲,以上两个数据模型,即Tree和Data应该是松耦合,或者是接口依赖,而不能是实际数据依赖。所以,接下来对Data进行重构,目标就是Tree无须知晓Data的内部形态。显然,我们要为Data定义operator<=()操作符。

代码如下:

/**
 * tree3.cc
 *
 * A example to show how to construct a binary tree.
 *
 * Command:
 * g++ tree3.cc
 * valgrind --tool=memcheck ./a.out
 * 
 */
#include <cstdlib>
#include <cassert>
#include <iostream>

class Data {
public:
    explicit Data(int id):_id(id){}
    Data(const Data& data);
    Data(): _id(-1) {}
    
    int getId() const {
        return _id;
    }
    
private:
    int _id;
};

bool operator<=(const Data& lhs, const Data& rhs)
{
    return lhs.getId() <= rhs.getId();
}

bool operator>(const Data& lhs, const Data& rhs)
{
    assert(false); // Not used now.
    return false;
}

Data::Data(const Data& data):_id(data._id)
{
}

class Tree
{
public:
    explicit Tree();
    
    ~Tree();
    
    void insert(const Data &data);

private:
    void insert_left(const Data &data);
    void insert_right(const Data &data);
    
private:
    Data* _data;
    Tree* left_child;
    Tree* right_child;

    friend std::ostream& operator<<(std::ostream &os, const Tree& tree);
};

Tree::Tree():_data(NULL), left_child(NULL), right_child(NULL) {}

Tree::~Tree()
{
    if (_data != NULL) {
        delete _data;
    }
    
    if (left_child != NULL) {
        delete left_child;
        left_child = NULL;
    }
    
    if (right_child != NULL) {
        delete right_child;
        right_child = NULL;
    }
}

void Tree::insert(const Data &data)
{
    if (NULL == _data) {
        _data = new Data(data);
        return;
    }
    
    if (data <= *_data) {
        insert_left(data);
    } else {
        insert_right(data);
    }
}

void Tree::insert_left(const Data &data)
{
    if (NULL == left_child) {
        left_child = new Tree();
        left_child->insert(data);
        return;
    }
    
    left_child->insert(data);
}

void Tree::insert_right(const Data &data)
{
    if (NULL == right_child) {
        right_child = new Tree();
        right_child->insert(data);
        return;
    }
    
    right_child->insert(data);
}

std::ostream& operator<<(std::ostream &os, const Tree& tree)
{
    if (tree.left_child != NULL) {
        os<<*(tree.left_child);
    }
    
    os<<tree._data->getId()<<'\t';
    
    if (tree.right_child != NULL) {
        os<<*(tree.right_child);
    }

    return os;
}


int main()
{
    Tree tree;
    
    int data[] = {1, 3, 5, 7, 2, 4, 6, 8};
    int i;
    
    for (i = 0; i < sizeof(data) / sizeof(int); i++) {
        tree.insert(Data(data[i]));
    }
    
    std::cout<<tree<<std::endl;
    
    return 0;
}

3.3 Data进一步抽象

让Tree有赖于Data的接口,而不是实现。






好的,下面是用C语言构造一个二叉树的代码示例,同时包含了二叉树的前序、中序和后序遍历函数: ```c #include <stdio.h> #include <stdlib.h> // 二叉树节点结构体 typedef struct TreeNode { int val; struct TreeNode* left; struct TreeNode* right; } TreeNode; // 构造二叉树函数 TreeNode* createTree() { int val; scanf("%d", &val); if (val == -1) { // 输入-1表示该节点为空 return NULL; } TreeNode* root = (TreeNode*)malloc(sizeof(TreeNode)); root->val = val; root->left = createTree(); root->right = createTree(); return root; } // 前序遍历函数 void preorder(TreeNode* root) { if (root == NULL) { return; } printf("%d ", root->val); preorder(root->left); preorder(root->right); } // 中序遍历函数 void inorder(TreeNode* root) { if (root == NULL) { return; } inorder(root->left); printf("%d ", root->val); inorder(root->right); } // 后序遍历函数 void postorder(TreeNode* root) { if (root == NULL) { return; } postorder(root->left); postorder(root->right); printf("%d ", root->val); } int main() { printf("请输入二叉树的节点值,-1表示空节点:\n"); TreeNode* root = createTree(); printf("前序遍历结果:"); preorder(root); printf("\n中序遍历结果:"); inorder(root); printf("\n后序遍历结果:"); postorder(root); printf("\n"); return 0; } ``` 示例输入: ``` 请输入二叉树的节点值,-1表示空节点: 1 2 -1 -1 3 4 -1 -1 5 -1 -1 ``` 示例输出: ``` 前序遍历结果:1 2 3 4 5 中序遍历结果:2 1 4 3 5 后序遍历结果:2 4 5 3 1 ``` 希望这个例子对你有所帮助!
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值