数据结构ky备战Day3 - 树

存储结构定义

typedef struct Node{
	DataType data; 
	struct Node *lchild, *rchild;
	int ltag = 0, rtag = 0;
}BitNode, *BiTree;

树的遍历

// 基操1 先序 中序 后序
void Traverse(BiTree root){
	if (root != NULL){
		// visit(); 先序 访问根节点
		Traverse(root->lchild); // 递归遍历左子树
		// visit(); 中序 访问根节点
		Traverse(root->rchild); // 递归遍历右子树
		// visit(); 后序 访问根节点
	}
}

先序 - 统计叶子数目(遍历)

// 应用 统计叶子结点数目/打印输出
int count = 0;
void leaf(BiTree root){
	if(root != NULL){
		leaf(root->lchild);
		// 访问时统计叶子结点操作
		if(root->lchild == NULL && root->rchild == NULL){
			count ++;
			printf("%d ", root->data);
		}
		leaf(root->rchild);
	}
}

先序- 建立链式二叉树(malloc创建结点)

// 基操2 建立一颗链式二叉树
void createBiTree(BiTree *T){
	char ch = getchar(); 
	if(ch = '#') *T = NULL;
	else {
		*T = (BiTree)malloc(sizeof(BitNode));
		(*T)->data = ch;
		createBiTree(&(*T)->lchild);
		createBiTree(&(*T)->rchild);
	}
}

先序- 统计二叉树高度(代参遍历)

// 法二 前序遍历 求二叉树高度 需要全局变量存储深度
int depth = 0;
void preTreeDepth(BiTree T, int h)
{
	if(T != NULL){
		if(h > depth) depth = h;
		preTreeDepth(T->lchild, h + 1); // 遍历左子树
		preTreeDepth(T->rchild, h + 1); // 遍历右子树
	}
}

中序-非递归解法(模拟栈操作)

// 基操4 中序遍历非递归算法
// 从根节点出发以此遍历左子树 非空的进栈,为空的话 弹出栈顶 遍历右子树
void inOrder(BiTree root){
	InitStack(&S);
	BiTree p = root;
	while(p != NULL || !isEmpty(S)){
		if(p!=NULL){
			push(&S, p);
			p = p->lchild;
		}
		else{
			pop(&S, &p);
			printf("%d", p->data);
			p = p->rchild;
		}
	}
}

中序-二叉线索树(pre指针保留)

// 基操5 以中序遍历为例建立一颗线索二叉树
BiTree pre = NULL;
void Inthread(BiTree root){
	if(root != NULL){
		Inthread(root->lchild);
		// 对结点进行visit()函数 利用空链域
		if(root->lchild == NULL) root->lchild == pre, root->ltag = 1; // 前继线索
		if(root->rchild == NULL && pre != NULL) pre->rchild = root,root->rtag = 1; // 后继线索
		pre = root;
		Inthread(root->rchild);
	}
}

二叉线索树前驱后继查找

// 基操6 中序线索树 找前继结点
BitNode* findPre(BitNode *p){
	BitNode *pre;
	// 如果ltag有线索,直接访问
	if(p->ltag == 1) pre = p->lchild;
	else{
		// 在p的左子树找最右下结点
		BitNode *q;
		for(q = p->lchild; q!=NULL; q = q->rchild);
		pre = q;
	}
	return pre;
}

// 基操7 中序线索树 找后继结点
BitNode* findNext(BitNode *p){
	BitNode *next;
	// 如果 rtag有线索,直接访问
	if(p->rtag == 1) next = p->rchild;
	else{
		// 在p的右子树中找最左下的结点
		BitNode *q;
		for(q = p->rchild; q != NULL; q = q->lchild);
		next = q;
	}
	return next;
}

// ps: 对于先序的线索遍历树:后继就是左孩子结点,如果没有左孩子就是右孩子,没有右孩子就是rchild 指针域
// ps2:对于先序的,前继要先看是不是根,是的话就是NULL,再看是不是双亲结点的左孩子,或者右孩子(左孩子为空)那双亲就是,如果左孩子不为空,那就是左孩子

中序-遍历二叉线索树(初始结点->rchild)

// 基操8 遍历中序二叉线索树
// 首先确定初始结点
BitNode* findFirst(BiTree T){
	if(T == NULL) return NULL;
	BitNode *p = T;
	while(p != NULL) p = p->lchild;
	return p;
}

// 从第一个结点开始 一直顺着rchild找
void TinOrder(BiTree T){
	BitNode *p = findFirst(T);
	while (p){
		printf("%d ", p->data);
		p = findNext(p);
	}
}

逆中序- 横向树形二叉树(代参遍历)

// 基操3 横向树形二叉树 逆中序实操
void printTree(BiTree T, int Layer){
	if(T == NULL) return ;
	printTree(T->rchild, Layer + 1);
	// 根据当前所在层数打印
	for(int i = 0; i < Layer; i++ ) printf(" ");
	printf("%c\n", T->data);
	printTree(T->lchild, Layer + 1);
}

后序 - 统计叶子数目(递推)

// 法二 后续遍历递归 统计个数
int leaf2(BiTree root){
	int leafCount = 0;
	
	if(root == NULL)  leafCount = 0;
	else if(root->lchild == NULL && root->rchild == NULL) leafCount = 1;
	else{
		// 叶子个数 = 左子树叶子数 + 右子树叶子数
		leafCount = leaf2(root->lchild) + leaf2(root->rchild);
		return leafCount;
	}
}

后序- 统计二叉树高度(递推)

// 基操3 后续遍历计算二叉树高度经典程序
// 核心递归式 H = max{H(L),H(R)} + 1
int getTreeHeight(BiTree T){
	int hl, hr; // 分别表示左子树高度 右子树高度
	if(T != NULL){
		hl = getTreeHeight(T->lchild); 
		hr = getTreeHeight(T->rchild);
		return (hl > hr ? hl : hr) +  1; // 非空树是左子树深度和右子树较大者 + 1
	}
	else return 0; // 空树 高度为0
}

后序-判断两颗树是否相似(同时遍历)

// 题1:判断两棵树是否为相似(形状相同)
// 后续遍历的一个应用 实际上我可以看空链域的对应父节点是否相同
bool isLike(BiTree T1, BiTree T2){
	if(T1 == NULL && T2 == NULL) return true; // 只有两者都为空时,形状才相同
	else if(T1 ==NULL || T2==NULL) return false; // 两者有一个为空,那么形状一定不同
	else{
		// 两个都是非空结点,继续往左右子树递归
		int test1 = isLike(T1->lchild, T2->lchild); // 左子树是否相同
		int test2 = isLike(T2->lchild, T2->rchild); // 右子树是否相同
		return (test1 && test2);
	}
}

层序遍历(队列)

// 题2 树的层次遍历
int LayerTraverse(BiTree T){
	SeqQueue Q;
	BiTree p;
	InitQueue(&Q);
	if(T == NULL) return Error;
	EnQueue(&Q, T); // 插入T的根节点
	while(Q.front != Q.rear){
		DeQueue(&Q, &p);
		printf("%d ", p->data);
		if(p->lchild) EnQueue(&Q, p->lchild);
		if(p->rchild) EnQueue(&Q, p->rchild);
	}
	return OK;
}

附录:队列和栈基操

typedef struct
{
	StackElemType elem[Stack_Size]; /* elem是存放栈中的一维数组 */
	int top; /* 栈顶元素的下标,空栈为-1 */
}SeqStack;

SeqStack S;

// 基操 1 初始化
void InitStack(SeqStack *S){
	S->top = -1;
}

// 基操2 入栈 注意判满 , 我用的是栈顶指针指向元素的版本
int push(SeqStack *S, StackElemType x)
{
	if(S->top == Stack_Size) return Error; // 栈满
	
	S->top ++ ;
	S->elem[S->top] = x ;
	return OK;
}

// 基操3 出栈 注意判空
int pop(SeqStack *S, StackElemType *x){
	if(S->top == -1) return Error;
	
	*x = S->elem[S->top]; 
	S->top--; 
	return OK;
}

// 基操4 取栈顶元素 注意判空
int getTop(SeqStack S, StackElemType *x){
	if(S.top == -1) return Error;
	
	*x = S.elem[S.top];
	return OK;
}

bool isEmpty(SeqStack S){
	return S.top == -1;
}

队列

typedef struct{
	QueueElemType elem[MAXSIZE];
	int front; // 指向表头
	int rear; // 指向表尾
}SeqQueue;

// 循环队列基操1 初始化
void InitQueue(SeqQueue *Q){
	Q->front = Q->rear = 0;
}

// 循环队列基操2 入队 注意队满的情况
int EnQueue(SeqQueue *Q, QueueElemType x){
	if((Q->rear + 1) % MAXSIZE == Q->front) return Error;
	Q->elem[Q->rear] = x;
	Q->rear = (Q->rear + 1) % MAXSIZE;
	return OK;
}


// 循环队列基操3 出队 注意队空的情况
int DeQueue(SeqQueue *Q, QueueElemType *x){
	if(Q->front == Q->rear) return Error;
	*x = Q->elem[Q->front]; // 队首元素出队
	Q->front = (Q->front + 1) % MAXSIZE;
	return OK;
}

// 队列基操 判空
int isEmpty(SeqQueue Q){
    return Q->front == Q->rear;
}
posted @ 2023-10-14 22:24  yuezi2048  阅读(8)  评论(0编辑  收藏  举报