数据结构(C语言)6.树

本文深入探讨了二叉树的基本概念及其多种应用,包括二叉树的性质、遍历方法、层次遍历、非递归遍历等核心内容,并介绍了二叉查找树、平衡二叉树、哈夫曼树等高级主题。

1.二叉树性质

二叉树性质:

性质1:在二叉树第i层上至多有2的i-1次方个结点

性质2:深度为k的二叉树至多有2的k次方-1个结点

       证明:深度为K,有K层,等比数列求和

             a1(1-q^n)/(1-q)

             1(1-2^k)/(1-2)  = 2^k-1

性质3:对于任何一颗二叉树,若度为2的结点数有n2个,则叶子数(n0)必定为n2+1,即n0=n2+1

       证明:设一共结点数为n,度为1的结点数位n1,叶子数为n0,它的度为0

       n=n1+n2+n0

       2*n2+1*n1+n0=n-1(计算每个节点的子节点总数,因为没算根节点)

推出:

      2*n2+n1=n1+n2+n0-1

        n0=n2+1;

性质4:具有n个结点的完全二叉树的深度必为 不大于log以2为底n为对数的最大整数+1

      

性质5:对完全二叉树,若从上至下,从左至右编号,编号为i的结点,其左孩子编号必为2i,其右孩子编号必为2i+1;其双亲的编号必为i/2(i=1时为根)

2.二叉树的遍历与实现

#pragma once
#include<stdio.h>
#include<stdlib.h>

typedef struct TreeNode
{
	char data;       //数据域
	struct TreeNode* lchild;  //左子树
	struct TreeNode* rchild;  //右子树
}TreeNode;

//创建二叉树
void CreateTree(TreeNode** T, char* data, int* index);

//递归方法
//先序遍历
void PreOrder(TreeNode* T);
//中序遍历
void inOrder(TreeNode* T);
//后序遍历
void postOrder(TreeNode* T);


#include"tree.h"

void CreateTree(TreeNode** T, char* data,int* index)
{
	//创建二叉树的逻辑

	//主函数写字符数组,直接传进来,写进二叉树
	//index为序号 在主函数定义,为全局变量
	//向后遍历时加加,index需要一直改变,所以指针传递
	char ch;
	ch = data[*index];
	*index += 1;

	if (ch == '#') //此时为空节点  人为假设输入#为空
	{
		*T = NULL;
	}
	else   //此时不为空
	{
		*T = (TreeNode*)malloc(sizeof(TreeNode)); //开辟一个节点的空间
		//赋值
		(*T)->data = ch;
		//递归思想  左子树和右子树都是树
		//创建左子树
		CreateTree(&((*T)->lchild), data, index);
		//创建右子树
		CreateTree(&((*T)->rchild), data, index);
	}
}

//先序遍历——递归
void PreOrder(TreeNode* T)
{
	if (T == NULL)
	{
		return;
	}
	else
	{
		printf("%c ", T->data);
		// 递归
		//先序遍历:根 左 右
		PreOrder(T->lchild);
		PreOrder(T->rchild);
	}
}

//中序遍历——递归
void inOrder(TreeNode* T)
{
	if (T == NULL)
	{
		return;
	}
	else
	{
		// 递归
		//中序遍历:左 根 右
		inOrder(T->lchild);
		printf("%c ", T->data);
		inOrder(T->rchild);
	}
}

//后序遍历——递归
void postOrder(TreeNode* T)
{
	if (T == NULL)
	{
		return;
	}
	else
	{
		// 递归
		//后序遍历:左 右 根
		postOrder(T->lchild);
		postOrder(T->rchild);
		printf("%c ", T->data);
	}
}
#include"tree.h"
int main()
{
	int index = 0;
	char data[] = { 'A','B','#','#','C','#','#' };
	TreeNode* T;
	CreateTree(&T,data,&index);

	PreOrder(T);
	printf("\n");
	inOrder(T);
	printf("\n");
	postOrder(T);
	printf("\n");

	return 0;
}

3.二叉树的层次遍历实现

#pragma once
#include<stdio.h>
#include<stdlib.h>
#include<assert.h>
#include<stdbool.h>

typedef struct TreeNode
{
	char data;       //数据域
	struct TreeNode* lchild;  //左子树
	struct TreeNode* rchild;  //右子树
}TreeNode;

//层次遍历需要用到队列
typedef struct QueueNode
{
	TreeNode* data;//将树的节点放入队列
	struct QueueNode* prev;
	struct QueueNode* next;
}QueueNode;

//创建二叉树
void CreateTree(TreeNode** T, char* data, int* index);
//创建队列
QueueNode* InitQueue();
void QueuePush(QueueNode* pq, TreeNode* x);//队尾插入
bool QueueEmpty(QueueNode* pq);//判断是否为空
QueueNode* QueuePop(QueueNode* pq);//队头删除
void levelTraverse(QueueNode* pq, TreeNode* T);//二叉树的层次遍历
void FreeTreeAndQueue(QueueNode* pq, TreeNode** T);//队列头节点的内存释放
#include"treelevel.h"

void CreateTree(TreeNode** T, char* data, int* index)
{
	//创建二叉树的逻辑

	//主函数写字符数组,直接传进来,写进二叉树
	//index为序号 在主函数定义,为全局变量
	//向后遍历时加加,index需要一直改变,所以指针传递
	char ch;
	ch = data[*index];
	*index += 1;

	if (ch == '#') //此时为空节点  人为假设输入#为空
	{
		*T = NULL;
	}
	else   //此时不为空
	{
		*T = (TreeNode*)malloc(sizeof(TreeNode)); //开辟一个节点的空间
		//赋值
		(*T)->data = ch;
		//递归思想  左子树和右子树都是树
		//创建左子树
		CreateTree(&((*T)->lchild), data, index);
		//创建右子树
		CreateTree(&((*T)->rchild), data, index);
	}
}

//带头双向链表
QueueNode* InitQueue()
{
	QueueNode* q = (QueueNode*)malloc(sizeof(QueueNode));
	TreeNode* T = (TreeNode*)malloc(sizeof(TreeNode));
	if (q == NULL)
	{
		printf("Queue create fail\n");
		exit(1);
	}
	if (T == NULL)
	{
		printf("TreeNode head create fail\n");
		exit(1);
	}
	T->data = ' ';
	T->lchild = NULL;
	T->rchild = NULL;

	q->data = T;
	q->next = q;
	q->prev = q;

	return q;
}

//队尾插入
void QueuePush(QueueNode* pq, TreeNode* x)
{
	assert(pq);
	QueueNode* newnode = (QueueNode*)malloc(sizeof(QueueNode));
	newnode->data = x;

	QueueNode* tail = pq->prev;
	tail->next = newnode;
	newnode->prev = tail;
	pq->prev = newnode;
	newnode->next = pq;

}

//判断是否为空
bool QueueEmpty(QueueNode* pq)
{
	return (pq->next == pq);
}

//队头删除
QueueNode* QueuePop(QueueNode* pq)
{
	if (QueueEmpty(pq))
	{
		return NULL;
	}
	else
	{
		QueueNode* next = (QueueNode*)malloc(sizeof(QueueNode));
		next = pq->next;

		if (next->next!= NULL)
		{
			pq->next = next->next;
			next->next->prev = pq;

			next->prev = next;
			next->next = next;
		}

		return next;
	}
}

//例如
//     A
//  B     C
// D E   F G
// 循环前队列:A
// 第一次循环
//出队:A 
//列队:B C 

// 第二次循环
//出队:A B
//列队:C D E

// 第三次循环
//出队:A B C
//列队:D E F G
 
// 第四次循环
//出队:A B C D
//列队:E F G

// 第五次循环
//出队:A B C D E
//列队:F G

// 第六次循环
//出队:A B C D E F
//列队G

// 第七次循环
//出队:A B C D E F G
//列队:
//出循环

void levelTraverse(QueueNode* pq, TreeNode* T)

{
	//根节点进队
	QueuePush(pq, T);

	//不为空时出队
	while (!QueueEmpty(pq))
	{
		QueueNode* node = (QueueNode*)malloc(sizeof(QueueNode)); 
		node = QueuePop(pq);
		/*free(QueuePop(pq));
		QueuePop(pq) = NULL;*/
		//node->data指向树节点
		printf("%c ", node->data->data);//打印树节点
		if (node->data->lchild!=NULL)//左节点不为空,进队
		{
			QueuePush(pq, node->data->lchild);
		}
		if (node->data->rchild!=NULL)//右节点不为空,进队
		{
			QueuePush(pq, node->data->rchild);
		}
		//释放空间
		//每个打印的树节点内存都释放  树节点还剩根节点未释放
		//存储它们的队列节点内存也都释放  队列还剩头节点未释放
		free(node->data);
		node->data = NULL;
		free(node);
		node = NULL;
	}
}

//队列头节点的内存释放
void FreeTreeAndQueue(QueueNode* pq, TreeNode** T)
{
	free(pq->data);
	pq->data = NULL;
	free(pq);
	pq = NULL;
}

4.二叉树前序、中序、后序遍历的非递归实现

#pragma once
#include<stdio.h>
#include<stdlib.h>
#include<stdbool.h>

//循环方法
//前序、中序遍历思想:
//1.入栈根节点
//2。循环,直到左孩子为空
//3.出栈,入栈右孩子

typedef struct TreeNode
{
	char data;
	struct TreeNode* lchild;
	struct TreeNode* rchild;

	int flag;//后序遍历时记录 右节点是否被访问过

}TreeNode;

//带头链表栈
typedef struct StackNode
{
	TreeNode* data;
	struct StackNode* next;
}StackNode;

void CreateTree(TreeNode** T, char* data, int* index);//二叉树的实现
StackNode* InitStack();//初始化栈
void StackPush(StackNode* S, TreeNode* data);//头插 入栈
StackNode* StackPop(StackNode* S);//头删 出栈
bool isEmpty(StackNode* S);//判断是否为空
void PreOrder(TreeNode* T);  //先序遍历——循环实现
void inOrder(TreeNode* T); //中序遍历——循环实现
void postOrder(TreeNode* T);//后序遍历——循环实现 方法一
void postOrder2(TreeNode* T);//后序遍历——循环实现 方法二
#include"tree.h"

void CreateTree(TreeNode** T, char* data, int* index)
{
	//创建二叉树的逻辑

	//主函数写字符数组,直接传进来,写进二叉树
	//index为序号 在主函数定义,为全局变量
	//向后遍历时加加,index需要一直改变,所以指针传递
	char ch;
	ch = data[*index];
	*index += 1;

	if (ch == '#') //此时为空节点  人为假设输入#为空
	{
		*T = NULL;
	}
	else   //此时不为空
	{
		*T = (TreeNode*)malloc(sizeof(TreeNode)); //开辟一个节点的空间
		//赋值
		(*T)->data = ch;
		(*T)->flag = 0;
		//递归思想  左子树和右子树都是树
		//创建左子树
		CreateTree(&((*T)->lchild), data, index);
		//创建右子树
		CreateTree(&((*T)->rchild), data, index);
	}
}

//创建栈
StackNode* InitStack()
{
	StackNode* S = (StackNode*)malloc(sizeof(StackNode));
	S->data = NULL;
	S->next = NULL;

	return S;
}

//头插
void StackPush(StackNode* S, TreeNode* data)
{
	StackNode* newnode = (StackNode*)malloc(sizeof(StackNode));
	newnode->data = data;

	newnode->next = S->next;
	S->next = newnode;
}

//判断是否为空
bool isEmpty(StackNode* S)
{
	return (S->next == NULL);
}

//头删 出栈
StackNode* StackPop(StackNode* S)
{
	if (isEmpty(S))
	{
		return NULL;
	}

	else
	{
		StackNode* cur = S->next;
		S->next = cur->next;
		return cur;
	}
}


//前序、中序遍历思想:
//1.入栈根节点
//2。循环,直到左孩子为空
//3.出栈,入栈右孩子

//先序遍历——循环实现

//     A
//  B     C
// D E   F G

//第一次循环
//输出 A
//栈: A

//第二次循环
//输出 B
//栈:A B

//第三次循环
//输出 D
//栈:A B D

// node=NULL
//出栈:D,同时node置为D
//node=E


//麻烦?效率?
void PreOrder(TreeNode* T)
{
	TreeNode* node = T;
	StackNode* S = InitStack();
	while (node || !isEmpty(S))
	{
		if (node)
		{
			//先序遍历 根 左 右
			printf("%c ", node->data);
			
			//入栈
			StackPush(S, node);

			node = node->lchild;
		}
		else//上一个节点的左孩子为空,出栈,入栈右孩子
		{
			//此时node为空,返回上一层
			//StackPop(S)是栈链表节点,它存储的数据是树节点
			node = StackPop(S)->data;
			
			node = node->rchild;//得到上一层的右节点
		}
	}
}

//中序遍历——循环实现 左 中 右
void inOrder(TreeNode* T)
{
	TreeNode* node = T;
	StackNode* S = InitStack();

	while (node || !isEmpty(S))
	{
		if (node)
		{
			//入栈
			StackPush(S, node);
			node = node->lchild;
			
		}
		else
		{
			node = StackPop(S)->data;
			printf("%c ", node->data);
			node = node->rchild;
		}
	}
}

//后序遍历——循环实现 左 右 根
//思想:
//1,从根节点开始,寻找最左边的节点,并依此入栈
//2.出栈前,判断栈顶元素是否有右子树以及右子树是否被访问过,如果有则将右子树入栈

//方法一
//获得栈顶,并且不真正出栈
StackNode* getTop(StackNode* S)
{
	if (isEmpty(S))
	{
		return NULL;
	}

	else
	{
		StackNode* cur = S->next;
		return cur;
	}
}

void postOrder(TreeNode* T)
{
	TreeNode* node = T;
	StackNode* S = InitStack();

	while (node || !isEmpty(S))
	{
		if (node)
		{
			StackPush(S, node);//进栈
			node = node->lchild;
		}
		else
		{
			TreeNode* top = getTop(S)->data;
			if (top->rchild && top->rchild->flag == 0)
			{
				top = top->rchild;
				StackPush(S, top);
				node = top->lchild;
			}
			else
			{
				top = StackPop(S)->data;
				printf("%c ", top->data);
				top->flag = 1;
			}
		}
	}
}

//方法二
void postOrder2(TreeNode* T)
{
	TreeNode* node = T;
	TreeNode* prevAccess = NULL;
	StackNode* S = InitStack();
	while (node || !isEmpty(S))
	{
		if (node)
		{
			StackPush(S, node);
			node = node->lchild;
		}
		else
		{
			node = StackPop(S)->data;
			if (node->rchild == NULL || node->rchild == prevAccess)
			{
				printf("%c ", node->data);
				prevAccess = node;
				node = NULL;
			}
			else
			{
				StackPush(S, node);
				node = node->rchild;
			}
		}
	}
	
}
#include"tree.h"
int main()
{
	int index = 0;
	char data[] = { 'A','B','D','#','#','E','#','#','C','F','#','#','G','#','#' };
	TreeNode* T;
	CreateTree(&T, data, &index);


	PreOrder(T);
	printf("\n");
	inOrder(T);
	printf("\n");
	postOrder(T);
	printf("\n");

	postOrder2(T);
	printf("\n");

	return 0;
}

5.求二叉树叶子节点个数

#include<stdio.h>
#include<stdlib.h>

typedef struct TreeNode
{
	char ch;
	struct TreeNode* lchild;
	struct TreeNode* rchind;
 }TreeNode;
 
void calculateLeafNum(TreeNode* root,int* num)
{
	//递归的结束条件 
	if(root==NULL)
	{
		return;
	}
	
	if(root->lchild==NULL&&root->rchind==NULL)
	{
		(*num)++;
	}
	
	calculateLeafNum(root->lchild,num);
	calculateLeafNum(root->rchind,num);
}

void test01()
{
	TreeNode nodeA={'A',NULL,NULL};
	TreeNode nodeB={'B',NULL,NULL};
	TreeNode nodeC={'C',NULL,NULL};
	TreeNode nodeD={'D',NULL,NULL};
	TreeNode nodeE={'E',NULL,NULL};
	TreeNode nodeF={'F',NULL,NULL};
	TreeNode nodeG={'G',NULL,NULL};
	TreeNode nodeH={'H',NULL,NULL};
	
	//建立关系
	
	nodeA.lchild=&nodeB;
	nodeA.rchind=&nodeF;
	nodeB.rchind=&nodeC;
	nodeC.lchild=&nodeD;
	nodeC.rchind=&nodeE;
	nodeF.rchind=&nodeG;
	nodeG.lchild=&nodeH;
	
	//求树中叶子节点的个数
	int num=0;
	calculateLeafNum(&nodeA,&num);
	printf("叶子节点的数量为:%d\n",num);
}

int main()
{
	test01();
	
	return 0;
}

6.求二叉树的高度(深度)

#include<stdio.h>
#include<stdlib.h>


typedef struct TreeNode
{
	char ch;
	struct TreeNode* lchild;
	struct TreeNode* rchind;
 }TreeNode;

int getTreeHeight(TreeNode* root)
{
	if(root==NULL)
	{
		return 0;
	}
	
	//求出左子树高度
	int LeftHeight=getTreeHeight(root->lchild);
	int RightHeight=getTreeHeight(root->rchind);
	
	int height=LeftHeight>RightHeight?LeftHeight+1:RightHeight+1;
	
	return height;
}


void test01()
{
	TreeNode nodeA={'A',NULL,NULL};
	TreeNode nodeB={'B',NULL,NULL};
	TreeNode nodeC={'C',NULL,NULL};
	TreeNode nodeD={'D',NULL,NULL};
	TreeNode nodeE={'E',NULL,NULL};
	TreeNode nodeF={'F',NULL,NULL};
	TreeNode nodeG={'G',NULL,NULL};
	TreeNode nodeH={'H',NULL,NULL};
	
	//建立关系
	
	nodeA.lchild=&nodeB;
	nodeA.rchind=&nodeF;
	nodeB.rchind=&nodeC;
	nodeC.lchild=&nodeD;
	nodeC.rchind=&nodeE;
	nodeF.rchind=&nodeG;
	nodeG.lchild=&nodeH;
	
	//求树的高度
	int height=getTreeHeight(&nodeA);
	printf("树的高度:%d\n",height);
}

int main()
{
	test01();
	
	return 0;
}

7.二叉树的拷贝及释放

#include<stdio.h>
#include<stdlib.h>
typedef struct TreeNode
{
	char ch;
	struct TreeNode* lchild;
	struct TreeNode* rchind;
 }TreeNode;
 
TreeNode* copyTree(TreeNode* root)
{
	if(root==NULL)
	{
		return NULL;
	}
	
	//先拷贝左子树
	TreeNode* lchild=copyTree(root->lchild);
	//再拷贝右子树
	TreeNode* rchild=copyTree(root->rchind);
	//再创建新的节点
	TreeNode* newroot=(TreeNode*)malloc(sizeof(TreeNode)); 
	newroot->ch=root->ch;
	newroot->lchild=lchild;
	newroot->rchind=rchild;
	
	return newroot;
}

void showTree(TreeNode* root)
{
	if(root==NULL)
	{
		return;
	}
	
	printf("%c ",root->ch);
	
	showTree(root->lchild);
	showTree(root->rchind);
}

void DestroyTree(TreeNode* root)
{
	if(root==NULL)
	{
		return;
	}
	
	DestroyTree(root->lchild);
	DestroyTree(root->rchind);
	
	printf("%c 被释放\n",root->ch);
	free(root);
	root=NULL;
 } 
 
void test01()
{
	TreeNode nodeA={'A',NULL,NULL};
	TreeNode nodeB={'B',NULL,NULL};
	TreeNode nodeC={'C',NULL,NULL};
	TreeNode nodeD={'D',NULL,NULL};
	TreeNode nodeE={'E',NULL,NULL};
	TreeNode nodeF={'F',NULL,NULL};
	TreeNode nodeG={'G',NULL,NULL};
	TreeNode nodeH={'H',NULL,NULL};
	
	//建立关系
	
	
	nodeA.lchild=&nodeB;
	nodeA.rchind=&nodeF;
	nodeB.rchind=&nodeC;
	nodeC.lchild=&nodeD;
	nodeC.rchind=&nodeE;
	nodeF.rchind=&nodeG;
	nodeG.lchild=&nodeH;
	
	TreeNode* newroot=copyTree(&nodeA);
	
	showTree(newroot);
	printf("\n");
	
	DestroyTree(newroot);

}


int main()
{
	test01();
	return 0;
}

8.一维数组树

#include<stdio.h>
#include<stdlib.h>
#include<stdbool.h>
#include<math.h>

#define MAX_SIZE 1024 //一维数组能够存放的最大结点数

//树的顺序存储结构——一般用于完全二叉树,这样可以避免内存浪费
//将二叉树按层序存放进一维数组中

typedef int TElemType;//树节点的结构类型,目前定义为整形

typedef int SeqTree[MAX_SIZE];//定义顺序数

typedef struct
{
	int level;//结点所在二叉树中的第几层
	int	order;//在这层的第几个,标为序号
}Position;

//放在头文件会引起,重定义的错误
TElemType Nil = 0;//设定整形以零为空

//初始化空二叉树
//因为tree是固定数组,不会改变,所以不需要传指针
void InitSeqTree(SeqTree tree)
{
	//将字符数组中每个元素都设置为空字符
	for (int i = 0; i < MAX_SIZE; i++)
	{
		tree[i] = Nil;
	}
}

//创建完全二叉树
void CreateSeqTree(SeqTree tree)
{
	int i = 0;

	//随便创建一个二叉树
	while (i < 10)
	{
		tree[i] = i + 1;

		//出现不为空的,无双亲结点
		//设根结点的序号为1,后面某二叉树结点序号为K
		//K/2为,此结点的双亲序号
		
		//此时,i从0开始,那么他的序号按上述算法该为i+1
		//它的双亲序号应为(i+1)/2
		//在数组中的存储下标应该再减一
		if (i != 0 && tree[(i + 1) / 2 - 1] == Nil && tree[i] != Nil)
		{
			printf("出现无双亲的非根结点:%d\n", tree[i]);
			return;
		}
		i++;
	}

	while (i < MAX_SIZE)
	{
		//将空赋给后面的值
		tree[i] = Nil;
		i++;
	}
}

//判断树是否为空
bool IsTreeEmpty(SeqTree tree)
{
	return tree[0] == Nil;//若根结点为空,则树为空
}

//计算二叉树的深度
int SeqTreeDepth(SeqTree tree)
{
	if (IsTreeEmpty(tree))
	{
		printf("二叉树为空\n");
		return 0;
	}
	else
	{
		int i = 0;
		for (i = MAX_SIZE - 1; i >= 0; i--)
		{
			if (tree[i] != Nil)//找到二叉树的最后一个结点
			{
				break;
			}
		}

		int j = 0;

		//结点数为下标加1
		//i+1<=2^j-1
		while ((i + 1) >= (powl(2, j) - 1))
		{
			j++;
		}
		return j;
	}
}

//返回二叉树的根
int SeqTreeRoot(SeqTree tree)
{
	if (IsTreeEmpty(tree))
	{
		return 0;
	}
	else
	{
		return tree[0];
	}
}

//返回本层序号的树结点的值
TElemType SeqTreeValue(SeqTree tree, Position e)
{
	if (IsTreeEmpty(tree))
	{
		return 0;
	}
	return tree[(int)powl(2, e.level - 1) - 1 + e.order - 1];
}

//给本层序号的树结点赋新值value
void Assign(SeqTree tree, Position e, TElemType value)
{
	if (IsTreeEmpty(tree))
	{
		return;
	}

	int i = (int)powl(2, e.level - 1) - 1 + e.order - 1;

	if (value != Nil && tree[(i + 1) / 2 - 1 ] == Nil)
	{
		printf("给双亲为空的树节点赋非空值\n");
		return;
	}
	else if (value == Nil && tree[2 * (i + 1) - 1] != Nil || tree[2 * (i + 1) - 1 + 1] != Nil)
	{
		printf("给双亲赋空值但叶子非空\n");
		return;
	}

	tree[i] = value;
}

// 若e是T的非根结点, 则返回它的双亲, 否则返回"空"
TElemType Parent(SeqTree tree, TElemType e)
{
	if (IsTreeEmpty(tree))
	{
		return Nil;
	}

	for (int i = 1; i < MAX_SIZE; i++)
	{
		if (tree[i] == e)
		{
			return tree[(i + 1) / 2 - 1];
		}
	}

	return Nil;
}

//返回e的左孩子。若e无左孩子,则返回"空"
TElemType LeftChild(SeqTree tree, TElemType e)
{
	if (IsTreeEmpty(tree))
	{
		return Nil;
	}

	for (int i = 0; i < MAX_SIZE; i++)
	{
		if (tree[i] == e)
		{
			return tree[2 * (i + 1) - 1];
		}
	}

	return Nil;
}

//返回e的右孩子。若e无右孩子,则返回"空"
TElemType RightChild(SeqTree tree, TElemType e)
{
	if (IsTreeEmpty(tree))
	{
		return Nil;
	}

	for (int i = 0; i < MAX_SIZE; i++)
	{
		if (tree[i] == e)
		{
			return tree[2 * (i + 1) + 1 - 1];
		}
	}

	return Nil;
}

//返回e的左兄弟。若e是T的左孩子或无左兄弟,则返回"空"
TElemType LeftSibling(SeqTree tree, TElemType e)
{
	if (IsTreeEmpty(tree))
	{
		return Nil;
	}

	for (int i = 0; i < MAX_SIZE; i++)
	{
		if (tree[i] == e && i % 2 == 0)
		{
			return tree[i - 1];
		}
	}

	return Nil;
}

//返回e的右兄弟。若e是T的右孩子或无右兄弟,则返回"空"
TElemType RightSibling(SeqTree tree, TElemType e)
{
	if (IsTreeEmpty(tree))
	{
		return Nil;
	}

	for (int i = 0; i < MAX_SIZE; i++)
	{
		if (tree[i] == e && i % 2 == 1)//e是左孩子
		{
			return tree[i + 1];
		}
	}

	return Nil;
}

//先序遍历调用
void PreTraverse(SeqTree tree, int e)
{
	printf("%d ", tree[e]);

	if (tree[2 * (e + 1) - 1] != Nil)//左子树不为空
	{
		PreTraverse(tree, 2 * (e + 1) - 1);
	}

	if (tree[2 * (e + 1) + 1 - 1] != Nil)//右子树不为空
	{
		PreTraverse(tree, 2 * (e + 1) + 1 - 1);
	}
}
//先序遍历
void PreOrderTraverse(SeqTree tree)
{
	if (IsTreeEmpty(tree))
	{
		return;
	}
	PreTraverse(tree, 0);
	printf("\n");
}

//中序遍历调用
void InTraverse(SeqTree tree, int e)
{
	if (tree[2 * (e + 1) - 1] != Nil)//左子树不为空
	{
		InTraverse(tree, 2 * (e + 1) - 1);
	}

	printf("%d ", tree[e]);

	if (tree[2 * (e + 1) + 1 - 1] != Nil)//右子树不为空
	{
		InTraverse(tree, 2 * (e + 1) + 1 - 1);
	}
}
//中序遍历
void InOrderTraverse(SeqTree tree)
{
	if (IsTreeEmpty(tree))
	{
		return;
	}
	InTraverse(tree, 0);
	printf("\n");
}

//后序遍历调用
void PostTraverse(SeqTree tree, int e)
{
	if (tree[2 * (e + 1) - 1] != Nil)//左子树不为空
	{
		PostTraverse(tree, 2 * (e + 1) - 1);
	}

	if (tree[2 * (e + 1) + 1 - 1] != Nil)//右子树不为空
	{
		PostTraverse(tree, 2 * (e + 1) + 1 - 1);
	}

	printf("%d ", tree[e]);

}
//后序遍历
void PostOrderTraverse(SeqTree tree)
{
	if (IsTreeEmpty(tree))
	{
		return;
	}
	PostTraverse(tree, 0);
	printf("\n");
}

//层序遍历
void LevelOrderTraverse(SeqTree tree)
{
	if (IsTreeEmpty(tree))
	{
		return;
	}

	int i = MAX_SIZE - 1;
	while (tree[i] == Nil)//找到最后一个非空结点的下标
	{
		i--;
	}

	for (int j = 0; j <= i; j++)//因为按层存进数组,所以遍历数组,就是按层序遍历
	{
		if (tree[j] != Nil)//只打印非空
		{
			printf("%d ", tree[j]);
		}
	}

	printf("\n");
}

//逐层、按本层序号输出二叉树
void LevelPrint(SeqTree tree)
{
	Position p;
	
	for (int j = 1; j <= SeqTreeDepth(tree); j++)//j是第几层
	{
		printf("第%d层:\n", j);

		for (int k = 1; k <= powl(2, j - 1); k++)
		{
			p.level = j;
			p.order = k;
			TElemType e = SeqTreeValue(tree, p);//返回本层序号的树结点的值
			if (e != Nil)
			{
				printf("第%d个:%d", k, e);
			}
		}
	}
}

//清空树
void ClearSeqTree(SeqTree tree)
{
	//将字符数组中每个元素都设置为空字符
	for (int i = 0; i < MAX_SIZE; i++)
	{
		tree[i] = Nil;
	}
}
int main()
{
	SeqTree tree;

	InitSeqTree(tree);
	CreateSeqTree(tree);

	printf("建立二叉树后,树空否?%d(1:是 0:否) 树的深度=%d\n", IsTreeEmpty(tree), SeqTreeDepth(tree));

	if (SeqTreeRoot(tree))
	{
		printf("二叉树的根为:%d\n", SeqTreeRoot(tree));
	}
	else
	{
		printf("树空,无根\n");
	}

	printf("层序遍历二叉树:\n");
	LevelOrderTraverse(tree);

	printf("前序遍历二叉树:\n");
	PreOrderTraverse(tree);

	printf("中序遍历二叉树:\n");
	InOrderTraverse(tree);

	printf("后序遍历二叉树:\n");
	PostOrderTraverse(tree);


	printf("修改结点的层号3本层序号2。");
	Position p;
	p.level = 3;
	p.order = 2;
	TElemType e = SeqTreeValue(tree, p);//返回3层 2序号的结点值
	printf("待修改结点的原值为%d请输入新值:50 ", e);
	e = 50;
	Assign(tree, p, e);//修改3层2序号结点的值为50
	printf("前序遍历二叉树:\n");
	PreOrderTraverse(tree);

	printf("结点%d的双亲为%d,", e, Parent(tree, e));
	printf("左右孩子分别为%d,%d,", LeftChild(tree, e), RightChild(tree, e));
	printf("左右兄弟分别为%d,%d\n", LeftSibling(tree, e), RightSibling(tree, e));

	ClearSeqTree(tree);
	printf("建立二叉树后,树空否?%d(1:是 0:否) 树的深度=%d\n", IsTreeEmpty(tree), SeqTreeDepth(tree));

	if (SeqTreeRoot(tree))
	{
		printf("二叉树的根为:%d\n", SeqTreeRoot(tree));
	}
	else
	{
		printf("树空,无根\n");
	}
	return 0;
}

9.线索二叉树

        (1)中序线索二叉树

#include<stdio.h>
#include<stdlib.h>

typedef struct TreeNode
{
	char data;
	struct TreeNode* lchild;
	struct TreeNode* rchild;

	int ltag;//==0 时,指向子树
	int rtag;//==0 时,指向子树
}TreeNode;

void CreateTree(TreeNode** T, char* data, int* index);//二叉树的创建

void inThreadTree(TreeNode* T, TreeNode** prev);//中序线索二叉树

TreeNode* getFirst(TreeNode* T);// 找到最左子树,即中序遍历的第一个

TreeNode* getNext(TreeNode* node);//找到后继

//二叉树的创建
void CreateTree(TreeNode** T, char* data, int* index)//先序创建
{
	//创建二叉树的逻辑

	//主函数写字符数组,直接传进来,写进二叉树
	//index为序号 在主函数定义,为全局变量
	//向后遍历时加加,index需要一直改变,所以指针传递

	char ch;
	ch = data[*index];
	*index += 1;
	if (ch == '#')
	{
		*T = NULL;
	}
	else
	{
		*T = (TreeNode*)malloc(sizeof(TreeNode)); //开辟一个节点的空间
		//赋值
		(*T)->data = ch;

		(*T)->ltag = 0;
		(*T)->rtag = 0;

		//递归思想  左子树和右子树都是树
		//创建左子树
		CreateTree(&((*T)->lchild), data, index);
		//创建右子树
		CreateTree(&((*T)->rchild), data, index);
	}

}

//中序线索二叉树
void inThreadTree(TreeNode* T, TreeNode** prev)//prev代表当前结点的前驱
{
	if (T)
	{
		inThreadTree(T->lchild, prev);
		
		//do something
		if (T->lchild == NULL)//当前结点的左孩子为空
		{
			T->ltag = 1;//标志为1
			T->lchild = *prev;//左孩子指向当前结点的前驱
		}

		if (*prev != NULL && (*prev)->rchild == NULL)//当前结点的前驱不为空,并且前驱的右孩子为空
		{
			(*prev)->rtag = 1;   //前驱的标志为1
			(*prev)->rchild = T; //递归中T代表的是当前结点,前驱的后继指向当前结点
		}
		*prev = T;//每次处理完,前驱变为当前结点

		inThreadTree(T->rchild, prev);//前面没有改变本结点的右孩子指向,所以可以直接传入
	}

}

// 找到最左子树,即中序遍历的第一个
TreeNode* getFirst(TreeNode* T)
{
	while (T->ltag == 0)
	{
		T = T->lchild;
	}

	return T;
}

//找到后继
TreeNode* getNext(TreeNode* node)
{
	if (node->rtag == 1)
	{
		return node->rchild;
	}

	else
	{
		return getFirst(node->rchild);
	}
}
int main()
{
	int index = 0;
	char data[] = { 'A','B','D','#','#','E','#','#','C','#','#'};
	TreeNode* T;
	CreateTree(&T, data, &index);

	TreeNode* prev = NULL;

	inThreadTree(T, &prev);

	prev->rtag = 1;
	prev->rchild = NULL;

	for (TreeNode* node = getFirst(T); node != NULL; node = getNext(node))
	{
		printf("%c ", node->data);
	}
	printf("\n");

	return 0;
}

        (2)先序线索二叉树

#include<stdio.h>
#include<stdlib.h>

typedef struct TreeNode
{
	char data;
	struct TreeNode* lchild;
	struct TreeNode* rchild;

	int ltag;//==0 时,指向子树
	int rtag;//==0 时,指向子树
}TreeNode;

void CreateTree(TreeNode** T, char* data, int* index);//先序二叉树的创建

void preThreadTree(TreeNode* T, TreeNode** prev);//先序线索二叉树

TreeNode* getNext(TreeNode* node);//找到后继

//二叉树的创建
void CreateTree(TreeNode** T, char* data, int* index)//先序创建
{
	//创建二叉树的逻辑

	//主函数写字符数组,直接传进来,写进二叉树
	//index为序号 在主函数定义,为全局变量
	//向后遍历时加加,index需要一直改变,所以指针传递

	char ch;
	ch = data[*index];
	*index += 1;
	if (ch == '#')
	{
		*T = NULL;
	}
	else
	{
		*T = (TreeNode*)malloc(sizeof(TreeNode)); //开辟一个节点的空间
		//赋值
		(*T)->data = ch;

		(*T)->ltag = 0;
		(*T)->rtag = 0;

		//递归思想  左子树和右子树都是树
		//创建左子树
		CreateTree(&((*T)->lchild), data, index);
		//创建右子树
		CreateTree(&((*T)->rchild), data, index);
	}

}

//先序线索二叉树
void preThreadTree(TreeNode* T, TreeNode** prev)//prev代表当前结点的前驱
{
	if (T)
	{
		//do something
		if (T->lchild == NULL)//当前结点的左孩子为空
		{
			T->ltag = 1;//标志为1
			T->lchild = *prev;//左孩子指向当前结点的前驱
		}

		if (*prev != NULL && (*prev)->rchild == NULL)//当前结点的前驱不为空,并且前驱的右孩子为空
		{
			(*prev)->rtag = 1;   //前驱的标志为1
			(*prev)->rchild = T; //递归中T代表的是当前结点,前驱的后继指向当前结点
		}
		*prev = T;//每次处理完,前驱变为当前结点


		//因为如果左右孩子为空,左右孩子将会指向前驱和后继
		//此时递归,就将前驱和后继传进,而原本则是想让左右子树结点传入递归
		//所以需要判断是否发生改变
		if (T->ltag == 0)
		{
			preThreadTree(T->lchild, prev);
		}

		if (T->rtag == 0)
		{
			preThreadTree(T->rchild, prev);
		}

	}

}

//不需要找到第一个结点的函数,因为先序遍历,第一个结点就为根节点
//找到后继
TreeNode* getNext(TreeNode* node)
{
	if (node->rtag == 1)//无右孩子,右孩子指向后继
	{
		return node->rchild;
	}

	else//有右孩子,则此节点为某一子树的根,先序遍历的顺序:根、左、右
	{
		if (node->ltag == 0)//如果有左孩子
		{
			return node->lchild;//此节点之后应为其左孩子
		}

		else//有右孩子,无左孩子,则根之后为右孩子
		{
			return node->rchild;
		}
	}
}

int main()
{
	int index = 0;
	char data[] = { 'A','B','#','#','C','#','#' };
	TreeNode* T;
	CreateTree(&T, data, &index);

	TreeNode* prev = NULL;

	preThreadTree(T, &prev);

	prev->rtag = 1;
	prev->rchild = NULL;

	for (TreeNode* node = T; node != NULL; node = getNext(node))
	{
		printf("%c ", node->data);
	}
	printf("\n");

	return 0;
}

        (3)后序线索二叉树

#include<stdio.h>
#include<stdlib.h>

typedef struct TreeNode
{
	char data;
	struct TreeNode* lchild;
	struct TreeNode* rchild;
	struct TreeNode* parent;

	int ltag;//==0 时,指向子树
	int rtag;//==0 时,指向子树
}TreeNode;

void CreateTree(TreeNode** T, char* data, int* index, TreeNode* parent);//先序二叉树的创建

void postThreadTree(TreeNode* T, TreeNode** prev);//后序线索二叉树

TreeNode* getFirst(TreeNode* T);// 找到最左子树,即中序遍历的第一个

TreeNode* getNext(TreeNode* node);//找到后继

//二叉树的创建
void CreateTree(TreeNode** T, char* data, int* index,TreeNode* parent)//先序创建
{
	//创建二叉树的逻辑

	//主函数写字符数组,直接传进来,写进二叉树
	//index为序号 在主函数定义,为全局变量
	//向后遍历时加加,index需要一直改变,所以指针传递

	char ch;
	ch = data[*index];
	*index += 1;
	if (ch == '#')
	{
		*T = NULL;
	}
	else
	{
		*T = (TreeNode*)malloc(sizeof(TreeNode)); //开辟一个节点的空间
		//赋值
		(*T)->data = ch;

		(*T)->ltag = 0;
		(*T)->rtag = 0;
		(*T)->parent = parent;

		//递归思想  左子树和右子树都是树
		//创建左子树
		CreateTree(&((*T)->lchild), data, index,*T);
		//创建右子树
		CreateTree(&((*T)->rchild), data, index,*T);
	}

}

//后序线索二叉树
void postThreadTree(TreeNode* T, TreeNode** prev)//prev代表当前结点的前驱
{
	if (T)
	{
		postThreadTree(T->lchild, prev);

		postThreadTree(T->rchild, prev);//前面没有改变本结点的右孩子指向,所以可以直接传入

		//do something
		if (T->lchild == NULL)//当前结点的左孩子为空
		{
			T->ltag = 1;//标志为1
			T->lchild = *prev;//左孩子指向当前结点的前驱
		}

		if (*prev != NULL && (*prev)->rchild == NULL)//当前结点的前驱不为空,并且前驱的右孩子为空
		{
			(*prev)->rtag = 1;   //前驱的标志为1
			(*prev)->rchild = T; //递归中T代表的是当前结点,前驱的后继指向当前结点
		}
		*prev = T;//每次处理完,前驱变为当前结点

	}

}

// 后序遍历的第一个
//1.找到最左边的结点
//2.判断这个结点,是否有右子树
//  如果有,则继续寻找以右子树为根的最左边结点
//  如果没有,那么第一个结点为就是最左边的
TreeNode* getFirst(TreeNode* T)
{
	while (T->ltag == 0)
	{
		T = T->lchild;
	}

	if (T->rtag == 0)
	{
		return getFirst(T->rchild);
	}

	else
	{
		return T;
	}
}

//找到后继
// 左,右,根
//是根节点。next=NULL
//是左孩子 判断父亲右子树是否为空,为空next=parent;不为空next=getFirst(parent->rchild)
//是右孩子 next=parent
TreeNode* getNext(TreeNode* node)
{
	if (node->rtag == 1)
	{
		return node->rchild;
	}

	else
	{   //如果是根节点
		if (node->parent == NULL)
		{
			return NULL;
		}

		//如果是右孩子
		else if (node->parent->rchild == node)
		{
			return node->parent;
		}

		else//如果是左孩子
		{
			if (node->parent->rtag == 1)//父亲右子树为空next=parent
			{
				return node->parent;
			}

			else//父亲右子树不为空next=getFirst(parent->rchild)
			{
				return getFirst(node->parent->rchild);
			}
		}
	}
}

int main()
{
	int index = 0;
	char data[] = { 'A','B','D','#','#','E','#','#','C','#','#' };
	TreeNode* T;
	CreateTree(&T, data, &index,NULL);

	TreeNode* prev = NULL;

	postThreadTree(T, &prev);

	/*prev->rtag = 1;
	prev->rchild = NULL;*/ //因为后序遍历的尾是根节点,所以不能改变

	for (TreeNode* node = getFirst(T); node != NULL; node = getNext(node))
	{
		printf("%c ", node->data);
	}
	printf("\n");

	return 0;
}

10.创建二叉查找树(二叉排序树)

        (1)特点:     

                        特点1:每个结点的值均大于其左子树上任意节点的值

                        特点2:每个结点的值均小于其右子树上任意节点的值

        (2)代码实现

#include<stdio.h>
#include<stdlib.h>

typedef struct TreeNode
{
	int data;
	struct TreeNode* lchild;
	struct TreeNode* rchild;
}TreeNode;

TreeNode* bstSearch(TreeNode* T, int key);//二叉查找树的查找
void bstInsert(TreeNode** T, int data);//二叉查找树的插入
void bstDelete(TreeNode** T, int data);//二叉查找树的删除
void bstDestroy(TreeNode* T);//二叉查找树的释放 

void preOrder(TreeNode* T);//先序遍历

//二叉查找树的查找
TreeNode* bstSearch(TreeNode* T, int key)
{
	if (T)
	{
		if (T->data == key)
		{
			return T;
		}

		else if (T->data > key)
		{
			return bstSearch(T->lchild, key);
		}

		else //T->data<key
		{
			return bstSearch(T->rchild, key);
		}
	}

	else
	{
		return NULL;
	}
}

//二叉查找树的插入
void bstInsert(TreeNode** T, int data)
{
	if (*T == NULL)
	{
		*T = (TreeNode*)malloc(sizeof(TreeNode));
		
		(*T)->data = data;
		(*T)->lchild = NULL;
		(*T)->rchild = NULL;
	}

	else if ((*T)->data == data)
	{
		return;
	}

	else if (data < (*T)->data)
	{
		bstInsert(&((*T)->lchild), data);
	}

	else
	{
		bstInsert(&((*T)->rchild), data);
	}
}

//二叉查找树的删除
void bstDelete(TreeNode** T, int data)
{
	
}

//先序遍历
void preOrder(TreeNode* T)
{
	if (T == NULL)
	{
		return;
	}
	
	else
	{
		printf("%d ", T->data);

		preOrder(T->lchild);
		preOrder(T->rchild);
	}
	
}

//二叉查找树的释放 
void bstDestroy(TreeNode* T)
{
	if (T)
	{
		TreeNode* L = T->lchild;
		TreeNode* R = T->rchild;

		free(T);
		bstDestroy(L);
		bstDestroy(R);
	}

	else
	{
		return;
	}
}

int main()
{
	TreeNode* T = NULL;

	int nums[6] = { 8,6,10,9,11,23 };

	for (int i = 0; i < 6; i++)
	{
		bstInsert(&T, nums[i]);
	}
	preOrder(T);
	printf("\n");

	bstDestroy(T);
	return 0;
}

11.平衡二叉树

1.什么是平衡二叉树(AVL)?

平衡二叉树是一棵合理的二叉排序树

2.怎么保证合理?

平衡二叉树的左右子树高度差不超过1

3.如何构建一个平衡二叉树

①本质上跟构建二叉排序树一致

②在构建二叉排序树的过程中,如果发现树不符合高度差为1,需要进行调整。

LL RR RL LR 如果遇到多棵树不平衡,选择最小树

4.如何判断调整类型:

①找到失衡树的根节点 root

②找到导致树失衡的结点  node

  Node在root的哪一侧

③判断node在root孩子的哪一侧

 

 

 

 

 

 

RL型:取最后一个结点,作为父节点,将它原先的父亲作为自己的右孩子,将父亲的父亲作为自己的左孩子,如果自己有左孩子或右孩子的话,自己原先得左孩子,连接到父亲的父亲右孩子上,自己原先的右孩子连接到父亲的左孩子上

代码实现

#include<stdio.h>
#include<stdlib.h>

typedef struct TreeNode
{
	int data;
	int height;

	struct TreeNode* lchild;
	struct TreeNode* rchild;
}TreeNode;

int getHeight(TreeNode* node);//得到树的高度,为了使代码更清晰
int Max(int a, int b);//取最大值

//调整函数
void llRotation(TreeNode* node, TreeNode** root);//LL型
void rrRotation(TreeNode* node, TreeNode** root);//RR型

void avlInsert(TreeNode** T, int data);//向平衡二叉树中插入元素
void preOrder(TreeNode* T);//前序遍历

//得到树的高度,为了使代码更清晰
int getHeight(TreeNode* node)
{
	return node ? node->height : 0;
}

int Max(int a, int b)
{
	return a > b ? a : b;
}

//调整函数
//LL型
void llRotation(TreeNode* node, TreeNode** root)
{
	TreeNode* temp = node->lchild;
	node->lchild = temp->rchild;
	temp->rchild = node;

	//位置移动,其树的高度发生改变
	node->height = Max(getHeight(node->lchild), getHeight(node->rchild)) + 1;
	temp->height = Max(getHeight(temp->lchild), getHeight(temp->rchild)) + 1;

	*root = temp;//改变根节点
}

//RR型
void rrRotation(TreeNode* node, TreeNode** root)
{
	TreeNode* temp = node->rchild;//node是temp的父亲
	node->rchild = temp->lchild;//如果temp有左孩子,那么temp的父亲(即node)的右孩子指向temp的左孩子
	                            //temp的右孩子不用处理,还在原处
	temp->lchild = node;//孩子的左孩子指向父亲

	//位置移动,其树的高度发生改变
	node->height = Max(getHeight(node->lchild), getHeight(node->rchild)) + 1;
    temp->height = Max(getHeight(temp->lchild), getHeight(temp->rchild)) + 1;

	*root = temp;//改变根节点
}

//向平衡二叉树中插入元素
void avlInsert(TreeNode** T, int data)
{
	if (*T == NULL)
	{
		*T = (TreeNode*)malloc(sizeof(TreeNode));

		(*T)->data = data;
		(*T)->height = 0;
		(*T)->lchild = NULL;
		(*T)->rchild = NULL;
	}
	else if (data == (*T)->data)
	{
		return;
	}

	else if (data < (*T)->data)
	{
		avlInsert(&((*T)->lchild), data);
		//拿到当前结点左右子树的高度
		int lHeight = getHeight((*T)->lchild);
		int rHeight = getHeight((*T)->rchild);

		//判断高度差
		if (lHeight - rHeight == 2)
		{
			if (data < (*T)->lchild->data)//LL调整
			{
				llRotation(*T, T);
			}
			else//LR调整=先RR(root->lchild)再LL(root)
			{
				rrRotation((*T)->lchild, &((*T)->lchild));

				llRotation(*T, T);
			}
		}
	}
	
	else
	{
		avlInsert(&((*T)->rchild), data);

		//拿到当前结点左右子树的高度
		int lHeight = getHeight((*T)->lchild);
		int rHeight = getHeight((*T)->rchild);

		//判断高度差
		if (rHeight - lHeight == 2)
		{
			if (data > (*T)->rchild->data)//RR调整
			{
				rrRotation(*T, T);
			}
			else//RL调整=先LL(root->rchild)再RR(root)
			{
				llRotation((*T)->rchild, &((*T)->rchild));
				rrRotation(T, *T);
			}
		}
	}

	(*T)->height = Max(getHeight((*T)->lchild), getHeight((*T)->rchild)) + 1;
}

//前序遍历
void preOrder(TreeNode* T)
{
	if (T)
	{
		printf("%d ", T->data);

		preOrder(T->lchild);
		preOrder(T->rchild);
	}
	else
	{
		return;
	}
}

int main()
{
	TreeNode* T = NULL;
	int nums[5] = { 8,7,9,5,6 };

	for (int i = 0; i < 5; i++)
	{
		avlInsert(&T, nums[i]);
	}

	preOrder(T);
	printf("\n");

	return 0;
}

12.哈夫曼树

  1. 权值:                                         根

树的结点赋值,这个点称为权值。           1          2

  1. 带权路径长度:每个叶子节点的权值乘以根节点到此结点的边个数
  2. 什么是哈夫曼树?保证所有叶子节点的带权路径长度 和最小
  3. 如何通过权值 结点列表 生成哈夫曼树?
    • 从节点列表中选出 第一小和第二小的结点,并组成一棵树,父亲结点的权值=两节点的权值之和
    • 将生成新树再次放入结点列表中,重复第一步,直到列表中只剩一个结点
  4. 哈夫曼编码

左边线为0,右边线为1

  1. 哈夫曼树的特点
  1. 没有度为1的结点
  2. 哈夫曼树的任意非叶子结点的左右子树交换之后,仍是哈夫曼树
  3. N个叶子节点的哈夫曼树共有2n-1个节点
  4. 对于同一种权值,存在不同构的两棵哈夫曼树
  5. 代码实现
    #include<stdio.h>
    #include<stdlib.h>
    
    typedef struct TreeNode {
    	int weight;//权值
    	int parent;
    	int lchild;
    	int rchild;
    }TreeNode;
    
    typedef struct HFTree
    {
    	TreeNode* data;
    	int length;
    }HFTree;
    
    HFTree* InitTree(int* weight, int length);//初始化
    int* selectMin(HFTree* T);//选择权值最小的
    
    void CreateHFTree(HFTree* T);//创建
    
    void preOrder(HFTree* T, int index);//层序遍历
    
    //初始化
    HFTree* InitTree(int* weight,int length)//weight权值列表
    {
    	HFTree* T = (HFTree*)malloc(sizeof(HFTree));
    
    	T->data = (TreeNode*)malloc(sizeof(TreeNode) * (2 * length - 1));
    	T->length = length;
    	
    	for (int i = 0; i < length; i++)
    	{
    		T->data[i].weight = weight[i];
    		T->data[i].parent = 0;
    		T->data[i].lchild = -1;
    		T->data[i].rchild = -1;
    	}
    
    	return T;
    }
    
    //选择权值最小的两个
    int* selectMin(HFTree* T)
    {
    	int min = 10000;
    	int secondMin = 10000;
    
    	int minIndex = 0;
    	int secondIndex = 0;
    
    	for (int i = 0; i < T->length; i++)
    	{
    		if (T->data[i].parent == 0)
    		{
    			if (T->data[i].weight < min)
    			{
    				min = T->data[i].weight;
    				minIndex = i;
    			}
    		}
    	}
    
    	for (int i = 0; i < T->length; i++)
    	{
    		if (T->data[i].parent == 0 && i != minIndex)
    		{
    			if (T->data[i].weight < secondMin)
    			{
    				secondMin = T->data[i].weight;
    				secondIndex = i;
    			}
    		}
    	}
    
    	int* res = (int*)malloc(sizeof(int) * 2);
    	res[0] = minIndex;
    	res[1] = secondIndex;
    
    	return res;
    }
    
    //创建
    void CreateHFTree(HFTree* T)
    {
    	int* res;
    	int min;
    	int secondMin;
    
    	int length = T->length * 2 - 1;
    	int length1 = T->length;
    	for (int i = length1; i < length; i++)
    	{
    		res = selectMin(T);
    		min = res[0];
    		secondMin = res[1];
    
    		T->data[i].weight = T->data[min].weight + T->data[secondMin].weight;//父亲的权值
    		T->data[i].parent = 0;
    		T->data[i].lchild = min;
    		T->data[i].rchild = secondMin;
    
    		T->data[min].parent = i;
    		T->data[secondMin].parent = i;
    
    		T->length++;
    	}
    }
    
    //层序遍历
    void preOrder(HFTree* T, int index)
    {
    	if (index != -1)//=-1树为空
    	{
    		printf("%d ", T->data[index].weight);
    		preOrder(T, T->data[index].lchild);
    		preOrder(T, T->data[index].rchild);
    	}
    	/*else
    	{
    		return;
    	}*/
    }
    
    int main()
    {
    	int weight[4] = { 1,2,3,4 };
    
    	HFTree* T = InitTree(weight, 4);
    
    	//int* res = selectMin(T);
    	//printf("res[0]=%d", res[0]);
    
    	CreateHFTree(T);
    	preOrder(T, T->length - 1);//T->length 为根节点下标
    	printf("\n");
    
    	return 0;
    }

    13.翻转二叉树部分代码

    #include<stdio.h>
    
    TreeNode invertTree(TreeNode root)
    {
    	if(root==NULL)
    	{
    		return NULL;
    	}
    	
    	invertTree(root.lchild);
    	invertTree(root.rchild);
    	
    	TreeNode temp=root.lchild;
    	root.lchild=root.rchild;
    	root.rchild=temp;
    	
    	return root;
    }

    14.判断是否为对称二叉树部分代码

    #include<stdio.h>
    
    bool deepCheck(TreeNode left,TreeNode right)
    {
    	//递归的终止条件是两个结点都为空
    	//或者两个结点一个为空
    	//或者两个结点不相等
    	
    	if(left==NULL&right==NULL)
    	{
    		return true;
    	 } 
    	 
    	if(left==NULL||right==NULL)
    	{
    		return false;
    	}
    	if(left.data!=right.data)
    	{
    		return false;
    	}
    	
    	//再递归比较 左节点的左孩子 和 右节点的右孩子
    	//以及比较 左结点的右孩子 和 右结点的左孩子
    	return deepCheck(left.lchild,right.rchild)&&deepCheck(left.rchild,right.lchild); 
     } 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值