LeetCode深度優先アルゴリズムのツリー(ツリー変換)
6909 ワード
以下の5つの練習問題はdfsで配列orチェーンテーブルと二叉木の相互変換の問題を解決し,統一的に一緒に処理することで学習を効果的に強化し,記憶を強固にすることができる.
105.前の順序と中の順序でシーケンスを遍歴して二叉木を構築する
タイトルの説明
1本の木の前順遍歴と中順遍歴に基づいて二叉樹を構築する.
注意:ツリーに重複する要素がないと仮定できます.
たとえば、
プリアンブル遍歴preorder=[3,9,20,15,7]のシーケンス遍歴inorder=[9,3,15,20,7]は、次のツリーを返します.
構想
まずこの問題を解決するには,まず前序と中序遍歴の特徴を知る.前順遍歴の特徴は中左右,すなわち配列の最初の要素であるツリールートノード全体の値であり,中順遍歴の特徴は左中右,すなわち左サブツリーのノードがルートノードの左側,右サブツリーがルートノードの右側である.
上記の特徴によれば,前シーケンス+中シーケンスの遍歴シーケンスに基づいて二叉木を構築することができる.ツリーのルートノード は、前のシーケンスのループシーケンスで見つける.このルートノード は、中間シーケンスにおいて見出される.
次いで、は、サブツリー を再帰的に構築する.
サブツリーを再帰的に構築するには、再帰的ないくつかの条件を明確にする必要があります.再帰の終了条件 再帰シーケンス構造 まず再帰の終了条件であり,我々はツリーの遍歴結果に基づいてツリーを構築するので,遍歴の配列に基づいて再帰条件を決定することができる.
次に再帰的な順序構造であり,ルートノードの位置を決定してから対応する左サブツリーと右サブツリーを見つけることができるため,この場合は先にノードを決定してから再帰することであり,先順遍歴と同様である.
また,mapを用いて中系列の遍歴結果をキャッシュし,重複遍歴を回避し,空間交換時間を用いることができる.
コード実装
106.中間シーケンスと後シーケンスからシーケンスを遍歴して二叉木を構築する
タイトルの説明
構想
この問題は上の105問題とほぼ似ていて、あまり違いはありません.後続のルートノードは、配列の最後の要素です.配列の境界は左開き右閉です.
コード#コード#
108.秩序配列を二叉探索ツリーに変換する
タイトルの説明
昇順に配列された秩序配列を、高さバランスツリーに変換します.
本題では,1つの高さバランスツリーとは,1つのツリーの各ノードの左右2つのサブツリーの高さ差の絶対値が1を超えないことを指す.
例:
与えられた秩序配列:[−10,−3,0,5,9],
1つの可能な答えは、[0,-3,9,-10,null,5]であり、次のような高度バランスの二叉探索ツリーを表すことができます.
構想
まず,問題記述に基づいて,この問題は昇順の配列を提供する.二叉探索ツリーの特徴は,中順遍歴が秩序化されていることを知っている.したがって,この問題を二叉探索ツリーにおけるシーケンス遍歴の結果として復元することができる.また,タイトルは高さ差が1を超えない,すなわち左右がバランスしているという条件を与えている.これにより二分探索を用いることができ,midインデックスは実はルートノードに対応する位置であり,midの左側がルートノードの左サブツリーであり,逆に右サブツリーであり,順次再帰すれば本題を解決できる.
コード#コード#
109.秩序チェーンテーブル変換二叉探索ツリー
タイトルの説明
要素が昇順にソートされ、高度にバランスのとれた二叉検索ツリーに変換される単一チェーンテーブルが与えられます.
本題では,1つの高さバランスツリーとは,1つのツリーの各ノードの左右2つのサブツリーの高さ差の絶対値が1を超えないことを指す.
例:
与えられた秩序チェーンテーブル:[−10,−3,0,5,9],
1つの可能な答えは、[0,−3,9,−10,null,5]であり、次のような高度バランスの二叉探索ツリーを表すことができる.
構想
本題は108とほぼ同じで,本題がチェーンテーブルからツリー構造に変換されている点が異なる.チェーンテーブルが中間ノードを計算する方法は配列ほど簡単ではなく,チェーンテーブルは高速ポインタによって実現される.
チェーンテーブルの中間ノードを計算する方法を知っていれば、二分構造を使用してツリー構造を構築し続けることができます.
コード#コード#
114.ツリーをチェーンテーブルに展開
タイトルの説明
ツリーを指定し、その場で単一チェーンテーブルに展開します.
構想
展開の順序をよく見ると実際には前順遍歴の順序であるため,まずチェーンテーブルを前順遍歴し,遍歴の結果をリストに格納することができる.
展開するときは順番に関係を作り、左ノードをnullにすればよい.
現在のノードの親ノードは、前のノードの右サブツリーです.
コード#コード#
105.前の順序と中の順序でシーケンスを遍歴して二叉木を構築する
タイトルの説明
1本の木の前順遍歴と中順遍歴に基づいて二叉樹を構築する.
注意:ツリーに重複する要素がないと仮定できます.
たとえば、
プリアンブル遍歴preorder=[3,9,20,15,7]のシーケンス遍歴inorder=[9,3,15,20,7]は、次のツリーを返します.
3
/ \
9 20
/ \
15 7
構想
まずこの問題を解決するには,まず前序と中序遍歴の特徴を知る.前順遍歴の特徴は中左右,すなわち配列の最初の要素であるツリールートノード全体の値であり,中順遍歴の特徴は左中右,すなわち左サブツリーのノードがルートノードの左側,右サブツリーがルートノードの右側である.
上記の特徴によれば,前シーケンス+中シーケンスの遍歴シーケンスに基づいて二叉木を構築することができる.
次いで、
サブツリーを再帰的に構築するには、再帰的ないくつかの条件を明確にする必要があります.
if (instart == inEnd || preStart == preEnd) return;
次に再帰的な順序構造であり,ルートノードの位置を決定してから対応する左サブツリーと右サブツリーを見つけることができるため,この場合は先にノードを決定してから再帰することであり,先順遍歴と同様である.
ensure the root node;
recursion left;
recursion right;
また,mapを用いて中系列の遍歴結果をキャッシュし,重複遍歴を回避し,空間交換時間を用いることができる.
コード実装
class Solution {
private Map map = new HashMap<>();
public TreeNode buildTree(int[] preorder, int[] inorder) {
int index = 0;
for (int cur : inorder) {
map.put(cur, index++);
}
return build(preorder, inorder, 0, preorder.length, 0, inorder.length);
}
TreeNode build(int[] pre, int[] in, int pStart, int pEnd, int inStart, int inEnd) {
if (pStart == pEnd) return null;
int rootVal = pre[pStart];
int rootIndex = map.get(rootVal);
TreeNode root = new TreeNode(rootVal);
int leftNum = rootIndex - inStart;
root.left = build(pre, in, pStart + 1, pStart + 1 + leftNum, inStart, rootIndex);
root.right = build(pre, in, pStart + 1 + leftNum, pEnd, rootIndex + 1, inEnd);
return root;
}
}
106.中間シーケンスと後シーケンスからシーケンスを遍歴して二叉木を構築する
タイトルの説明
。
:
。
,
inorder = [9,3,15,20,7]
postorder = [9,15,7,20,3]
:
3
/ \
9 20
/ \
15 7
構想
この問題は上の105問題とほぼ似ていて、あまり違いはありません.後続のルートノードは、配列の最後の要素です.配列の境界は左開き右閉です.
コード#コード#
class Solution {
Map map = new HashMap<>();
public TreeNode buildTree(int[] inorder, int[] postorder) {
int index = 0;
for (int cur : inorder) {
map.put(cur, index++);
}
return build(inorder, postorder, 0, inorder.length, 0, postorder.length);
}
TreeNode build(int[] in, int[] post,int iStart, int iEnd, int pStart, int pEnd) {
if (iStart == iEnd || pStart == pEnd) return null;
int rootVal = post[pEnd - 1];
TreeNode root = new TreeNode(rootVal);
int rootIndex = map.get(rootVal);
int leftNum = rootIndex - iStart;
root.left = build(in, post, iStart, rootIndex, pStart, pStart + leftNum);
root.right = build(in, post, rootIndex + 1, iEnd, pStart + leftNum, pEnd - 1);
return root;
}
}
108.秩序配列を二叉探索ツリーに変換する
タイトルの説明
昇順に配列された秩序配列を、高さバランスツリーに変換します.
本題では,1つの高さバランスツリーとは,1つのツリーの各ノードの左右2つのサブツリーの高さ差の絶対値が1を超えないことを指す.
例:
与えられた秩序配列:[−10,−3,0,5,9],
1つの可能な答えは、[0,-3,9,-10,null,5]であり、次のような高度バランスの二叉探索ツリーを表すことができます.
0
/ \
-3 9
/ /
-10 5
構想
まず,問題記述に基づいて,この問題は昇順の配列を提供する.二叉探索ツリーの特徴は,中順遍歴が秩序化されていることを知っている.したがって,この問題を二叉探索ツリーにおけるシーケンス遍歴の結果として復元することができる.また,タイトルは高さ差が1を超えない,すなわち左右がバランスしているという条件を与えている.これにより二分探索を用いることができ,midインデックスは実はルートノードに対応する位置であり,midの左側がルートノードの左サブツリーであり,逆に右サブツリーであり,順次再帰すれば本題を解決できる.
コード#コード#
class Solution {
public TreeNode sortedArrayToBST(int[] nums) {
if (nums == null || nums.length == 0) return null;
return build(nums, 0, nums.length - 1);
}
TreeNode build(int[] nums, int left, int right) {
if (left > right) return null;
int mid = (right - left) / 2 + left;
TreeNode root = new TreeNode(nums[mid]);
root.left = build(nums, left, mid - 1);
root.right = build(nums, mid + 1, right);
return root;
}
}
109.秩序チェーンテーブル変換二叉探索ツリー
タイトルの説明
要素が昇順にソートされ、高度にバランスのとれた二叉検索ツリーに変換される単一チェーンテーブルが与えられます.
本題では,1つの高さバランスツリーとは,1つのツリーの各ノードの左右2つのサブツリーの高さ差の絶対値が1を超えないことを指す.
例:
与えられた秩序チェーンテーブル:[−10,−3,0,5,9],
1つの可能な答えは、[0,−3,9,−10,null,5]であり、次のような高度バランスの二叉探索ツリーを表すことができる.
0
/ \
-3 9
/ /
-10 5
構想
本題は108とほぼ同じで,本題がチェーンテーブルからツリー構造に変換されている点が異なる.チェーンテーブルが中間ノードを計算する方法は配列ほど簡単ではなく,チェーンテーブルは高速ポインタによって実現される.
slow = node;
fast = node;
while (fast.next != boarder && != fast.next.next != null) {
fast = fast.next.next;
slow = slow.next;
}
return slow;
チェーンテーブルの中間ノードを計算する方法を知っていれば、二分構造を使用してツリー構造を構築し続けることができます.
コード#コード#
class Solution {
public TreeNode sortedListToBST(ListNode head) {
if (head == null) return null;
return build(head, null);
}
TreeNode build(ListNode left, ListNode right) {
if (left == right) return null;
ListNode mid = getMid(left, right);
TreeNode root = new TreeNode(mid.val);
root.left = build(left, mid);
root.right = build(mid.next, right);
return root;
}
ListNode getMid(ListNode left, ListNode right){
ListNode slow = left;
ListNode fast = left;
while (fast.next != right && fast.next.next != right) {
slow = slow.next;
fast = fast.next.next;
}
return slow;
}
114.ツリーをチェーンテーブルに展開
タイトルの説明
ツリーを指定し、その場で単一チェーンテーブルに展開します.
1
/ \
2 5
/ \ \
3 4 6
:
1
\
2
\
3
\
4
\
5
\
6
構想
展開の順序をよく見ると実際には前順遍歴の順序であるため,まずチェーンテーブルを前順遍歴し,遍歴の結果をリストに格納することができる.
展開するときは順番に関係を作り、左ノードをnullにすればよい.
現在のノードの親ノードは、前のノードの右サブツリーです.
コード#コード#
class Solution {
List list = new ArrayList<>();
public void flatten(TreeNode root) {
if (root == null) return ;
pre(root);
TreeNode pre = null;
TreeNode cur = null;
for (int i = 1; i < list.size(); i++) {
pre = list.get(i - 1);
cur = list.get(i);
pre.left = null;
pre.right = cur;
}
}
void pre(TreeNode root) {
if (root != null) {
list.add(root);
pre(root.left);
pre(root.right);
}
}
}