7.2 二叉树

有序二叉树

1)树:

        具有一对多的特点

2)二叉树特点:

        1.每个节点最多有两个子节点

        2.单根性:每个子节点有且仅有一个父节点,整棵树只有一个根节点

                左子树:根节点左边的子树

                右子树:根节点右边的子树

        3.一般用递归函数来处理

3)有序二叉树(二叉的核心)

        概念:一般来说,当左子树不为空时,左子树的元素值小于根节点

        当右子树不为空时,右子树的元素值大于根节点

        一句话:不管站在哪个节点去看,左边总是小于右边

三种遍历方式:

        先序遍历:处理节点本身->处理左节点->处理右节点

        中序遍历:处理左节点->处理节点本身->处理右节点

        后序遍历:处理左节点->处理右节点->处理节点本身

 

        中序遍历读取的有序二叉树是有序的

4)有序二叉树涉及的两个结构体

        描述节点属性的结构体

        struct node {

                int data; //节点数据

                struct node *left; //左节点首地址

                struct node *right; //右节点首地址

        };

        struct tree {

                struct node *root; //保存根节点的首地址

                int cnt; //保存节点的个数

        };

        参见:有序二叉树的内存示意图!

        删除节点:9个字,三字经:找节点,找新爹,提一级

         vim tree.h

//tree.h:声明
#ifndef __TREE_H
#define __TREE_H
//包含公共的头文件
#include <stdio.h>
#include <stdlib.h>
//声明描述节点属性的结构体
typedef struct node {
    int data; //数据
    struct node *left; //保存左节点的首地址
    struct node *right;//保存右节点的首地址
}node_t;
//描述二叉树的结构体
typedef struct tree {
    struct node *root; //保存根节点的首地址
    int cnt; //保存节点个数
}tree_t;
//声明操作函数
extern void tree_travel(tree_t *tree);//遍历
extern void tree_insert(tree_t *tree, int data);//插入新节点
extern void tree_clear(tree_t *tree);//清空所有节点
extern void tree_del(tree_t *tree, int data);//删除data所在的节点
extern void tree_modify(tree_t *tree, int old_data, int new_data);//修改节点数据
#endif

         vim tree.c

//tree.c:定义
#include "tree.h"
//travel(50)
//  travel(50的左边20)
//      travel(20的左边10)
//          travel(10的左边NULL)
//              return
//          printf:10
//          travel(10的右边NULL)
//              return
//          return
//      printf:20
//      travel(20的右边40)
//          travel(40的左边30)
//              travel(30的左边NULL)
//                  return
//              printf:30
//              travel(30的右边NULL)
//                  return
//              return
//          printf:40
//          travel(40的右边NULL)
//              return
//          return
//      return
//  printf:50
//  travel(50的右边70)
//      ...
//定义遍历的递归函数
static void travel(node_t *proot) {
#ifdef A
    //中序遍历
    if(proot != NULL) {
        travel(proot->left); //打印左节点数据
        printf("%d ", proot->data); //打印节点数据
        travel(proot->right); //打印右节点数据
        return;
    }
#endif
#ifdef B
    //先序遍历
    if(proot != NULL) {
        printf("%d ", proot->data); //打印节点数据
        travel(proot->left); //打印左节点数据
        travel(proot->right); //打印右节点数据
        return;
    }
#endif
#ifdef C
    //后序遍历
    if(proot != NULL) {
        travel(proot->left); //打印左节点数据
        travel(proot->right); //打印右节点数据
        printf("%d ", proot->data); //打印节点数据
        return;
    }
#endif
    return;
}
//定义遍历函数
void tree_travel(tree_t *tree) {
    //调用递归函数从根节点开始遍历
    travel(tree->root);
    printf("\n");
}
//clear(50)
//  clear(50的左边20)
//      clear(20的左边10)
//          clear(10的左边NULL)
//              return
//          clear(10的右边NULL)
//              return
//          free(10)
//          20的L->NULL
//          return
//      clear(20的右边40)
//          clear(40的左边30)
//              clear(30的左边NULL)
//                  return
//              clear(30的右边NULL)
//                  return
//              free(30)
//              40的L->NULL
//              return
//          clear(40的右边NULL)
//              return
//          free(40)
//          20的R->NULL
//          return
//      free(20)
//      50的L->NULL
//      return 
//  clear(50的右边70)
//      ...
//定义清空所有节点的递归函数
static void clear(node_t **pproot) {
    if(*pproot != NULL) {
        clear(&(*pproot)->left);//释放左节点
        clear(&(*pproot)->right);//释放右节点
        free(*pproot); //释放节点内存
        *pproot = NULL; //让父节点的L和R指向NULL
        return;
    }
    return;
}
//定义清空所有节点的函数
void tree_clear(tree_t *tree) {
    //调用递归函数从根节点开始释放所有节点
    clear(&tree->root);
    tree->cnt = 0; //节点个数清0
}
//定义创建新节点内存函数
static node_t *create_node(int data) {
    node_t *pnew = (node_t *)malloc(sizeof(node_t));
    pnew->data = data;
    pnew->left = NULL;
    pnew->right = NULL;
    return pnew; //返回新节点首地址
}
//insert(NULL, 50)
//  root->50新节点
//  return
//cnt++;
//insert(50, 20)
//  insert(50的左边NULL, 20)
//      50的L->20
//      return
//  return
//cnt++;
//insert(50, 70)
//  insert(50的右边NULL, 70)
//      50的R->70
//      retrun
//  return
//cnt++
//insert(50, 10)
//  insert(50的左边20, 10)
//      insert(20的左边NULL, 10)
//          20的L->10
//          return
//      return
//  return
//cnt++
//定义插入新节点的递归函数
static void insert(node_t **pproot, node_t *pnew) {
    if(*pproot == NULL) {
        *pproot = pnew; //插入新节点
        return;
    }
    if((*pproot)->data > pnew->data) { //插入到左子树
        insert(&(*pproot)->left, pnew);
        return;
    } else {
        insert(&(*pproot)->right, pnew); //插入到右子树
        return;
    }
    return;
}
//定义插入新节点的函数
void tree_insert(tree_t *tree, int data) {
    //1.创建新节点
    node_t *pnew = create_node(data);
    //2.调用递归函数插入新节点
    insert(&tree->root, pnew);
    //3.更新计数
    tree->cnt++;
}
//find(50, 5)
//  find(50的左边20, 5)
//      find(20的左边10, 5)
//          find(10的左边NULL, 5)
//              return NULL
//          return NULL
//      return NULL
//  return NULL
//没有找到
//find(50, 80)
//  find(50的右边70, 80)
//      find(70的右边90, 80)
//          find(90的左边80, 80)
//              return 80
//          return 80
//      return 80
//  return 80
//找到了
//定义查找节点的递归函数
static node_t **find(node_t **pproot, int data) {
    //1.停止查找
    if(*pproot == NULL)
        return pproot; //没有找到
    //2.比较
    if((*pproot)->data == data)
        return pproot; //找到了
    else if((*pproot)->data > data)
        return find(&(*pproot)->left, data); //左边找
    else 
        return find(&(*pproot)->right, data); //右边找
}
//定义查找节点的函数
static node_t **find_node(tree_t *tree, int data) {
    //调用递归函数从根节点开始查找并且返回对应的二级指针
    return find(&tree->root, data);
}
//定义删除指定数字所在的节点函数
void tree_del(tree_t *tree, int data) {
    //1.找节点
    node_t **ppnode = find_node(tree, data);
    if(*ppnode == NULL) {
        printf("没有找到节点.\n");
        return;
    }
    //2.找新爹:将删除的节点的左子树插入到右子树上
    if((*ppnode)->left != NULL) 
        insert(&(*ppnode)->right, (*ppnode)->left);
    //3.提一级:将指向删除节点的父节点的左子树指向要删除的节点的右子树上
    //例如:50的L->40
    //(*ppnode):表示要删除的节点,也就是50的L
    //(*ppnode)->right:右节点,也就是40
    node_t *ptmp = *ppnode; //临时备份要删除的节点,用于将来free
    *ppnode = (*ppnode)->right;//50的L->40
    free(ptmp); //释放要删除的节点
    tree->cnt--; //更新计数
}
//修改节点值的函数
void tree_modify(tree_t *tree, int old_data, int new_data) {
    //1.先删除节点
    tree_del(tree, old_data);
    //2.添加新节点
    tree_insert(tree, new_data);
}

        vim main.c

#include "tree.h"
int main(void) {
    tree_t tree;
    tree.root = NULL;
    tree.cnt = 0;
    tree_insert(&tree, 50);
    tree_insert(&tree, 70);
    tree_insert(&tree, 60);
    tree_insert(&tree, 20);
    tree_insert(&tree, 40);
    tree_insert(&tree, 30);
    tree_insert(&tree, 10);
    tree_insert(&tree, 90);
    tree_insert(&tree, 80);
    tree_travel(&tree);
    tree_del(&tree, 40);
    tree_del(&tree, 40);
    tree_travel(&tree);
    tree_modify(&tree, 10, 520);
    tree_travel(&tree);
    tree_clear(&tree);
    tree_travel(&tree);
    return 0;
}

        vim Makefile

#Makefile
BIN=tree
OBJ=main.o tree.o
CC=gcc
#make CFLAGS=A或者B或者C 可以动态修改CFLAGS变量值,从而选择先序后序还是中序
CFLAGS=A
$(BIN):$(OBJ)
	$(CC) -o $(BIN) $(OBJ)

%.o:%.c
	$(CC) -D$(CFLAGS) -c -o $@ $<

clean:
	rm $(BIN) $(OBJ)

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值