各ノードの次の右側のノードポインタを入力します
問題
完全なバイナリツリーが与えられると、そのすべてのリーフノードは同じレベルにあり、各親ノードには2つの子ノードがあります。バイナリツリーは次のように定義されます。
struct Node { int val; Node * left; Node * right; Node * next; }次の各ポインターを埋めて、このポインターが次の右ノードを指すようにします。次の右側のノードが見つからない場合、次のポインタはNULLに設定されます。
初期状態では、次のすべてのポインタはNULLに設定されます。
例:
输入:{
"$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":{
"$id":"6","left":null,"next":null,"right":null,"val":6},"next":null,"right":{
"$id":"7","left":null,"next":null,"right":null,"val":7},"val":3},"val":1}
输出:{
"$id":"1","left":{
"$id":"2","left":{
"$id":"3","left":null,"next":{
"$id":"4","left":null,"next":{
"$id":"5","left":null,"next":{
"$id":"6","left":null,"next":null,"right":null,"val":7},"right":null,"val":6},"right":null,"val":5},"right":null,"val":4},"next":{
"$id":"7","left":{
"$ref":"5"},"next":null,"right":{
"$ref":"6"},"val":3},"right":{
"$ref":"4"},"val":2},"next":null,"right":{
"$ref":"7"},"val":1}
解释:给定二叉树如图 A 所示,你的函数应该填充它的每个 next 指针,以指向其下一个右侧节点,如图 B 所示。
促す:
- 一定の余分なスペースのみを使用できます。
- 再帰を使用して問題を解決することも要件を満たします。この問題で再帰プログラムが占めるスタックスペースは、追加のスペースの複雑さとしてカウントされません。
アイデア
バイナリツリーを見て、最初に考えるのは、プレオーダートラバーサル、ミドルオーダートラバーサル、ポストオーダートラバーサル、深度優先検索(DFS)、幅優先検索(BFS)です。
この問題は、幅優先検索(BFS)メソッドを使用すると簡単に実行できます。
public void levelOrder(TreeNode tree) {
if (tree == null)
return;
Queue<TreeNode> queue = new LinkedList<>();
queue.add(tree);//相当于把数据加入到队列尾部
while (!queue.isEmpty()) {
//poll方法相当于移除队列头部的元素
TreeNode node = queue.poll();
System.out.println(node.val);
if (node.left != null)
queue.add(node.left);
if (node.right != null)
queue.add(node.right);
}
}
メソッド1を取得するように変更します。
方法1:レベルトラバーサル
class Solution {
public Node connect(Node root) {
if (root == null) {
return root;
}
// 初始化队列同时将第一层节点加入队列中,即根节点
Queue<Node> queue = new LinkedList<Node>();
queue.add(root);
// 外层的 while 循环迭代的是层数
while (!queue.isEmpty()) {
// 记录当前队列大小
int size = queue.size();
// 遍历这一层的所有节点
for (int i = 0; i < size; i++) {
// 从队首取出元素
Node node = queue.poll();
// 连接
if (i < size - 1) {
node.next = queue.peek();
}
// 拓展下一层节点
if (node.left != null) {
queue.add(node.left);
}
if (node.right != null) {
queue.add(node.right);
}
}
}
// 返回根节点
return root;
}
}
方法2:確立された次のポインターを使用する
現在のノードが左側のノードである場合、現在のノードの親ノードがわかっているので、右側のノードを簡単に接続できます(タイトルは完全なバイナリツリーであることに注意してください)。
しかし、問題は、下の図に示すように、同じレイヤー内の2つのノードの親ノードが異なることです。
上位レイヤーのノードが接続されている場合、上位レイヤーを介して次のノードを見つけることができますか?
など
curr.right.next = curr.next.left;
具体的には:
ルートノードから開始します。レベル0にはノードが1つしかないため、処理は必要ありません。next
ポインタの下のレイヤー上のレイヤーとして確立できます。この方法の最も重要なポイントは、x +1番目のレイヤーがx番目のレイヤーにあるときにそのnext
ポインターを確立することです。これらの接続操作が完了したら、x + 1レイヤーに移動して、x +2レイヤーの次のポインターを確立します。
レイヤーのノードをトラバースすると、レイヤーノードnext
ポインターが確立されます。これにより、キューが不要になり、スペースが節約されます。次のレイヤーの左端のノードを知るたびに、そのノードから開始して、リンクされたリストをトラバースするのと同じように、このレイヤーのすべてのノードをトラバースできます。
コード
class Solution {
public Node connect(Node root) {
if (root == null) {
return root;
}
// 从根节点开始
Node leftmost = root;
while (leftmost.left != null) {
// 遍历这一层节点组织成的链表,为下一层的节点更新 next 指针
Node head = leftmost;
while (head != null) {
// CONNECTION 1
head.left.next = head.right;
// CONNECTION 2
if (head.next != null) {
head.right.next = head.next.left;
}
// 指针向后移动
head = head.next;
}
// 去下一层的最左的节点
leftmost = leftmost.left;
}
return root;
}
}
再帰
前のレベルの方法を理解していれば、dfsを理解することは難しくありません。
public Node connect(Node root) {
dfs(root, null);
return root;
}
private void dfs(Node curr, Node next) {
if (curr == null)
return;
curr.next = next;
dfs(curr.left, curr.right);
dfs(curr.right, curr.next == null ? null : curr.next.left);
}
BFSと再帰を解決する4つの方法を参照してください
(最後の3つの方法はユーザーの100%を打ち負かしました)