二叉树常考题型:
- 能够结合队列、栈、链表、字符串等很多数据结构;
- 需要掌握图的基本遍历方式,比如BFS和DFS;
- 掌握递归函数的使用,并自己设计出递归过程;
- 与实际工作紧密结合。
二叉树类型:
- 满二叉树:除了最后一层节点无任何子节点外,剩下每一层节点都有两个子节点。层数为L,节点数为N,则N=2的L次方-1。
- 完全二叉树:除了最后一层外,其他每一层的节点数都是满的。最后一层如果也满了,也是一棵满二叉树,也是完全二叉树。最后一层不满,缺少的节点也全部集中在右边。
- 平衡二叉树(AVL树):1.空树是平衡二叉树;2.如果一棵树不为空,并且其中所有的子树都满足各自的左子树与右子树的高度差都不超过1。(后序遍历)
- 搜索二叉树:每棵子树的头节点值都比各自左子树上的所有节点值要大,也都比各自右子树上的所有节点值要小。(中序遍历后从小到大排列)(红黑树,平衡搜索二叉树)
- 前驱节点:这个节点在中序遍历序列中的上一个节点。
- 后继节点:这个节点在中序遍历序列中的下一个节点。
二叉树遍历方式:
- 先序遍历:中、左、右;
- 中序遍历:左、中、右;
- 后序遍历:左、右、中。
//请用递归方式实现二叉树的先序、中序和后序的遍历打印。 //请用非递归方式实现二叉树的先序、中序和后序的遍历打印。 //给定一个二叉树的根结点root,请依次返回二叉树的先序,中序和后续遍历(二维数组的形式)。
//递归方法声明 void preOrderByRecursion(TreeNode* root, vector<int>& preOrder); void midOrderByRecursion(TreeNode* root, vector<int>& midOrder); void postOrderByRecursion(TreeNode* root, vector<int>& postOrder);
//非递归方法声明 void preOrderNotRecursion(TreeNode* root, vector<int>& preOrder); void midOrderNotRecursion(TreeNode* root, vector<int>& midOrder); void postOrderNotRecursion(TreeNode* root, vector<int>& postOrder);
vector<vector<int>> TreeToSequence::convert(TreeNode* root){
//递归方法向量声明 vector<int> preOrderByRec; vector<int> midOrderByRec; vector<int> postOrderByRec; vector<vector<int>> convertResByRec; //递归方法遍历 preOrderByRecursion(root, preOrderByRec); midOrderByRecursion(root, midOrderByRec); postOrderByRecursion(root, postOrderByRec);
convertResByRec.push_back(preOrderByRec); convertResByRec.push_back(midOrderByRec); convertResByRec.push_back(postOrderByRec);
//非递归方法向量声明 vector<int> preOrderNotRec; vector<int> midOrderNotRec; vector<int> postOrderNotRec; vector<vector<int>> convertResNotRec; //非递归方法遍历 preOrderNotRecursion(root, preOrderNotRec); midOrderNotRecursion(root, midOrderNotRec); postOrderNotRecursion(root, postOrderNotRec);
convertResNotRec.push_back(preOrderNotRec); convertResNotRec.push_back(midOrderNotRec); convertResNotRec.push_back(postOrderNotRec);
return convertResByRec; //return convertResByRec;
}
//先序遍历的递归实现 void preOrderByRecursion(TreeNode* root, vector<int>& preOrder){ if (root == NULL) return; preOrder.push_back(root->val); preOrderByRecursion(root->left, preOrder); preOrderByRecursion(root->right, preOrder); }
//中序遍历的递归实现 void midOrderByRecursion(TreeNode* root, vector<int>& midOrder){ if (root == NULL) return;
midOrderByRecursion(root->left, midOrder); midOrder.push_back(root->val); midOrderByRecursion(root->right, midOrder); }
//后序遍历的递归实现 void postOrderByRecursion(TreeNode* root, vector<int>& postOrder){ if (root == NULL) return;
postOrderByRecursion(root->left, postOrder); postOrderByRecursion(root->right, postOrder); postOrder.push_back(root->val); }
/* 先序遍历的非递归实现: 1.首先申请一个新的栈,记为stack。 2.然后将头节点head压入stack中。 3.每次从stack中弹出栈顶节点,记为cur;然后打印cur节点的值。如果cur右孩子不为空的话,将cur的右孩子先压入stack中。 最后如果cur的左孩子不为空的话,将cur的左孩子压入stack中。 4.不断重复步骤3,直到stack为空,全部过程结束。 */ void preOrderNotRecursion(TreeNode* root, vector<int>& preOrder){ stack<TreeNode*> nodeStack; TreeNode* curNode = NULL;
nodeStack.push(root); while (!nodeStack.empty()){ curNode = nodeStack.top(); nodeStack.pop(); preOrder.push_back(curNode->val); if (curNode->right != NULL)nodeStack.push(curNode->right); if (curNode->left != NULL)nodeStack.push(curNode->left); } }
/* 中序遍历的非递归实现: 1.申请一个新的栈,记为stack,申请一个变量cur,初始时令cur等于头节点。 2.先把cur节点压入栈中,对以cur节点为头的整棵子树来说,依次把整棵树的左边界压入栈中, 即不断令cur=cur->left,然后重复步骤二。 3.不断重复步骤2,直到发现cur为空,此时从stack中弹出一个节点,记为node。 打印node的值,并让cur=node->right,然后继续重复步骤2. 4.当stack为空且cur为空时,整个过程结束。 */ void midOrderNotRecursion(TreeNode* root, vector<int>& midOrder){ stack<TreeNode*> nodeStack; TreeNode* curNode = root->left; TreeNode* node = NULL;
nodeStack.push(root); while (!nodeStack.empty() || curNode != NULL ){ while (curNode != NULL){ nodeStack.push(curNode); curNode = curNode->left; } node = nodeStack.top(); nodeStack.pop(); midOrder.push_back(node->val); curNode = node->right; } }
/* 后序遍历的非递归实现: 1.申请一个栈,记为s1,然后将头节点压入s1中。 2.从s1中弹出的节点记为cur,然后先把cur的左孩子压入s1中,然后把cur1的右孩子压入s1中。 3.在整个过程中,每一个从s1中弹出的节点都放进第二个栈s2中。 4.不断重复步骤2和步骤3,直到s1为空,过程停止。 5.从s2中依次弹出节点并打印,打印的顺序就是后序遍历的顺序了。 */ void postOrderNotRecursion(TreeNode* root, vector<int>& postOrder){ stack<TreeNode*> nodeStack1; stack<TreeNode*> nodeStack2; TreeNode* curNode = root;
nodeStack1.push(root); while (!nodeStack1.empty()){ curNode = nodeStack1.top(); nodeStack2.push(curNode); nodeStack1.pop(); if (curNode->left != NULL) nodeStack1.push(curNode->left); if (curNode->right != NULL) nodeStack1.push(curNode->right);
} while (!nodeStack2.empty()){ postOrder.push_back(nodeStack2.top()->val); nodeStack2.pop(); } }
1.按层遍历二叉树
//有一棵二叉树,请设计一个算法,按照层次打印这棵二叉树。 // //给定二叉树的根结点root,请返回打印结果,结果按照每一层一个数组进行储存, //所有数组的顺序按照层数从上往下,且每一层的数组内元素按照从左往右排列。 //保证结点数小于等于500。
vector<vector<int> > TreePrinter::printTree(TreeNode* root){ //遍历的节点 TreeNode* node = NULL; //遍历的每一行的最后一个节点,用于换行 TreeNode* last = root; //下一行的最后一个节点,用于记录下一行的最后一个节点 TreeNode* nlast = root; //队列记录要扫描的节点 queue<TreeNode*> treeQueue; vector<int> floor; vector<vector<int> > res;
treeQueue.push(root); while (!treeQueue.empty()){ //扫描队列中的节点 node = treeQueue.front(); treeQueue.pop(); //把出队列的节点打印出来 floor.push_back(node->val); //如果当前出队列的节点的左孩子不为NULL,则加入队列,nlast后移 if (node->left != NULL){ treeQueue.push(node->left); nlast = node->left; } //如果当前出队列的节点的右孩子不为NULL,则加入队列,nlast后移 if (node->right != NULL){ treeQueue.push(node->right); nlast = node->right; } //如果当前节点是一行的最后一个节点,则last换为下一行的最后一个节点 //每一行的打印值存入res,并清除当前floor中的数,用于记录下一行 if (node == last){ last = nlast; res.push_back(floor); floor.clear(); } }
return res; }
2.判断平衡二叉树
//有一棵二叉树,请设计一个算法判断这棵二叉树是否为平衡二叉树。 // //给定二叉树的根结点root,请返回一个bool值,代表这棵树是否为平衡二叉树。 int check(TreeNode* root);
//递归计算左子树与右子树的高,如果相差大于1则返回-1,相差小于1则左右子树中大的那个加1 bool CheckBalanceTree::checkBalanceTree(TreeNode* root){ bool res = check(root) >= 0 ? true : false; return res; }
int check(TreeNode* root){ if (root == NULL) return 0;
int left = check(root->left); int right = check(root->right);
if (left < 0 || right < 0)return -1; if (abs(left – right)>1)return -1;
return left – right>=0 ? left + 1 : right + 1; }
3.判断完全二叉树
//有一棵二叉树, 请设计一个算法判断它是否是完全二叉树。 // //给定二叉树的根结点root,请返回一个bool值代表它是否为完全二叉树。树的结点个数小于等于500。
bool CheckCompletionTree::checkCompletionTree(TreeNode* root){ queue<TreeNode*> nodeQueue; TreeNode* last = root; TreeNode* nlast = root; //按层遍历判断是否前面有一节点为NULL,后面仍然有节点不为空 TreeNode* completion = root; TreeNode* node = NULL;
nodeQueue.push(root); while (!nodeQueue.empty()){ node = nodeQueue.front(); nodeQueue.pop(); //如果左孩子为空右孩子不为空,直接返回false if (node->left == NULL && node->right != NULL){ return false; } if (node->left != NULL){ //如果前面有一个节点为空,现在后面有节点不为空,返回false if (completion == NULL) return false; nodeQueue.push(node->left); nlast = node->left; completion = node->left; } //如果有一个节点为空 else{ completion = NULL; } if (node->right != NULL){ //如果前面有一个节点为空,现在后面有节点不为空,返回false if (completion == NULL) return false; nodeQueue.push(node->right); nlast = node->right; completion = node->right; } //如果有一个节点为空 else{ completion = NULL; } //按层遍历换行 if (node == last){ last = nlast; } } return true; }
4.折纸练习
//请把纸条竖着放在桌⼦上,然后从纸条的下边向上⽅对折,压出折痕后再展 开。 //此时有1条折痕,突起的⽅向指向纸条的背⾯,这条折痕叫做“下”折痕 ;突起的⽅向指向纸条正⾯的折痕叫做“上”折痕。 //如果每次都从下边向上⽅ 对折,对折N次。请从上到下计算出所有折痕的⽅向。 // //给定折的次数n, 请返回从上到下的折痕的数组,若为下折痕则对应元素为”down”, 若为上折痕则为”up”. // //测试样例: //1 //返回:[“down”]
//折纸练习:折纸规律为根节点为down,右孩子为down,左孩子为up的n层满二叉树 //输出的顺序为:按右中左遍历此满二叉树
vector<string> FoldPaper::foldPaper(int n){
vector<string> res; printDirection(n, “down”, res); return res; }
vector<string> printDirection(int n, string str, vector<string> &res){ if (n == 0) return res;
printDirection(n – 1, “down”,res); res.push_back(str); printDirection(n – 1, “up”, res); return res; }