树的相关笔试面试题

1. 树的创建

已知一个先序遍历数的结果用数组表示, 其中空节点用 null_node 表示, 要求创建出这棵树. 同样采用递归的思想, 先定义一个指针, 指向数组中的第一个元素, 然后给数组的第一个结点创建相应的结点, 然后指针后移, 递归创建根节点的左子树, 递归创建根节点的右子树, 最后返回这个新结点就可以了

TreeNode* TreeCreate(TreeNodeType array[], int size, TreeNodeType null_node)
{if(array == NULL){return NULL;//非法输入}if(size == 0){return NULL;//非法输入}if(array[0] == null_node){return NULL;//空树}int index = 0;return _TreeCreate(array, size, &index, null_node);
}TreeNode* TreeClone(TreeNode* root)
{if(root == NULL){return NULL;//空树}TreeNode* new_root;TreeInit(&new_root);new_root = CreateTreeNode(root -> data);if(new_root == NULL){return NULL;}//递归创建左子树new_root -> lchild = TreeClone(new_root -> lchild);//递归创建右子树new_root -> rchild = TreeClone(new_root -> rchild);return new_root;
}

2. 克隆一棵树

克隆一棵树也采用递归的思想, 先创建一个根节点, 然后递归创建根节点的左子树, 再递归创建根节点的右子树

TreeNode* TreeClone(TreeNode* root)
{if(root == NULL){return NULL;//空树}TreeNode* new_root;TreeInit(&new_root);new_root = CreateTreeNode(root -> data);if(new_root == NULL){return NULL;}//递归创建左子树new_root -> lchild = TreeClone(new_root -> lchild);//递归创建右子树new_root -> rchild = TreeClone(new_root -> rchild);return new_root;
}

3. 求二叉树的叶子结点数

求二叉树的所有结点数即就是求二叉树的左子树上的结点数加上二叉树的右子树上的结点数, 然后左右字数的结点数相加即就是二叉树的所有叶子结点数

int TreeLeafSize(TreeNode* root)
{if(root == NULL){return 0;}if(root -> lchild == NULL && root -> rchild == NULL){return 1;}return TreeLeafSize(root -> lchild) + TreeLeafSize(root -> rchild);
}

4. 求二叉树的节点数

求二叉树的节点数即就是先判断二叉树是不是只有一个结点, 如果不是, 就递归求解出二叉树的左子树的结点数, 再递归求解二叉树的有子树的结点数, 最后根节点的左右子树之和加1就是二叉树的结点总数

int TreeSize(TreeNode* root)
{if(root == NULL){return 0;}return TreeSize(root -> lchild) + TreeSize(root -> rchild) + 1;
}

5. 求二叉树的高度

如果只有一个根节点, 则二叉树的高度为 1, 否则二叉树的高度就是根节点的左子树和右子树的高度的最大值 加 1

int TreeHeight(TreeNode* root)
{if(root == NULL){return 0;}if(root -> lchild == NULL && root -> rchild == NULL){return 1;}int Lhight = TreeHeight(root -> lchild);int Rhight = TreeHeight(root -> rchild);return 1 + (Lhight > Rhight ? Lhight : Rhight);
}

6. 求某个节点的父节点

如果已知结点和根节点相等, 则直接返回根节点, 否则就递归的在根节点的左子树中找, 如果没有找的就在根节点的右子树中递归的找

TreeNode* Parent(TreeNode* root, TreeNode* node)
{if(root == NULL || node == NULL){return NULL;}if(root -> lchild == node || root -> rchild == node){return root;}TreeNode* Lparent = Parent(root -> lchild, node);if(Lparent != NULL){return Lparent;}TreeNode* Rparent = Parent(root -> rchild, node);return Rparent;
}

7. 销毁一个二叉树

先递归销毁二叉树的左子树, 再递归销毁二叉树的右子树, 最后销毁根节点

void TreeDestroy(TreeNode** root)
{if(root == NULL){return;}if(*root == NULL){return;//空树}TreeDestroy(&(*root) -> lchild);TreeDestroy(&(*root) -> rchild);free(*root);*root = NULL;
}

8. 在二叉树中找出给定指的结点

比较根节点的 data 和 to_find 是否相等, 相等就返回根节点, 不相等递归的和根节点的左子树的 data 比较, 最后和根节点的右子树的data比较

TreeNode* TreeFind(TreeNode* root, TreeNodeType to_find)
{if(root == NULL){return NULL;//空树}if(root -> data == to_find){return root;}TreeNode* l_node = TreeFind(root -> lchild, to_find);if(l_node != NULL){return l_node;}TreeNode* r_node = TreeFind(root -> rchild, to_find);return r_node;
}

9. 非递归先序遍历

先定义一个栈, 将根节点入栈, 取栈顶元素到 cur 中, 同时访问当前结点, 将当前结点出栈, 如果当前结点的右子树不为空, 就将当前结点的右子树入栈, 如果当前结点的左子树不为空, 入栈当前结点的左子树, 循环取栈顶元素, 直到栈为空.

void TreePreOrderByLoop(TreeNode* root)
{if(root ==NULL){return;}SeqStack stack;SeqStackInit(&stack);SeqStackPush(&stack, root);SeqStackType cur;while(SeqStackGetFront(&stack, &cur)){printf("%c ", cur -> data);SeqStackPop(&stack);if(cur -> rchild != NULL){SeqStackPush(&stack, cur -> rchild);}if(cur -> lchild != NULL){SeqStackPush(&stack, cur -> lchild);}}
}

10. 非递归中序遍历

先定义一个指针指向该节点, 如果当前结点不为空, 入栈当前结点, 同时让当前结点一直往左走, 直到当前结点为空. 取栈顶结点, 访问当前结点, 出栈. 让当前结点指向它的右子树

void TreeInOrderByLoop(TreeNode* root)
{if(root == NULL){return;}SeqStack stack;SeqStackInit(&stack);TreeNode* cur = root;while(1){while(cur != NULL){SeqStackPush(&stack, cur);cur = cur -> lchild;}int ret = SeqStackGetFront(&stack, &cur);if(ret == 0){return;}printf("%c ", cur -> data);SeqStackPop(&stack);cur = cur -> rchild;}
}

11. 非递归后序遍历

定义一个指针prev初始化为 NULL来表示访问的上一个结点, 同时定义一个指针 cur 指向该节点, 再定义一个指针 top 表示栈顶结点. 将 cur 入栈, 让当前结点向左走, 直到当前结点为空, 此时取栈顶元素, 如果当前结点 cur -> rchild 和 访问的上一个结点相等, 则访问当前结点, 同时将prev的值赋为访问结点的值, 然后让 cur 等于 cur -> rchild, 一直循环, 直到取栈顶元素失败

void TreePostByLoop(TreeNode* root)
{if(root == NULL){return;//空树}TreeNode* prev = NULL;TreeNode* cur = root;SeqStackType top;SeqStack stack;SeqStackInit(&stack);while(1){while(cur != NULL){SeqStackPush(&stack, cur);cur = cur -> lchild;}int ret = SeqStackGetFront(&stack, &top);if(ret == 0){return;}if(top -> rchild == NULL || top -> rchild == prev){printf("%c ", top -> data);prev = top;SeqStackPop(&stack);}else{cur = top -> rchild;}}
}

12. 树的镜像

(1)非递归版本

定义一个指针 cur 指向根节点, 定义一个队列, 将 cur 入队列, 取队首元素, 将当前队首元素左右字数进行交换, 出队列, 如果cur -> lchild != NULL, 就将 cur -> lchild 入队列, 如果 cur -> rchild != NULL, cur -> rchild 入队列, 循环上述过程, 直到取栈顶元素失败退出循环就说明当前树已经逆置

void TreeMirrorByLoop(TreeNode* root)
{if(root == NULL){return;}SeqQue q;SeqQueInit(&q);SeqQueType top = root;SeqQuePush(&q, root);int ret = 0;while(SeqQueGetFront(&q, &top)){TreeNodeSwap(&(top -> lchild), &(top -> rchild));SeqQuePop(&q);if(top -> lchild != NULL){SeqQuePush(&q, top -> lchild);}if(top -> rchild != NULL){SeqQuePush(&q, top -> rchild);}}printf("\n");
}

(2)非递归版本求树的镜像

如果当前树是空树则直接返回, 如果当前树只有一个根节点, 就直接返回, 否则就逆置当前结点的左子树, 逆置当前结点的右子树

void TreeMirror(TreeNode* root)
{if(root == NULL){return;//空树}if(root -> rchild == NULL && root -> rchild == NULL){return;}TreeMirror(root -> rchild);TreeMirror(root -> lchild);
}

13. 判断二叉树是否为完全二叉树

分为两个阶段, 第一阶段判断当前结点是否具有左右子树, 如果具有左右字数, 就将当前结点左右子树入队列, 如果当前结点只有右子树, 没有左子树, 一定不是右子树, 如果当前结点只有左子树没有右子树, 进入阶段2, 如果当前结点左右子树都没有, 进入第二阶段, 第二阶段就是当前结点的后面结点都没有子树, 一直循环, 如果循环结束, 并且满足上述条件, 则说明这个树是完全二叉树, 否则不是

int IsComPletTree(TreeNode* root)
{if(root == NULL){return;}TreeNode* cur = root;int start_step_two = 0;SeqQue q;SeqQueInit(&q);SeqQuePush(&q, cur);while(SeqQueGetFront(&q, &cur)){if(start_step_two == 0){if(cur -> rchild != NULL && cur -> lchild != NULL){SeqQuePush(&q, cur -> rchild);SeqQuePush(&q, cur -> lchild);}if(cur -> lchild == NULL && cur -> rchild != NULL){return 0;}if(cur -> lchild != NULL && cur -> rchild == NULL){start_step_two = 1;}if(cur -> lchild == NULL && cur -> rchild == NULL){start_step_two = 1;}}else{if(cur -> lchild == NULL && cur -> rchild == NULL){;}else{return 0;}}}return 1;
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/384005.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

UVa202

[题目描述] 传送门 [题目分析] 就是一个模拟,不过稍微有点小复杂,而且输出格式有点小毒瘤. 不过只是RE了两发,PE了一发就过了,还是很开心. 需要注意数组要开很大,可能循环节出现在很后. 每个输出样例应该输出一个空行,最后面也应该有,不然会PE [AC代码] #include<cst…

linux线程同步(5)-屏障

http://www.cnblogs.com/yuuyuu/p/5152560.html 一.概述 barrier(屏障)与互斥量&#xff0c;读写锁&#xff0c;自旋锁不同&#xff0c;它不是用来保护临界区的。相反&#xff0c;它跟条件变量一样&#xff0c;是用来协同多…

浅谈软件测试

一. 什么是软件测试 软件测试是一个过程或者一系列过程, 用来测试计算机代码完成了其应该完成的功能, 不执行不该有的操作.或者说软件测试是根据软件开发各阶段的功能和说明而精心设计的一批测试用例, 并根据测试用例运行程序, 以发现程序错误的过程. 二. 软件测试的心理学和…

UVa10340

【题目描述】 传送门 【题目分析】 求字串&#xff0c;最好还是处理母串&#xff0c;每次找到一个子串就加1&#xff0c;这样处理不用处理细节 【AC代码】 #include<cstdio> #include<cstring> #include<cstdlib> #include<algorithm> #include&l…

浅析linux下的条件变量

一.条件变量 条件变量是用来等待线程而不是上锁的&#xff0c;条件变量通常和互斥锁一起使用。条件变量之所以要和互斥锁一起使用&#xff0c;主要是因为互斥锁的一个明显的特点就是它只有两种状态&#xff1a;锁定和非锁定&#xff0c;而条件变量可以通过允许线程阻塞和等待另…

UVa1587

【题目描述】 传送门 【题目分析】 刚开始想简单了&#xff0c;认为只要相对的面相等就可以了。然后发现三个不同方向的面的边应该有相等的关系&#xff0c;即如果两个面公用一条边&#xff0c;那么这两个面的另外两条边就是另一个面的两条边。而且这三个量里面肯定有一个最…

Linux多线程与同步

https://www.cnblogs.com/freedomabcd/p/7774743.html 典型的UNIX系统都支持一个进程创建多个线程(thread)。在Linux进程基础中提到&#xff0c;Linux以进程为单位组织操作&#xff0c;Linux中的线程也都基于进程。尽管实现方式有异于其它的UNIX系统&#xff0c;但Linux的多线程…

内存管理(二)

页面置换算法 当发生缺页中断的时候, 系统会在内存中选择一个页面将其换出内存, 而当换出内存的时候如果该页面的内容在内存中发生修改,则必须将该新数据重新写回到磁盘, 然后再将需要换进的数据覆盖掉原来的数据, 而当该数据在内存中没有被修改的时候, 此时就直接用需要换进的…

两个栈实现一个队列/两个队列实现一个栈

http://blog.csdn.net/sinat_30472685/article/details/70157227 1两个栈实现一个队列 1.原理分析&#xff1a; 队列的主要操作有两个&#xff1a;入队操作和出队操作&#xff0c;出队时从队头出&#xff0c;入队是从队尾插入&#xff0c;入队的操作和入栈的操作类似&#xff0…

UVa1588

【题目描述】 传送门 【题目分析】 刚开始想了一会没有想到什么很好的算法&#xff0c;看到了长度最多为100&#xff0c;就知道自己想的没有什么意义了&#xff0c;直接暴力&#xff0c;把每一种填法都试一下就知道了。适当剪枝一下&#xff08;一个简单的乐观函数&#xff…

转:C++中const、volatile、mutable的用法

const修饰普通变量和指针 const修饰变量&#xff0c;一般有两种写法&#xff1a; const TYPE value; TYPE const value; 这两种写法在本质上是一样的。它的含义是&#xff1a;const修饰的类型为TYPE的变量value是不可变的。对于一个非指针的类型TYPE&#xff0c;无论怎么写&…

数据链路

广播信道的数据链路层 局域网的优点 网络为一个单位所拥有, 地理范围和站点数有限 局域网具有广播特性, 可以从一个站点方便地访问到整个网络. 各个主机之间可以共享资源, 无论是局域网上的硬件资源还是局域网上的软件资源 便于系统的扩展换和演化, 各个设备之间的位置可灵…

UVa11809

【题目描述】 传送门 【题目分析】 终于把这道题做完了&#xff0c;之前一直连题意都看不懂。实在不行上网找了一下大佬的博客&#xff0c;看懂题意后自己写&#xff0c;发现读入很难处理&#xff0c;就又学习了一下大佬的读入方法&#xff0c;用的是C里面的sstream&#xf…

数据链路层:基本概念

数据链路层的定义 对数据链路层有对上的网络层接口. 对下提供物理层的接口. 定义合适的传输差错率 对传输流进行管理, 以免快速的传输的数据被淹没. 比如发送端发送信号太快, 接受方接受速度较慢, 此时数据链路层就需要提供一定的功能解决这个问题 物理层上传输的基本单元是…

C++的沉迷与爱恋

每年的 09/28 於我都是一个特殊的日子 -- 不只是因为教师节。今年很特殊地没有普天同庆&#xff0c;那麽我就写篇文章自己庆祝一下好了。我於今年七月发表了一本着作《多型与虚拟》和一本译作《深度探索C物件模型》&#xff0c;获得很大的回响。这些作品都不是针对 C 的完全初学…

Insertion Sort——打表找规律

【题目描述】 Insertion sort is a simple sorting algorithm that builds the final sorted array one item at an iteration.More precisely, insertion sort iterates, consuming one input element each repetition, and growing a sorted output list. At each iteration…

数据链路层: 可靠性传输 六个协议

可靠性传输 1. 差错控制 发送方将数据帧发送, 但是当发送方发送的是一个 1的时候此时接受方却接受的是一个 0. (1)校验 接收方如果帧校验接受到的帧没有问题, 则对发送方发送一个肯定性的确认, 当对这个数据帧进行校验发现这个帧有问题的时候, 此时接受方一种是将这个数据帧…

c语言实现配置文件的读写

配置文件的格式如下&#xff1a; key1 value1 key2 value2 . . . 名值对以一个链接&#xff0c;一条记录以换行符分割 头文件&#xff1a; #include<stdio.h> #include<stdlib.h> #include <string.h> 函数原型&#xff1a; void trim(char *strIn, char *…

Educational Codeforces Round 73 (Rated for Div. 2)

A 很简单的一个模拟&#xff0c;只要前面的数字有两个以上就能合成后面的&#xff0c;我们进行一遍合成看能不能出现2048就可以了。 #include<cstdio> #include<cstring> #include<cstdlib> #include<algorithm> #include<iostream> #include&…

数据链路层: HDLC

一. 协议机 发送方和接收方. 同时有限状态机把协议形式化为一个四元组 (S,M,I,T), 其中你S表示进程和信道可能进入的集合, M 表示数据帧的状态, I 表示进程的初始状态, T 表示两两状态之间的转化. 每个系统状态可以分为发送状态, 接受状态和信道状态. 把状态用一个点进行表示,…