題目描述:
給你二叉樹的根節點 root ,返回它節點值的 前序 遍歷。
思路分析:
遞迴法:
前序遍歷的順序是中左右的順序。那麼每個子樹都是這個順序,所以可以使用遞迴進行遍歷。遞迴遍歷有3部曲
1.確定遞迴函式的引數和返回值。
因為返回值要求儲存在一個陣列中,所以遞迴函式的引數應該包括樹的根節點和結果陣列
` func preOrder(root *TreeNode,res *[]int) `
2.確定遞迴函式的終止條件
對於前序遍歷來說,當某個根節點沒有孩子節點的時候終止遍歷。
```
if root==nil {
return
} ```
3.確定單層遞迴的邏輯
按照順序進行遍歷即可。
*res = append(*res, root.Val)
preOrder(root.Left,res)
preOrder(root.Right,res)
最終的程式碼為:
點選檢視程式碼
func preorderTraversal(root *TreeNode) []int {
// 遞迴函式3步驟:
// 1.確定遞迴函式的引數 2.確定遞迴函式的停止條件 3.確定單層遞迴的呼叫邏輯
var res []int
preOrder(root,&res)
return res
}
func preOrder(root *TreeNode,res *[]int){
if root==nil {
return
}
*res = append(*res, root.Val)
preOrder(root.Left,res)
preOrder(root.Right,res)
}
中序遍歷:
點選檢視程式碼
func inorderTraversal(root *TreeNode) []int {
var res []int
if root==nil{
return res
}
getOrder(root,&res)
return res
}
func getOrder(root *TreeNode, res *[]int) {
if root!=nil {
getOrder(root.Left,res)
*res = append(*res, root.Val)
getOrder(root.Right,res)
}
}
後序遍歷:
點選檢視程式碼
func postorderTraversal(root *TreeNode) []int {
var res []int
postOrder(root,&res)
return res
}
func postOrder(root *TreeNode,res *[]int) {
if root==nil{
return
}
postOrder(root.Left,res)
postOrder(root.Right,res)
*res = append(*res, root.Val)
}
非遞迴版本
上方是三種遍歷的遞迴方式,一定要明確遞迴的3個步驟。但是遞迴一般複雜度高,很難解決資料量大的資料。在計算機中遞迴就是用棧實現的,所以我們可以用棧來實現遞迴函式的非遞迴版本。但是三種遍歷方式的寫法會有區別。
三種遍歷的非遞迴其實都是從根節點開始按照根左右的方式入棧,區別就在於什麼時候應該出棧。
前序遍歷
前序遍歷的順序是根左右,由於是棧來實現,所有實際的入棧順序是根右左,當訪問到根的時候將根節點入棧--記錄其值,然後將根節點出棧,按照根節點的右左孩子的順序入棧,再根據棧頂元素作為根節點進行處理。函式結束條件則是棧的長度大於0.
點選檢視程式碼
func preorderTraversal(root *TreeNode) []int{
var res []int
if root==nil{
return res
}
var stack []*TreeNode
stack = append(stack, root)
for len(stack)>0 {
node:=stack[len(stack)-1]
stack = stack[:len(stack)-1]
if node!=nil {
res = append(res, node.Val)
}
if node.Right != nil {
stack = append(stack, node.Right)
}
if node.Left!=nil {
stack = append(stack, node.Left)
}
}
return res
}
中序遍歷
中序的遍歷順序是左中右。
入棧順序:從根節點開始,首先將當前節點的左子樹一路壓入棧中,直到當前節點沒有左子樹為止。
出棧條件:當棧頂節點沒有左子樹時,出棧該節點並訪問它,然後繼續對其右子樹進行遍歷。
函式結束條件是 root!=nil || len(stack)>0
這是因為當遍歷完左子樹之後,回溯到整個樹的root的時候,棧已經空了,但是右子樹還沒有入棧,所以還需要判斷root是否為空。
點選檢視程式碼
func inorderTraversal(root *TreeNode) []int{
if root==nil {
return []int{}
}
res,stack:=[]int{},[]*TreeNode{}
for root!=nil || len(stack)>0 {
// 先遍歷左子樹
for root!=nil {
stack = append(stack, root)
root = root.Left
}
// 處理當前節點,最左下方的節點
root = stack[len(stack)-1]
stack = stack[:len(stack)-1]
res = append(res, root.Val)
// 遍歷右子樹
root = root.Right
}
return res
}
後序遍歷
後序遍歷的順序是:左-右-根。
入棧順序: 從根節點開始,首先將當前節點壓入棧中,然後按照“左右”的順序將節點的左孩子和右孩子壓入棧中。
出棧條件: 每次出棧棧頂元素後,僅在左右子樹都已訪問完成的情況下才訪問根節點。為了保證這一點,通常需要藉助一個額外的標記。所以我們透過pre指標來判斷左右孩子是否已經訪問過,只有他們都被訪問過才能訪問根節點。
因為入棧順序,所以棧頂元素必然是葉子節點,透過prev記錄上一次訪問的節點,如果當前節點是葉子節點,直接記錄;若不是葉子節點,如果prev是當前節點的任一孩子,說明孩子節點訪問完畢「2種情況,2個孩子和只有一個孩子,模擬一下便知」,則可以訪問根節點了。
點選檢視程式碼
func postorderTraversal(root *TreeNode) []int{
if root==nil{
return []int{}
}
res,stack := []int{},[]*TreeNode{}
var prev *TreeNode
stack = append(stack, root)
for len(stack)>0{
current:=stack[len(stack)-1]
if(current.Left== nil && current.Right ==nil) || (prev!=nil && (prev == current.Left || prev==current.Right)){
res = append(res, current.Val)
stack = stack[:len(stack)-1]
prev = current
}else{
if current.Right!=nil {
stack = append(stack, current.Right)
}
if current.Left != nil {
stack = append(stack, current.Left)
}
}
}
return res
}