树型结构:
1、树的基本概念
一种表示层次关系(一对多)的数据结构
有且仅有一个特定的节点,该节点没有前趋,被称为根节点
剩余的n个互不相交的子集,其中的每个子集也都是一棵树,都被称为根节点的子树
注意:树型结构具有递归性(树中有树)
2、树的表示方法:
倒悬树、嵌套法、凹凸法
3、树的专业术语
节点: 组成树的基础元素,同时它也是一棵树
节点的度: 该节点的子树的数量,与该节点有直接关系的
树的度(密度):树中节的度的最大值
树的深度: 树的最大层次树为树的深度
节点的层次:根节点的层次为1,它的孩子层次为2,孩子的孩子层次为3,以此类推
叶子节点: 节点的度为0的节点
双亲和孩子:节点的子树称为它的孩子节点,则该节点为孩子节点的双亲
兄弟节点: 具有同一个双亲节点的节点
祖先: 从根节点出发到该节点,经过的所有节点都被称为该节点的祖先
子孙: 一个节点的子树中任意的一个节点都被称为它的子孙
堂兄弟: 双亲不是同一个节点,但是双亲在同一层的节点互为堂兄弟
森林: n个不相交的树的集合称为森林
4、树的存储
树可以顺序存储、链式存储,也可以混合存储,由于存储的信息不同,有以下表示方式:
位置 data 双亲
0 A -1
1 B 0
2 C 0
3 D 1
4 E 2
5 F 2
6 G 3
7 H 4
8 I 4
优点:方便找到双亲
缺点:查找孩子节点不方便
list_queue.h
#ifndef LIST_QUEUE_H
#define LIST_QUEUE_H
#include <stdlib.h>
#include <stdbool.h>
#define TYPE int
// 设计节点
typedef struct Node
{
TYPE data;
struct Node* next;
}Node;
// 设计链式队列结构
typedef struct ListQueue
{
Node* head; // 指向队头节点的指针
Node* tail; // 指向队尾节点的指针
size_t size;// 节点个数
}ListQueue;
// 创建节点
Node* create_node(TYPE val);
// 创建队列结构
ListQueue* create_list_queue(void);
// 队空
bool empty_list_queue(ListQueue* queue);
// 入队
void push_list_queue(ListQueue* queue,TYPE val);
// 出队
bool pop_list_queue(ListQueue* queue);
// 队头
TYPE head_list_queue(ListQueue* queue);
// 队尾
TYPE tail_list_queue(ListQueue* queue);
// 数量
size_t size_list_queue(ListQueue* queue);
// 销毁
void destory_list_queue(ListQueue* queue);
#endif//LIST_QUEUE_H
list_queue.c
#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>
#include "list_queue.h"
// 顺序存储 双亲表示法
// 设计节点
typedef struct TreeNode
{
char data; // 数据
int parent; // 双亲节点下标
}TreeNode;
// 设计树的顺序结构
typedef struct Tree
{
TreeNode* arr; // 存储节点内存的首地址
size_t cal; // 容量
size_t cnt; // 节点个数
}Tree;
// 创建
Tree* create_tree(size_t cal)
{
Tree* tree = malloc(sizeof(Tree));
tree->arr = malloc(sizeof(TreeNode)*cal);//申请出一块连续的存储节点的内存
tree->cal = cal;
tree->cnt = 0;
return tree;
}
// parent是双亲的data
// 添加子树
bool add_tree(Tree* tree,char data,char parent)
{
// 树不存在满的情况,可以扩容
if(tree->cnt >= tree->cal)
{
tree->cal *= 2;
tree->arr = realloc(tree->arr,sizeof(TreeNode)*tree->cal);//扩容tree->arr,sizeof(TreeNode)*tree->cal是扩容后的大小)
}
// 默认根节点的双亲是'\0'
if('\0' == parent)
{
tree->arr[0].data = data;
tree->arr[0].parent = -1;
tree->cnt = 1;
return true;
}
for(int i=0; i<tree->cnt; i++)
{
if(tree->arr[i].data == parent)
{
tree->arr[tree->cnt].data = data;
tree->arr[tree->cnt++].parent = i;
return true;
}
}
return false;
}
void show_tree(Tree* tree)
{
for(int i=0; i<tree->cnt; i++)
{
printf("index: %d data: %c parent: %d\n",
i,tree->arr[i].data,tree->arr[i].parent);
}
}
// 计算节点的度
int node_count(Tree* tree,char data)
{
int count = 0,index = -1;
for(int i=0; i<tree->cnt; i++)
{
if(data == tree->arr[i].data)
{
index = i;
break;
}
}
if(-1 == index) return -1;
for(int i=0; i<tree->cnt; i++)
{
if(index == tree->arr[i].parent)
{
count++;
}
}
return count;
}
// 层序遍历
void level_show_tree(Tree* tree)
{
// 创建一个队列
ListQueue* queue = create_list_queue();//创建queue队列
// 根节点入队
push_list_queue(queue,tree->arr[0].data);//入队
while(!empty_list_queue(queue))//队列为空时结束
{
// 出队队头并显示
char data = head_list_queue(queue);//查看队头,赋给data
printf("%c ",data);
pop_list_queue(queue);//出队
// 找出该节点的子节点并入队
int index = -1;
for(int i=0; i<tree->cnt; i++)
{
if(data == tree->arr[i].data)
{
index = i;
break;
}
}
for(int i=0; i<tree->cnt; i++)
{
if(index == tree->arr[i].parent)
{
push_list_queue(queue,tree->arr[i].data);
}
}
}
// 销毁队列,queue是自己创的,次数多了的话会造成内存泄露
destory_list_queue(queue);
}
int main(int argc,const char* argv[])
{
Tree* tree = create_tree(10);
add_tree(tree,'A','\0');
add_tree(tree,'B','A');
add_tree(tree,'C','A');
add_tree(tree,'D','B');
add_tree(tree,'E','C');
add_tree(tree,'F','C');
add_tree(tree,'G','D');
add_tree(tree,'H','E');
add_tree(tree,'I','E');
add_tree(tree,'X','B');
add_tree(tree,'Y','B');
show_tree(tree);
printf("count:%d\n",node_count(tree,'C'));
level_show_tree(tree);
}
孩子表示法:
顺序: 浪费内存
位置 data sub_arr(存储子节点下标的数组)
0 A 1、2
1 B 3
2 C 4、5
3 D 6
4 E 7、8
5 F
6 G
7 H
8 I
链式: 节约空间
位置 data ListHead
0 A 1->2->N
1 B 3-N
2 C 4->5->N
3 D 6->N
4 E 7->8->N
5 F
6 G
7 H
8 I
优点:方便找孩子
缺点:查找双亲节点麻烦
兄弟表示法:
双亲只存储第一个子节点,然后链式地指向所有的兄弟节点
数据域:
第一个子节点,数据,兄弟节点指针
优点:可以方便地查询到所有的兄弟节点
缺点:查找双亲麻烦
总结:普通树不常用,一般需要转换为二叉树使用
二叉树:
二叉树是n个有限元素的集合,该集合或者为空、或者由一个称为根(root)的元素及两个不相交的、被分别称为左子树和右子树的二叉树组成,是有序树
定义:节点的度最多为2
特殊的二叉树:
每层的节点数都是2^(i-1),这种树叫做满二叉树
深度为k,有n个结点的二叉树当且仅当其每一个结点都与深度为k的满二叉树中编号从1到n的结点一一对应时,称为完全二叉树
二叉树的性质:
性质1:二叉树的第i层上最多有2^(i-1)个节点
性质2:深度为h的二叉树中至多含有2^h-1个节点
性质3:若在任意一棵二叉树中,有n0个叶子节点,有n2个度为2的节点,则必有 n0=n2+1
性质4:具有n个节点的完全二叉树深为log(2)x+1(其中x表示不大于n的最大整数)
性质5:若对一棵有n个节点的完全二叉树进行顺序编号(1≤i≤n),那么,对于编号为i(i≥1)的节点:
当i=1时,该节点为根,它无双亲节点
当i>1时,该节点的双亲节点的编号为i/2
若2i≤n,则有编号为2i的左子节点,否则没有左子节点
若2i+1≤n,则有编号为2i+1的右子节点,否则没有右子节点
二叉树的操作:
构建、销毁、遍历、深度、密度、插入、删除、查询、求左、求右、求根
二叉树的遍历:
前序:根、左、右
中序:左、根、右
后序:左、右、根
注意:前中后序由根节点决定,并且左右子树的次序不能改变
注意:前序+中序、后序+中序 能够还原一棵树,前序+后序是无法还原的(因为无法确定根的左右子树)
层序:从上到下、从左到右遍历一棵树,必须与队列配合使用
gcc -std=gnu99 list_queue.c bin_array_tree.c
二叉树的存储:
顺序:
必须按照完全二叉树的格式存储,空位置使用特殊数据代替
数据项:
容量
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <stdbool.h>
#include "list_queue.h"
#define TREE_TYPE char
#define EMPTY '#'
typedef struct BinTree
{
TREE_TYPE* arr; // 存储节点的内存首地址
size_t cal;
}BinTree;
// 构建
BinTree* create_tree(TREE_TYPE* arr,size_t len)//数组长度
{
BinTree* tree = malloc(sizeof(BinTree));
tree->arr = malloc(sizeof(TREE_TYPE)*len);
// 把arr拷贝到tree->arr
memcpy(tree->arr,arr,sizeof(TREE_TYPE)*len);
tree->cal = len;
return tree;
}
// 销毁
void destory_tree(BinTree* tree)
{
free(tree->arr);
free(tree);
}
void _dlr_show(BinTree* tree,size_t index)
{
if(index-1 >= tree->cal || EMPTY == tree->arr[index-1]) return;
printf("%c ",tree->arr[index-1]);
_dlr_show(tree,index*2);
_dlr_show(tree,index*2+1);
}
// 前序遍历
void dlr_show(BinTree* tree)
{
_dlr_show(tree,1);
printf("\n");
}
void _ldr_show(BinTree* tree,size_t index)
{
if(index-1 >= tree->cal || EMPTY == tree->arr[index-1]) return;
_ldr_show(tree,index*2);
printf("%c ",tree->arr[index-1]);
_ldr_show(tree,index*2+1);
}
// 中序遍历
void ldr_show(BinTree* tree)
{
_ldr_show(tree,1);
printf("\n");
}
void _lrd_show(BinTree* tree,size_t index)
{
if(index-1 >= tree->cal || EMPTY == tree->arr[index-1]) return;
_lrd_show(tree,index*2);
_lrd_show(tree,index*2+1);
printf("%c ",tree->arr[index-1]);
}
// 后序遍历
void lrd_show(BinTree* tree)
{
_lrd_show(tree,1);
printf("\n");
}
// 层序遍历
void level_show(BinTree* tree)
{
// 创建队列
ListQueue* queue = create_list_queue();
// 根入队
push_list_queue(queue,1);
while(!empty_list_queue(queue))
{
// 获取队头
int index = head_list_queue(queue);
// 计算左右子树编号
int left = index*2;
// 子树入队
if(left-1 < tree->cal && EMPTY != tree->arr[left-1])
{
push_list_queue(queue,left);
}
int right = index*2+1;
if(right-1 < tree->cal && EMPTY != tree->arr[right-1])
{
push_list_queue(queue,right);
}
// 打印队头
printf("%c ",tree->arr[index-1]);
// 队头出队
pop_list_queue(queue);
}
destory_list_queue(queue);
}
int _high_tree(BinTree* tree,size_t index)
{
// 出口
if(index-1 >= tree->cal || EMPTY == tree->arr[index-1])
return 0;
int lh = _high_tree(tree,index*2);
int rh = _high_tree(tree,index*2+1);
return lh > rh ? lh+1 : rh+1;
}
// 树的高度
int high_tree(BinTree* tree)
{
return _high_tree(tree,1);
}
int _density_tree(BinTree* tree,size_t index)
{
if(index-1 >= tree->cal || EMPTY == tree->arr[index-1])
return 0;
return 1+_density_tree(tree,index*2)+_density_tree(tree,index*2+1);
}
// 树的密度 求节点总个数
int density_tree(BinTree* tree)
{
// return _density_tree(tree,1);
// 循环比递归快
int density = 0;
for(int i=0; i<tree->cal; i++)
{
if(EMPTY != tree->arr[i]) density++;
}
return density;
}
// 插入
bool insert_tree(BinTree* tree,TREE_TYPE pdata,TREE_TYPE data)
{
size_t index = 1;
while(index-1 < tree->cal)
{
if(tree->arr[index-1] == pdata)
{
// 扩容
if(index*2-1 >= tree->cal)
{
tree->arr = realloc(tree->arr,tree->cal*2+1);
for(int i=tree->cal; i<(tree->cal*2+1); i++)
tree->arr[i] = EMPTY;
tree->cal = tree->cal*2+1;
}
if(EMPTY == tree->arr[index*2-1])
return tree->arr[index*2-1] = data;
if(EMPTY == tree->arr[index*2])
return tree->arr[index*2] = data;
return false;
}
index++;
}
return false;
}
// 删除 只删除叶子节点
bool del_tree(BinTree* tree,TREE_TYPE data)
{
size_t index = 1;
while(index-1 < tree->cal)
{
if(data == tree->arr[index-1])
{
// 左右子树超出范围,一定是叶子节点
// 如果不超范围,左右都为空也一定是叶子节点
// 也可以用高度判断叶子节点,叶子结点的高度为1,如果高度为其他数则不是
if(index*2-1 < tree->cal && EMPTY != tree->arr[index*2-1])
return false;
if(index*2 < tree->cal && EMPTY != tree->arr[index*2])
return false;
tree->arr[index-1] = EMPTY;
return true;
}
index++;
}
return false;
}
// 求左子树的下标
int left_tree(BinTree* tree,TREE_TYPE data)
{
int index = 1;
while(index-1 < tree->cal)
{
if(tree->arr[index-1] == data)
{
if(index*2-1 < tree->cal && EMPTY != tree->arr[index*2-1])
{
return index*2-1; //左子树的下标
}
return -1;
}
index++;
}
return -1;
}
// 求右
int right_tree(BinTree* tree,TREE_TYPE data)
{
int index = 1;
while(index-1 < tree->cal)
{
if(tree->arr[index-1] == data)
{
if(index*2 < tree->cal && EMPTY != tree->arr[index*2])
{
return index*2;
}
return -1;
}
index++;
}
return -1;
}
// 求根
int root_tree(BinTree* tree,TREE_TYPE data)
{
int index = 1;
while(index-1 < tree->cal)
{
if(tree->arr[index-1] == data)
{
return index-1;
}
index++;
}
return -1;
}
int main(int argc,const char* argv[])
{
BinTree* tree = create_tree("ABCD#EFG###HI",13);
dlr_show(tree);
ldr_show(tree);
lrd_show(tree);
level_show(tree);
printf("\nhigh:%d\n",high_tree(tree));
printf("density:%d\n",density_tree(tree));
printf("left:%d\n",left_tree(tree,'H'));
insert_tree(tree,'G','X');
dlr_show(tree);
del_tree(tree,'A');
dlr_show(tree);
}
链式:
由一个个的节点组成,每个节点就是一棵树
节点数据项:
数据
左子树指针
右子树指针
#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>
#define TREE_TYPE char
#define EMPTY '#'
#define END '\0'
// 设计树的节点
typedef struct TreeNode
{
TREE_TYPE data;
struct TreeNode* left;
struct TreeNode* right;
}TreeNode;
// 创建树的节点
TreeNode* create_tree_node(TREE_TYPE data)
{
TreeNode* node = malloc(sizeof(TreeNode));
node->data = data;
node->left = NULL;
node->right = NULL;
return node;
}
/*TreeNode* create_tree(void)
{
TREE_TYPE data = 0;
scanf("%c",&data);
if(EMPTY == data) return NULL;
TreeNode* node = create_tree_node(data);
node->left = create_tree();
node->right = create_tree();
return node;
}
*/
TreeNode* _create_tree(char** str)//*str的指向一直在被改变,所以要传二级指针,共享这个指针
{
if(EMPTY == **str || END == **str) return NULL;
TreeNode* node = create_tree_node(**str);
*str += 1;
node->left = _create_tree(str);//这里的str已经并定义成二级指针
*str += 1;
node->right = _create_tree(str);
return node;//返回给他的调用者,即上一个指向他的地址
}
// 构建二叉树 调用者按照前序结果来构建
TreeNode* create_tree(char* str)
{
return _create_tree(&str);//指针取地址,即为二级指针
}
// 前序遍历
void dlr_show(TreeNode* root)
{
if(NULL == root) return;
printf("%c ",root->data);
dlr_show(root->left);
dlr_show(root->right);
}
// 中序
void ldr_show(TreeNode* root)
{
if(NULL == root) return;
ldr_show(root->left);
printf("%c ",root->data);
ldr_show(root->right);
}
// 后序
void lrd_show(TreeNode* root)
{
if(NULL == root) return;
lrd_show(root->left);
lrd_show(root->right);
printf("%c ",root->data);
}
// 树的高度
int high_tree(TreeNode* root)
{
if(NULL == root) return 0;
int lh = high_tree(root->left);
int rh = high_tree(root->right);
return lh>rh?lh+1:rh+1;
}
// 树的密度
int density_tree(TreeNode* root)
{
if(NULL == root) return 0;
return 1+density_tree(root->left)+density_tree(root->right);
}
// 求左
TreeNode* left_tree(TreeNode* root,TREE_TYPE data)
{
if(NULL == root) return NULL;
if(root->data == data)
return root->left;
TreeNode* left = left_tree(root->left,data);
if(left) return left;
TreeNode* right = left_tree(root->right,data);
if(right) return right;
return NULL;
}
// 求右
TreeNode* right_tree(TreeNode* root,TREE_TYPE data)
{
if(NULL == root) return NULL;
if(root->data == data)
return root->right;
TreeNode* left = right_tree(root->left,data);
if(left) return left;
TreeNode* right = right_tree(root->right,data);
if(right) return right;
return NULL;
}
// 求根
TreeNode* root_tree(TreeNode* root,TREE_TYPE data);
// 插入
bool insert_tree(TreeNode* root,TREE_TYPE pdata,TREE_TYPE data)
{
if(NULL == root) return false;
if(root->data == pdata)
{
if(NULL == root->left)
return root->left = create_tree_node(data);
if(NULL == root->right)
return root->right = create_tree_node(data);
return false;
}
bool lflag = insert_tree(root->left,pdata,data);
bool rflag = insert_tree(root->right,pdata,data);
return lflag || rflag;
}
// 删除
bool del_tree(TreeNode** root,TREE_TYPE data)
{
if(NULL == *root) return false;
if((*root)->data == data)
{
if((*root)->left || (*root)->right) return false;
free(*root);
*root = NULL;
return true;
}
bool lflag = del_tree(&(*root)->left,data);
bool rflag = del_tree(&(*root)->right,data);
return lflag || rflag;
}
int main(int argc,const char* argv[])
{
TreeNode* root = create_tree("ABDG####CEH##I##F##\0");
/* printf("high:%d\n",high_tree(root));
printf("density:%d\n",density_tree(root));
TreeNode* left = right_tree(root,'C');
printf("%c\n",left->data);
insert_tree(root,'F','X');
insert_tree(root,'F','Y');
*/
del_tree(&root,'H');
del_tree(&root,'A');
ldr_show(root);
}
有序二叉树:
左子树的数据小于根,右子树的数据大于等于根,这种二叉树称为有序二叉树
注意:有序二叉树的中序遍历就是从小到大,所以有序二叉树也是一种排序算法
对有序二叉树的查找又天然是二分查找,所以经常考
注意:由于这种树的节点需要频繁地插入删除,因此不适合用顺序存储

#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>
// 有序二叉树
typedef struct TreeNode
{
int data;
struct TreeNode* left;
struct TreeNode* right;
}TreeNode;
TreeNode* create_tree_node(int data)
{
TreeNode* node = malloc(sizeof(TreeNode));
node->data = data;
node->left = NULL;
node->right = NULL;
return node;
}
void _insert_tree(TreeNode** root,TreeNode* node)
{
if(NULL == *root) //*root指83,即根
{
*root = node;
return;
}
if((*root)->data > node->data)
_insert_tree(&(*root)->left,node);//*root是解一次引用,相当于TreeNode*,是一个指向节点的指针,对指针取地址相当于变成了二级指针
else
_insert_tree(&(*root)->right,node);
}
// 插入 有序二叉树重点是插入
void insert_tree(TreeNode** root,int data)//类似不带头节点的链表,需要二级指针
{
_insert_tree(root,create_tree_node(data));
}
// 前序
void dlr_show(TreeNode* root)
{
if(NULL == root) return;
printf("%d ",root->data);
dlr_show(root->left);
dlr_show(root->right);
}
// 中序
void ldr_show(TreeNode* root)
{
if(NULL == root) return;
ldr_show(root->left);
printf("%d ",root->data);
ldr_show(root->right);
}
// 后序
void lrd_show(TreeNode* root)
{
if(NULL == root) return;
lrd_show(root->left);
lrd_show(root->right);
printf("%d ",root->data);
}
// 查询
bool query_tree(TreeNode* root,int data)
{
if(NULL == root) return false;
if(root->data == data) return true;
if(root->data > data)
return query_tree(root->left,data);
else
return query_tree(root->right,data);
}
// 密度
int density_tree(TreeNode* root)
{
if(NULL == root) return 0;
return 1+density_tree(root->left)+density_tree(root->right);
}
// 高度
int high_tree(TreeNode* root)
{
if(NULL == root) return 0;
int lh = high_tree(root->left);
int rh = high_tree(root->right);
return lh>rh?lh+1:rh+1;
}
// 销毁
void destory_tree(TreeNode* root)
{
if(NULL == root) return;
destory_tree(root->left);
destory_tree(root->right);
free(root);
}
// 按照中序来查找数据
bool _access_tree(TreeNode* root,size_t index,int* count,int* val)
{
if(NULL == root) return false;
_access_tree(root->right,index,count,val);
if((*count)++ == index)
{
*val = root->data;
return true;
}
_access_tree(root->left,index,count,val);
}
// 访问第index小的数
bool access_tree(TreeNode* root,size_t index,int* val)
{
int count = 1;
return _access_tree(root,index,&count,val);
}
// 按值删除
bool del_tree(TreeNode** root,int data)
{
if(NULL == *root) return false;
if((*root)->data == data)//(*root)->
{
TreeNode* temp = *root;
*root = temp->left;
if(temp->right)
_insert_tree(root,temp->right);//需要加判断temp->right 是否为空,若为空加进去会出现段错误
free(temp);
return true;
}
if(data < (*root)->data)
return del_tree(&(*root)->left,data);
else
return del_tree(&(*root)->right,data);
}
/*-------------------------*/
// 镜像树
void mirror_tree(TreeNode* root)
{
if(NULL == root) return false;
TreeNode* temp = root->left;
root->left = root->right;
root->right = temp;
mirror_tree(root->left);
mirror_tree(root->right);
}
bool _is_sym(TreeNode* root1,TreeNode* root2)
{
if(root1->data != root2->data) return false;
if(NULL == root1 && NULL != root2) return false;
if(NULL != root1 && NULL == root2) return false;
if(NULL == root1 && NULL == root2) return true;
bool lflag = _is_sym(root1->left,root2->right);
bool rflag = _is_sym(root1->right,root2->left);
return lflag && rflag;
}
// 对称
bool is_sym(TreeNode* root)
{
return _is_sym(root->left,root->right);
}
// 两课树比较
bool treecmp(TreeNode* r1,TreeNode* r2)
{
if(NULL == r1 && NULL == r2) return true;
if(NULL == r1 && NULL != r2) return false;
if(NULL != r1 && NULL == r2) return true;
if(r1->data != r2->data) return false;
return treecmp(r1->left,r2->left) && treecmp(r1->right,r2->right);
}
// 判断子结构
bool is_son(TreeNode* root1,TreeNode* root2)
{
if(NULL == root1) return false;
if(root1->data == root2->data)
return treecmp(root1,root2);
return is_son(root1->left,root2) || is_son(root1->right,root2);
}
void __add_tail_list(TreeNode** head,TreeNode* node)
{
printf("%d\n",node->data);
if(NULL == *head)
{
*head = node;
}
else
{
(*head)->left->right = node;
node->left = (*head)->left;
}
(*head)->left = node;
// node->right = *head;
}
void _tree_to_list(TreeNode* root,TreeNode** head)
{
if(NULL == root) return;
//按照中序添加进链表
_tree_to_list(root->left,head);
__add_tail_list(head,root);
_tree_to_list(root->right,head);
}
// 有序二叉树转有序双向链表
TreeNode* tree_to_list(TreeNode* root)
{
// 不带头节点的链表
TreeNode* head = NULL;
_tree_to_list(root,&head);
head->left->right = head;
return head;
}
void z_show(TreeNode* root)
{
ListStack* s1 = create_list_stack();
ListStack* s2 = create_list_stack();
// 根入s1
push_list_stack(s1,root);
while(!empty_list_stack(s1) || !empty_list_stack(s2))
{
while(!empty_list_stack(s1))
{
TreeNode* node = top_list_stack(s1);
pop_list_stack(s1);
printf("%d ",node->data);
if(node->left) push_list_stack(s2,node->left);
if(node->right) push_list_stack(s2,node->right);
}
while(!empty_list_stack(s2))
{
TreeNode* node = top_list_stack(s2);
pop_list_stack(s2);
printf("%d ",node->data);
if(node->right) push_list_stack(s1,node->right);
if(node->left) push_list_stack(s1,node->left);
}
}
}
int main(int argc,const char* argv[])
{
TreeNode* root = NULL;
for(int i=0; i<10; i++)
{
int data = rand()%100;
insert_tree(&root,data);
}
z_show(root);
/*dd
TreeNode* head = tree_to_list(root);
TreeNode* n = head;
do{
printf("%d ",n->data);
n = n->right;
}while(n!=head);
TreeNode* root2 = NULL;
insert_tree(&root2,93);
insert_tree(&root2,86);
insert_tree(&root2,92);
printf("flag=%d\n",is_son(root,root2));
printf("\n");
ldr_show(root);
printf("\n%d\n",query_tree(root,93));
int num = 0;
access_tree(root,100,&num);
printf("num=%d\n",num);
*/
}
作业:
1、把一棵二叉树转换成它的镜像树
// 镜像树
void mirror_tree(TreeNode* root)
{
if(NULL == root) return false;
TreeNode* temp = root->left;
root->left = root->right;
root->right = temp;
mirror_tree(root->left);
mirror_tree(root->right);
}
2、输入两棵二叉树A,B,判断B是不是A的子结构(约定空树不是任何树的子结构)
// 两课树比较
bool treecmp(TreeNode* r1,TreeNode* r2)
{
if(NULL == r1 && NULL == r2) return true;
if(NULL == r1 && NULL != r2) return false;
if(NULL != r1 && NULL == r2) return true;
if(r1->data != r2->data) return false;
return treecmp(r1->left,r2->left) && treecmp(r1->right,r2->right);
}
// 判断子结构
bool is_son(TreeNode* root1,TreeNode* root2)
{
if(NULL == root1) return false;
if(root1->data == root2->data)
return treecmp(root1,root2);
return is_son(root1->left,root2) || is_son(root1->right,root2);
}
3、将一棵有序二叉树转换为有序的双向链表
void __add_tail_list(TreeNode** head,TreeNode* node)//尾添加
{
printf("%d\n", node->data);
if(NULL == head)
{
*head = node;
}
else
{
(*head)->left->right = node;
node->left=(*head)->left;
}
(*head)->left = node;
}
void _tree_to_list(TreeNode* root,TreeNode** head)
{
if(NULL == root) return;
_tree_to_list(root->left,head);
__add_tail_list(head,node)'
_tree_to_list(root->right,head);
}
// 有序二叉树转有序双向链表
TreeNode* tree_to_list(TreeNode* root)
{
TreeNode* head = NULL;
_tree_to_list(root,*head);
head->left->right = head;
return head;
}
4、计算出有序二叉树的第K大的值是多少
5、判断一棵二叉树是否对称的
bool _is_sym(TreeNode* root1,TreeNode* root2)
{
if(root1->data != root2->data) return false;
if(NULL == root1 && NULL !=root2) return flase;
if(NULL != root1 && NULL == root2) return false;
if(NULL == root1 && NULL == root2) return true;
int lflag = _is_sym(root1->left,root2->right);
int rflag = _is_sym(root1->right,root2->left);
return lflag && rflag;
}
6、请实现一个函数,该函数按照Z字型遍历打印二叉树,即第一行从左到右打印节点,第二行从右
void z_show(TreeNode* root)
{
ListStack* s1 = create_list_stack();
ListStack* s2 = create_list_stack();
// 根入s1
push_list_stack(s1,root);
while(!empty_list_stack(s1) || !empty_list_stack(s2))
{
while(!empty_list_stack(s1))
{
TreeNode* node = top_list_stack(s1);
pop_list_stack(s1);
printf("%d ",node->data);
if(node->left) push_list_stack(s2,node->left);
if(node->right) push_list_stack(s2,node->right);
}
while(!empty_list_stack(s2))
{
TreeNode* node = top_list_stack(s2);
pop_list_stack(s2);
printf("%d ",node->data);
if(node->right) push_list_stack(s1,node->right);
if(node->left) push_list_stack(s1,node->left);
}
}
}
线索二叉树:
规律:在有n个节点的链式二叉树中,必定存在n+1个空指针域
链式二叉树中有很多的空指针域,可以让这些右指针指向下一个节点,这样在遍历树时可以不用递归而是使用循环,可以提高树的遍历速度
中序线索二叉树节点数据项:
数据
左子树指针
右子树指针
右子树指针标志 (假表示指向的是右子树,真表示指向下一个节点)
实现过程:
1、按照中序遍历过程创建线索
2、按照线索来进行遍历(循环)
#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>
// 线索二叉树
typedef struct TreeNode
{
int data;
struct TreeNode* left;
struct TreeNode* right;
bool rclue; //为真时右子树为线索
}TreeNode;
TreeNode* create_node(int data)
{
TreeNode* node = malloc(sizeof(TreeNode));
node->data = data;
node->left = NULL;
node->right = NULL;
node->rclue = false;
return node;
}
void _insert_tree(TreeNode** root,TreeNode* node)
{
if(NULL == *root)
{
*root = node;
return;
}
if(node->data < (*root)->data)
_insert_tree(&(*root)->left,node);
else
_insert_tree(&(*root)->right,node);
}
void insert_tree(TreeNode** root,int data)
{
_insert_tree(root,create_node(data));
}
void ldr_show(TreeNode* root)
{
if(NULL == root) return;
ldr_show(root->left);
printf("%d ",root->data);
ldr_show(root->right);
}
// 上一个节点
TreeNode* prev = NULL;
// 按照中序遍历,并创建线索
void create_clue(TreeNode* root)
{
if(NULL == root) return;//return跳出此时的create函数
// 左
create_clue(root->left);
// 根
// 通过中序遍历找到root的上一个节点prve,如果该节点存在,并且右子树为空,则右子树指向下一个节点root
if(NULL != prev && NULL == prev->right)
{
prev->right = root;
prev->rclue = true;
}
// 处理完后,root就成为了上一个节点
prev = root;
// 右
create_clue(root->right);
}
// 按照线索进行遍历
void clue_show(TreeNode* root)
{
while(root)
{
while(root->left)
root = root->left;
printf("%d ",root->data);
while(root->rclue)
{
root = root->right;
printf("%d ",root->data);
}
root = root->right;
}
}
int main(int argc,const char* argv[])
{
TreeNode* root = NULL;
for(int i=0; i<10; i++)
{
int data = rand()%100;
printf("%d ",data);
insert_tree(&root,data);
}
printf("\n");
// ldr_show(root); 按照递归遍历
create_clue(root);
clue_show(root);//按照循环遍历
}
选择树:(胜者树、败者树)
是一种完全二叉树,把所有要比较的数据放在最后一层,根节点是左右子树中的一个,是它们的最大或最小,选择树的功能是快速地找出其中的最大或者最小值
堆:
是一种完全二叉树,不适合链式存储
大顶堆(大根堆):
根节点比左右子树大
小顶堆(小根堆):
根节点比左右子树小
数据项:
存储节点的内存的首地址
容量
数量
运算:
创建、添加、删除、堆满、堆空、堆顶
堆可以实现优先队列的效果
#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>
#define swap(a,b) {typeof(a) (t)=(a);(a)=(b);(b)=(t);}//typeof(int*) a,b;等价于: int *a,*b;
#define TYPE int
typedef struct Heap
{
TYPE* arr;
size_t cal;
size_t cnt;
}Heap;
// 创建
Heap* create_heap(size_t cal)
{
Heap* heap = malloc(sizeof(Heap));
heap->arr = malloc(sizeof(TYPE)*cal);
heap->cal = cal;
heap->cnt = 0;
return heap;
}
// 空堆
bool empty_heap(Heap* heap)
{
return !heap->cnt;
}
// 满堆
bool full_heap(Heap* heap)
{
return heap->cnt == heap->cal;
}
// 添加
bool add_heap(Heap* heap,TYPE data)//这里heap不要二级指针,这是顺序的,指针指向不会改变,顺序是定好的
{
if(full_heap(heap)) return false;
heap->arr[heap->cnt++] = data;
// 调整成大顶堆
// 编号
int i = heap->cnt;
while(i > 1)
{
if(heap->arr[i-1] > heap->arr[i/2-1])
{
swap(heap->arr[i-1],heap->arr[i/2-1]);
}
i = i/2;
}
return true;
}
// 删除
bool del_heap(Heap* heap)
{
if(empty_heap(heap)) return false;
// 交换堆顶和末尾
swap(heap->arr[0],heap->arr[heap->cnt-1]);
// 删除末尾
heap->cnt--;
// 从上到下,调整成大顶堆
int i = 1;
while(i-1 < heap->cnt)
{
// 右子树存在,比较左右子树,左子树中放左右的最大值
if(i*2 < heap->cnt && heap->arr[i*2] > heap->arr[i*2-1])
{
swap(heap->arr[i*2],heap->arr[i*2-1]);
}
// 左子树存在,比较根与左子树,根放最大值
if(i*2-1 < heap->cnt && heap->arr[i*2-1] > heap->arr[i-1])
{
swap(heap->arr[i*2-1],heap->arr[i-1]);
i = i*2;
}
else
{
break;
}
}
return true;
}
// 堆顶
TYPE top_heap(Heap* heap)
{
return heap->arr[0];
}
// 遍历
void show_heap(Heap* heap)
{
for(int i=0; i<heap->cal; i++)
{
printf("%d ",heap->arr[i]);
}
printf("\n");
}
// 堆排序
void sort_heap(int* arr,int len)
{
// 把数组调整成堆结构
for(int i=len; i>1; i--)
{
int j = i;
while(1 < j)
{
if(arr[j-1] > arr[j/2-1]) swap(arr[j-1],arr[j/2-1]);
j = j/2;
}
}
// 删除堆顶,并从上到下调整,直到堆为空
while(len > 1)
{
swap(arr[0],arr[len-1]);
len--;
int i = 1;
while(i-1 < len)
{
if(i*2 < len && arr[i*2] > arr[i*2-1])
swap(arr[i*2],arr[i*2-1]);
if(i*2-1 < len && arr[i*2-1] > arr[i-1])
swap(arr[i*2-1],arr[i-1]);
i = i*2;
}
}
}
int main(int argc,const char* argv[])
{
int arr[10] = {};
for(int i=0; i<10; i++)
{
arr[i] = rand()%100;
}
sort_heap(arr,10);
for(int i=0; i<10; i++)
{
printf("%d ",arr[i]);
}
/*
Heap* heap = create_heap(10);
for(int i=0; i<10; i++)
{
int data = rand()%100;
add_heap(heap,data);
}
//show_heap(heap);
for(int i=0; i<10; i++)
{
printf("top:%d\n",top_heap(heap));
del_heap(heap);
}
*/
}
让下标与编号相等版
#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>
#define TYPE int
#define swap(a,b) {typeof(a) t=(a);(a)=(b);(b)=t;}
typedef struct Heap
{
TYPE* arr;
size_t cal;
size_t cnt;
}Heap;
Heap* create_heap(size_t cal)
{
Heap* heap = malloc(sizeof(Heap));
// 第一个位置不使用,让下标与编号相等
heap->arr = malloc(sizeof(TYPE)*(cal+1));
heap->cal = cal;
heap->cnt = 0;
return heap;
}
// 销毁堆
void destory_heap(Heap* heap)
{
free(heap->arr);
free(heap);
}
// 堆空
bool empty_heap(Heap* heap)
{
return !heap->cnt;
}
// 堆满
bool full_heap(Heap* heap)
{
return heap->cnt == heap->cal;
}
// 添加
bool add_heap(Heap* heap,TYPE data)
{
if(full_heap(heap)) return false;
heap->arr[++heap->cnt] = data;
// 从下到上调整
int i = heap->cnt;
while(i > 1)
{
if(heap->arr[i] > heap->arr[i/2])
swap(heap->arr[i],heap->arr[i/2]);
i = i/2;
}
return true;
}
// 删除
bool del_heap(Heap* heap)
{
if(empty_heap(heap)) return false;
swap(heap->arr[1],heap->arr[heap->cnt]);
heap->cnt--;
// 从上到下调整
int i = 1;
while(i <= heap->cnt)
{
if(i*2+1 <= heap->cnt && heap->arr[i*2+1] > heap->arr[i*2])
swap(heap->arr[i*2+1],heap->arr[i*2]);
if(i*2 <= heap->cnt && heap->arr[i*2] > heap->arr[i])
swap(heap->arr[i*2],heap->arr[i]);
i = i*2;
}
return true;
}
// 遍历
void show_heap(Heap* heap)
{
for(int i=1; i<=heap->cal; i++)
{
printf("%d ",heap->arr[i]);
}
printf("\n");
}
int main(int argc,const char* argv[])
{
Heap* heap = create_heap(10);
for(int i=0; i<10; i++)
{
int data = rand()%100;
add_heap(heap,data);
}
do{
show_heap(heap);
}while(del_heap(heap));
}
平衡二叉树:
前提是有序的二叉树,它的左右子树高度相差不超过1,它所有的子树也要满足这个要求
如果一个有序的二叉树呈单支状(接近单支),它的效率接近链表,查找、访问效率较低,因此当达到平衡时它的效率才最高
由于节点的位置受到值的影响,因此只能调整,而不能强行修改
二叉树不平衡的基础原因:
x y
/ \ / \
y t1 以y为轴向右旋转 z x
/ \ / \ / \
z t2 t3 t4 t2 t1
/ \
t3 t4
x y
/ \ 以y为轴向左旋转 / \
t1 y x z
/ \ / \ / \
t2 z t1 t2 t3 t4
/ \
t3 t4
x x z
/ \ / \ / \
y t1 z t1 y x
/ \ / \ / \ / \
t2 z y t4 t2 t3 t4 t1
/ \ / \
t3 t4 t2 t3
以z为轴向左旋转 以z为轴向右旋转 最终达到平衡
x x z
/ \ / \ / \
t1 y t1 z x y
/ \ / \ / \ / \
z t2 t3 y t1 t3 t4 t2
/ \ / \
t3 t4 t4 t2
以z为轴向右旋转 以z为轴向左旋转 最终达到平衡
#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>
typedef struct TreeNode
{
int data;
struct TreeNode* left;
struct TreeNode* right;
}TreeNode;
TreeNode* create_node(int data)
{
TreeNode* node = malloc(sizeof(TreeNode));
node->data = data;
node->left = NULL;
node->right = NULL;
return node;
}
// 求高度
int high_tree(TreeNode* root)
{
if(NULL == root) return 0;
int lh = high_tree(root->left);
int rh = high_tree(root->right);
return lh > rh ? lh+1 : rh+1;
}
// 求左右子树的高度差,以此检查左右子树是否平衡
int is_balance(TreeNode* root)
{
return high_tree(root->left) - high_tree(root->right);
}
// 左旋转
TreeNode* left_rotate(TreeNode* x)
{
TreeNode* y = x->right;
TreeNode* t2 = y->left;
y->left = x;
x->right = t2;
return y;
}
// 右旋转
TreeNode* right_rotate(TreeNode* x)
{
TreeNode* y = x->left;
TreeNode* t2 = y->right;
y->right = x;
x->left = t2;
return y;
}
// 自动平衡
TreeNode* auto_balance(TreeNode* x)
{
if(NULL == x) return NULL;
int lh = high_tree(x->left);
int rh = high_tree(x->right);
if(lh - rh >1)
{
// 左子树高
// y的左子树高于等于右子树
if(is_balance(x->left) >= 0)
{
// 右旋转
x = right_rotate(x);
}
else
{
// 左旋转
x->left = left_rotate(x->left);
// 右旋转
x = right_rotate(x);
}
}
if(rh - lh >1)
{
// 右子树高
// y的右子树高于等于左子树
if(is_balance(x->right) <= 0)
{
// 左旋转
x = left_rotate(x);
}
else
{
// 右旋转
x->right = right_rotate(x->right);
// 左旋转
x = left_rotate(x);
}
}
return x;
}
// 插入
TreeNode* insert_tree(TreeNode* root,int data)
{
if(NULL == root)
return create_node(data);
if(data < root->data)
root->left = insert_tree(root->left,data);
else
root->right = insert_tree(root->right,data);
// 调整成平衡
root = auto_balance(root);
return root;
}
// 中序
void ldr_show(TreeNode* root)
{
if(NULL == root) return;
ldr_show(root->left);
printf("%d ",root->data);
ldr_show(root->right);
}
// 前序
void dlr_show(TreeNode* root)
{
if(NULL == root) return;
printf("%d ",root->data);
dlr_show(root->left);
dlr_show(root->right);
}
/*
删除节点
1、待删除的节点是叶子节点,直接删除
2、待删除的节点左或者右子树为空,则使用非空节点替换
3、待删除的节点左右子树非空,根据左右子树的高度,选择左边的最大节点替换,或者右边的最小节点替换
*/
TreeNode* max_tree(TreeNode* root)
{
TreeNode* max = root;
while(max->right) max = max->right;
return max;
}
TreeNode* min_tree(TreeNode* root)
{
TreeNode* min = root;
while(min->left) min = min->left;
return min;
}
TreeNode* del_tree(TreeNode* root,int data)
{
if(NULL == root) return NULL;
if(root->data == data)
{
// 1、左右子树都为空
if(NULL == root->left && NULL == root->right)
{
free(root);
return NULL;
}
// 2、左子树为空,替换为右子树
if(NULL == root->left)
{
TreeNode* temp = root->right;
free(root);
return temp;
}
// 3、右子树为空,替换为左子树
if(NULL == root->right)
{
TreeNode* temp = root->left;
free(root);
return temp;
}
int lh = high_tree(root->left);
int rh = high_tree(root->right);
if(lh >= rh)
{
// 获取左子树最大节点
TreeNode* node = max_tree(root->left);
// 替换值
root->data = node->data;
// 删除node
root->left = del_tree(root->left,root->data);
}
else
{
// 获取右子树最小节点
TreeNode* node = min_tree(root->right);
root->data = node->data;
root->right = del_tree(root->right,root->data);
}
return root;
}
if(data < root->data)
root->left = del_tree(root->left,data);
else
root->right = del_tree(root->right,data);
// 调整平衡
root = auto_balance(root);
return root;
}
int main(int argc,const char* argv[])
{
TreeNode* root = NULL;
for(int i=0; i<10; i++)
{
int data = rand()%100;
root = insert_tree(root,i);//需要用root接一下,因为这里调用的是一级指针,如果不接root不改变
}
root = del_tree(root,2);
root = del_tree(root,0);
printf("high:%d\n",high_tree(root));
ldr_show(root);
printf("\n");
dlr_show(root);
}
红黑树:
是一种自平衡的树,它的平衡不是根据子树的高度来调整的,而是给节点设置一个颜色,来达到平衡。
优点:插入和删除的效率,比AVL树要高
缺点:没有AVL树均匀,所以查找、访问效率没有AVL树高
规则:
1.节点是红色或黑色
2.根节点是黑色。
3.每个叶子节点都是黑色的空节点(NIL节点)。
4 每个红色节点的两个子节点都是黑色。(从每个叶子到根的所有路径上不能有两个连续的红色节点)
5.从任一节点到其每个叶子的所有路径都包含相同数目的黑色节点。