数据结构:树

#个人自用

目录

树(常规)

双亲表示法 / 父亲表示法

孩子表示法

孩子兄弟表示法 / 二叉存储法

二叉树

性质

存储

遍历

层序遍历

深度优先遍历

先序遍历

中序遍历

后序遍历

线索化

二叉排序树(BST)

AVL树

结构体组成

功能函数

初始化

四种失衡调整函数

操作

算法

并查集

哈夫曼树


概念:树是存储具有一对多关系的数据的逻辑结构,其在形状上表现为一棵倒着的树
在树中,用结点/节点存储数据(不区分结点与节点的区别)

节点的分类:
    根节点:没有前驱节点
    叶子节点:没有后继节点
    内部节点:其余节点

度/阶:表示节点分了多少个叉
节点的度:该节点子数目/分叉数目
树的度:max(所有节点的度)
假如树的度是n,则称树为n叉树

树的高度和深度:在数值上是一样的

存储结构:数据 + 关系

树(常规)

双亲表示法 / 父亲表示法

存储方式:顺序存储方式存数据 => 数组

        p.s.存数据时,存父亲下标

优点:方便找父亲

缺点:不方便找孩子

结构体组成:数据 + 父亲节点的下标

struct node {
	int data;
	int fi;//父 father_i,若fi==-1,认为是根节点
}t[1000];
int size;//数据数量

初始化

//初始化
void init(int r)
{
	//加入根节点
	t[1].data = r;
	t[1].fi = -1;
	size = 1;
}

 操作

//查找数的下标
int find(int fx)
{
	for (int i = 1; i <= size; i++)
	{
		if (t[i].data == fx)
			return i;
	}
	return -1;
}
//增
void insert(int x, int fx)
{
	size++;
	t[size].data = x;
	int fx_i = find(fx);
	if (fx_i == -1)
		printf("父不存在\n");
	else
	{
		t[size].fi = fx_i;
	}
}
//找父
void find_fa(int x)
{
	int x_i = find(x);
	int fa_i = t[x_i].fi;
	if (fa_i == -1)
		printf("该节点是根节点\n");
	else
		printf("%d\n", t[fa_i]);
}
//找子
void find_ch(int x)
{
	int x_i = find(x);
	int sum = 0;
	for (int i = 1; i <= size; i++)
	{
		if (t[i].fi == x_i)
		{
			sum++;
			printf("%d ", t[i].data);
		}
	}
	if (sum == 0)
		printf("无子");
	printf("\n");
}

参考语言

int main()
{
	int n;//节点总数
	scanf("%d", &n);
	int root;//根节点数据
	scanf("%d", &root);
	init(root);
	int x, fx;//fx是x的父
	for (int i = 2; i <= n; i++)
	{
		scanf("%d%d", &x, &fx);
		insert(x, fx);
	}
	scanf("%d", &x);
	find_ch(x);
	find_fa(x);
}
孩子表示法

存储方式:顺序存储方式存数据 => 数组

p.s.链表来存储子数据

结构体组成:

        树:数据 + 孩子节点

        孩子链表:数据 + 下一个孩子

//孩子链表的节点结构
typedef struct chNode {
	int data;
	struct chNode* next;
}chNode;
//树的结构
struct Tree {
	int data;
	chNode* first;
}t[1000];
int size;//数据数量

初始化

//初始化
void init(int r, int n)
{
	t[1].data = r;
	size = 1;
	t[1].first = NULL;
}

操作

//查找数的下标
int find(int x)
{
	for (int i = 1; i <= size; i++)
	{
		if (t[i].data == x)
			return i;
	}
	return -1;
}

//增
void insert(int x, int fx)
{
	size++;
	t[size].data = x;//存放数据
	t[size].first = NULL;
	//建立关系
	int fx_i = find(fx);
	if (fx_i != -1)
	{
		chNode* s = (chNode*)malloc(sizeof(chNode));
		s->data = x;
		//采用头插法
		s->next = t[fx_i].first;
		t[fx_i].first = s;
	}
}

//找父
void find_fa(int x)
{
	chNode* s = NULL;
	int flag = 0;
	for (int i = 1; i <= size; i++)
	{
		s = t[i].first;
		while (s != NULL)
		{
			if (s->data == x)
			{
				printf("%d\n", t[i].data);
				flag = 1;
				return;
			}
			s = s->next;
		}
	}
	if (flag == 0)
		printf("无父,为根节点\n");
}

//找子
void find_ch(int x)
{
	int x_i = find(x);
	chNode* s = t[x_i].first;
	if (s == NULL)
		printf("无子\n");
	else
	{
		while (s != NULL)
		{
			printf("%d ", s->data);
			s = s->next;
		}
	}
}

参考语言

int main()
{
	int n;//节点总数
	scanf("%d", &n);
	int root;//根节点数据
	scanf("%d", &root);
	init(root, n);
	int x, fx;//fx是x的父
	for (int i = 2; i <= n; i++)
	{
		scanf("%d%d", &x, &fx);
		insert(x, fx);
	}
	scanf("%d", &x);
	find_ch(x);
	find_fa(x);
}
孩子兄弟表示法 / 二叉存储法

存储方式:二叉链表

结构体组成:数据 + 第一个孩子节点 + 右边第一个亲兄弟节点

typedef struct Node {
	int data;
	struct Node* first;//指向第一个孩子
	struct Node* bro;//指向右边第一个亲兄弟
}tNode, * TreeList;

初始化

//初始化
TreeList init(int root)
{
	TreeList r = (tNode*)malloc(sizeof(tNode));
	if (r != NULL)
	{
		r->data = root;
		r->first = r->bro = NULL;
		return r;
	}
}

操作

//查找
//1.与根节点对比,若无,则2
//2.在子代找 or 在兄弟中找
tNode* find(TreeList r, int fx)
{
	tNode* ans = NULL;
	if (r == NULL)
		return NULL;
	if (r->data == fx)
		return r;
	/*if (r == NULL || r->data == fx)
		return r;*/
	if (r->first != NULL)//在子代中找
	{
		ans = find(r->first, fx);
		if (ans != NULL && ans->data == fx)
			return ans;
	}
	if (r->bro != NULL)//在兄弟中找
	{
		ans = find(r->bro, fx);
		if (ans != NULL && ans->data == fx)
			return ans;
	}
	return NULL;//若不存在fx,则返回NULL
}
//插入
void insert(TreeList r, int x, int fx)
{
	tNode* f = find(r, fx);
	if (f != NULL)
	{
		tNode* s = (tNode*)malloc(sizeof(tNode));
		s->data = x;
		s->first = NULL;
		if (f->first==NULL)//s是长子
		{
			f->first = s;
			s->bro = NULL;
		}
		else
		{
			//头插
			s->bro = f->first;
			f->first = s;
		}
	}
	else
	{
		printf("该父节点不存在,插入失败\n");
		return;
	}
}
//找子
void find_ch(TreeList r, int x)
{
	tNode* p = find(r, x);
	tNode* fir = p->first;
	if (fir == NULL)
		printf("无子\n");
	else
	{
		p = fir;
		while (p != NULL)
		{
			printf("%d\n", p->data);
			p = p->bro;
		}
	}
}
//找父
//改结构:加一个指向父亲节点的指针

参考语言

int main()
{
	int n;//节点个数
	int root;//根节点数据
	TreeList r = NULL;
	scanf("%d%d", &n, &root);
	r = init(root);
	int x, fx;//子数据,父数据
	for (int i = 02; i <= n; i++)
	{
		scanf("%d%d", &x, &fx);
		insert(r, x, fx);
	}
	scanf("%d", &x);
	find_ch(r, x);
}

二叉树

性质

 p.s.默认根节点是第一层

1)在二叉树的第i层,最多有2^(i-1)个节点

2)深度为k的二叉树,最多有(2^k)-1个节点

3)在一棵二叉树中,叶子节点的数目 比 度为2的节点数目 多一个
    度为0的节点/叶子节点:n0
    度为1的节点:n1
    度为2的节点:n2
    (1)节点总数:n=n0+n1+n2;
    (2)节点总数:n=1 + 0*n0+1*n1+2*n2(孩子节点)
∴n0=n2+1,得证

4)具有n个节点的 完全二叉树 的深度/高度为logn+1 (其中logn向下取整)(若log底数不给出,默认为2)
   具有n个节点的 完全二叉树 的深度/高度为log(n+1) (向上取整)
    假设完全二叉树深度为k,k=logn+1(其中logn向下取整)
    1+(2^(k-1))-1 <= n < (2^k)-1+1
    2^(k-1) <= n < (2^k)
    k-1 <= logn < k
∴k=logn+1(其中logn向下取整)得证
同理, (2^(k-1))-1 < n <= (2^k)-1
    2^(k-1) < n+1 <= (2^k)
    k-1 < log(n+1) <= k
∴k=log(n+1)(其中logn向上取整)得证

5)对于一颗 完全二叉树 进行编号,编号规则:根节点是1,然后从上到下,从左到右。
除根节点,节点i的父亲节点编号为i/2(向下取整)。
除叶子节点,节点i的左孩子节点编号为2*i,右孩子节点编号为2*i+1
补充:任何一个二叉树都可以加NULL节点,从而补成一颗完全二叉树
=>存储方式:顺序存储(基于数组)(缺点:存非完全二叉树,会有空间浪费)

两种特殊的二叉树:

1)满二叉树:深度为k,且节点个数是(2^k)-1的二叉树

2)完全二叉树:对满二叉树,从下向上,从右到左一次删除若干个节点,剩下的树就能成为完全二叉树
p.s.满二叉树是完全二叉树

存储

存储方式:

1.顺序存储,基于数组:存非完全二叉树,会有空间浪费

        a[i]父亲:a[i/2]
        a[i]左孩子:a[2*i]
        a[i]右孩子:a[2*i+1]

2.基于二叉链表

结构体组成:数据 + 左孩子节点 + 右孩子节点

typedef struct BTNODE {
	int data;
	struct BTNODE* left;
	struct BTNODE* right;
}BTNode, * BTTree;
int flag;//0为左孩子,1为右孩子

初始化

//初始化
BTTree init(int x)
{
	BTTree r = (BTNode*)malloc(sizeof(BTNode));
	if (r == NULL)
		printf("分配失败\n");
	else
	{
		r->data = x;
		r->left = r->right = NULL;
	}
	return r;
}

操作

//查找-递归
BTNode* find(BTTree r, int fx)
{
	if (r == NULL || r->data == fx)
		return r;
	if (r->left != NULL)
	{
		BTNode* f = find(r->left, fx);
		if (f != NULL && f->data == fx)
			return f;
	}
	if (r->right != NULL)
	{
		BTNode* f = find(r->right, fx);
		if (f != NULL && f->data == fx)
			return f;
	}
	return NULL;
}
//插入
void insert(BTTree r, int x, int fx, int flag)
{
	BTNode* f = find(r, fx);
	if (f == NULL)
		printf("父亲节点不存在\n");
	else
	{
		BTTree s = (BTTree)malloc(sizeof(BTNode));
		s->data = x;
		s->left = s->right = NULL;
		if (flag == 0)
			f->left = s;
		else
			f->right = s;
	}
}
//找子
void find_ch(BTTree r, int x)
{
	BTNode* p = find(r, x);
	if (p != NULL)
	{
		if (p->left != NULL)
			printf("左:%d\n", p->left->data);
		if (p->right != NULL)
			printf("右:%d\n", p->right->data);
	}
}
//找父
//加父亲指针

参考语言

int main()
{
	int n, x;
	scanf("%d", &n);//输入节点个数n
	BTTree r = NULL;
	scanf("%d", &x);//根节点x
	r = init(x);
	for (int i = 2; i <= n; i++)
	{
		int fx;
		scanf("%d %d %d", &x, &fx, &flag);
		insert(r, x, fx, flag);
	}
	scanf("%d", &x);
	find_ch(r, x);
}
遍历

概念:沿着某条路径,对树中的节点都遍历(访问)一次,有且仅遍历一次

由于树是非线性结构,每个节点有两个后继节点。当访问完一个节点后,会有不同的选择,可以得到不同的访问顺序。

层序遍历

遍历顺序:从上往下,依次输出每一层(从左往右)的节点

实现:当访问一个节点时,存下该节点的孩子,之后进行遍历时,直接去存放的地方找
        声明队列 -> 根节点入队 -> 循环 -> 队列空,结束

建立链式队列

//链式队列
typedef struct QNode {
	int data;
	struct QNode* next;
}qNode, * qLink;
qLink front, rear;//队头指针&队尾指针
//初始化
void initQNode()
{
	qLink q = (qNode*)malloc(sizeof(qNode));
	if (q == NULL)
		printf("队列分配失败\n");
	else
	{
		front = rear = q;
		front->next = NULL;
	}
	return;
}
//入队
void enQueue(int x)
{
	qNode* s = (qNode*)malloc(sizeof(qNode));
	s->data = x;
	s->next = NULL;//新节点插入到链尾
	rear->next = s;
	rear = s;
}
//判空
int empty()
{
	if (front->next == NULL)
		return 1;//空
	return 0;//非空
}
//出队
int deQueue()
{
	if (!empty())//判空
	{
		qNode* q = front->next;//队头
		front->next = q->next;
		int x = q->data;//保留出队元素
		if (front->next == NULL)//若原队列有且仅有一个结点,则删除变空,需要处理尾指针
			rear = front;
		free(q);
		q = NULL;
		return x;
	}
	else
		printf("队空\n");
}

层序遍历

//层序遍历
void levelunderBTTree(BTTree r)
{
	int x;
	BTNode* q = NULL;
	initQNode();//初始化队列
	if (r == NULL)
	{
		printf("空树\n");
		return;
	}
	enQueue(r->data);//根节点数据入队
	while (!empty())
	{
		x = deQueue();
		printf("%d\n", x);
		q = find(r, x);
		if (q->left != NULL)
			enQueue(q->left->data);
		if (q->right != NULL)
			enQueue(q->right->data);
	}

}

参考语言(存储部分见上)

int main()
{
	int n, x;
	scanf("%d", &n);//输入节点个数n
	BTTree r = NULL;
	scanf("%d", &x);//根节点x
	r = initBT(x);
	for (int i = 2; i <= n; i++)
	{
		int fx;
		scanf("%d %d %d", &x, &fx, &flag);
		insert(r, x, fx, flag);
	}
	levelunderBTTree(r);
}
深度优先遍历

非递归版:栈的构建

//整个节点入栈
typedef struct stackNode {
	struct BTNode* data;
	struct stackNode* next;
}mystack;
mystack* s = NULL;
//初始化
void initStack()
{
	s = (mystack*)malloc(sizeof(mystack));
	if (s != NULL)
		s->next = NULL;
}
//入栈
void ppush(BTNode* k)
{
	mystack* p = (mystack*)malloc(sizeof(mystack));
	if (p == NULL)
	{
		printf("分配失败\n");
		return;
	}
	p->data = k;
	p->next = s->next;
	s->next = p;
}
//判空
int empty()
{
	if (s->next == NULL)
		return 1;
	return 0;
}
//出栈:返回栈顶元素,将其在栈中删除
BTNode* ppop()
{
	if (empty())
	{
		printf("栈空\n");
		return NULL;
	}
	BTNode* k = s->next->data;
	mystack* p = s->next;
	s->next = p->next;
	free(p);
	p = NULL;
	return k;
}
先序遍历

遍历顺序:根  左子树  右子树

1)递归版

//先序遍历
void perOrder(BTTree r)
{
	if (r == NULL)
		return;
	printf("%d ", r->data);
	if (r->left != NULL)
		perOrder(r->left);
	if (r->right != NULL)
		perOrder(r->right);
}

2)非递归版:栈

//先序遍历
void PerOrder(BTTree r)
{
	initStack();
	if (r == NULL)
		return;
	ppush(r);
	while (!empty())
	{
		BTNode* node = ppop();
		printf("%d ", node->data);
		if (node->right != NULL)
			ppush(node->right);
		if (node->left != NULL)
			ppush(node->left);
	}
}

中序遍历

遍历顺序:左子树  根  右子树

1)递归版

//中序遍历
void inOrder(BTTree r)
{
	if (r == NULL)
		return;
	if (r->left != NULL)
		inOrder(r->left);
	printf("%d ", r->data);
	if (r->right != NULL)
		inOrder(r->right);
}

2)非递归版:栈

//中序遍历
void InOrder(BTTree r)
{
	initStack();
	if (r == NULL)
		return;
	BTNode* node = r;
	BTNode* k = NULL;
	while (!empty() || node != NULL)
	{
		if (node != NULL)//向左
		{
			ppush(node);
			node = node->left;
		}
		else
		{
			k = ppop();//取出栈顶
			printf("%d ", k->data);
			node = k->right;
		}
	}
}
后序遍历

遍历顺序:左子树  右子树  根

1)递归版

//后序遍历
void postOrder(BTTree r)
{
	if (r == NULL)
		return;
	if (r->left != NULL)
		postOrder(r->left);
	if (r->right != NULL)
		postOrder(r->right);
	printf("%d ", r->data);
}

2)非递归版:栈

//后序遍历
void PostOrder(BTTree r)
{
	initStack();
	if (r == NULL)
		return;
	BTNode* node = r;
	BTNode* pre = NULL;
	BTNode* k = NULL;
	while (!empty() || node != NULL)
	{
		if (node != NULL)//向左
		{
			ppush(node);
			node = node->left;
		}
		else
		{
			k = s->next->data;//栈顶元素
			if (k->right != NULL && pre != k->right)
				node = k->right;
			else
			{
				k = ppop();
				printf("%d ", k->data);
				pre = k;
				node = NULL;//返回上一个父亲节点
			}
		}
	}
}
线索化

若一个节点的左孩子指针域为空,则将该指针域指向其前驱节点;
若一个节点的右孩子指针域为空,则将该指针域指向其后继节点;

优点:加快了找前驱和后继的速度

应用场景:计算机网络-CIDR,选择下一跳 最长前缀匹配:线索化

树的存储

typedef struct BTNODE {
	int data;
	struct BTNODE* left;
	struct BTNODE* right;
	int lflag, rflag;//标记左右指针指向的是孩子节点(0),还是前驱后继(1)
}BTNode, * BTTree;

int flag;//0为左孩子,1为右孩子
BTTree pre = NULL;

//初始化
BTTree init(int x)
{
	BTTree r = (BTNode*)malloc(sizeof(BTNode));
	if (r == NULL)
		printf("分配失败\n");
	else
	{
		r->data = x;
		r->left = r->right = NULL;
	}
	return r;
}
//查找-递归
BTNode* find(BTTree r, int fx)
{
	if (r == NULL || r->data == fx)
		return r;
	if (r->left != NULL && r->lflag == 0)
	{
		BTNode* f = find(r->left, fx);
		if (f != NULL && f->data == fx)
			return f;
	}
	if (r->right != NULL && r->rflag == 0)
	{
		BTNode* f = find(r->right, fx);
		if (f != NULL && f->data == fx)
			return f;
	}
	return NULL;
}
//插入
void insert(BTTree r, int x, int fx, int flag)
{
	BTNode* f = find(r, fx);
	if (f == NULL)
		printf("父亲节点不存在\n");
	else
	{
		BTTree s = (BTTree)malloc(sizeof(BTNode));
		s->data = x;
		s->left = s->right = NULL;
		if (flag == 0)
		{
			f->left = s;
			f->lflag = 0;
		}
		else
		{
			f->right = s;
			f->rflag = 0;
		}
	}
}

访问

//访问
void visit(BTTree r)
{
	if (r->left == NULL)
	{
		r->left = pre;
		r->lflag = 1;//加标记
	}
	if (pre != NULL && pre->right == NULL)
	{
		pre->right = r;
		pre->rflag = 1;//加标记
	}
	pre = r;
}

深度优先遍历

//先序遍历
void perOrder(BTTree r)
{
	if (r == NULL)
		return;
	visit(r);
	if (r->left != NULL && r->lflag == 0)
		perOrder(r->left);
	if (r->right != NULL && r->rflag == 0)
		perOrder(r->right);
}
//中序遍历(可不加lflag和rflag的判断)
void inOrder(BTTree r)
{
	if (r == NULL)
		return;
	if (r->left != NULL && r->lflag == 0)
		inOrder(r->left);
	visit(r);
	if (r->right != NULL && r->rflag == 0)
		inOrder(r->right);
}
//后序遍历
void postOrder(BTTree r)
{
	if (r == NULL)
		return;
	if (r->left != NULL && r->lflag == 0)
		postOrder(r->left);
	if (r->right != NULL && r->rflag == 0)
		postOrder(r->right);
	visit(r);
}

寻找前驱后继

//中序线索化找前驱后继
//找后继
BTNode* find_pre(BTTree r, int k)
{
	BTNode* x = find(r, k);
	if (x->rflag == 1)
		return x->right;
	else if (x->rflag == 0)
	{
		BTTree p = x->right;
		while (p->left != NULL && p->lflag == 0)
			p = p->left;
		return p;
	}
}
//找前驱
BTNode* find_post(BTTree r, int k)
{
	BTNode* x = find(r, k);
	if (x->lflag == 1)
		return x->left;
	else if (x->lflag == 0)
	{
		BTNode* p = x->left;
		while (p->right != NULL && p->rflag == 0)
			p = p->right;
		return p;
	}
}

语言参考

int main()
{
	int n, x;
	scanf("%d", &n);//输入节点个数n
	BTTree r = NULL;
	scanf("%d", &x);//根节点x
	r = init(x);
	for (int i = 2; i <= n; i++)
	{
		int fx;
		scanf("%d %d %d", &x, &fx, &flag);
		insert(r, x, fx, flag);
	}
	inOrder(r); 
	scanf("%d", &x);
	BTNode* ans = find_pre(r, x);
	printf("%d\n", ans->data);
}
二叉排序树(BST)

特殊的二叉树,又称 二叉排序树/二叉搜索树/二叉查找树(BST:Binary Search Tree)

定义/性质:在二叉树的基础上,增加以下约束/规则,构成BST:
1)如果树中一个节点(x)的左子树非空,则左子树所有节点的值都小于该节点(x)的值
2)如果树中一个节点(x)的右子树非空,则右子树所有节点的值都大于该节点(x)的值
3)左右子树也满足BST树的规则 --> 递归定义

用处/特点:
1)BST的中序遍历序列有序(递增)
2)减少查找次数

建立:
1)在空树的基础上,先用第一个数据建立根节点
2)执行n-1次插入操作

结构体组成

typedef struct BSTNODE {
	int data;
	struct BSTNODE* left;
	struct BSTNODE* right;
}BSTNode, * BSTTree;

操作

//初始化
BSTTree init(int x)
{
	BSTTree r = (BSTNode*)malloc(sizeof(BSTNode));
	if (r == NULL)
		printf("分配失败\n");
	else
	{
		r->data = x;
		r->left = r->right = NULL;
	}
	return r;
}
//插入-非递归
void insert(BSTTree r, int x)
{
	BSTNode* s = (BSTNode*)malloc(sizeof(BSTNode));
	s->data = x;
	s->left = s->right = NULL;
	BSTNode* p = r;
	BSTNode* pre = NULL;
	while (p != NULL)
	{
		if (x < p->data)
		{
			pre = p;
			p = p->left;
		}
		else
		{
			pre = p;
			p = p->right;
		}
	}
	if (x < pre->data)
		pre->left = s;
	else
		pre->right = s;
}
//插入-递归
BSTTree insert_BST(BSTTree r, int x)
{
	if (r == NULL)
	{
		BSTNode* s = (BSTNode*)malloc(sizeof(BSTNode));
		s->data = x;
		s->left = s->right = NULL;
		return s;
	}
	if (x < r->data)
		r->left = insert_BST(r->left, x);
	else
		r->right = insert_BST(r->right, x);
	return r;
}
//中序遍历
void inOrder(BSTTree r)
{
	if (r == NULL)
		return;
	if (r->left != NULL)
		inOrder(r->left);
	printf("%d ", r->data);
	if (r->right != NULL)
		inOrder(r->right);
}
//查找
BSTTree search(BSTNode* r, int x)
{
	BSTNode* p = r;
	while (p != NULL)
	{
		if (x < p->data)
			p = p->left;
		else if (x > p->data)
			p = p->right;
		else if (x == p->data)
			return p;
	}
	return NULL;
}
//查找:以r为根的树中最靠右的节点
BSTNode* find_p(BSTTree r)
{
	BSTNode* p = r;
	while (p->right != NULL)
		p = p->right;
	return p;
}
//删除
BSTNode* del(BSTTree r, int x)
{
	if (x < r->data)
		r->left = del(r->left, x);
	else if (x > r->data)
		r->right = del(r->right, x);
	else if (x == r->data)
	{
		if (r->left != NULL && r->right != NULL)
		{
			BSTNode* p = find_p(r->left);//左子树中最靠右的
			r->data = p->data;
			r->left = del(r->left, p->data);
		}
		else//只有一个孩子
		{
			BSTNode* ch = NULL;
			if (r->left != NULL)//度为1
				ch = r->left;
			else//度为1/0
				ch = r->right;
			free(r);
			r = NULL;
			return ch;
		}
	}
	return r;
}

参考语言

int main()
{
	int n, x;
	scanf("%d", &n);//输入节点个数n
	BSTTree r = NULL;
	scanf("%d", &x);//根节点x
	r = init(x);
	for (int i = 2; i <= n; i++)
	{
		scanf("%d", &x);
		insert(r, x);
		//r = insert_BST(r, x);
	}
	r = del(r, 1);
	inOrder(r);
	printf("\n");
}
AVL树

AVL树,又称 平衡二叉树 / 二叉平衡树

平衡因子:一个节点的平衡因子是其左右子树的高度差。
概念:在BST树的基础上,每个节点的平衡因子的绝对值≤1

结构体组成
typedef struct AVLNODE {
	int data;
	int h;//高度
	struct AVLNODE* left;
	struct AVLNODE* right;
}AVLNode, * AVLTree;
功能函数
//比大小
int MAX(int a, int b)
{
	return a > b ? a : b;
}
//算高度
int geth(AVLTree ro)
{
	if (ro == NULL)
		return 0;
	else
		return ro->h;
}
初始化
//初始化
AVLTree init(int x)
{
	AVLTree r = (AVLNode*)malloc(sizeof(AVLNode));
	if (r == NULL)
		printf("分配失败\n");
	else
	{
		r->data = x;
		r->left = r->right = NULL;
		r->h = 1;
	}
	return r;
}
四种失衡调整函数

p.s.假设最小失衡子树的根节点是A

LL:往A的左孩子的左子树中插入一个节点,导致A的左子树比右子树高
调整策略:以A为中心右旋

//LL-右旋
AVLTree LL_rotation(AVLNode* x)
{
	AVLNode* y = x->left;
	x->left = y->right;
	y->right = x;
	x->h = MAX(geth(x->left), geth(x->right)) + 1;
	y->h = MAX(geth(y->left), geth(y->right)) + 1;
	return y;
}

RR:往A的右孩子的右子树中插入一个节点,导致A的右子树比左子树高
调整策略:以A为中心左旋

//RR-左旋
AVLTree RR_rotation(AVLNode* x)
{
	AVLNode* y = x->right;
	x->right = y->left;
	y->left = x;
	x->h = MAX(geth(x->left), geth(x->right)) + 1;
	y->h = MAX(geth(y->left), geth(y->right)) + 1;
	return y;
}

LR:往A的左孩子的右子树中插入一个节点,导致A的左子树比右子树高
调整策略:①以A->left为中心进行左旋,转化成LL;②按照LL情况操作;

//LR
AVLTree LR_rotation(AVLNode* x)
{
	x->left = RR_rotation(x->left);
	x = LL_rotation(x);
	return x;
}

RL:往A的右孩子的左子树中插入一个节点,导致A的右子树比左子树高
调整策略:①以A->right为中心进行右旋,转化成LL;②按照RR情况操作;

//RL
AVLTree RL_rotation(AVLNode* x)
{
	x->right = LL_rotation(x->right);
	x = RR_rotation(x);
	return x;
}
操作

1)查找(同BST)

2)插入

①进行BST的插入;
②算插入节点的所有祖先节点的平衡因子 => 高度差
    节点x的高度差x->h=max(x->left->h,x->right->h)+1
    节点x的平衡因子:abs(x->left->h-x->right->h)
③判断失衡,有节点失衡之后进行旋转操作,优先调整最小失衡子树
    4种调整类型:LL  LR  RR  RL

//插入-递归
AVLTree insert_BST(AVLTree root, int x)
{
	if (root == NULL)
	{
		AVLNode* s = (AVLNode*)malloc(sizeof(AVLNode));
		s->data = x;
		s->left = s->right = NULL;
		s->h = 1;
		return s;
	}
	if (x < root->data)
	{
		root->left = insert_BST(root->left, x);
		//判断r是否失衡:LL LR
		if (geth(root->left) - geth(root->right) > 1)
		{
			AVLNode* l = root->left;
			if (x < l->data)//LL
				root = LL_rotation(root);
			else//LR
				root = LR_rotation(root);
		}
	}
	else if (x > root->data)
	{
		root->right = insert_BST(root->right, x);
		//判断r是否失衡:RR RL
		if (geth(root->right) - geth(root->left) > 1)
		{
			AVLNode* r = root->right;
			if (x > r->data)//RR
				root = RR_rotation(root);
			else//RL
				root = RL_rotation(root);
		}
	}
	root->h = MAX(geth(root->left), geth(root->right)) + 1;
	return root;
}

3)删除

①执行BST的删除操作
②判断失衡:在x的子树中删除一个节点,失衡节点是x节点
    当在x的左子树中删除一个节点时,左子树高度-1,导致失衡
    => 右子树一棵树也随左子树删了一个节点,但又插回来了,此时就变成插入导致的失衡了
    等价于:
    在x节点的一边的子树中删除了一个节点,就等价于在其另一边子树插入了一个节点导致的失衡
    左删除 相当于 右插入:RR RL
    右删除 相当于 左插入:LL LR

//查找:以r为根的树中最靠右的节点
AVLNode* find_p(AVLTree r)
{
	AVLNode* p = r;
	while (p->right != NULL)
		p = p->right;
	return p;
}
//删除
AVLNode* del(AVLTree r, int x)
{
	if (r == NULL)
		return NULL;
	if (x < r->data)
	{
		r->left = del(r->left, x);
		//判断失衡:RR RL
		if (geth(r->right) - geth(r->left) > 1)
		{
			AVLNode* ans = r->right;
			if (geth(ans->right) >= geth(ans->left))
				r = RR_rotation(r);
			else
				r = RL_rotation(r);
		}
	}
	else if (x > r->data)
	{
		r->right = del(r->right, x);
		//判断失衡:LL LR
		if (geth(r->left) - geth(r->right) > 1)
		{
			AVLNode* ans = r->left;
			if (geth(ans->left) >= geth(ans->right))
				r = LL_rotation(r);
			else
				r = LR_rotation(r);
		}
	}
	else if (x == r->data)
	{
		if (r->left != NULL && r->right != NULL)
		{
			AVLNode* p = find_p(r->left);//左子树中最靠右的
			r->data = p->data;
			r->left = del(r->left, p->data);
			//判断失衡:RR RL
			if (geth(r->right) - geth(r->left) > 1)
			{
				AVLNode* ans = r->right;
				if (geth(ans->right) >= geth(ans->left))
					r = RR_rotation(r);
				else
					r = RL_rotation(r);
			}
		}
		else//只有一个孩子
		{
			AVLNode* ch = r;
			if (r->left != NULL)//度为1
				r = r->left;
			else//度为1/0
				r = r->right;
			free(ch);
			ch = NULL;
			return r;
		}
	}
	r->h= MAX(geth(r->left), geth(r->right)) + 1;
	return r;
}

参考语言

int main()
{
	int n, x;
	scanf("%d", &n);//输入节点个数n
	AVLTree root = NULL;
	scanf("%d", &x);//根节点x
	root = init(x);
	for (int i = 2; i <= n; i++)
	{
		scanf("%d", &x);
		root = insert_BST(root, x);
	}
	inOrder(root);
	del(root, 4);
	inOrder(root);
}

算法

基于树形结构的算法

并查集

概念:利用树形结构来处理一些不相交的集合的合并和查询问题

int n, m, p;
int f[5005];

//查找-非递归
int find(int x)
{
	while (f[x] != x)
		x = f[x];
	return x;
}
//查找-递归
int find2(int x)
{
	if (f[x] == x)
		return x;
	else
		return f[x] = find2(f[x]);//路径压缩
}

int main()
{
	int x, y, px, py;
	scanf("%d %d %d", &n, &m, &p);
	//初始化fi=i
	for (int i = 1; i <= n; i++)
		f[i] = i;
	for (int i = 1; i <= m; i++)
	{
		scanf("%d%d", &x, &y);
		px = find(x);
		py = find(y);
		f[px] = py;
	}
	for (int i = 1; i <= p; i++)
	{
		scanf("%d%d", &x, &y);
		px = find(x);
		py = find(y);
		if (px == py)
			printf("Yes\n");
		else
			printf("No\n");
	}
}
哈夫曼树

1.节点i的路径长度:从根节点到节点i的路径上所经过的边数
2.树的路径长度:所有节点的路径长度之和为该树的路径长度
   内部路径长度:所有内部节点的路径长度之和
   外部路径长度(树的路径长度):所有叶子节点的路径长度之和
3.节点i的带权路径长度:节点i的路径长度*节点i权值
4.树的带权路径长度(WPL):所有叶子节点的带权路径长度之和

概念:给出n个带权值的节点,可再自行加入若干不确定的节点。用这n个节点全做叶子节点,自行加入的节点做内部节点去建立一棵树。其中WPL最小的一颗二叉树成为哈夫曼树(最优二叉树)

应用场景:通过编码进行数据压缩
哈夫曼编码 属于 变长编码
变长编码 比 定长编码 省空间

构建:对于给定的n个节点(带权值且只做叶子节点)构造哈夫曼树
树的合并,一开始认为这n个节点是n棵树

结构体构成

typedef struct {
	int w;//权值
	int f;//父亲节点的下标
	int left, right;//左右孩子节点的下标
}HuffmanNode, * HuffmanTree;

操作

//寻找权值最小的两个根节点的下标
void find(HuffmanTree t, int x, int* w1, int* w2)
{
	//先找最小
	int minn = 0;
	for (int i = 1; i <= x; i++)
	{
		if (t[i].f == i)
		{
			minn = i;
			break;
		}
	}
	for (int i = 1; i <= x; i++)
	{
		if (t[i].f == i)
		{
			if (t[i].w < t[minn].w)
				minn = i;
		}
	}
	*w1 = minn;//最小根节点的下标保存在w1
	//找第二小
	for (int i = 1; i <= x; i++)
	{
		if (t[i].f == i && i != (*w1))
		{
			minn = i;
			break;
		}
	}
	for (int i = 1; i <= x; i++)
	{
		if (t[i].f == i && i != (*w1))
		{
			if (t[i].w < t[minn].w)
				minn = i;
		}
	}
	*w2 = minn; //第二小根节点的下标保存在w1
}
//建树
HuffmanTree createHuffmanTree(int* w, int n)
{
	int m = 2 * n;//数组大小
	HuffmanTree t = (HuffmanTree)malloc(sizeof(HuffmanNode) * m);
	for (int i = 0; i < m; i++)//初始化
	{
		t[i].f = t[i].right = t[i].left = 0;
		t[i].w = 0;
	}
	for (int i = 1; i <= n; i++)
	{
		t[i].w = w[i - 1];
		t[i].f = i;
	}
	int w1, w2;//权值最小的两个根节点的下标
	for (int i = n + 1; i < m; i++)
	{
		find(t, i - 1, &w1, &w2);
		t[w1].f = t[w2].f = i;
		t[i].f = i;
		t[i].w = t[w1].w + t[w2].w;
		t[i].left = w1;
		t[i].right = w2;
	}
	return t;
}
//编码
char** createHuffmanCode(HuffmanTree t, int n)
{
	char* temp = (char*)malloc(sizeof(char) * n);//0~n-1
	char** code = (char*)malloc(sizeof(char*) * n);
	int start;//一开始放编码的位置
	int pos, p;
	for (int i = 1; i <= n; i++)
	{
		start = n - 1;
		temp[start] = '\0';
		pos = i;//当前节点
		p = t[pos].f;//父
		while (t[pos].f != pos)
		{
			start--;
			if (t[p].left == pos)
				temp[start] = '0';
			else if (t[p].right == pos)
				temp[start] = '1';
			pos = p;
			p = t[pos].f;
		}
		code[i - 1] = (char*)malloc(sizeof(char) * (n - start));
		strcpy(code[i - 1], &temp[start]);
	}
	free(temp);
	temp = NULL;
	return code;
}

参考语言

int main()
{
	char s[8] = { 'A','B','C','D','E','F','G','H' };
	int w[8] = { 5,29,7,8,14,23,3,11 };
	int* a = (int*)malloc(sizeof(int) * 10);
	HuffmanTree t = createHuffmanTree(w, 8);//建树
	char** code = createHuffmanCode(t, 8);
	for (int i = 0; i <= 8; i++)
		printf("%c : %s\n", s[i], code[i]);
}

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值