这一篇先写个二叉树的题目,二叉树也是面试中常考到的算法与数据结构的知识点。
一、二叉树的生成与层序遍历
这一篇写个从#include从main开始的完整代码。
首先stNode定义了二叉树的节点结构,即存储一个数据,指向左右子节点。
func()是输出不分层的实现方法,即层序遍历后把整个树一层层展开、接续,串行地存储到一个std::vector中;levelOrder()是输出分层的实现方法,按树的原有层次存储遍历的结果,使用了一层嵌套的std::vector结构。
gen()是二叉树生成算法,用了递归,输入n是树的层数,cur是当前该生成第几层了,root是第cur层的上层父节点,即cur层生成的是root的左右子节点;main()函数中调用gen()生成了一个5层的完全二叉树,并用func()做了一次输出不分层的层序遍历,感兴趣的朋友可以拿levelOrder()看看输出分层的结果。
#include <iostream>
#include <stdio.h>
#include <string>
#include <vector>
#include <map>
#include <future>
//二叉树生成、层序遍历(仅遍历,输出不分层)
#include <queue>
using namespace std;
struct stNode
{
stNode(int n) : num(n), left(nullptr), right(nullptr) {}
int num;
stNode* left;
stNode* right;
};
//
// 层序遍历,输出不分层
void func(stNode* root, std::vector<int>& tmpVector)
{
std::queue<stNode*> tmpQueue;
if (root == nullptr)
return;
tmpQueue.push(root);
while (tmpQueue.size() > 0)
{
stNode* tmp = tmpQueue.front();
tmpVector.push_back(tmp->num);
if (tmp->left != nullptr)
tmpQueue.push(tmp->left);
if (tmp->right != nullptr)
tmpQueue.push(tmp->right);
tmpQueue.pop();
}
}
//
// 输出分层
std::vector<std::vector<int>> levelOrder(TreeNode* root)
{
// write code here
std::vector<std::vector<int>> res;
if (!root) return res;
std::queue<TreeNode*> qu;
qu.push(root);
while (!qu.empty())
{
int size = qu.size();
std::vector<int> vec;
while (size--)
{
TreeNode* node = qu.front();
qu.pop();
vec.push_back(node->val);
if (node->left) qu.push(node->left);
if (node->right) qu.push(node->right);
}
if (vec.size() > 0) res.push_back(vec);
}
return res;
}
void gen(int n, int cur, stNode* root)
{
if (cur <= n)
{
root->left = new stNode(root->num * 2);
root->right = new stNode(root->num * 2 + 1);
gen(n, cur + 1, root->left);
gen(n, cur + 1, root->right);
}
}
int main()
{
stNode* root1 = new stNode(1);
int datum = 2;
gen(5, 2, root1);
std::vector<int> res;
func(root1, res);
for (auto a : res)
std::cout << a << std::endl;
return 0;
}
在输出不分层方法func()中,也仅是输出结构展开成一维,元素的先后顺序还是要遵循原有的层级及左右顺序,考虑到这一层隐含约束,采用先入先出的队列结构std::queue作为中转比较合适,从root层开始,每个节点先将自己加入队列,然后加入其左右子节点,将queue的队首弹出并push进vector中;如此循环往复,直到queue为空即可。
在输出分层方法levelOrder()中,queue的作用和不分层方法中一样,只不过入队出队的控制有所区别。因为输出要分层,所以对树的每一层都保存一个vector,同时对queue的size做精细控制与使用,获取size时总是表征当前层中节点的个数,如此便能向每层的vector中完整地保存该层节点的遍历结果;每遍历完一层,将本层vectorpush进外层vector;如此循环往复,即可实现输出分层的层序遍历,且保留同一层中各节点的左右先后顺序。
二、输出整数的各个位上的数字
比如数字703,依次将百位的7、十位的0、个位的3输出,不考虑正负,这是一个典型的除与模的计算。
倒序输出如下,输出的依次是3、0、7,这是最简单的做法:
void func(unsigned int number)
{
do
{
std::cout << number % 10 << std::endl;
} while ((number /= 10) != 0);
}
正序输出,见有的用一边scanf一边printf的,这种纯属取巧而且万一直接给你个完整数字呢?还有用数组或者vector存放每一位的,但显然也不是最优解,最优解应当是要求不用缓存。代码如下:
void func(unsigned int number)
{
unsigned int pow = 1, tmp = number;
while ((tmp /= 10) != 0)
{
pow *= 10;
}
do
{
std::cout << number / pow << std::endl;
number %= pow;
pow /= 10;
} while (pow != 0);
}
先把最高位找出来,是10的多少次方,存到pow里,然后倒着逐步往个位找,这里注意,尤其第二个while的控制条件,要防止出现中间如果有哪一位是0的时候不输出的bug。
三、翻转一个数字
接上一题,把数字反转输出,比如输入723,输出327,需要考虑正负,比如输入-723,输出-327。
在上一题倒序输出的基础上,代码如下:
void func(int number)
{
if (number < 0)
{
std::cout << "-";
number = abs(number);
}
do
{
std::cout << number % 10;
} while ((number /= 10) != 0);
}