go int 最大值_用Go擼一個二叉搜尋樹
阿新 • • 發佈:2021-01-04
技術標籤:go int 最大值
本來轉自@colobu
https://colobu.com/2020/07/15/implement-bst-in-Go/
前幾天Redis的作者antirez說他朋友面試的時候考到排序問題,然後他說要是他也會考實現一個二叉搜尋樹,我說在中國某公司,據說面試直接就擼一個紅黑樹。不是說你技術渣,試問在座的各位有幾個現在直接裸寫出紅黑樹?
紅黑樹太過偏門,但是常用的二叉搜尋樹你能寫出來嗎?快排呢?堆排序呢?
什麼是二叉搜尋樹
二叉搜尋樹(binary search tree,BST)也叫排序的二叉樹,根節點比左邊子樹的所有節點都大,比右邊子樹上的所有節點都小,如下圖就是一個二叉搜尋樹:
要實現一個二叉搜尋樹, 我們需要實現節點的插入和刪除,要實現節點的查詢(搜尋),要實現前序遍歷、中序遍歷和後序遍歷,要實現最大節點和最小節點的查詢。
下面就讓我們實現這個二叉搜尋樹。
定義基本資料結構
常規地,我們定義節點的型別,每個節點包含它的值以及左右節點。因為目前Go泛型還沒有釋出,所以這裡我們實現一個元素為int型別的具體的二叉搜尋樹,等泛型實現後可以改成抽象的二叉搜尋樹。
樹只要包含根節點可以了。
// Node 定義節點.type Node struct { value int // 因為目前Go的泛型還沒有釋出,所以我們這裡以一個int具體型別為例 left *Node // 左子節點 right *Node // 右子節點}// BST 是一個節點的值為int型別的二叉搜尋樹.type BST struct { root *Node}
資料結構有了,接下來就是實現各個方法。
插入和刪除
既然是一棵樹,就需要增加節點用來構造樹,大部分情況下也需要刪除節點。
增加節點的時候,需要判斷應該往左邊子樹上新增,還是往右邊子樹上新增。天然地,既然二叉搜尋樹是一個有序的,那麼我們就可以進行比較,然後遞迴的實現。
刪除有些麻煩,如果是 刪除葉節點就比較容易,刪除即可。 但是如果不是刪除葉節點,那麼就需要將子節點提升。// Insert 插入一個元素.func (bst *BST) Insert(value int) { newNode := &Node{value, nil, nil} // 如果二叉樹為空,那麼這個節點就當作跟節點 if bst.root == nil { bst.root = newNode } else { insertNode(bst.root, newNode) }}// 從根節點依次比較func insertNode(root, newNode *Node) { if newNode.value < root.value { // 應該放到根節點的左邊 if root.left == nil { root.left = newNode } else { insertNode(root.left, newNode) } } else if newNode.value > root.value { // 應該放到根節點的右邊 if root.right == nil { root.right = newNode } else { insertNode(root.right, newNode) } } // 否則等於根節點}
// Remove 刪除一個元素.func (bst *BST) Remove(value int) bool { _, existed := remove(bst.root, value) return existed}// 用來遞迴移除節點的輔助方法.// 返回替換root的新節點,以及元素是否存在func remove(root *Node, value int) (*Node, bool) { if root == nil { return nil, false } var existed bool // 從左邊找 if value < root.value { root.left, existed = remove(root.left, value) return root, existed } // 從右邊找 if value > root.value { root.right, existed = remove(root.right, value) return root, existed } // 如果此節點正是要移除的節點,那麼返回此節點,同時返回之前可能需要調整. existed = true // 如果此節點沒有孩子,直接返回即可 if root.left == nil && root.right == nil { root = nil return root, existed } // 如果左子節點為空, 提升右子節點 if root.left == nil { root = root.right return root, existed } // 如果右子節點為空, 提升左子節點 if root.right == nil { root = root.left return root, existed } // 如果左右節點都存在,那麼從右邊節點找到一個最小的節點提升,這個節點肯定比左子樹所有節點都大. // 也可以從左子樹節點中找一個最大的提升,道理一樣. smallestInRight, _ := min(root.right) // 提升 root.value = smallestInRight // 從右邊子樹中移除此節點 root.right, _ = remove(root.right, smallestInRight) return root, existed}
搜尋
檢查一個節點是否存在比較簡單,因為二叉搜尋樹是有序的。
// Search 搜尋元素(檢查元素是否存在)func (bst *BST) Search(value int) bool { return search(bst.root, value)}func search(n *Node, value int) bool { if n == nil { return false } if value < n.value { return search(n.left, value) } if value > n.value { return search(n.right, value) } return true}
同時,我們還可以實現查詢一個二叉搜尋樹的最大最小值。
// Min 二叉搜尋樹中的最小值func (bst *BST) Min() (int, bool) { return min(bst.root)}func min(node *Node) (int, bool) { if node == nil { return 0, false } n := node // 從左邊找 for { if n.left == nil { return n.value, true } n = n.left }}// Max 二叉搜尋樹中的最大值func (bst *BST) Max() (int, bool) { return max(bst.root)}func max(node *Node) (int, bool) { if node == nil { return 0, false } n := node // 從右邊找 for { if n.right == nil { return n.value, true } n = n.right }}
遍歷
可以實現先序遍歷、中序遍歷和後序遍歷,先中後指的是根節點相對子節點的處理順序。
// PreOrderTraverse 前序遍歷func (bst *BST) PreOrderTraverse(f func(int)) { preOrderTraverse(bst.root, f)}func preOrderTraverse(n *Node, f func(int)) { if n != nil { f(n.value) // 前 preOrderTraverse(n.left, f) preOrderTraverse(n.right, f) }}// PostOrderTraverse 後序遍歷func (bst *BST) PostOrderTraverse(f func(int)) { postOrderTraverse(bst.root, f)}func postOrderTraverse(n *Node, f func(int)) { if n != nil { postOrderTraverse(n.left, f) postOrderTraverse(n.right, f) f(n.value) // 後 }}
是不是你還可以通過廣度搜索按照層級進行遍歷?