32 从上到下打印二叉树(举例让抽象问题具体化)
题目一描述:不分行从上到下打印二叉树,即树的广度优先遍历
从上往下打印出二叉树的每个节点,同层节点从左至右打印。(不分行)
测试用例:
功能测试(完全二叉树;所有节点只有左/右子树的二叉树)
特殊输入测试(二叉树的根节点为nullptr;只有一个节点的二叉树)
解题思路:
1)使用队列:
每次打印一个节点时,如果该节点有子节点,则把该节点的子节点放到一个队列的末尾。接下来到队列的头部取出最早进入队列的节点,重复前面的打印操作,直至队列中所有的节点都被打印出来。
//使用双向队列,用单向的队列就可以
/* struct TreeNode { int val; struct TreeNode *left; struct TreeNode *right; TreeNode(int x) : val(x), left(NULL), right(NULL) { } };*/ class Solution { public: vector<int> PrintFromTopToBottom(TreeNode* root) { vector<int> result; if(root==nullptr) return result; deque<TreeNode*> saveNodes; saveNodes.push_back(root); //error: while(saveNodes) saveNodes没有对应的bool值 while(!saveNodes.empty()){ //队列非空的时候 TreeNode* currNode = saveNodes.front(); saveNodes.pop_front(); result.push_back(currNode->val); if(currNode->left) //右子树非空,追加在队列的后面 saveNodes.push_back(currNode->left); if(currNode->right) //左子树非空,追加在队列的后面 saveNodes.push_back(currNode->right); } return result; } };
//单向队列
class Solution { public: vector<int> PrintFromTopToBottom(TreeNode* root) { vector<int> result; if(root==nullptr) return result; queue<TreeNode*> saveNodes; saveNodes.push(root); //error: while(saveNodes) saveNodes没有对应的bool值 while(!saveNodes.empty()){ //队列非空的时候 TreeNode* currNode = saveNodes.front(); saveNodes.pop(); result.push_back(currNode->val); if(currNode->left) //右子树非空,追加在队列的后面 saveNodes.push(currNode->left); if(currNode->right) //左子树非空,追加在队列的后面 saveNodes.push(currNode->right); } return result; } };
题目二描述: 分行从上到下打印二叉树(把二叉树打印成多行)
从上到下按层打印二叉树,同一层结点从左至右输出。每一层输出一行。
解题思路:
1)为了分行打印,增加两个变量:一个变量表示在当前层中还没有打印的节点数;另一个变量表示下一层节点的数目。
class Solution { public: vector<vector<int> > Print(TreeNode* pRoot) { vector<vector<int> > result; if(pRoot==nullptr) return result; vector<int> row; queue<TreeNode*> treeNodes; treeNodes.push(pRoot); int toBePrint = 1; //初始化为1,根节点 int nextLevel = 0; while(!treeNodes.empty()){ TreeNode* pNode = treeNodes.front(); treeNodes.pop(); row.push_back(pNode->val); toBePrint--; if(pNode->left){ treeNodes.push(pNode->left); nextLevel++; } if(pNode->right){ treeNodes.push(pNode->right); nextLevel++; } if(toBePrint==0){ result.push_back(row); //删除行!! vector<int>().swap(row); toBePrint = nextLevel; nextLevel = 0; } } return result; } };
错误的写法:
class Solution { public: vector<vector<int> > Print(TreeNode* pRoot) { vector<vector<int> > result; if(pRoot==nullptr) return result; int row = 0; queue<TreeNode*> treeNodes; treeNodes.push(pRoot); int toBePrint = 1; //初始化为1,根节点 int nextLevel = 0; while(!treeNodes.empty()){ TreeNode* pNode = treeNodes.front(); treeNodes.pop(); (result[row]).push_back(pNode->val); //error: 不能这样使用 result[row].push_back toBePrint--; if(pNode->left){ treeNodes.push(pNode->left); nextLevel++; } if(pNode->right){ treeNodes.push(pNode->right); nextLevel++; } if(toBePrint==0){ row++; toBePrint = nextLevel; nextLevel = 0; } } return result; } };
错误原因:
vector<vector<int> > result(10); (result[row]).push_back(pNode->val); //可以使用 result[row]
但是若初始化时,没有指定vector的大小,则不能使用result[row],属于数组越界,非法访问。这种情况只能用push_back在尾部追加。
2)用队列的长度来记录每层的节点数
class Solution { public: vector<vector<int> > Print(TreeNode* pRoot) { vector<vector<int> > result; if(pRoot == NULL) return result; queue<TreeNode*> q; q.push(pRoot); while(!q.empty()) { //每一个while循环打印一行 int index = 0; //用于遍历每行元素 int numNodes = q.size(); //当前行的元素数==队列里的元素数 vector<int> row; //用于存储每行的元素。 while(index++ < numNodes) { TreeNode *currNode = q.front(); q.pop(); row.push_back(currNode->val); if(currNode->left) q.push(currNode->left); if(currNode->right) q.push(currNode->right); } result.push_back(row); } return result; } };
题目二描述: 按之字形打印二叉树
请实现一个函数按照之字形打印二叉树,即第一行按照从左到右的顺序打印,第二层按照从右至左的顺序打印,第三行按照从左到右的顺序打印,其他行以此类推。
解题思路:
1)与分行打印树思路相同,只不过在偶数行时,将每行vector<int>倒序存入
class Solution { public: vector<vector<int> > Print(TreeNode* pRoot) { vector<vector<int> > result; if(pRoot==nullptr) return result; queue<TreeNode*> treeNodes; treeNodes.push(pRoot); int count=1; //记录单行还是双行 while(!treeNodes.empty()){ vector<int> row; //用于存每行的元素 //每次循环都会被清零 int numNodesInRow = treeNodes.size(); //每行元素的个数 while(numNodesInRow--){ TreeNode* curr = treeNodes.front(); //访问 treeNodes.pop(); //删除 row.push_back(curr->val); //读取当前节点的元素 if(curr->left) treeNodes.push(curr->left); if(curr->right) treeNodes.push(curr->right); } if((count & 0x1)==1){//第几行 result.push_back(row); }else{ vector<int> inverseRow(row.rbegin(),row.rend()); result.push_back(inverseRow); } count++; } return result; } };
* 大家的实现很多都是将每层的数据存进ArrayList中,偶数层时进行reverse操作,
* 在海量数据时,这样效率太低了。
* (我有一次面试,算法考的就是之字形打印二叉树,用了reverse,
* 直接被鄙视了,面试官说海量数据时效率根本就不行。
2)使用两个栈:(不必将每层的数据存进queue中,偶数层时进行reverse操作,直接按打印顺序存入)
在打印某一层节点时,把下一层子节点保存到相应的栈中。如果当前打印的时奇数层,从左到右打印,并先保存左子节点再保存右子节点到第一个栈中(奇数栈);如果当前打印的是偶数层,从右向左打印,并先保存右子节点再保存左子节点到第二个栈中(偶数栈)。
//实现1
class Solution { public: vector<vector<int> > Print(TreeNode* pRoot) { vector<vector<int> > result; if(pRoot==nullptr) return result; //使用两个栈存储 stack<TreeNode*> oddRow,evenRow; vector<int> row; //存储每行的遍历元素 int numRow = 1; evenRow.push(pRoot); while(oddRow.size() || evenRow.size()){//两个栈有一个非空时,进入循环 if((numRow & 0x1)==1){ //奇数行 从左到右 且先遍历左子树 while(evenRow.size()){ TreeNode* pNode = evenRow.top(); evenRow.pop(); row.push_back(pNode->val); if(pNode->left) oddRow.push(pNode->left); if(pNode->right) oddRow.push(pNode->right); } }else{ //偶数行 从右到左 且先遍历右子树 while(oddRow.size()){ TreeNode* pNode = oddRow.top(); oddRow.pop(); row.push_back(pNode->val); if(pNode->right) evenRow.push(pNode->right); if(pNode->left) evenRow.push(pNode->left); } } //遍历完一行 result.push_back(row); vector<int>().swap(row); //将row清空,便于下一行的存储 numRow++; } return result; } };
//实现2 class Solution { public: vector<vector<int> > Print(TreeNode* pRoot) { vector<vector<int> > result; if(pRoot==nullptr) return result; //使用两个栈存储 stack<TreeNode*> levels[2]; vector<int> row; //存储每行的遍历元素 int current = 0; int next = 1; levels[current].push(pRoot); // while(levels[0].size() || levels[1].size() ){ TreeNode* pNode = levels[current].top(); //读取当前节点 levels[current].pop(); //删除读取过的节点 row.push_back(pNode->val); if(current==0){ //奇数行,先存左子树 if(pNode->left) levels[next].push(pNode->left); if(pNode->right) levels[next].push(pNode->right); }else{ //偶数行,先存右子树 if(pNode->right) levels[next].push(pNode->right); if(pNode->left) levels[next].push(pNode->left); } if(levels[current].empty()){ //当前栈为空时,换栈 result.push_back(row); vector<int> ().swap(row); //清空row current = 1 - current; //交换栈 next = 1 - next; } } return result; } };