c++二叉树详解(附基本操作代码版)

文章目录

前言

创建二叉树

展示二叉树

销毁二叉树

判断是否为完全二叉树

计算树的高度

计算树的叶子数量

计算树的宽度

层次遍历

前序遍历

递归写法

非递归写法

中序遍历

递归写法

非递归写法

后序遍历

递归写法

非递归写法

输出根节点到所有叶子节点的路径(递归)

输出根节点到所有叶子节点的路径(非递归)

计算二叉树的公共祖先(lca)

递归写法

非递归写法

验证


前言

二叉树可以说是树形数据结构中最基础的,且在多种领域都有应用(比如语法树,二值决策树),熟悉其各种操作是必须的。本文通过实现几个基本的函数来实现一个基本的二叉树:

#include <iostream>
#include <cstring>
#define N 100
using namespace std;

typedef struct node
{
	char data;
	struct node *left, *right;
}BT;

BT *createbt(char *in, char *pre, int k);	// 根据输入二点中序表达式和前序表达式创建一颗二叉树
void showBt(BT *T);		// 展示一颗二叉树,以A(B, C(D, E))这样加了括号的前序遍历来实现
void *destroy(BT *T);	// 销毁二叉树
bool iscomplete(BT *T);	// 判断这颗树是不是完全二叉树
int height(BT *T);		// 计算树的高度
int leaf(BT *T);	// 计算叶子的数量
int width(BT *T);		// 计算树的宽度
void layer(BT *T);		// 层次遍历
void preorder(BT *T);	// 前序遍历
void inorder(BT *T);	// 中序遍历
void postorder(BT *T);	// 后序遍历	
void getPath(BT *T, BT **path, int top)		// 输出根节点到所有叶子节点的路径(递归)
void getPath(BT *T);	// 输出根节点到所有叶子节点的路径(非递归)
BT *lca(BT *T, char a, char b);		// 返回节点数据为a和b的两个节点的最近公共祖先

其中,我们的二叉树的是以链式结构建立的,链式结构的每个节点都用BT结构体表示,这个结构体的data代表树的该节点存储的数据(此处为了方便使用char作为存储数据,实际使用时,可以使用各种各样的数据类型代表这个data,甚至在BT下挂载多个数据也是没问题的)。*left*right分别代表指向该节点的左节点和右节点的指针。


创建二叉树

为了方便,此处使用前序遍历和中序遍历的表达式递归地创建一颗二叉树。

BT *createbt(char *in, char *pre, int k)
{
	if (k <= 0)
		return NULL;
	else
	{
		BT *node = new BT;
		node->data = pre[0];	// 前序表达地第一个元素就是这颗子树地根节点
		int i;
		for (i = 0; in[i] != pre[0]; ++ i);		// 根据一个计数器i获取中序表达式中node->data的位置
		node->left = createbt(in, pre + 1, i);
		node->right = createbt(in + i + 1, pre + i + 1, k - i - 1);
		return node;
	}
}

展示二叉树

展示一棵二叉树。它的顺序和前序遍历是一样的,只是我们给他们加上括号。比如我们的二叉树如下图:

 很显然,它的前序表达式为ABCDE,那么我们的展示函数就在前序表达式的基础上加上括号来代表其层次关系:A(B,C(D,E))。

void showBt(BT *T)
{
	if (T)
	{
		cout << T->data << " ";
		if (T->left || T->right)	// 判断一下当前递归到的节点有没有字节点,如果没有子节点,我们就不需要打上括号了
		{
			cout << "(";
			showBt(T->left);
			cout << ",";
			showBt(T->right);
			cout << ")";
		}
	}
}


销毁二叉树

递归地销毁一棵二叉树,比较简单,不做赘述。注意一下带哦用递归和销毁当前节点的顺序。

void *destroy(BT *T)
{
	if (T)
	{
		destroy(T->left);
		destroy(T->right);
		delete T;
	}
}

判断是否为完全二叉树

如果一棵树是满叉的二叉树,那么它肯定是完全二叉树,如果一棵树不是满叉的二叉树,那么不满叉的那一层的所有节点都要靠左。比如如下的不满叉的二叉树是完全二叉树:

 而下面的这颗不满叉的二叉树则不是完全二叉树:

 因为D这个节点没有靠到最左边。由是,我们可以得到一个简单的判断逻辑。

如果根节点为NULL,则不是完全二叉树,直接返回false。否则,我们通过层次遍历来遍历整颗树,通过一个变量flag来判断是否已经到达过了叶子节点或者只有左子节点的节点(默认为false,表示没有到达过),因为我们只有遍历到了这两类节点的话,那么后续所有的节点都必须为叶子节点,在遍历的过程中做如下的判断:

  1. 如果当前节点没有左子节点,但有右子节点,返回false。
  2. 如果当前节点是叶子节点,则flag=true。
  3. 如果当前节点只有左子节点,判断flag是否为true,若是,则返回false,否则flag=true。
  4. 如果当前节点既有左子节点又有右子节点,则判断flag是否为true,若是,则返回false。

整个树遍历完后,返回true

bool iscomplete(BT *T)
{
	if (!T)
		return false;
		
	BT *q[N], *p;
	int front, rear;
	front = rear = 0;
	q[rear ++] = T;
	bool flag = false;
	
	while (front != rear)
	{
		p = q[front ++];
		
		if (!p->left && !p->right)
			flag = true;
		else if (!p->left && p->right)
			return false;
		else if (p->left && !p->right)
		{
			if (flag)
				return false;
			else
				flag = true;
		}
		else
		{
			if (flag)
				return false;
		}
		
		if (p->left)
			q[rear ++] = p->left;
		if (p->right)
			q[rear ++] = p->right;	
	}
	return true;
}


计算树的高度

树的高度可以认为是层数,也就是根节点到叶子节点中路径最长的那条路径包含的节点数量。此后通过递归实现:

int height(BT *T)
{
	if (!T)
		return 0;
	else
		return max(height(T->left) + 1, height(T->right) + 1);
}

计算树的叶子数量

思路和计算树的高度差不多,不过需要额外判断一下叶子节点的情况:

int leaf(BT *T)
{
	if (!T)		
		return 0;
	else if (!T->left && !T->right)	// 当前节点为叶子,那么叶子的数量当然是1
		return 1;
	else
		return leaf(T->left) + leaf(T->right);
}

计算树的宽度

树的宽度定义为含有节点为最多节点的那一层所含有的节点数,比如下面这幅图:

 上面这幅图中DEFG这层有的节点数量最多,为4个,因此这颗二叉树的宽度为4。

很明显,我们是通过比较层的节点数量来确定宽度,因此我们需要通过层次遍历来获取宽度。不过不同于一般的层次遍历的写法,我们需要统计完一层的节点树,然后直接把这一层的节点全部弹出,然后把它们的子节点入队。

我们记答案为max_wid,每层的节点数为count,很明显count = rear - front,那么我们通过count这个值做count次出队,入队操作就可以把第n层的节点全部弹出,把n+1层的节点全部入队。
 

int width(BT *T)
{
	BT *q[N], *p;
	int front, rear;
	front = rear = 0;
	q[rear ++] = T;
	
	int max_wid = 0;
	
	while (front != rear)
	{
		int count = rear - front;	// 通过队列的front和rear直接得出当前层的节点数量
		max_wid = max(max_wid, count);	// 更新当前的最大宽度
		
		while (count --)	// 根据count将当前层的所有节点弹出,把它们的子节点入队,完成层次遍历的队列更新
		{
			p = q[front ++];
			if (p->left)
				q[rear ++] = p->left;
			if (p->right)
				q[rear ++] = p->right;
		}
	}
	return max_wid;
}

层次遍历

层次遍历通过队列是实现,每出队一个元素,就将它的左子节点和右子节点入队(如果有的话)。

void layer(BT *T)
{
	BT *q[N], *p;
	int front, rear;
	front = rear = 0;
	q[rear ++] = T;
	
	while (front != rear)
	{
		p = q[front ++];	// 队首元素出队
		cout << p->data << " ";
		
		// 将出队的元素的子节点入队
		if (p->left)
			q[rear ++] = p->left;
		if (p->right)
			q[rear ++] = p->right;
	}
}

前序遍历

递归写法

前序的递归写法非常直观,不多说:

void preorder(BT *T)	// 前序遍历 
{
	if (T)
	{
		cout << T->data << " ";
		preorder(T->left);
		preorder(T->right);
	}
}

非递归写法

前序遍历的非递归通过栈来实现,要先输出的元素后入栈,要后输出的元素先入栈:

void preorder(BT *T)
{
	BT *s[N], *p;
	int top = 0;
	s[top] = T;
	
	while (top >= 0)
	{
		// 首元素的栈筛比较简单,就是直接输出就完事儿
		p = s[top --];
		cout << p->data << " ";
		
		// 后续要递归的栈筛入栈
		if (p->right)
			s[++ top] = p->right;
		if (p->left)
			s[++ top] = p->left;
		
	}
}

中序遍历

递归写法

void inorder(BT *T)	// 中序遍历 
{
	if (T)
	{
		preorder(T->left);
		cout << T->data << " ";
		preorder(T->right);
	}
}

非递归写法

中序遍历的非递归还是通过栈来实现的,只不过与前序遍历相比,我们的根节点不能直接输出,所以我们需要额外的栈筛来保存中间输出的各个子树的根节点,这个节点为p

void inorder(BT *T)
{
	BT *s[N], *p = T;
	int top = -1;
	
	while (top >=0 || p)
	{
		while (p)	// 由于中序遍历需要将所有的左子树输出,所以首先将左子树的栈筛入栈
		{
			s[++ top] = p;
			p = p->left;
		}
		
		p = s[top --];
		cout << p->data << " ";
		p = p->right;	// 如果该节点有右节点,则根据中序遍历的规则进入右子树(该节点被当作局部子树的根节点);如果没有右节点,根据代码结构,打印上一层的节点(该节点被当作局部子树的左节点)
	}
}

后序遍历

递归写法

void postorder(BT *T)	// 后序遍历 
{
	if (T)
	{
		preorder(T->left);
		preorder(T->right);
		cout << T->data << " ";
	}
}

非递归写法

后续遍历的非递归写法也是通过栈来实现的,因为此时的各个子树的根节点是在最后才输出的,所以我们需要保存左子树的栈筛和右子树的栈筛。我们还需要一个临时变量来指明当前遍历到的节点的右子节点是否被访问过。

void postorder(BT *T)
{
	BT *s[N], *p = T, *last;	// last变量用来判断当前访问的节点的右子节点是否被访问过
	int top = -1;
	
	do
	{
		while (p)	// 左子树的栈筛全部
		{
			s[++ top] = p;
			p = p->left;
		}
		last = NULL;
		while (top >= 0)	// 遍历打印没有右节点的根节点,或者访问没有访问过的右子树
		{
			p = s[top];
			if (p->right == last)
			{
				top --;
				cout << p->data << " ";
				last = p;
			}
			else
			{
				p = p->right;
				break;
			}
		}	
	}while (top >= 0);
}

输出根节点到所有叶子节点的路径(递归)

递归写法没什么好说的=_=,还是很简单的。注意在主函数中调用该函数时,需要额外传入一个存储参数的path数组,起始填入的top为-1。

void getPath(BT *T, BT **path, int top)
{	
	if (!T)
		return;
	else if (!T->left && !T->right)
	{
		for (int i = 0; i <= top; ++ i)
			cout << path[i]->data << " ";
		cout << T->data << endl;
	}
	else
	{
		path[++ top] = T;
		getPath(T->left, path, top);
		getPath(T->right, path, top);
	}
} 

输出根节点到所有叶子节点的路径(非递归)

此处一定记住,只要是涉及到输出路径输出,一定是有限考虑后序遍历,因为后序遍历是左子树->右子树->根节点的顺序,这就使得使用后序遍历访问到一个元素时,栈中剩余元素恰为根节点到该元素的路径,所以我们只要将后序遍历中的cout改成一个判断:若当前元素是叶子,则将栈中元素依次输出。

void getPath(BT *T)
{
	BT *s[N], *p = T, *last;
	int top = -1;
	
	do
	{
		while (p)
		{
			s[++ top] = p;
			p = p->left;
		}
		last = NULL;
		while (top >= 0)
		{
			p = s[top];
			if (p->right == last)
			{
				top --;
				if (!p->left && !p->right)	// 判断当前访问的节点是否为叶子节点,若是,则输出栈内元素
				{
					for (int i = 0; i <= top; ++ i)
						cout << s[i]->data << " ";
					cout << p->data << endl;
				}
				last = p;
			}
			else
			{
				p = p->right;
				break;
			}
		}
	}while(top >= 0);
}

计算二叉树的公共祖先(lca)

递归写法

计算lca的递归写法比较简单,首先判断当前的根节点是否为空,若为空,直接返回NULL;若要寻找lca的两个节点其中一个就是当前的根节点,说明其中一个节点就是另一个节点的祖先,则返回当前的根节点;接下来进入递归,往根节点的左节点和右节点去递归,得到以根节点的左节点为根节点的lca节点left和以以根节点的右节点为根节点的lca节点right

如果left和right都是空,说明递归的两个分支都没有答案,那么就是没有答案,返回NULL;如果left和right都不为空,说明两边递归下去都有结果,说明我们要寻找lca的两个节点分散在根节点的左右分支上,所以当前的根节点就是lca;如果left和right中一个为空,一个不为空,则说明两个节点都集中在其中的一个分支上,返回那个非空节点,它就是lca。

BT* lca(BT *T, char a, char b)
{
	if (!T)
		return NULL;
	if (T->data == a || T->data == b)
		return T;
	
	BT *l = lca(T->left, a, b);
	BT *r = lca(T->right, a, b);
	
	if (!l && !r)
		return NULL;
	else if (l && r)
		return T;
	else if (!l && r)
		return r;
	else if (l && !r);
		return l;
}

非递归写法

lca的非递归写法可以使用一个暴力的做法来实现,比如我们现在寻找下面这棵树中D和H的lca节点。

我们可以先找到根节点A到D和H的路径,它们的路径分别ABD和ABEH,然后我们来同时从这两条路径的开头遍历这两条路径,很显然,它们从开头开始同时遍历最后一个相同的节点就是它们的lca。比如我们同时遍历ABD和ABEH,A与A相同,B与B相同,D与E不相同,循环结束。最后一个相同的元素为B,所以B就是D和H的lca。

为了获取指定根节点到指定节点的路径,我们可以额外写一个函数,使用后序遍历来获取路径:

// 获取根节点到指定元素target的路径
// BT *T:操作的树的根节点
// char target:需要寻找的节点的data,也就是路径中最后一位的data
// BT **s:BT指针数组,是后序遍历需要的栈,同时也存着我们需要的路径
// int n:找到的路径的长度
void getPathByData(BT *T, char target, BT **s, int &n)
{
	BT *p = T, *last;
	int top = -1;
	// 后续遍历
	do
	{
		while (p)
		{
			s[++ top] = p;
			p = p->left;
		}
		last = NULL;
		while (top >= 0)
		{
			p = s[top];
			if (p->right == last)
			{
				top --;
				if (p->data == target)	// 一旦当前访问的元素就是我们指定的元素,此时留在栈中的元素就是不含结尾元素的路径,我们稍作记录,直接结束函数
				{
					s[++ top] = p;
					n = top;
					return;
				}
				last = p;
			}
			else
			{
				p = p->right;
				break;
			}
		}		
	} while (top >= 0);	
}

然后我们就可以写我们的非递归lca了,与递归相同,开头的判断根节点是否为空与根节点是否就是需要求lca的两个节点中的一个需要写一下:

BT* lca(BT *T, char a, char b)
{
	if (!T)	// 判断是否为空
		return NULL;
	if (T->data == a || T->data == b)	// 判断其中一个元素是否为根节点
		return T;
	
	BT *path1[N], *path2[N];
	int top1, top2;
	
	// 获取
	getPathByData(T, a, path1, top1);
	getPathByData(T, b, path2, top2);
	
	int i;	// 最后一个相同元素的索引
	for (i = 0; i <= min(top1, top2) && path1[i]->data == path2[i]->data; ++ i);
	return path1[i - 1];
}

验证

下面为主函数,快速验证一下上述的函数是否没有问题:

main()
{
	char pre[] = "ABGDEHFCKL", in[] = "GBEHDFAKCL";
	int k = strlen(in);
	BT *T = createbt(in, pre, k);
	showBt(T);
	cout << endl;
	
	if (iscomplete(T))
		cout << "T 是二叉树" << endl;
	else
		cout << "T 不是二叉树" << endl;
	
	cout << "T的高度为:" << height(T) << endl;
	cout << "T叶子的数量为:" << leaf(T) << endl;
	cout << "T的宽度为:" << width(T) << endl;
	
	cout << "\n层次遍历:";
	layer(T);  
	cout << "\n前序遍历:";
	preorder(T);
	cout << "\n中序遍历:";
	inorder(T);
	cout << "\n后序遍历:";
	postorder(T);
	
	cout << endl << "根节点到子节点的所有路径为(递归):" << endl; 
	BT *path[N]; 
	getPath(T, path, -1);
	cout << endl;
	
	cout << endl << "根节点到子节点的所有路径为(非递归):" << endl; 
	getPath(T);
	cout << endl;
	
	BT *t = lca(T, 'G', 'K');
	cout << "\nG和K的公共祖先为:" << t->data << endl;
	
	cout << "\n销毁二叉树"; 
	destroy(T);
} 

输出结果为:

A (B (G ,D (E (,H ),F )),C (K ,L ))
T 不是二叉树
T的高度为:5
T叶子的数量为:5
T的宽度为:4

层次遍历:A B C G D K L E F H
前序遍历:A B G D E H F C K L
中序遍历:G B E H D F A K C L
后序遍历:G H E F D B K L C A
根节点到子节点的所有路径为(递归):
A B G
A B D E H
A B D F
A C K
A C L


根节点到子节点的所有路径为(非递归):
A B G
A B D E H
A B D F
A C K
A C L


G和K的公共祖先为:A

销毁二叉树

  • 11
    点赞
  • 38
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
《数据结构》(c语言)是为“数据结构”课程编写的教材,也可作为学习数据结构及其算法的C程序设计的参数教材。学了数据结构后,许多以前写起来很繁杂的代码现在写起来很清晰明了. 本书的前半部分从抽象数据类型的角度讨论各种基本类型的数据结构及其应用;后半部分主要讨论查找和排序的各种实现方法及其综合分析比较。 全书采用类C语言作为数据结构和算法的描述语言。 本书概念表述严谨,逻辑推理严密,语言精炼,用词达意,并有配套出的《数据结构题集》(C语言),便于教学,又便于自学。 本书后有光盘。光盘内容可在DOS环境下运行的以类C语言描述的“数据结构算法动态模拟辅助教学软件,以及在Windows环境下运行的以类PASCAL或类C两种语言描述的“数据结构算法动态模拟辅助教学软件”。 目录: 第1章 绪论 1.1 什么是数据结构 1.2 基本概念和术语 1.3 抽象数据类型的表现与实现 1.4 算法和算法分析 第2章 线性表 2.1 线性表的类型定义 2.2 线性表的顺序表示和实现 2.3 线性表的链式表示和实现 2.4 一元多项式的表示及相加 第3章 栈和队列 3.1 栈 3.2 栈的应有和举例 3.3 栈与递归的实现 3.4 队列 3.5 离散事件模拟 第4章 串 4.1 串类型的定义 4.2 串的表示和实现 4.3 串的模式匹配算法 4.4 串操作应用举例 第5章 数组和广义表 5.1 数组的定义 5.2 数组的顺序表现和实现 5.3 矩阵的压缩存储 5.4 广义表的定义 5.5 广义表的储存结构 5.6 m元多项式的表示 5.7 广义表的递归算法第6章 树和二叉树 6.1 树的定义和基本术语 6.2 二叉树 6.2.1 二叉树的定义 6.2.2 二叉树的性质 6.2.3 二叉树的存储结构 6.3 遍历二叉树和线索二叉树 6.3.1 遍历二叉树 6.3.2 线索二叉树 6.4 树和森林 6.4.1 树的存储结构 6.4.2 森林与二叉树的转换 6.4.3 树和森林的遍历 6.5 树与等价问题 6.6 赫夫曼树及其应用 6.6.1 最优二叉树(赫夫曼树) 6.6.2 赫夫曼编码 6.7 回溯法与树的遍历 6.8 树的计数 第7章 图 7.1 图的定义和术语 7.2 图的存储结构 7.2.1 数组表示法 7.2.2 邻接表 7.2.3 十字链表 7.2.4 邻接多重表 7.3 图的遍历 7.3.1 深度优先搜索 7.3.2 广度优先搜索 7.4 图的连通性问题 7.4.1 无向图的连通分量和生成树 7.4.2 有向图的强连通分量 7.4.3 最小生成树 7.4.4 关节点和重连通分量 7.5 有向无环图及其应用 7.5.1 拓扑排序 7.5.2 关键路径 7.6 最短路径 7.6.1 从某个源点到其余各顶点的最短路径 7.6.2 每一对顶点之间的最短路径 第8章 动态存储管理 8.1 概述 8.2 可利用空间表及分配方法 8.3 边界标识法 8.3.1 可利用空间表的结构 8.3.2 分配算法 8.3.3 回收算法 8.4 伙伴系统 8.4.1 可利用空间表的结构 8.4.2 分配算法 8.4.3 回收算法 8.5 无用单元收集 8.6 存储紧缩 第9章 查找 9.1 静态查找表 9.1.1 顺序表的查找 9.1.2 有序表的查找 9.1.3 静态树表的查找 9.1.4 索引顺序表的查找 9.2 动态查找表 9.2.1 二叉排序树和平衡二叉树 9.2.2 B树和B+树 9.2.3 键树 9.3 哈希表 9.3.1 什么是哈希表 9.3.2 哈希函数的构造方法 9.3.3 处理冲突的方法 9.3.4 哈希表的查找及其分析 第10章 内部排序 10.1 概述 10.2 插入排序 10.2.1 直接插入排序 10.2.2 其他插入排序 10.2.3 希尔排序 10.3 快速排序 10.4 选择排序 10.4.1 简单选择排序 10.4.2 树形选择排序 10.4.3 堆排序 10.5 归并排序 10.6 基数排序 10.6.1 多关键字的排序 10.6.2 链式基数排序 10.7 各种内部排序方法的比较讨论 第11章 外部排序 11.1 外存信息的存取 11.2 外部排序的方法 11.3 多路平衡归并的实现 11.4 置换一选择排序 11.5 最佳归并树 第12章 文件 12.1 有关文件的基本概念 12.2 顺序文件 12.3 索引文件 12.4 ISAM文件和VSAM文件 12.4.1 ISAM文件 12.4.2 VSAM文件 12.5 直接存取文件(散列文件) 12.6 多关键字文件 12.6.1 多重表文件 12.6.2 倒排文件 录A 名词索引 录B 函数索引 参考书目
全国计算机二级JAVA笔试分类模拟题算法和数据结构、程序设计基础全文共21页,当前为第1页。全国计算机二级JAVA笔试分类模拟题算法和数据结构、程序设计基础全文共21页,当前为第1页。二级JAVA笔试分类模拟题算法和数据结构、程序设计基础 全国计算机二级JAVA笔试分类模拟题算法和数据结构、程序设计基础全文共21页,当前为第1页。 全国计算机二级JAVA笔试分类模拟题算法和数据结构、程序设计基础全文共21页,当前为第1页。 一、选择题 1. 下列关于栈叙述正确的是______。 A.栈顶元素最先被删除 B.栈顶元素最后才能被删除 C.栈底元素永远不能被删除 D.以上三种说法部不对 答案:A 栈是允许在同一端进行插入和删除操作的特殊线性表。允许进行插入和删除操作的一端称为栈顶(Top),另一端为栈底(Bottom);栈底固定,而栈顶浮动;栈中元素个数为0时称为空栈。插入一般称为进栈(PUSH),删除则称为退栈(POP)。栈也称为先进后出表。 2. 下列叙述中正确的是______。 A.有一个以上根节点的数据结构不一定是非线性结构 B.只有一个根节点的数据结构不一定是线性结构 C.循环链表是非线性结构 D.双向链表是非线性结构 答案:B 循环链表是另一种形式的链式存储结构。它的特点是表中最后一个节点的指针全国计算机二级JAVA笔试分类模拟题算法和数据结构、程序设计基础全文共21页,当前为第2页。全国计算机二级JAVA笔试分类模拟题算法和数据结构、程序设计基础全文共21页,当前为第2页。域指向头节点,整个链表形成一个环。双向链表也叫双链表,是链表的一种,它的每个数据节点中都有两个指针,分别指向直接后继和直接前驱。所以,从双向链表中的任意一个节点开始,都可以很方便地访问它的前驱节点和后继节点;循环链表和双向链表都是线性结构。有一个以上根节点的结构一定是非线性结构。 3. 某二叉树共有7个节点,其中叶子节点只有1个,则该二叉树的深度为______(假设根节点在第1层)。 A.3 B.4 C.6 D.7 答案:D 二叉树是一种很有用的非线性结构,它具有以下两个特点: 1)非空二叉树只有一个根节点; 2)每一个节点最多有两棵子树,且分别称为该节点的左子树与右子树。 根据二叉树的概念可知,二叉树的度可以为0(叶子节点)、1(只有一棵子树)或2(有2棵子树)。由于只有一个叶子节点,所以该二叉树没有分叉,7个节点连成一线,深度为7。 4. 下列叙述正确的是______。 A.算法就是程序 B.设计算法时只需要考虑数据结构的设计 C.设计算法时只需要考虑结果的可靠性 D.以上三种说法都不对 全国计算机二级JAVA笔试分类模拟题算法和数据结构、程序设计基础全文共21页,当前为第3页。全国计算机二级JAVA笔试分类模拟题算法和数据结构、程序设计基础全文共21页,当前为第3页。 答案:D 算法是求解问题的方法。程序设计时要设计算法,但算法不是程序。设计算法除了要考虑数据结构外,还要考虑算法的可行性、可靠性等。 5. 下列关于线性链表的叙述中,正确的是______。 A.各数据节点的存储空间可以不连续,但它们的存储顺序与逻辑顺序必须一致 B.各数据节点的存储顺序与逻辑顺序可以不一致,但它们的存储空间必须连续 C.进行插入与删除时,不需要移动表中的元素 D.以上三种说法都不对 答案:C 节点的存储顺序和逻辑顺序不一定一致,存储空间也不一定连续。插入和删除元素的时候,不需要移动表中的元素。 6. 下列关于二叉树的叙述中,正确的是______。 A.叶子节点总是比度为2的节点少一个 B.叶子节点总是比度为2的节点多一个 C.叶子节点数是度为2的节点数的两倍 D.度为2的节点数是度为1的节点数的两倍 答案:B 全国计算机二级JAVA笔试分类模拟题算法和数据结构、程序设计基础全文共21页,当前为第4页。全国计算机二级JAVA笔试分类模拟题算法和数据结构、程序设计基础全文共21页,当前为第4页。 二叉树叶子节点总是比度为2的节点多一个,这是二叉树的性质。 7. 下列叙述中正确的是______。 A.栈是一种先进先出的线性表 B.队列是一种后进先出的线性表 C.栈与队列都是非线性结构 D.以上三种说法都不对 答案:D 栈和队列都是特殊的线性表,栈(Stack)只能在表的一端进行插入和删除运算,所以,栈是一种"先进后出"的线性表;而队列(Queue)只允许在一端删除,在另一端插入,所以,队列是一种"先进先出"的线性表。 8. 一棵二叉树共有25个节点,其中5个是叶子节点,则度为1的节点数为______。 A.4 B.10 C.6 D.16 答案:D 从题干中我们知道,在该二叉树中有5个叶子节点,由二叉树的性质之一:任何一棵二叉树,度为0的节点(也就是叶子节点)总是比度为
二叉树是一种常见的数据结构,它包含一个根节点和最多两个子节点(左子节点和右子节点)。对于二叉树基本操作,其中最重要的四种遍历方式是先序遍历、中序遍历、后序遍历和层序遍历。 先序遍历是指在遍历根节点之前先遍历左子树,然后遍历右子树。代码实现如下: ```python # 先序遍历 def preorder_traversal(node): if node is None: return print(node.value) # 打印当前节点的值 preorder_traversal(node.left) # 递归遍历左子树 preorder_traversal(node.right) # 递归遍历右子树 ``` 中序遍历是指在遍历根节点之前先遍历左子树,然后遍历根节点,最后遍历右子树。代码实现如下: ```python # 中序遍历 def inorder_traversal(node): if node is None: return inorder_traversal(node.left) # 递归遍历左子树 print(node.value) # 打印当前节点的值 inorder_traversal(node.right) # 递归遍历右子树 ``` 后序遍历是指在遍历根节点之前先遍历左子树,然后遍历右子树,最后遍历根节点。代码实现如下: ```python # 后序遍历 def postorder_traversal(node): if node is None: return postorder_traversal(node.left) # 递归遍历左子树 postorder_traversal(node.right) # 递归遍历右子树 print(node.value) # 打印当前节点的值 ``` 层序遍历是按从上到下、从左到右的顺序逐层遍历二叉树的节点。代码实现如下: ```python # 层序遍历 def level_order_traversal(root): if root is None: return queue = [root] # 使用队列保存待处理的节点 while queue: node = queue.pop(0) # 取出队列中的第一个节点 print(node.value) # 打印当前节点的值 if node.left: queue.append(node.left) # 将左子节点加入队列 if node.right: queue.append(node.right) # 将右子节点加入队列 ``` 以上是四种二叉树遍历方式的代码实现,可以根据具体情况选择合适的方法来遍历二叉树的节点。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

浪子小院

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值