目次
私は誰もが二分木探索の再帰的方法に非常に精通していると信じています、実際、それは同様のフレームワークです
if(p!=null){
//前序遍历时访问结点
if(p.left!=null){
//递归进入左子节点遍历
}
//中序遍历时访问结点
if(p.right!=null){
//递归进入右子节点遍历
}
//后序遍历时访问结点
}
書く前、書いた後、書いた後の再帰的なトラバーサル方法は非常に単純ですが、この方法はプログラミングを学ぶのにあまり意味がありませんが、反復法は、特にインタビューで、人の思考能力を反映することができます。バイナリツリーを最初、中間、最後の順序でトラバースする非再帰的な方法のコードをどのように書くか?
二分木のフロントミドルポストオーダートラバーサルフレームワーク
フロントミドルポストオーダートラバーサルにも対応するフレームワークがあります。コアコードは次のとおりです。(Leetcode Guo Guoから)
プレオーダーコアコード
while(root != null || !stack.isEmpty()){
//go left down to the ground
while(root != null){
res.add(root.val);
stack.push(root);
root = root.left;
}
//if we reach to the leaf, go back to the parent right, and repeat the go left down.
TreeNode cur = stack.pop();
root = cur.right;
}
中間コアコード
while (root != null || !stack.isEmpty()) {
while (root != null) {
stack.push(root);
root = root.left;
}
root = stack.pop();
res.add(root.val);
root = root.right;
}
コアコードの後
while(root != null || !stack.isEmpty()){
while(root != null){
res.add(root.val);
stack.push(root);
root = root.right;
}
TreeNode cur = stack.pop();
root = cur.left;
}
二分木のプレオーダートラバーサル
leetcode144の質問に対応
問題の説明
二分木のルートノードルートを与え、そのノード値のプレオーダートラバーサルを返します。
例1:
入力:root = [1、null、2,3]
出力:[1,2,3]
例2:
输入:root = []
输出:[]
例3:
输入:root = [1]
输出:[1]
例4:
输入:root = [1,2]
输出:[1,2]
問題解決のアイデア:
プレオーダートラバーサルの順序は、ルートの左右
です。プレオーダートラバーサルのフレームワークは次のとおりです。
while(root != null || !stack.isEmpty()){
//go left down to the ground
while(root != null){
res.add(root.val);
stack.push(root);
root = root.left;
}
//if we reach to the leaf, go back to the parent right, and repeat the go left down.
TreeNode cur = stack.pop();
root = cur.right;
}
プレオーダートラバーサルの場合、最初にルートノードを取得してから左に移動します。フレームワークでは、最初に対応するノード値にアクセスし、次にノードをスタックにプッシュして、左端まで移動します。つまり、その右の子ノードをポイントします。whileループが終了すると、左側が空であることがわかり、要素がスタックからポップされて右側の子ノードに移動します。3の右側の子ノードは最初は空なので、そのままにしておくことができます。単独で、次に次の外側のループそのとき、ルートは空であるため、内側のwhileループを経由せずに、要素2がスタックからポップされ、次に2の右側の子ノードにアクセスします。4と入力し、4の左端まで移動すると、前回のトラバーサルでは常に可能な限り左下に移動していることがわかります。左下に到達して移動する方法がない場合、ポップアップします。要素を選択し、現在のノードの右側に移動してから、左端まで移動します。つまり、ここには行くところがなくなるまで左に下がる傾向があります。このとき、もう一度スタックをポップして、右に行く方法があるかどうかを確認します。まだ右側にある場合は、右に行き、すぐに左に下りました。
実装コード
class Solution {
List<Integer> list; //用list集合来保存我们遍历过程中的元素
public List<Integer> preorderTraversal(TreeNode root) {
list=new ArrayList<>();
//创建一个栈,模拟递归时的系统栈
Stack<TreeNode> stack=new Stack<TreeNode>();
//当结点不为空并且栈也不为空的时候,就说明还有元素待遍历
while(root!=null || !stack.empty()){
//如果可以一直往左走,就一直往左右,一直走到底
while(root!=null){
list.add(root.val); //前序遍历先访问根节点
stack.push(root); //将结点入栈
root=root.left; //一直往走左
}
//如果不能再往左走,就弹出一个元素
root=stack.pop(); //弹出栈顶元素
//然后先往右走一步
root=root.right;
}
return list;
}
}
二分木のポストオーダートラバーサル
問題の説明
二分木が与えられた場合、そのポストオーダートラバーサルを返します。
例:
输入: [1,null,2,3]
1
\
2
/
3
输出: [3,2,1]
高度:再帰的アルゴリズムは非常に単純ですが、反復アルゴリズムを使用して実行できますか?
問題解決のアイデア
一連の
帰りがけ順は、左と右のルーツである。帰りがけ順のシーケンスは、左と右のルーツである。我々は順序を逆にした場合、それは根右になり、左。これは、同じ効果を持っていますプレオーダートラバーサル。逆の方法で左右のルートを取得できます。左右のルートと左右のルートの違いは何ですか?プレオーダートラバーサルでは、root = root.leftです。ここでは、root = root、right、frontです。シーケンストラバーサルでは、root = root.rightであるため、ここでは、root = root、leftであり、他のコードは実際には驚くほど似ています。写真を見ると、可能な限り右に向かっています。行き場がなくなるまで、要素をポップアップして、この要素の左側のノードを検討します。しかし、新しいノードを取得したら、どこにも行けなくなるまで再び右に固執してから上に移動します。
最後に、Javaコレクションクラスによって提供される逆関数を使用して、順序を逆にすることができます。
実装コード
class Solution {
private List<Integer> list;
public List<Integer> postorderTraversal(TreeNode root) {
list=new ArrayList<Integer>();
Stack<TreeNode> stack=new Stack<TreeNode>();
//后序遍历序列是左右根,我们遵循根右左的规则,它与前序遍历左右刚好相反
while(root!=null || !stack.empty()){
//我们选择一直往右走,直到走到底
while(root!=null){
list.add(root.val); //根右左,同样是先访问根节点
stack.push(root); //入栈
root=root.right; //一直往右走
}
root=stack.pop(); //弹出一个元素
//走到底之后,就向左走一步
root=root.left;
}
//上面我们得到了一个根右左的序列,要想得到左右根,转置一下即可。
Collections.reverse(list);
return list;
}
}
二分木の順序通りの走査
問題の説明
二分木のルートノードルートが与えられた場合、その中次走査を返します。
例1:
输入:root = [1,null,2,3]
输出:[1,3,2]
例2:
输入:root = []
输出:[]
例3:
输入:root = [1]
输出:[1]
例4:
输入:root = [1,null,2]
输出:[1,2]
問題解決のアイデア:
真ん中の順序でトラバースされたシーケンスは左ルート右です
while (root != null || !stack.isEmpty()) {
while (root != null) {
stack.push(root);
root = root.left;
}
root = stack.pop();
res.add(root.val);
root = root.right;
}
プレオーダートラバーサルとポストオーダートラバーサルでは、内側のwhileループトラバーサル中にノードにアクセスします(つまり、ノード値をコンテナーに追加します)が、ミドルオーダートラバーサルはプッシュ時に値を入力しません。スタックイン。順序は左ルート右であるため、どこにも到達しないまで、ポップアップする最初の要素が結果に入れられます。これは、プレオーダートラバーサルとリバースポストオーダートラバーサルの両方が最初にルートノードにアクセスするためです。 whileループトラバーサル中にアクセスされます。ここでのプロセスは、左端まで移動すると、3のポイントに移動してポップアップし、結果セットに3を追加してから、右のノードができるまでポップアップを続けます。右に一歩進んでから、もう一度前のプロセスを続けます。
実装コード
class Solution {
public List<Integer> inorderTraversal(TreeNode root) {
List<Integer> list=new ArrayList<Integer>();
Stack<TreeNode> stack=new Stack<TreeNode>(); //辅助栈
while(root!=null || !stack.empty()){
//一直往左走,直到走到底,但是我们不将中间拿到的结点值放入到容器中
while(root!=null){
stack.push(root);
root=root.left;
}
//出栈一个结点,并将其放入到容器中
root=stack.pop();
list.add(root.val);
root=root.right;
}
return list;
}
}
総括する:
pre-middle-post-orderトラバーサルの場合、それらの非再帰的トラバーサルコードは非常に似ています。返されるのはリストセットです。ここで重要な点は、取得したノード値をコンテナに追加するときです。これが必要なことです。検討してください。