116. 填充同一層的兄弟節點
描述
給定一個二叉樹,填充它的每個 next 指針,讓這個指針指向其下一個右側節點。如果找不到下一個右側節點,則將 next 指針設置為 NULL。 初始狀態下,所有 next 指針都被設置為 NULL。
說明
- 你只能使用額外常數空間。
- 使用遞歸解題也符合要求,本題中遞歸程序占用的棧空間不算做額外的空間復雜度。
- 你可以假設它是一個完美二叉樹(即所有葉子節點都在同一層,每個父節點都有兩個子節點)。
示例
struct TreeLinkNode {
TreeLinkNode *left;
TreeLinkNode *right;
TreeLinkNode *next;
}
給定完美二叉樹,
1
/ \
2 3
/ \ / \
4 5 6 7
調用你的函數后,該完美二叉樹變為:
1 -> NULL
/ \
2 -> 3 -> NULL
/ \ / \
4->5->6->7 -> NULL
思路
- 一開始的想法是從節點出發,如果是左節點,則next是右節點,如果是右節點,則next是父節點next的左節點。整體是以單個節點為單元考慮的,需要區分左、右節點的情況,不容易變現為代碼。
- 往上,視野更寬些。以根節點+左右兩個節點為單元考慮就清晰多了。一個單元內的左節點next是右節點,右節點的next是父節點next的左節點(沒有就是NULL)。將三者放在一起考慮,遞歸就出來了。(參考)
- 如果不考慮額外的空間,可以利用一個queue來實現對樹的層次遍歷BSF. 每個節點的next都指向queue中的下一個節點。
1)小技巧,在每層最后一個元素后面加一個NULL
2)這樣不僅可以讓最后一個元素直接指向NULL,還可以用于判斷一層元素的遍歷結束。
class Solution_116_01 {
public:
void connect(TreeLinkNode* root) {
if (!root) return;
if (root->left) root->left->next = root->right;
if (root->right) root->right->next = root->next ? root->next->left : NULL;
connect(root->left);
connect(root->right);
}
};
// 將01解法的尾遞歸改為循環
class Solution_116_02 {
public:
void connect(TreeLinkNode* root) {
if (!root) return;
stack<TreeLinkNode*> mStack;
mStack.push(root);
while (!mStack.empty()) {
TreeLinkNode* node = mStack.top();
mStack.pop();
if (node->right) {
node->right->next = node->next ? node->next->left : NULL;
mStack.push(node->right);
}
if (node->left) {
node->left->next = node->right;
mStack.push(node->left);
}
}
}
};
// 利用queue實現層次遍歷
class Solution_116_03 {
public:
void connect(TreeLinkNode* root) {
if (!root) return;
queue<TreeLinkNode*> mQueue;
mQueue.push(root);
mQueue.push(NULL);
while (true) {
TreeLinkNode* node = mQueue.front();
mQueue.pop();
if (node) {
node->next = mQueue.front();
if (node->left) mQueue.push(node->left);
if (node->right) mQueue.push(node->right);
} else { // 當前節點為空,即本層節點遍歷完畢
if (mQueue.size() == 0) return;
mQueue.push(NULL); // 下一層節點都已裝載完畢,加入一個NULL的標記
}
}
}
};