Given a binary tree
struct Node { int val; Node *left; Node *right; Node *next; }
Populate each next pointer to point to its next right node. If there is no next right node, the next pointer should be set to NULL
.
Initially, all next pointers are set to NULL
.
Example:
Input: {"$id":"1","left":{"$id":"2","left":{"$id":"3","left":null,"next":null,"right":null,"val":4},"next":null,"right":{"$id":"4","left":null,"next":null,"right":null,"val":5},"val":2},"next":null,"right":{"$id":"5","left":null,"next":null,"right":{"$id":"6","left":null,"next":null,"right":null,"val":7},"val":3},"val":1} Output: {"$id":"1","left":{"$id":"2","left":{"$id":"3","left":null,"next":{"$id":"4","left":null,"next":{"$id":"5","left":null,"next":null,"right":null,"val":7},"right":null,"val":5},"right":null,"val":4},"next":{"$id":"6","left":null,"next":null,"right":{"$ref":"5"},"val":3},"right":{"$ref":"4"},"val":2},"next":null,"right":{"$ref":"6"},"val":1} Explanation: Given the above binary tree (Figure A), your function should populate each next pointer to point to its next right node, just like in Figure B.
Note:
- You may only use constant extra space.
- Recursive approach is fine, implicit stack space does not count as extra space for this problem.
這道是之前那道 Populating Next Right Pointers in Each Node 的延續,原本的完全二叉樹的條件不再滿足,但是整體的思路還是很相似,仍然有遞歸和非遞歸的解法。我們先來看遞歸的解法,這里由於子樹有可能殘缺,故需要平行掃描父節點同層的節點,找到他們的左右子節點。代碼如下:
解法一:
class Solution { public: Node* connect(Node* root) { if (!root) return NULL; Node *p = root->next; while (p) { if (p->left) { p = p->left; break; } if (p->right) { p = p->right; break; } p = p->next; } if (root->right) root->right->next = p; if (root->left) root->left->next = root->right ? root->right : p; connect(root->right); connect(root->left); return root; } };
對於非遞歸的方法,我驚喜的發現之前的方法直接就能用,完全不需要做任何修改,算法思路可參見之前的博客 Populating Next Right Pointers in Each Node,代碼如下:
解法二:
// Non-recursion, more than constant space class Solution { public: Node* connect(Node* root) { if (!root) return NULL; queue<Node*> q; q.push(root); while (!q.empty()) { int len = q.size(); for (int i = 0; i < len; ++i) { Node *t = q.front(); q.pop(); if (i < len - 1) t->next = q.front(); if (t->left) q.push(t->left); if (t->right) q.push(t->right); } } return root; } };
雖然以上的兩種方法都能通過OJ,但其實它們都不符合題目的要求,題目說只能使用constant space,可是OJ卻沒有寫專門檢測space使用情況的test,那么下面貼上constant space的解法,這個解法也是用的層序遍歷,只不過沒有使用queue了,我們建立一個dummy結點來指向每層的首結點的前一個結點,然后指針cur用來遍歷這一層,我們實際上是遍歷一層,然后連下一層的next,首先從根結點開始,如果左子結點存在,那么cur的next連上左子結點,然后cur指向其next指針;如果root的右子結點存在,那么cur的next連上右子結點,然后cur指向其next指針。此時root的左右子結點都連上了,此時root向右平移一位,指向其next指針,如果此時root不存在了,說明當前層已經遍歷完了,我們重置cur為dummy結點,root此時為dummy->next,即下一層的首結點,然后dummy的next指針清空,或者也可以將cur的next指針清空,因為前面已經將cur賦值為dummy了。那么現在想一想,為什么要清空?因為我們用dummy的目的就是要指到下一行的首結點的位置即dummy->next,而一旦將root賦值為dummy->next了之后,這個dummy的使命就已經完成了,必須要斷開,如果不斷開的話,那么假設現在root是葉結點了,那么while循環還會執行,不會進入前兩個if,然后root右移賦空之后,會進入最后一個if,之前沒有斷開dummy->next的話,那么root又指向之前的葉結點了,死循環誕生了,跪了。所以一定要記得清空哦,呵呵噠~
這里再來說下dummy結點是怎樣指向每層的首結點的前一個結點的,過程是這樣的,dummy是創建出來的一個新的結點,其目的是為了指向root結點的下一層的首結點的前一個,具體是這么做到的呢,主要是靠cur指針,首先cur指向dummy,然后cur再連上root下一層的首結點,這樣dummy也就連上了。然后當root層遍歷完了之后,root需要往下移動一層,這樣dummy結點之后連接的位置就正好賦值給root,然后cur再指向dummy,dummy之后斷開,這樣又回到了初始狀態,以此往復就可以都連上了,代碼如下:
解法三:
// Non-recursion, constant space class Solution { public: Node* connect(Node* root) { Node *dummy = new Node(0, NULL, NULL, NULL), *cur = dummy, *head = root; while (root) { if (root->left) { cur->next = root->left; cur = cur->next; } if (root->right) { cur->next = root->right; cur = cur->next; } root = root->next; if (!root) { cur = dummy; root = dummy->next; dummy->next = NULL; } } return head; } };
類似題目:
Populating Next Right Pointers in Each Node
參考資料:
https://leetcode.com/problems/populating-next-right-pointers-in-each-node-ii/