数据结构(4)二叉树的基本操作——C语言代码实现

作为一个数据结构学习的新手,这篇文章也算是笔记了,请根据目录食用

目录

单链表的操作(C语言)

一、存储结构

二、前序递归建立二叉树

三、前序递归遍历二叉树

四、前序非递归遍历二叉树

五、中序非递归遍历二叉树

六、后序非递归遍历二叉树

七、层序遍历

八、层序遍历Pro(按层输出)(舍去,采用Pro++)

九、层序遍历Pro++(按层输出)

层序遍历Pro和层序遍历Pro++的对比反思

十、树的深度——递归实现

十一、结点数统计——递归实现

十二、度为0的结点的个数——递归实现

十三、度为1的结点的个数——递归实现

十四、度为2的结点的个数——递归实现

实现的总代码


单链表的操作(C语言)

一、存储结构

typedef struct Bitnode {
	Elemtype data;
	Bitnode *lchild, *rchild;
} Bitnode, *Bitree;

二、前序递归建立二叉树

void Creat(Bitree &T) {
	Elemtype n;
	scanf("%c", &n);
	if (n == '#')
		T = NULL;
	else {
		T = (Bitnode *)malloc(sizeof(Bitnode));
		T->data = n;
		T->lchild = NULL;
		T->rchild = NULL;
		Creat(T->lchild);
		Creat(T->rchild);
	}
}

三、前序递归遍历二叉树

void Inorder(Bitree T) {
	if (T) {
		printf("%c", T->data);//根
		Inorder(T->lchild);//左
		Inorder(T->rchild);//右
	}
}

中序递归和后序递归只需要调整语句的顺序即可,中序左根右,后序左右根

四、前序非递归遍历二叉树

void Fir_Inorder(Bitree T) {
	Bitree stack[100], p = T;    //用栈来实现
	int top = 0;
	while (p || top) {
		while (p) {
			stack[top] = p;
			top++;
			printf("%c", p->data);
			p = p->lchild;
		}
		top--;
		p = stack[top];
		p = p->rchild;
	}
}

【思路】由于前序的输出顺序为根左右,当遇到根的时候,将根压到栈里,并输出的data,再检查它的左孩子,当没有孩子的时候(p==NULL),指向栈顶,然后检查它的孩子;当有左孩子的时候(p ! = NULL),左孩子成为现在的根节点,重复上述步骤,直至所有结点都输出(栈空)。

五、中序非递归遍历二叉树

void Mid_Inorder(Bitree T) {
	Bitree stack[100], p = T;
	int top = 0;
	while (p || top) {
		while (p) {
			stack[top] = p;
			top++;
			p = p->lchild;
		}
		top--;
		p = stack[top];
		printf("%c", p->data);
		p = p->rchild;
	}
}

【思路】和前序非递归遍历的思路基本一致,只是输出顺序发生改变

六、后序非递归遍历二叉树

void Las_Inorder(Bitree T) {
	Bitree stack[100], p = T;
	int tag[100];
	int top = 0;
	while (p || top) {
		while (p) {
			top++;
			stack[top] = p;
			p = p->lchild;
			tag[top] = 1;
		}
		if (tag[top] == 1) {
			p = stack[top];
			tag[top] = 2;
			p = p->rchild;
		} else {
			p = stack[top];
			printf("%c", p->data);
			top--;
			p = NULL;
		}
	}
}

【思路】由于后序遍历的输出顺序为左右根,也就是说,需要检查并分别输出根节点的左右孩子后,才能输出根节点,也就是说在第三次访问根节点时才输出该根节点。

第一次访问,入栈,并设置标志(tag[top] = 1),访问该结点的左孩子;

第二次访问(该结点的左孩子为空),指针指向栈顶元素,并设置标志(tag[top] = 2),访问该结点的右孩子;

第三次访问(该结点的右孩子为空),指针指向栈顶元素,输出栈顶元素,栈顶指针减一。

        关键的是这个p = NULL,现在这个位置已经输出了(相当于左孩子或者右孩子为空了),需要让程序去处理前一个结点(前一个结点必然是被访问过一次或两次的),所以指针p指空,这样才可以进行第二次或第三次的访问。

七、层序遍历

void Level(Bitree T) {
	Bitree Q[100], p = T;   //用队列来实现
	int rear, front;
	rear = front = 0;
	rear++;
	Q[rear] = p;
	while (rear != front) {
		front++;
		p = Q[front];
		printf("%c", p->data);
        //每输出一个队头的结点,就要将它的左右孩子入队
		if (p->lchild) {
			rear++;
			Q[rear] = p->lchild;
		}
		if (p->rchild) {
			rear++;
			Q[rear] = p->rchild;
		}
	}
}

【思路】每输出一个队头的结点,就要将它的左右孩子入队,实现层序遍历

 当一层的结点全被访问过后,下一层的结点也全部入队了(对于实现按层输出有用的一个点)

【进一步思考】这样的输出结果,并不能让人直观地看出,哪个数据是属于哪一层的,于是有了下面的代码

八、层序遍历Pro(按层输出)(舍去,采用Pro++)

void Lever_pro(Bitree T) {
	Bitree Q[100], p = T;
	int rear, front, tag[10], i = 1, j = 0, k = 0, num = 0;
	//i标志层数,j执行循环,k标志每层的结点数,由于k每次需要归零,
	//故还需要设置变量num记录每层的结点数并参与执行循环
	rear = front = 0;
	rear++;
	Q[rear] = p;
	num = 1;
	while (rear != front) {
		printf("第%d层的数据为:", i);
		for (j = 0; j < num; j++) {
			front++;
			p = Q[front];
			printf("%c", p->data);
			if (p->lchild) {
				rear++;
				Q[rear] = p->lchild;
				k++;
			}
			if (p->rchild) {
				rear++;
				Q[rear] = p->rchild;
				k++;
			}
		}
		num = k;
		k = 0;
		printf("\n");
		i++;
	}
}

【思路】记录每一层元素的个数,进而控制输出次数。

【进一步思考】循环的时间复杂度就要比较高了,那么可以不使用循环吗?可以。只要每次输出之后,将现在所在层的结点个数减一不就完事了嘛。

        当然,还需要进行一个判断,判断结点数是否为0,当当前层结点数为零的时候,开始输出下一层,需要先将下一层结点数赋值给当前层结点数。代码如下:

九、层序遍历Pro++(按层输出)

void Lever_pro_pro(Bitree T) {
	Bitree Q[100], p = T;
	int rear, front, nowcount = 0, nextcount = 0, i = 1, tag = 0;
	rear = front = 0;
	rear++;
	Q[rear] = p;
	nowcount = 1;
	while (rear != front) {
		if (tag == 0)
			printf("第%d层的元素为:", i);
		front++;
		p = Q[front];
		printf("%c", p->data);
		nowcount--;
		if (p->lchild) {
			rear++;
			Q[rear] = p->lchild;
			nextcount++;
		}
		if (p->rchild) {
			rear++;
			Q[rear] = p->rchild;
			nextcount++;
		}
		tag = 1;
		if (nowcount == 0) {
			nowcount = nextcount;
			nextcount = 0;
			i++;
			printf("\n");
			tag = 0;
		}
	}
}

【分析】只需在输出的时候设置一个标志(nowcount)控制输出即可,其余代码与“层序遍历”基本相同

层序遍历Pro和层序遍历Pro++的对比反思

设置的标志该怎么巧妙地使用?

        根据“当一层的结点全被访问过后,下一层的结点也全部入队了”这一个特点进行考虑

十、树的深度——递归实现

int Depth(Bitree T) {
	int m, n;
	if (T == NULL)
		return 0;
	else {
		m = Depth(T->lchild);
		n = Depth(T->rchild);
		if (m > n)
			return m + 1;
		else
			return n + 1;
	}
}

十一、结点数统计——递归实现

int NodeCount(Bitree T) {
	if (T)
		return NodeCount(T->lchild) + NodeCount(T->rchild) + 1;
	else
		return 0;

}

【思路】结点数 = 根节点  +  左子树结点数  +  右子树结点数

十二、度为0的结点的个数——递归实现

int Node0Count(Bitree T) {
	if (T) {
		if (T->lchild == NULL && T->rchild == NULL)
			return Node0Count(T->lchild) + Node0Count(T->rchild) + 1;
		else
			return Node0Count(T->lchild) + Node0Count(T->rchild);
	} else
		return 0;
}

十三、度为1的结点的个数——递归实现

int Node1Count(Bitree T) {
	if (T) {
		if (T->lchild != NULL && T->rchild == NULL || T->lchild == NULL && T->rchild != NULL)
			return Node1Count(T->lchild) + Node1Count(T->rchild) + 1;
		else
			return	Node1Count(T->lchild) + Node1Count(T->rchild);
	} else
		return 0;

}

十四、度为2的结点的个数——递归实现

int Node2Count(Bitree T) {
	if (T) {
		if (T->lchild != NULL && T->rchild != NULL)
			return Node2Count(T->lchild) + Node2Count(T->rchild) + 1;
		else
			return Node2Count(T->lchild) + Node2Count(T->rchild);
	}
	return 0;
}

实现的总代码

#include <stdio.h>
#include <stdlib.h>
//二叉树的基本操作
typedef char Elemtype;

typedef struct Bitnode {
	Elemtype data;
	Bitnode *lchild, *rchild;
} Bitnode, *Bitree;

//前序递归建立二叉树
void Creat(Bitree &T) {
	Elemtype n;
	scanf("%c", &n);
	if (n == '#')
		T = NULL;
	else {
		T = (Bitnode *)malloc(sizeof(Bitnode));
		T->data = n;
		T->lchild = NULL;
		T->rchild = NULL;
		Creat(T->lchild);
		Creat(T->rchild);
	}
}

//前序递归遍历二叉树
void Inorder(Bitree T) {
	if (T) {
		printf("%c", T->data);
		Inorder(T->lchild);
		Inorder(T->rchild);
	}
}

//前序非递归遍历二叉树
void Fir_Inorder(Bitree T) {
	Bitree stack[100], p = T;
	int top = 0;
	while (p || top) {
		while (p) {
			stack[top] = p;
			top++;
			printf("%c", p->data);
			p = p->lchild;
		}
		top--;
		p = stack[top];
		p = p->rchild;
	}
}

//中序非递归遍历
void Mid_Inorder(Bitree T) {
	Bitree stack[100], p = T;
	int top = 0;
	while (p || top) {
		while (p) {
			stack[top] = p;
			top++;
			p = p->lchild;
		}
		top--;
		p = stack[top];
		printf("%c", p->data);
		p = p->rchild;
	}
}

//后序非递归遍历
void Las_Inorder(Bitree T) {
	Bitree stack[100], p = T;
	int tag[100];
	int top = 0;
	while (p || top) {
		while (p) {
			top++;
			stack[top] = p;
			p = p->lchild;
			tag[top] = 1;
		}
		if (tag[top] == 1) {
			p = stack[top];
			tag[top] = 2;
			p = p->rchild;
		} else {
			p = stack[top];
			printf("%c", p->data);
			top--;
			p = NULL;
		}
	}
}

//层序遍历
void Level(Bitree T) {
	Bitree Q[100], p = T;
	int rear, front;
	rear = front = 0;
	rear++;
	Q[rear] = p;
	while (rear != front) {
		front++;
		p = Q[front];
		printf("%c", p->data);
		if (p->lchild) {
			rear++;
			Q[rear] = p->lchild;
		}
		if (p->rchild) {
			rear++;
			Q[rear] = p->rchild;
		}
	}
}

//按层输出
void Lever_pro(Bitree T) {
	Bitree Q[100], p = T;
	int rear, front, tag[10], i = 1, j = 0, k = 0, num = 0;
	//i标志层数,j执行循环,k标志每层的结点数,由于k每次需要归零,
	//故还需要设置变量num记录每层的结点数并参与执行循环
	rear = front = 0;
	rear++;
	Q[rear] = p;
	num = 1;
	while (rear != front) {
		printf("第%d层的数据为:", i);
		for (j = 0; j < num; j++) {
			front++;
			p = Q[front];
			printf("%c", p->data);
			if (p->lchild) {
				rear++;
				Q[rear] = p->lchild;
				k++;
			}
			if (p->rchild) {
				rear++;
				Q[rear] = p->rchild;
				k++;
			}
		}
		num = k;
		k = 0;
		printf("\n");
		i++;
	}
}

void Level_pro_pro(Bitree T) {
	Bitree Q[100], p = T;
	int rear, front, nowcount = 0, nextcount = 0, i = 1, tag = 0;
	rear = front = 0;
	rear++;
	Q[rear] = p;
	nowcount = 1;
	while (rear != front) {
		if (tag == 0)
			printf("第%d层的元素为:", i);
		front++;
		p = Q[front];
		printf("%c", p->data);
		nowcount--;
		if (p->lchild) {
			rear++;
			Q[rear] = p->lchild;
			nextcount++;
		}
		if (p->rchild) {
			rear++;
			Q[rear] = p->rchild;
			nextcount++;
		}
		tag = 1;
		if (nowcount == 0) {
			nowcount = nextcount;
			nextcount = 0;
			i++;
			printf("\n");
			tag = 0;
		}
	}
}


//树的深度
int Depth(Bitree T) {
	int m, n;
	if (T == NULL)
		return 0;
	else {
		m = Depth(T->lchild);
		n = Depth(T->rchild);
		if (m > n)
			return m + 1;
		else
			return n + 1;
	}
}

//树的结点数
int NodeCount(Bitree T) {
	if (T)
		return NodeCount(T->lchild) + NodeCount(T->rchild) + 1;
	else
		return 0;

}

//统计二叉树中度为0的结点的个数
int Node0Count(Bitree T) {
	if (T) {
		if (T->lchild == NULL && T->rchild == NULL)
			return Node0Count(T->lchild) + Node0Count(T->rchild) + 1;
		else
			return Node0Count(T->lchild) + Node0Count(T->rchild);
	} else
		return 0;
}

//统计二叉树中度为1的结点的个数
int Node1Count(Bitree T) {
	if (T) {
		if (T->lchild != NULL && T->rchild == NULL || T->lchild == NULL && T->rchild != NULL)
			return Node1Count(T->lchild) + Node1Count(T->rchild) + 1;
		else
			return	Node1Count(T->lchild) + Node1Count(T->rchild);
	} else
		return 0;

}

//统计二叉树中度为2的结点的个数
int Node2Count(Bitree T) {
	if (T) {
		if (T->lchild != NULL && T->rchild != NULL)
			return Node2Count(T->lchild) + Node2Count(T->rchild) + 1;
		else
			return Node2Count(T->lchild) + Node2Count(T->rchild);
	}
	return 0;
}

int main() {
	Bitree T;
	int depth;
	Creat(T);
	printf("前序非递归的结果:");
	Fir_Inorder(T);
	printf("\n");
	printf("中序非递归的结果:");
	Mid_Inorder(T);
	printf("\n");
	printf("后序非递归的结果:");
	Las_Inorder(T);
	printf("\n");
	printf("层序递归的结果:");
	Level(T);
	printf("\n");
	Lever_pro(T);
	depth = Depth(T);
	printf("树的深度为:%d\n", depth);
	printf("树的结点的个数:");
	printf("%d\n", NodeCount(T));
	printf("度为0的结点的个数:");
	printf("%d\n", Node0Count(T));
	printf("度为1的结点的个数:");
	printf("%d\n", Node1Count(T));
	printf("度为2的结点的个数:");
	printf("%d\n", Node2Count(T));
	return 0;
}

【输出结果 】

猜你喜欢

转载自blog.csdn.net/qq_56117689/article/details/121716177