算法基礎知識——二叉樹
目錄:
- 基礎知識
- 基本定義
- 應用實例
- 重建二叉樹【劍指Offer_編程題】
- 二叉樹的鏡像【劍指Offer_編程題】
- 從上往下打印二叉樹【劍指Offer_編程題】
- 二叉搜索樹的後續遍歷序列【劍指Offer_編程題】
- 二叉樹中和爲某一值的路徑【劍指Offer_編程題】
- 二叉樹的深度【劍指Offer_編程題】
- 平衡二叉樹【劍指Offer_編程題】
- 二叉搜索樹與雙向鏈表【劍指Offer_編程題】
- 對稱的二叉樹【劍指Offer_編程題】
- 二叉樹的下一個結點【劍指Offer_編程題】
- 把二叉樹打印成多行【劍指Offer_編程題】
- 二叉搜索樹的第k個結點【劍指Offer_編程題】
- 按之字形順序打印二叉樹【劍指Offer_編程題】
- 高度最小的BST【程序員面試金典_ 編程題】
- 輸出單層結點【程序員面試金典_ 編程題】
一、基礎知識
1、基本定義:
- 度:樹中一個結點的子結點的個數稱爲結點的度,樹中結點的最大度數稱爲樹的度。
- 有序樹和無序樹:
- 有序樹:樹中結點的子樹從左到右是有序的,不能交換。
- 無序樹:樹中結點的子結點位置可以互換。
- 路徑:樹中兩個結點的路徑是由這兩個結點之間所經過的結點序列構成的。
- 路徑長度:路徑上所經過的邊的個數。
二、應用實例:
1、題目描述:輸入某二叉樹的前序遍歷和中序遍歷的結果,請重建出該二叉樹。假設輸入的前序遍歷和中序遍歷的結果中都不含重複的數字。例如輸入前序遍歷序列{1,2,4,7,3,5,6,8}和中序遍歷序列{4,7,2,1,5,3,8,6},則重建二叉樹並返回。
/**
* Definition for binary tree
* struct TreeNode {
* int val;
* TreeNode *left;
* TreeNode *right;
* TreeNode(int x) : val(x), left(NULL), right(NULL) {}
* };
*/
TreeNode* reConstructBinaryTree(vector<int> pre,vector<int> vin) {
//write code here
}【劍指Offer_編程題】
- 輸入格式:前序遍歷序列和中序遍歷序列
- 輸出格式:重建的二叉樹
- 樣例輸入:無
- 樣例輸出:無
示例代碼:
class Solution {
public:
TreeNode* reConstructBinaryTree(vector<int> pre,vector<int> vin){
if(pre.size() == 0 || vin.size() == 0){
return NULL;
}
vector<int> left_pre, right_pre, left_vin, right_vin;
TreeNode *head = new TreeNode(pre[0]);
int gen = 0;
for(int i = 0; i < vin.size(); i++){
if(vin[i] == head->val){
gen = i;
break;
}
}
for(int i = 0; i < gen; i++){
left_vin.push_back(vin[i]);
left_pre.push_back(pre[i + 1]);
}
for(int i = gen + 1; i < vin.size(); i++){
right_vin.push_back(vin[i]);
right_pre.push_back(pre[i]);
}
head->left = reConstructBinaryTree(left_pre, left_vin);
head->right = reConstructBinaryTree(right_pre, right_vin);
return head;
}
};
2、題目描述:題目描述:操作給定的二叉樹,將其變換爲源二叉樹的鏡像。
二叉樹的鏡像定義:源二叉樹
8
/ \
6 10
/ \ / \
5 7 9 11
鏡像二叉樹
8
/ \
10 6
/ \ / \
11 9 7 5
void Mirror(TreeNode *pRoot) {
//write code here
}【劍指Offer_編程題】
- 輸入格式:源二叉樹
- 輸出格式:鏡像二叉樹
- 樣例輸入:無
- 樣例輸出:無
示例代碼:
class Solution {
public:
void Mirror(TreeNode *pRoot) {
if(pRoot == NULL){
return;
}
TreeNode *p = pRoot->right;
pRoot->right = pRoot->left;
pRoot->left = p;
Mirror(pRoot->right);
Mirror(pRoot->left);
}
};
3、題目描述:從上往下打印出二叉樹的每個節點,同層節點從左至右打印。
vector<int> PrintFromTopToBottom(TreeNode* root) {
//write code here
}【劍指Offer_編程題】
- 輸入格式:二叉樹根節點指針
- 輸出格式:節點列表
- 樣例輸入:無
- 樣例輸出:無
示例代碼:
/*
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) {
queue<TreeNode *> myQueue;
vector<int> result;
if(root != NULL){
myQueue.push(root);
}
while(!myQueue.empty()){
TreeNode *current = myQueue.front();
myQueue.pop();
result.push_back(current->val);
if(current->left){
myQueue.push(current->left);
}
if(current->right){
myQueue.push(current->right);
}
}
return result;
}
};
4、題目描述:輸入一個整數數組,判斷該數組是不是某二叉搜索樹的後序遍歷的結果。如果是則輸出Yes,否則輸出No。假設輸入的數組的任意兩個數字都互不相同。
bool VerifySquenceOfBST(vector<int> sequence) {
//write code here
}【劍指Offer_編程題】
- 輸入格式:整型序列
- 輸出格式:如果是則輸出Yes,否則輸出No
- 樣例輸入:無
- 樣例輸出:無
示例代碼:
class Solution {
public:
bool VerifySquenceOfBST(vector<int> sequence){
if(sequence.empty()){
return false;
}
int length = sequence.size();
if(length == 1){
return true;
}
vector<int> leftSeq, rightSeq;
int i = length - 2;
while(i >= 0 && sequence[i] > sequence[length - 1]){
rightSeq.push_back(sequence[i]);
i--;
}
while(i >= 0 && sequence[i] < sequence[length - 1]){
leftSeq.push_back(sequence[i]);
i--;
}
if(i >= 0){
return false;
}
VerifySquenceOfBST(leftSeq);
VerifySquenceOfBST(rightSeq);
return true;
}
};
5、題目描述:輸入一顆二叉樹的根節點和一個整數,打印出二叉樹中結點值的和爲輸入整數的所有路徑。路徑定義爲從樹的根結點開始往下一直到葉結點所經過的結點形成一條路徑。(注意: 在返回值的list中,數組長度大的數組靠前)
/*
struct TreeNode {
int val;
struct TreeNode *left;
struct TreeNode *right;
TreeNode(int x) :
val(x), left(NULL), right(NULL) {
}
};*/
vector<vector<int> > FindPath(TreeNode* root,int expectNumber) {
//wirte code here
}【劍指Offer_編程題】
- 輸入格式:TreeNode* root,int expectNumber
- 輸出格式:vector<vector<int> >
- 樣例輸入:無
- 樣例輸出:無
示例代碼:
class Solution {
public:
vector<vector<int> > resultList;
vector<int> myVector;
vector<vector<int> > FindPath(TreeNode* root,int expectNumber){
if(root == NULL){
return resultList;
}
myVector.push_back(root->val);
expectNumber -= root->val;
if(expectNumber == 0 && root->left == NULL && root->right == NULL){
resultList.push_back(myVector);
}
FindPath(root->left, expectNumber);
FindPath(root->right, expectNumber);
myVector.erase(myVector.end() - 1);
return resultList;
}
};
6、題目描述:輸入一棵二叉樹,求該樹的深度。從根結點到葉結點依次經過的結點(含根、葉結點)形成樹的一條路徑,最長路徑的長度爲樹的深度。
/*
struct TreeNode {
int val;
struct TreeNode *left;
struct TreeNode *right;
TreeNode(int x) :
val(x), left(NULL), right(NULL) {
}
};*/
int TreeDepth(TreeNode* pRoot){
//write code here
}【劍指Offer_編程題】
- 輸入格式:根結點指針變量
- 輸出格式:樹的深度
- 樣例輸入:無
- 樣例輸出:無
示例代碼:
class Solution {
public:
int maxDepth = 0;
void CountDepth(TreeNode* pRoot, int count){
if(pRoot == NULL){
return;
}
if(pRoot->left == NULL && pRoot->right == NULL){
maxDepth = max(maxDepth, count);
}
CountDepth(pRoot->left, count + 1);
CountDepth(pRoot->right, count + 1);
}
int TreeDepth(TreeNode* pRoot){
maxDepth = 0;
if(pRoot != NULL){
CountDepth(pRoot, 1);
}
return maxDepth;
}
};
7、題目描述:輸入一棵二叉樹,判斷該二叉樹是否是平衡二叉樹。
bool IsBalanced_Solution(TreeNode* pRoot) {
//write code here
}【劍指Offer_編程題】
- 輸入格式:樹根結點指針變量
- 輸出格式:判斷結果
- 樣例輸入:無
- 樣例輸出:無
示例代碼:
class Solution {
public:
vector<int> myVector;
void JudgeBST(TreeNode* pRoot){
//中序遍歷進行判斷
if(pRoot != NULL){
JudgeBST(pRoot->left);
myVector.push_back(pRoot->val);
JudgeBST(pRoot->right);
}
}
int getDepth(TreeNode *p){
if(p == NULL){
return 0;
}
int left = getDepth(p->left);
if(left == -1){
return -1;
}
int right = getDepth(p->right);
if(right == -1){
return -1;
}
return abs(right - left) > 1 ? -1 : max(right, left) + 1;
}
bool IsBalanced_Solution(TreeNode* pRoot){
if(pRoot == NULL){
return true;
}
//理論上需要先判斷是否是一棵二叉排序樹
/*myVector.clear();
JudgeBST(pRoot);
for(int i = 1; i < myVector.size(); i++){
if(myVector[i] <= myVector[i - 1]){
return false;
}
}*/
if(getDepth(pRoot) == -1){
return false;
}
return true;
}
};
8、題目描述:輸入一棵二叉搜索樹,將該二叉搜索樹轉換成一個排序的雙向鏈表。要求不能創建任何新的結點,只能調整樹中結點指針的指向。
TreeNode* Convert(TreeNode* pRootOfTree){
//write code here
}【劍指Offer_編程題】
- 輸入格式:根結點指針
- 輸出格式:排序後的根結點指針
- 樣例輸入:無
- 樣例輸出:無
示例代碼:
class Solution {
public:
void ConvertHelper(TreeNode *cur, TreeNode *&prev){
if(cur != NULL){
ConvertHelper(cur->left, prev);
cur->left = prev;
if(prev){
prev->right = cur;
}
prev = cur;
ConvertHelper(cur->right, prev);
}
}
TreeNode* Convert(TreeNode *pRootOfTree){
if(pRootOfTree == NULL){
return pRootOfTree;
}
TreeNode *prev = NULL;
ConvertHelper(pRootOfTree, prev);
TreeNode *res = pRootOfTree;
while(res->left){
res = res->left;
}
return res;
}
};
9、題目描述:請實現一個函數,用來判斷一顆二叉樹是不是對稱的。注意,如果一個二叉樹同此二叉樹的鏡像是同樣的,定義其爲對稱的。
bool isSymmetrical(TreeNode* pRoot){
//write code here
}【劍指Offer_編程題】
- 輸入格式:根結點指針
- 輸出格式:判斷是否對稱
- 樣例輸入:無
- 樣例輸出:無
示例代碼1:
class Solution {
public:
bool isSymmetrical(TreeNode* pRoot){
if(pRoot == NULL){
return true;
}
queue<TreeNode *> myQueue;
myQueue.push(pRoot);
bool flag = true; //第一次根結點時flag爲true,之後列爲false
while(!myQueue.empty()){
TreeNode *leftTree = NULL, *rightTree = NULL;
if(flag){ //初始時
TreeNode *node = myQueue.front();
myQueue.pop();
if(node->left != NULL && node->right != NULL){
if(node->left->val != node->right->val){
return false;
}else{
myQueue.push(node->left);
myQueue.push(node->right);
}
}
flag = false;
}else{
leftTree = myQueue.front();
myQueue.pop();
rightTree = myQueue.front();
myQueue.pop();
if(leftTree->left != NULL && rightTree->right != NULL){
if(leftTree->left->val == rightTree->right->val){
myQueue.push(leftTree->left);
myQueue.push(rightTree->right);
}else{
return false;
}
}else if(leftTree->left == NULL && rightTree->right != NULL
|| leftTree->left != NULL && rightTree->right == NULL){
return false;
}
if(leftTree->right != NULL && rightTree->left != NULL){
if(leftTree->right->val == rightTree->left->val){
myQueue.push(leftTree->right);
myQueue.push(rightTree->left);
}else{
return false;
}
}else if(leftTree->right == NULL && rightTree->left != NULL
|| leftTree->right != NULL && rightTree->left == NULL){
return false;
}
}
}
return true;
}
};
示例代碼2:
class Solution {
public:
bool SymmetricalHelper(TreeNode *left, TreeNode *right){
if(left == NULL) return right == NULL;
if(right == NULL) return false;
if(left->val != right->val) return false;
return SymmetricalHelper(left->right, right->left)
&& SymmetricalHelper(left->left, right->right);
}
bool isSymmetrical(TreeNode *pRoot){
if(pRoot == NULL){
return true;
}
return SymmetricalHelper(pRoot->left, pRoot->right);
}
};
10、題目描述:給定一個二叉樹和其中的一個結點,請找出中序遍歷順序的下一個結點並且返回。注意,樹中的結點不僅包含左右子結點,同時包含指向父結點的指針。
/*
struct TreeLinkNode {
int val;
struct TreeLinkNode *left;
struct TreeLinkNode *right;
struct TreeLinkNode *next;
TreeLinkNode(int x) :val(x), left(NULL), right(NULL), next(NULL) {
}
};
*/
TreeLinkNode* GetNext(TreeLinkNode* pNode){}
//write code here
}【劍指Offer_編程題】
- 輸入格式:二叉樹的其中一個結點
- 輸出格式:中序遍歷順序的下一個結點
- 樣例輸入:無
- 樣例輸出:無
示例代碼1:
class Solution {
public:
vector<TreeLinkNode *> nodeList;
void InOrder(TreeLinkNode *T){
if(T != NULL){
InOrder(T->left);
nodeList.push_back(T);
InOrder(T->right);
}
}
TreeLinkNode* GetNext(TreeLinkNode *pNode){
TreeLinkNode *head = NULL, *tmp = pNode;
while(tmp->next != NULL){
tmp = tmp->next;
}
head = tmp;
nodeList.clear();
InOrder(head);
for(int i = 0; i < nodeList.size() - 1; i++){
if(nodeList[i] == pNode){
return nodeList[i + 1];
}
}
return NULL;
}
};
示例代碼2:
class Solution {
public:
TreeLinkNode* GetNext(TreeLinkNode *pNode){
if(pNode == NULL){
return NULL;
}
//該結點的右孩子爲空
if(pNode->right == NULL){
//向上找父結點,找到第一個父結點是左孩子則停止
while(pNode->next != NULL){
if(pNode->next->left == pNode){
return pNode->next;
}else{
pNode = pNode->next;
}
}
}
//該結點右孩子不爲空
else{
pNode = pNode->right;
while(pNode->left){
pNode = pNode->left;
}
return pNode;
}
return NULL;
}
};
11、題目描述:從上到下按層打印二叉樹,同一層結點從左至右輸出。每一層輸出一行。
vector<vector<int> > Print(TreeNode* pRoot) {
//write code here
}【劍指Offer_編程題】
- 輸入格式:根結點指針變量
- 輸出格式:二維數組,按層輸出
- 樣例輸入:無
- 樣例輸出:無
示例代碼:
class Solution {
public:
vector<vector<int> > Print(TreeNode* pRoot) {
vector<vector<int> > resultList;
if(pRoot == NULL){
return resultList;
}
queue<TreeNode *> myQueue;
vector<int> tmpValVector;
vector<TreeNode *> tmpVector;
myQueue.push(pRoot);
while(!myQueue.empty()){
tmpVector.clear();
tmpValVector.clear();
while(!myQueue.empty()){
if(myQueue.front()->left != NULL){
tmpVector.push_back(myQueue.front()->left);
}
if(myQueue.front()->right != NULL){
tmpVector.push_back(myQueue.front()->right);
}
tmpValVector.push_back(myQueue.front()->val);
myQueue.pop();
}
resultList.push_back(tmpValVector);
for(int i = 0; i < tmpVector.size(); i++){
myQueue.push(tmpVector[i]);
}
}
return resultList;
}
};
12、題目描述:給定一棵二叉搜索樹,請找出其中的第k小的結點。例如,(5,3,7,2,4,6,8)中,按結點數值大小順序第三小結點的值爲4。
TreeNode* KthNode(TreeNode* pRoot, int k){
//write code here
}【劍指Offer_編程題】
- 輸入格式:樹的根結點指針,整型k
- 輸出格式:第k小的結點的指針變量
- 樣例輸入:無
- 樣例輸出:無
示例代碼1:
class Solution {
public:
int index = 0;
TreeNode* KthNode(TreeNode* pRoot, int k){
if(pRoot){
TreeNode *node = KthNode(pRoot->left, k);
if(node != NULL){
return node;
}
index++;
if(index == k){
return pRoot;
}
node = KthNode(pRoot->right, k);
if(node != NULL){
return node;
}
}
return NULL;
}
};
示例代碼2:
class Solution {
public:
vector<TreeNode *> resultList;
void InOrder(TreeNode* pRoot){
if(pRoot != NULL){
InOrder(pRoot->left);
resultList.push_back(pRoot);
InOrder(pRoot->right);
}
}
TreeNode* KthNode(TreeNode* pRoot, int k){
if(k <= 0 || pRoot == NULL){
return NULL;
}
resultList.clear();
InOrder(pRoot);
if(k > resultList.size()){
return NULL;
}
return resultList[k - 1];
}
};
13、題目描述:請實現一個函數按照之字形打印二叉樹,即第一行按照從左到右的順序打印,第二層按照從右至左的順序打印,第三行按照從左到右的順序打印,其他行以此類推。
vector<vector<int> > Print(TreeNode* pRoot){
//write code here
}【劍指Offer_編程題】
- 輸入格式:根結點指針變量
- 輸出格式:二維數組,按層輸出
- 樣例輸入:無
- 樣例輸出:無
示例代碼:
class Solution {
public:
vector<vector<int> > Print(TreeNode* pRoot){
vector<vector<int> > resultList;
if(pRoot == NULL){
return resultList;
}
int level = 1;
queue<TreeNode *> myQueue;
myQueue.push(pRoot);
while(!myQueue.empty()){
vector<int> tmpValVector;
vector<TreeNode *> tmpNodeVector;
while(!myQueue.empty()){
TreeNode *node = myQueue.front();
myQueue.pop();
tmpValVector.push_back(node->val);
if(node->left != NULL){
tmpNodeVector.push_back(node->left);
}
if(node->right != NULL){
tmpNodeVector.push_back(node->right);
}
}
if(level % 2 == 0){
reverse(tmpValVector.begin(), tmpValVector.end());
}
resultList.push_back(tmpValVector);
for(int i = 0; i < tmpNodeVector.size(); i++){
myQueue.push(tmpNodeVector[i]);//從左到右入結點
}
level++;
}
return resultList;
}
};
14、題目描述:對於一個元素各不相同且按升序排列的有序序列,請編寫一個算法,創建一棵高度最小的二叉查找樹。給定一個有序序列int[] vals,請返回創建的二叉查找樹的高度。
int buildMinimalBST(vector<int> vals) {
// write code here
}【程序員面試金典_ 編程題】
- 輸入格式:有序序列
- 輸出格式:高度值
- 樣例輸入:無
- 樣例輸出:無
示例代碼:
class MinimalBST {
public:
int buildMinimalBST(vector<int> vals) {
int length = vals.size();
int count = 0;
while(length > 0){
count++;
length /= 2;
}
return count;
}
};
15、題目描述:對於一棵二叉樹,請設計一個算法,創建含有某一深度上所有結點的鏈表。給定二叉樹的根結點指針TreeNode* root,以及鏈表上結點的深度,請返回一個鏈表ListNode,代表該深度上所有結點的值,請按樹上從左往右的順序鏈接,保證深度不超過樹的高度,樹上結點的值爲非負整數且不超過100000。
/*
struct TreeNode {
int val;
struct TreeNode *left;
struct TreeNode *right;
TreeNode(int x) :
val(x), left(NULL), right(NULL) {
}
};*/
/*
struct ListNode {
int val;
struct ListNode *next;
ListNode(int x) : val(x), next(NULL) {}
};*/
ListNode* getTreeLevel(TreeNode* root, int dep){
//write code here
}【程序員面試金典_ 編程題】
- 輸入格式:根結點指針變量,深度值
- 輸出格式:含有某一深度上所有結點的鏈表
- 樣例輸入:無
- 樣例輸出:無
示例代碼:
class TreeLevel {
public:
ListNode* getTreeLevel(TreeNode* root, int dep){
if(root == NULL || dep <= 0){
return NULL;
}
ListNode *head = new ListNode(-1);
ListNode *p = head;
queue<TreeNode *> myQueue;
myQueue.push(root);
int curr = 1;
while(!myQueue.empty()){
vector<TreeNode *> tmpVector;
while(!myQueue.empty()){
TreeNode *node = myQueue.front();
myQueue.pop();
if(curr == dep){
p->next = new ListNode(node->val);
p = p->next;
}else{
if(node->left){
tmpVector.push_back(node->left);
}
if(node->right){
tmpVector.push_back(node->right);
}
}
}
for(int i = 0; i < tmpVector.size(); i++){
myQueue.push(tmpVector[i]);
}
curr++;
}
return head->next;
}
};