題目描述
輸入一顆二叉樹和一個整數,打印出二叉樹中結點值的和爲輸入整數的所有路徑。路徑定義爲從樹的根結點開始往下一直到葉結點所經過的結點形成一條路徑。
分析:不要被問題嚇到,感覺很複雜。找個例子來分析一下,找規律:
10
5 12
4 7
找和爲22的路徑
1. 10->5->4 19 != 22
2. 10->5->7 22 == 22
3. 10->12 22 == 22
因此我們只要遍歷每一條路徑,判斷路徑的值是否爲要求的值,如果是,則輸出或者加入結果中。可以看出遍歷的方式是先序遍歷,如果遍歷到葉子結點證明一條路徑已經訪問完,判斷和是否與要求值相同,然後把當前葉子刪除出路徑,返回父結點(記得當前和也要刪除這個葉子結點的值,這裏劍指offer紀念版p.145代碼是沒有減去,是有錯誤的)。
總結一下就是:總體框架是先序遍歷遞歸方式,遍歷一個結點就把他加入到當前路徑中,它的值也加入到當前和中,遍歷到葉子結點就判斷和值,符合則加入結果。然後在路徑中刪除該葉子結點,當前和中減去該葉子的值,返回父結點。
代碼:
#include <stdio.h>
#include <stdlib.h>
#include <vector>
using namespace std;
struct TreeNode {
int val;
struct TreeNode *left;
struct TreeNode *right;
TreeNode(int x) :
val(x), left(NULL), right(NULL) {
}
};
void FindPathCore
(
TreeNode *root,
int expectedSum,
vector<int>& path,
vector<vector<int> >& result,
int currentSum
)
{
currentSum += root->val;
path.push_back(root->val);
//葉節點且路徑和等於要求和
bool leaf = (!root->left && !root->right);
if (leaf && currentSum == expectedSum)
{
vector<int> tempPath(path); //複製一個path的拷貝
result.push_back(tempPath); //直接加path後期修改path會改變原來的值吧?
}
//如果不是葉子,則遍歷它的左、右孩子
if (root->left)
FindPathCore(root->left, expectedSum, path, result, currentSum);
if (root->right)
FindPathCore(root->right, expectedSum, path, result, currentSum);
//在返回父結點之前,刪除路徑上的當前點
currentSum -= root->val;
path.pop_back();
}
vector<vector<int> > FindPath(TreeNode *root, int expectedSum)
{
vector<vector<int> >result;
vector<int> path;
if (root)
{
FindPathCore(root, expectedSum, path, result, 0);
}
return result;
}
//劍指offer原書版,根據先序遍歷序列、中序遍歷序列重建二叉樹
TreeNode* ConstructByPreIn(int *preOrder, int *inOrder, int length)
{
if (preOrder == NULL || inOrder == NULL || length <= 0) return NULL;
//新建結點,當前先序序列第一個元素爲根結點
int root_val = preOrder[0];
TreeNode *root = new TreeNode(root_val);
//初始化左右子樹參數
int leftLength = 0;
int rightLength = 0;
//在中序序列中找到根節點的位置(必存在)
for (int i = 0; i < length; i++)
{
if (inOrder[i] == root_val)
{
leftLength = i;
rightLength = length - i - 1;
break;
}
}
//中序序列中,根節點左邊的爲左子樹,右邊的爲右子樹
int *leftPre = preOrder + 1;
int *leftIn = inOrder;
int *rightPre = preOrder + leftLength + 1;
int *rightIn = inOrder + leftLength + 1;
//遞歸構建左右子樹
root->left = ConstructByPreIn(leftPre, leftIn, leftLength);
root->right = ConstructByPreIn(rightPre, rightIn, rightLength);
return root;
}
int main()
{
//構建二叉樹
int pre[] = {10, 5, 4, 7, 12};
int in[] = {4, 5, 7, 10, 12};
TreeNode *tree = ConstructByPreIn(pre, in, 5);
//測試
vector<vector<int> > result =
FindPath(tree, 22);
//輸出
for (auto v : result)
{
for (auto i : v)
{
printf("%d ", i);
}
printf("\n");
}
getchar();
}
測試結果: