zoukankan      html  css  js  c++  java
  • 二叉树

      今天介绍二叉树,主要介绍c的代码实现,更多关于二叉树的概念,大家可以百度或者看书。

    先介绍二叉树的存储结构

    typedef struct node
    {
        int data;
        struct node * left;
        struct node * right;
    } Node;
    
    typedef struct tree
    {
        Node * root;
    } Tree;

    Node节点的data是数据域,left,right是指针域。

    Tree节点里只记录了root,也就是根。当然可以增加一些其他信息。

    先说查找操作,因为之后的很多函数都会用。

    //这个结构体是为了方便插入删除操作,child是找到的节点,parent是父节点
    typedef struct pair
    {
        Node * parent;
        Node * child;
    } Pair;

    这个结构体主要是为了删除操作使用的,因为删除时我们要知道删除节点的父亲。而插入操作只要找到插入位置就可以了。

    static Pair search_node(Tree * tree, const int key)
    {
        Pair pair;
    
        pair.parent = NULL;//parent一直是child的父节点
        pair.child = tree->root;
        
        while ( pair.child != NULL )
        {
            if ( pair.child->data > key )//如果当前结点的值大于key,则向左查找
            {
                pair.parent = pair.child;
                pair.child = pair.child->left;
            }
            else if ( pair.child->data  < key )//如果当前节点的值小于key,则向右查找
            {
                pair.parent = pair.child;
                pair.child = pair.child->right;
            }
            else//如果相等,直接返回Pair结构体,结构体中的child就是找到的结点
            {
                return pair;
            }
        }
    
        return pair;
    }

    这个函数会返回一个pair结构体

    如果找到关键字节点。child指向该节点,parent是该节点的父亲。

    否则,child为NULL,parent是child的父亲节点。(如果是插入操作,parent的左孩子或者右孩子就是插入位置)

    插入操作。

    void t_insert(Tree * tree, const int data)
    {
        Pair pair;
        pair = search_node(tree, data);//找到要插入的位置
        
        if ( pair.parent == NULL )//第一次插入
            tree->root = make_node(data);
        else if (pair.child != NULL)//是否存在该节点
            return;//存在则插入失败
        else
        {
            if ( pair.parent->data > data)//根据新节点与父节点的比较,决定新节点的插入位置
                pair.parent->left = make_node(data);
            else
                pair.parent->right = make_node(data);
        }
    }

     因为我们已经实现了查找功能。所以插入变的非常容易。先判断是不是根节点,再判断是否存在该元素。最后就是根据元素的大小判断插入位置。(这里不是必须左孩子比右孩子小)

     查找函数就不介绍了。只是调用了search_node函数。

    接下来介绍删除操作。

    相比较插入而言,删除操作有点难度,但难度并不大。

    首先要找到删除节点。然后将他的孩子连接到父亲上,释放删除节点内存就OK了。

    这里分两种情况。删除节点有左右孩子,和只有一个孩子或者没有孩子。

    一个孩子或者没有孩子的情况好处理,只要把这个孩子的父亲变成删除节点的父亲就可以了。

    如果删除节点的有两个孩子。我们可以把左孩子的父亲变成右孩子的子树中最小的值。(右孩子的左子树的左子树…)

    根据我们之前的对插入操作,我们可以知道,以某一节点为根,这棵树上的最小值一定是他的左子树的左子树…(while ( current->left != NULL ) current = current->left;)

    当然这里还有其他 方法,稍后会介绍一种叫替换的方法。里面涉及到删除点和真正的删除点。他是红黑树里使用的删除方法。

    void t_remove(Tree * tree, const int key)
    {
        Pair pair = search_node(tree, key);
        Node * current;

        if ( pair.child == NULL )//没有找到关键字
            return;
        if ( pair.child->left && pair.child->right )
        {
            current = pair.child->right;
            while ( current->left != NULL )//找到删除节点左孩子的新位置
                current = current->left;
            current->left = pair.child->left;
            if ( pair.parent )
            {
                if ( pair.parent->left == pair.child )
                    pair.parent->left = pair.child->right;
                else
                    pair.parent->right = pair.child->right;
            }
            else
                tree->root = pair.child->right;
        }
        else
        {
            if ( pair.parent )
            {
                if ( pair.child->left )//只有左孩子
                {
                    if ( pair.parent->left == pair.child )
                        pair.parent->left = pair.child->left;
                    else
                        pair.parent->right = pair.child->left;
                }
                else//右孩子或者没有孩子
                {
                    if ( pair.parent->left == pair.child )
                        pair.parent->left = pair.child->right;
                    else
                        pair.parent->right = pair.child->right;
                }
            }
            else
            {
                if ( pair.child->left )
                    tree->root = pair.child->left;  
                else
                    tree->root = pair.child->right;  
            }
        }    

        free(pair.child);

     最后是二叉树的遍历

    static void treaverse_node(Node * current, void (*func)(Node * current))
    {
        if ( current == NULL )
            return;
        else//递归,中序遍历二叉树
        {
            treaverse_node(current->left, func);
            func(current);
            treaverse_node(current->right, func);
        }
    }

    前序遍历:根,左,右

    中序遍历:左,根,右

    后序遍历:左,右,根

    我们可以简单的理解成,把func放到两个递归函数的前面就是前序,中间是中序,后面是后续。

    之后我还会介绍非递归的遍历算法。

    源码

    btree.h

    #ifndef _TREE_H
    #define _TREE_H
    
    #define MAXDEPTH 10
    
    typedef struct node
    {
        int data;
        struct node * left;
        struct node * right;
    } Node;
    
    typedef struct tree
    {
        Node * root;
    } Tree;
    
    void t_init(Tree * tree);//初始化
    void t_insert(Tree * tree, const int data);//添加
    Node* t_find(Tree * tree, const int key);//查找
    void t_remove(Tree * tree, const int key);//移除
    void t_treaverse(Tree * tree, void (*func)(Node * current));//遍历
    void t_destroy(Tree * tree);//销毁
    bool t_empty(Tree * tree);//为空
    int t_depth(Tree * tree);//深度
    Node* t_root(Tree * tree);//
    int t_value(Node * current);//返回值
    
    #endif //_TREE_H

    btree.c

    #include <stdio.h>
    #include <stdlib.h>
    #include <assert.h>
    
    #include "btree.h"
    
    //这个结构体是为了方便插入删除操作,child是找到的节点,parent是父节点
    typedef struct pair
    {
        Node * parent;
        Node * child;
    } Pair;
    
    static Node* make_node(const int data);//创建
    static Pair search_node(Tree * tree, const int key);//查找
    static void treaverse_node(Node * current, void (*func)(Node * node));//遍历
    static void destroy_node(Node * current);//销毁
    static int depth_node(Node * current);//
    
    void t_init(Tree * tree)
    {
        tree->root = NULL;
    }
    
    void t_insert(Tree * tree, const int data)
    {
        Pair pair;
        pair = search_node(tree, data);//找到要插入的位置
        
        if ( pair.parent == NULL )//第一次插入
            tree->root = make_node(data);
        else if (pair.child != NULL)//是否存在该节点
            return;//存在则插入失败
        else
        {
            if ( pair.parent->data > data)//根据新节点与父节点的比较,决定新节点的插入位置
                pair.parent->left = make_node(data);
            else
                pair.parent->right = make_node(data);
        }
    }
    
    Node* t_find(Tree * tree, const int key)
    {
        return search_node(tree, key).child;
    }
    void t_remove(Tree * tree, const int key)
    {
        Pair pair = search_node(tree, key);
        Node * current;

        if ( pair.child == NULL )//没有找到关键字
            return;
        if ( pair.child->left && pair.child->right )
        {
            current = pair.child->right;
            while ( current->left != NULL )//找到删除节点左孩子的新位置
                current = current->left;
            current->left = pair.child->left;
            if ( pair.parent )
            {
                if ( pair.parent->left == pair.child )
                    pair.parent->left = pair.child->right;
                else
                    pair.parent->right = pair.child->right;
            }
            else
                tree->root = pair.child->right;
        }
        else
        {
            if ( pair.parent )
            {
                if ( pair.child->left )//只有左孩子
                {
                    if ( pair.parent->left == pair.child )
                        pair.parent->left = pair.child->left;
                    else
                        pair.parent->right = pair.child->left;
                }
                else//右孩子或者没有孩子
                {
                    if ( pair.parent->left == pair.child )
                        pair.parent->left = pair.child->right;
                    else
                        pair.parent->right = pair.child->right;
                }
            }
            else
            {
                if ( pair.child->left )
                    tree->root = pair.child->left;  
                else
                    tree->root = pair.child->right;  
            }
        }    

        free(pair.child);
    }
    void t_treaverse(Tree * tree, void (*func)(Node * current)) { assert( tree != NULL ); treaverse_node(tree->root, func); } void t_destroy(Tree * tree) { treaverse_node(tree->root, destroy_node); } bool t_empty(Tree * tree) { return tree->root == NULL; } int t_depth(Tree * tree) { depth_node(tree->root); } Node* t_root(Tree * tree) { return tree->root; } int t_value(Node * current) { assert( current != NULL ); return current->data; } static Node* make_node(const int data) { Node * node = (Node *)malloc( sizeof(Node) ); if (node == NULL) exit(0); node->data = data; node->left = NULL; node->right = NULL; return node; } static Pair search_node(Tree * tree, const int key) { Pair pair; pair.parent = NULL;//parent一直是child的父节点 pair.child = tree->root; while ( pair.child != NULL ) { if ( pair.child->data > key )//如果当前结点的值大于key,则向左查找 { pair.parent = pair.child; pair.child = pair.child->left; } else if ( pair.child->data < key )//如果当前节点的值小于key,则向右查找 { pair.parent = pair.child; pair.child = pair.child->right; } else//如果相等,直接返回Pair结构体,结构体中的child就是找到的结点 { return pair; } } return pair; } static void treaverse_node(Node * current, void (*func)(Node * current)) { if ( current == NULL ) return; else//递归,中序遍历二叉树 { treaverse_node(current->left, func); func(current); treaverse_node(current->right, func); } } static void destroy_node(Node * current) { assert( current != NULL ); free( current ); } static int depth_node(Node * current) { int m, n; if ( current == NULL )//当节点为NULL时返回0 return 0; else { m = depth_node(current->left);//计算左子树深度 n = depth_node(current->right);//计算右子树深度 if ( m > n )//返回大的子树深度 return m + 1; else return n + 1; } }

    好吧,里面还有一些函数没有介绍,不过都很简单。数据结构的书上都有讲。

    感谢大家的阅读,如有错误,欢迎指正。

  • 相关阅读:
    原生和jQuery的ajax用法
    sublime常用快捷键
    用filter:grayscale将图片过滤成灰色
    Docker搭建Zookeeper集群问题总结
    Linux下jdk环境配置
    window MySQL解压缩版部署及配置
    Windows下Nginx的配置及配置文件部分介绍
    JS 特性:可选链(?.)
    509道Java面试题解析:2020年最新Java面试题
    阿里面试题BIO和NIO数量问题附答案和代码
  • 原文地址:https://www.cnblogs.com/ITgaozy/p/5150911.html
Copyright © 2011-2022 走看看