데이터 구조 연구 노트 - 이진 트리를 구현하기 위한 이진 트리 탐색 및 체인 저장 코드

1. 이진 트리 순회

이진 트리의 순회는 정해진 순서에 따라 트리의 모든 노드를 방문하는 것이며, 각 노드는 한 번만 방문됩니다. 이진 트리는 루트 노드(D), 왼쪽 하위 트리(L) 및 이진 트리의 첫 번째, 중간, 마지막 순회로 나눌 수 있는 오른쪽 하위 트리(R) 구성. 또한 다음 표와 같이 각 계층의 순회, 즉 레벨 순회가 있습니다.

횡단 노드를 방문하는 순서
선주문 순회 ~에 대한
중위순회 왼쪽오른쪽
후순위 순회 ~에 대한

여기에 이미지 설명을 삽입하세요.

이진 트리의 첫 번째, 중간, 마지막 순회는 재귀 알고리즘을 통해 구현할 수 있으며, 재귀 종료 조건은 T==NULL, 즉 이진 트리가 비어 있으면 순회가 종료됩니다.

(1) 이진 트리의 선주문 순회(DLR)

이진 트리의 선순서 순회에서는 루트 노드를 먼저 순회하고 루트 노드의 왼쪽 하위 트리를 순회한 다음 루트 노드의 오른쪽 하위 트리를 순회하여 모든 노드를 순서대로 순회합니다. 예를 들어 아래 그림의 이진 트리는 다음과 같습니다.여기에 이미지 설명을 삽입하세요.

선주문 순회 순서는 ABDCE입니다.

(2) 이진 트리의 순차 순회(LDR)

이진 트리의 순차 순회에서는 먼저 루트 노드의 왼쪽 하위 트리를 순회한 다음 루트 노드, 마지막으로 루트 노드의 오른쪽 하위 트리를 순회한 다음 모든 노드를 순회합니다. 순서 탐색(order traversal) 증가하는 순서의 시퀀스를 얻을 수 있습니다.

예를 들어, 이진 트리에서 순차 순회에 따르면 순차 순회 시퀀스의 마지막 노드는 루트 노드에서 시작하여 오른쪽 하위 트리를 따라 맨 아래 노드로 이동해야 함을 알 수 있습니다. 리프 노드, 그 다음 첫 번째 순차 순회 시퀀스와 순차 순회 시퀀스의 마지막 노드가 이 노드입니다. 리프 노드가 아닌 경우 왼쪽 자식 노드도 가지며 왼쪽 자식 노드는 선주문 순회 시퀀스의 마지막 노드입니다.

여기에 이미지 설명을 삽입하세요.
순차 순회 시퀀스는 DBACE입니다. 순차 순회 시퀀스의 마지막 노드는 루트 노드에서 시작하여 오른쪽 하위 트리를 따라 아래쪽 노드까지 이동해야 하므로 순차 순회 시퀀스의 마지막 노드는 E입니다. , 이 노드는 리프 노드이므로 선순 순회 시퀀스와 순순 순회 시퀀스의 마지막 노드가 이 노드입니다.

선서: ABDCE
중서: DBACE

(3) 이진 트리의 후위 순회(LRD)

이진 트리의 후위 순회에서는 먼저 루트 노드의 왼쪽 하위 트리를 순회한 다음 루트 노드의 오른쪽 하위 트리를 순회하고 마지막으로 루트 노드를 순회하는 방식으로 모든 항목이 순회됩니다. 노드는 순회됩니다. 즉, 이진 트리의 맨 아래 수준에서 상위 수준으로 순회합니다.
여기에 이미지 설명을 삽입하세요.
이 이진 트리의 후위 순회 시퀀스는 DBECA입니다.

(4) 선주문, 순차 및 후순 순회 관련 속성

  • 이진 트리의 선순 순회, 순순 순회, 후순 순회 순서에서 所有叶子结点的先后顺序是相同的.

예를 들어 이 이진 트리의 세 순회는 ABDCE, DBACE, DBECA이며, 리프 노드 D와 E의 순서는 항상 동일하고 D 노드가 항상 E 노드 앞에 옴을 알 수 있습니다.
여기에 이미지 설명을 삽입하세요.

  • 이진 트리가 빈 트리이거나 루트 노드만 있는 경우先序遍历序列和后序遍历序列相同; 이진 트리가 단일 오른쪽 가지 이진 트리이거나 격리된 노드인 경우 그先序遍历序列和中序遍历序列相同.

예를 들어, 다음 단일 오른쪽 분기 이진 트리에서 선순 순회 시퀀스와 순순 순회 시퀀스가 ​​동일하면 둘 다 ABC입니다.
여기에 이미지 설명을 삽입하세요.

  • 이진 트리가 빈 트리이거나 노드에 오른쪽 하위 트리가 없는 단일 가지 트리인 경우中序遍历序列和后序遍历序列相同.

예를 들어, 다음 이진 트리에서 모든 노드는 최대 왼쪽 하위 트리를 가지며 순차 순회 시퀀스와 후순 순회 시퀀스는 동일하며 둘 다 DCBA입니다.
여기에 이미지 설명을 삽입하세요.

  • 비어 있지 않은 이진 트리에 리프 노드가 하나만 있거나 이진 트리의 높이가 노드 수와 같은 경우先序遍历序列和后序遍历序列相反.

예를 들어, 다음 이진 트리에는 리프 노드가 하나만 있고 노드 수가 3개이므로 높이 h=3과 같습니다. 따라서 선순 순회 시퀀스와 후순 순회 시퀀스는 반대입니다. ABC 및 CBA:
여기에 이미지 설명을 삽입하세요.

(5) 이진 트리의 레벨 순회

계층적 순회에서는 레벨에 우선순위가 주어지며, 한 레이어의 모든 노드를 순회한 후 다음 레이어를 순회하며, 각 노드의 왼쪽 및 오른쪽 자식을 순서대로 순회합니다.
예를 들어 다음 이진 트리의 레벨 순회는 ABCDEFG입니다.
여기에 이미지 설명을 삽입하세요.
이진 트리의 레벨 순회는 체인 큐를 통해 구현할 수 있습니다. 첫째, 루트 이진 트리의 노드를 대기열에 추가한 후 루프에 진입합니다. 루프 조건은 대기열이 비어 있는지 여부입니다. 비어 있지 않으면 대기열의 현재 선두 노드가 대기열에서 제외됩니다. 이때 노드는 액세스되고 노드의 왼쪽 및 오른쪽 하위 노드가 대기열에 삽입됩니다.

  • 이진 트리가 빈 트리이거나 노드에 최대 오른쪽 하위 트리가 있는 경우中序遍历序列与层次遍历序列相同.

2. 바이너리 트리 구현 코드(체인 스토리지)

(1) 이진 트리의 정의

체인형 저장소 구조를 통해 구현된 코드는 다음과 같으며, 여기에는 데이터 필드와 두 개의 포인터가 포함됩니다.

#incldue<stdio.h>
/*二叉树的定义*/
typedef struct BNode {
    
    
	int data;		//数据域
	struct BNode *lchild,*rchild;		//左孩子、右孩子指针
} *BTree;

(2) 이진 트리 구축

이진 트리를 생성하고 이진 트리의 순서(널 포인터가 있는 이진 트리의 순서, 널 포인터도 포함됨), 즉 루트 노드, 왼쪽 하위 트리, right subtree [사용된 순서는 first order] Sequence], 빈 노드가 있으면 0으로 표현하고, 재귀적 방법을 사용하여 왼쪽과 오른쪽 자식 노드를 설정하며, 구현 코드는 다음과 같다.

#include <malloc.h>
/*二叉树的建立*/
BTree CreateTree() {
    
    
	BTree T;
	char ch;
	scanf("%c",&ch);
	getchar();	//getchar()用于接收每次输入字符结点后的回车符,从而以便输入下一个字符结点
	if(ch=='0')	//当为0时,将结点置空
		T=NULL;
	else {
    
    
		T=(BTree)malloc(sizeof(BTree));	//分配一个新的结点
		T->data=ch;
		printf("请输入%c结点的左孩子结点:",T->data);
		T->lchild=CreateTree();		//通过递归建立左孩子结点
		printf("请输入%c结点的右孩子结点:",T->data);
		T->rchild=CreateTree();		//通过递归建立右孩子结点
	}
	return T;
}

(3) 일반화된 테이블 출력 이진 트리

일반화된 테이블인 비어 있지 않은 이진 트리 T를 통해 설정된 이진 트리를 표시합니다. 왼쪽 자식 노드이거나 오른쪽 자식 노드인 경우 왼쪽 괄호 "(는 "를 출력하고, 왼쪽 하위 트리를 재귀적으로 처리하고, ","를 출력하여 노드를 분리한 다음, 오른쪽 하위 트리를 재귀적으로 처리하고, 오른쪽 대괄호 ")"를 출력하여 다음 루트 노드를 완성합니다. 왼쪽/오른쪽 노드가 처리됩니다.

/*广义表输出二叉树*/
void ShowTree(BTree T) {
    
    
	if(T!=NULL) {
    
    
		//当二叉树不为空时
		printf("%c",T->data);	//输入出该结点的数据域
		if(T->lchild!=NULL) {
    
    		//若该结点的左子树不为空
			printf("(");	//输出一个左括号
			ShowTree(T->lchild);	//通过递归继续输出结点的左子树结点下的各结点
			if(T->rchild!=NULL) {
    
    	//若该结点右子树不为空
				printf(",");	//输出一个逗号
				ShowTree(T->rchild);	//通过递归继续输出结点的右子树结点下的各结点
			}
			printf(")");	//输出一个右括号
		} else {
    
    	//若左子树为空,右子树不为空
			if(T->rchild!=NULL) {
    
    
				printf("(");	//输出一个左括号
				ShowTree(T->lchild);	//通过递归继续输出结点的左子树结点下的各结点
				if(T->rchild!=NULL) {
    
    		//若该结点的右子树不为空	
					printf(",");	//输出一个逗号
					ShowTree(T->rchild);	//通过递归继续输出结点的右子树结点下的各结点
				}
				printf(")");	//输出一个右括号
			}
		}
	}
}

예를 들어 아래와 같은 이진 트리가 있는데, 이진 트리가 구축되어 체인 저장 구조를 통해 출력됩니다.
여기에 이미지 설명을 삽입하세요.
코드는 다음과 같습니다.

#include <stdio.h>
#include <malloc.h>
/*1、二叉树的定义*/
typedef struct BNode {
    
    
	int data;		//数据域
	struct BNode *lchild,*rchild;		//左孩子、右孩子指针
} *BTree;

/*2、二叉树的建立*/
BTree CreateTree() {
    
    
	BTree T;
	char ch;
	scanf("%c",&ch);
	getchar();	//getchar()用于接收每次输入字符结点后的回车符,从而以便输入下一个字符结点
	if(ch=='0')	//当为0时,将结点置空
		T=NULL;
	else {
    
    
		T=(BTree)malloc(sizeof(BTree));	//分配一个新的结点
		T->data=ch;
		printf("请输入%c结点的左孩子结点:",T->data);
		T->lchild=CreateTree();		//通过递归建立左孩子结点
		printf("请输入%c结点的右孩子结点:",T->data);
		T->rchild=CreateTree();		//通过递归建立右孩子结点
	}
	return T;
}

/*3、广义表输出二叉树*/
void ShowTree(BTree T) {
    
    
	if(T!=NULL) {
    
    
		//当二叉树不为空时
		printf("%c",T->data);	//输入出该结点的数据域
		if(T->lchild!=NULL) {
    
    		//若该结点的左子树不为空
			printf("(");	//输出一个左括号
			ShowTree(T->lchild);	//通过递归继续输出结点的左子树结点下的各结点
			if(T->rchild!=NULL) {
    
    	//若该结点右子树不为空
				printf(",");	//输出一个逗号
				ShowTree(T->rchild);	//通过递归继续输出结点的右子树结点下的各结点
			}
			printf(")");	//输出一个右括号
		} else {
    
    	//若左子树为空,右子树不为空
			if(T->rchild!=NULL) {
    
    
				printf("(");	//输出一个左括号
				ShowTree(T->lchild);	//通过递归继续输出结点的左子树结点下的各结点
				if(T->rchild!=NULL) {
    
    		//若该结点的右子树不为空	
					printf(",");	//输出一个逗号
					ShowTree(T->rchild);	//通过递归继续输出结点的右子树结点下的各结点
				}
				printf(")");	//输出一个右括号
			}
		}
	}
}

/*主函数*/
int main() {
    
    
	BTree T;
	T=NULL;
	printf("请输入二叉树的根结点:");
	T=CreateTree();		//建立二叉树
	printf("建立的二叉树如下:\n");
	ShowTree(T);		//通过广义表显示二叉树
}

각 노드의 왼쪽, 오른쪽 자식 노드를 순서대로 입력하고, 해당 노드가 없으면 0을 입력한다. 예를 들어 트리에서 노드 d의 왼쪽 자식 노드는 존재하지 않고, 노드 f, g는 존재하지 않는다. , h, i, j 왼쪽과 오른쪽 자식이 존재하지 않습니다.
실행 결과는 다음과 같으며, 결과는 일반화된 테이블의 정의를 통해 표시됩니다.
여기에 이미지 설명을 삽입하세요.

(4) 이진 트리의 첫 번째, 중간 및 마지막 순회

예를 들어 아래 그림의 이진 트리의 경우 첫 번째, 중간 및 마지막 순회를 수행합니다.
여기에 이미지 설명을 삽입하세요.
1. 이진 트리의 선주문 순회:

/*先序遍历二叉树*/
bool ProTree(BTree T) {
    
    
	if(T==NULL)
		return false;			//递归结束
	else {
    
    
		printf("%c ",T->data);	//输出当前结点的数据域
		ProTree(T->lchild);		//递归继续遍历该结点的左子树
		ProTree(T->rchild);		//递归继续遍历该结点的右子树
		return true;
	}
}

실행 결과는 다음과 같습니다.
여기에 이미지 설명을 삽입하세요.
2. 이진 트리의 순차 순회:

/*中序遍历二叉树*/
bool InTree(BTree T) {
    
    
	if(T==NULL)
		return false;			//递归结束
	else {
    
    
		InTree(T->lchild);		//递归继续遍历该结点的左子树
		printf("%c ",T->data);	//输出当前结点的数据域
		InTree(T->rchild);		//递归继续遍历该结点的右子树
		return true;
	}
}

실행 결과는 다음과 같습니다:
여기에 이미지 설명을 삽입하세요.
3. 이진 트리의 후순 순회:

/*后序遍历二叉树*/
bool PostTree(BTree T) {
    
    
	if(T==NULL)
		return false;				//递归结束
	else {
    
    
		PostTree(T->lchild);		//递归继续遍历该结点的左子树
		PostTree(T->rchild);		//递归继续遍历该结点的右子树
		printf("%c ",T->data);		//输出当前结点的数据域
		return true;
	}
}

실행 결과는 다음과 같습니다.
여기에 이미지 설명을 삽입하세요.

(5) 이진 트리의 레벨 순회

여기에 이미지 설명을 삽입하세요.
이진 트리의 계층적 순회:

/*7、层次遍历二叉树*/
void LevelTree(BTree T) {
    
    
	BTree q[MAXSIZE];		//MAXSIZE的值可自行定义
	int front=0,rear=0;		//初始化队头指针和队尾指针为0
	if(T!=NULL) {
    
    			//当二叉树不为空
		q[rear++]=T;					//根结点入队
		while(front!=rear) {
    
    			//当队列不为空时
			BTree head=q[front++];
			printf("%c ",head->data);	//访问队头结点的数据域
			if(head->lchild) 			//若当前结点的左孩子存在,将队头结点的左孩子入队
				q[rear++]=head->lchild;
			if(head->rchild) 			//若当前结点的右孩子存在,将队头结点的右孩子入队
				q[rear++]=head->rchild;
		}
	}
}

위 그림의 이진트리이기도 하며, 레벨 순회를 수행한 결과는 다음과 같습니다.
여기에 이미지 설명을 삽입하세요.

(6) 이진 트리의 깊이

이진 트리의 깊이도 최대 깊이를 추구하며 재귀적 사고를 사용하여 왼쪽 및 오른쪽 하위 트리의 깊이를 재귀적으로 계산한 후 왼쪽 및 오른쪽 하위 트리의 깊이에서 최대값, 즉 깊이를 반환합니다. 이진 트리 구현 코드는 다음과 같습니다.

/*二叉树的深度*/
int DepthTree(BTree T) {
    
    
	int ldepth=0,rdepth=0;		//分别代表左、右子树的深度,初始值都为0
	if(T==NULL)
		return 0;
	else {
    
    
		ldepth=DepthTree(T->lchild);	//递归继续统计结点的左子树深度
		rdepth=DepthTree(T->rchild);	//递归继续统计结点的右子树深度
		if(ldepth>rdepth)		//求最大深度
			return ldepth+1;
		else
			return rdepth+1;
	}
}

위 그림의 이진 트리의 경우 실행 결과는 다음과 같습니다.
여기에 이미지 설명을 삽입하세요.

(7) 이진 트리의 리프 노드 수

이진 트리의 리프 노드 수를 구하는 것도 재귀적 방법으로 구현됩니다. 노드의 왼쪽과 오른쪽 자식이 모두 비어 있으면 이것이 리프 노드라는 것을 알고 있습니다. 재귀를 통해 최종 반환이 반환됩니다. 리프 노드 수 구현 코드는 다음과 같습니다.

/*二叉树的叶子结点数*/
int LeavesNum(BTree T) {
    
    
	if(T!=NULL) {
    
    	//当根结点不为空
		if(T->lchild==NULL&&T->rchild==NULL)	//若一个结点的左、右孩子都为空,即这是一个叶子结点 
			return 1;
	}
	return (LeavesNum(T->lchild)+LeavesNum(T->rchild));
}

위 그림의 이진 트리의 경우 실행 결과는 다음과 같습니다.
여기에 이미지 설명을 삽입하세요.

(8) 이진 트리의 총 노드 수

또한 재귀적입니다. 이진 트리가 비어 있지 않으면 이진 트리의 총 노드 수는 왼쪽 하위 트리 노드 수 + 오른쪽 하위 트리 노드 수와 같으며 1(이진 트리의 루트 노드)을 더합니다. 트리) 구현 코드는 다음과 같습니다.

/*求二叉树的结点总数*/
int SumLeaves(BTree T) {
    
    
	if(T!=NULL)
		return (SumLeaves(T->lchild)+SumLeaves(T->rchild)+1);
}

위 그림의 이진 트리의 경우 실행 결과는 다음과 같습니다.
여기에 이미지 설명을 삽입하세요.

❤️이진 트리의 전체 코드

이진 트리의 전체 코드는 다음과 같습니다.

#incldue<stdio.h>
#include <malloc.h>
/*二叉树的定义*/
typedef struct BNode {
    
    
	int data;		//数据域
	struct BNode *lchild,*rchild;		//左孩子、右孩子指针
} *BTree;

/*二叉树的建立*/
BTree CreateTree() {
    
    
	BTree T;
	char ch;
	scanf("%c",&ch);
	getchar();	//getchar()用于接收每次输入字符结点后的回车符,从而以便输入下一个字符结点
	if(ch=='0')	//当为0时,将结点置空
		T=NULL;
	else {
    
    
		T=(BTree)malloc(sizeof(BTree));	//分配一个新的结点
		T->data=ch;
		printf("请输入%c结点的左孩子结点:",T->data);
		T->lchild=CreateTree();		//通过递归建立左孩子结点
		printf("请输入%c结点的右孩子结点:",T->data);
		T->rchild=CreateTree();		//通过递归建立右孩子结点
	}
	return T;
}

/*广义表输出二叉树*/
void ShowTree(BTree T) {
    
    
	if(T!=NULL) {
    
    
		//当二叉树不为空时
		printf("%c",T->data);	//输入出该结点的数据域
		if(T->lchild!=NULL) {
    
    		//若该结点的左子树不为空
			printf("(");	//输出一个左括号
			ShowTree(T->lchild);	//通过递归继续输出结点的左子树结点下的各结点
			if(T->rchild!=NULL) {
    
    	//若该结点右子树不为空
				printf(",");	//输出一个逗号
				ShowTree(T->rchild);	//通过递归继续输出结点的右子树结点下的各结点
			}
			printf(")");	//输出一个右括号
		} else {
    
    	//若左子树为空,右子树不为空
			if(T->rchild!=NULL) {
    
    
				printf("(");	//输出一个左括号
				ShowTree(T->lchild);	//通过递归继续输出结点的左子树结点下的各结点
				if(T->rchild!=NULL) {
    
    		//若该结点的右子树不为空	
					printf(",");	//输出一个逗号
					ShowTree(T->rchild);	//通过递归继续输出结点的右子树结点下的各结点
				}
				printf(")");	//输出一个右括号
			}
		}
	}
}

/*先序遍历二叉树*/
bool ProTree(BTree T) {
    
    
	if(T==NULL)
		return false;			//递归结束
	else {
    
    
		printf("%c ",T->data);	//输出当前结点的数据域
		ProTree(T->lchild);		//递归继续遍历该结点的左子树
		ProTree(T->rchild);		//递归继续遍历该结点的右子树
		return true;
	}
}

/*中序遍历二叉树*/
bool InTree(BTree T) {
    
    
	if(T==NULL)
		return false;			//递归结束
	else {
    
    
		InTree(T->lchild);		//递归继续遍历该结点的左子树
		printf("%c ",T->data);	//输出当前结点的数据域
		InTree(T->rchild);		//递归继续遍历该结点的右子树
		return true;
	}
}

/*后序遍历二叉树*/
bool PostTree(BTree T) {
    
    
	if(T==NULL)
		return false;				//递归结束
	else {
    
    
		PostTree(T->lchild);		//递归继续遍历该结点的左子树
		PostTree(T->rchild);		//递归继续遍历该结点的右子树
		printf("%c ",T->data);		//输出当前结点的数据域
		return true;
	}
}

/*层次遍历二叉树*/
void LevelTree(BTree T) {
    
    
	BTree q[100];		//MAXSIZE的值可自行 定义
	int front=0,rear=0;		//初始化队头指针和队尾指针为0
	if(T!=NULL) {
    
    			//当二叉树不为空
		q[rear++]=T;					//根结点入队
		while(front!=rear) {
    
    			//当队列不为空时
			BTree head=q[front++];
			printf("%c ",head->data);	//访问队头结点的数据域
			if(head->lchild) 			//若当前结点的左孩子存在,将队头结点的左孩子入队
				q[rear++]=head->lchild;
			if(head->rchild) 			//若当前结点的右孩子存在,将队头结点的右孩子入队
				q[rear++]=head->rchild;
		}
	}
}

/*二叉树的深度*/
int DepthTree(BTree T) {
    
    
	int ldepth=0,rdepth=0;		//分别代表左、右子树的深度,初始值都为0
	if(T==NULL)
		return 0;
	else {
    
    
		ldepth=DepthTree(T->lchild);	//递归继续统计结点的左子树深度
		rdepth=DepthTree(T->rchild);	//递归继续统计结点的右子树深度
		if(ldepth>rdepth)		//求最大深度
			return ldepth+1;
		else
			return rdepth+1;
	}
}

/*二叉树的叶子结点数*/
int LeavesNum(BTree T) {
    
    
	if(T!=NULL) {
    
    	//当根结点不为空
		if(T->lchild==NULL&&T->rchild==NULL)	//若一个结点的左、右孩子都为空,即这是一个叶子结点 
			return 1;
	}
	return (LeavesNum(T->lchild)+LeavesNum(T->rchild));
}

/*求二叉树的结点总数*/
int SumLeaves(BTree T) {
    
    
	if(T!=NULL)
		return (SumLeaves(T->lchild)+SumLeaves(T->rchild)+1);
}

Supongo que te gusta

Origin blog.csdn.net/qq_43085848/article/details/134723359
Recomendado
Clasificación