數據結構以前綴樹

介紹

Trie樹:又稱爲單詞查找樹,是一種樹形結構,能夠應用於統計字符串,會在搜索引擎系統中用於對文本的詞頻統計,下圖是一個Trie樹的結構,同時它也是在插入數時的一個動圖.golang

trietree

流程

  • 首先應該先建立一個結構體,裏面保存的是每個節點的信息
  • 初始化根節點,根節點應該初始化啥?啥也不用初始化,給個空就好看上圖
  • 插入:串轉字符數組;遍歷數組,若是下一個節點爲空,建立,則繼續遍歷
  • 查找:串轉字符數組,遍歷如何全部字符都在樹裏面存在,並則最後一個字符Node中的end不爲零,就視爲存在
  • 刪除: 字符串轉數組,遍歷數組,在樹上找到對應的字符,path-1

代碼

type Node struct {
	path     int
	end      int
	children [26]*Node
}
複製代碼

在這個結構體裏面有一個path,它的做用是啥呢?當有通過此字符的時候這個path就加一數組

image-20210622210956727

end又是幹啥的呢?當一個單詞的詞尾是這個字符的時候end這個值就加一,就表明着這個字符作爲一個單詞的結尾markdown

children是保存的啥呢?這個裏面固然是保存的子節點啦,不用多說了叭~~~flex

初始化
func main() {
	list := &Node{path: 0, end: 0}
}
複製代碼

初始化根節點,上面說過根節點裏面是不用保存數據的,這個我就把裏面的參數初始化成0,固然也能夠不用初始化裏面的參數,children這裏就沒有建立出來,由於下面我就要開始插入的操做了ui

插入
/* * 插入數據 */
func insertTrie(str string, root *Node) {
	if len(str) == 0 {
		return
	}
	tempNode := root
	for _, value := range str {
		if tempNode.children[value-'a'] == nil {
			tempNode.children[value-'a'] = &Node{path: 0, end: 0}
		}
		tempNode = tempNode.children[value-'a']
		tempNode.path++
	}
	tempNode.end++
}
複製代碼

在插入以前先說一點:在傳入的參數中,str我傳入前我將其轉換成了小寫的,固然也能夠轉換成大寫或者是大小寫都有的搜索引擎

插入以前先對字符串進行了一個判空的處理,若是爲空就return了,在整個過程當中,對字符串進行了遍歷,像我在流程中那樣說的將字符串轉成字符數組,是應該這樣操做,可是我發如今golang中能夠直接對一個字符串進行了遍歷,或許將語言換成了Java就須要將其轉成字符數組了編碼

for循環裏面if判斷時爲何數組的下標要用value-'a'這個東西來表示?能夠想像一下,一個節點的children裏面有26個子元素,好比這裏的vlaue是b,那麼就至關因而b-a,就是b的ASCII碼減去a的ASCII碼,這個就獲得的是1lua

索引 字符
0 a
1 b
2 c

噹噹前的字符在數組裏面沒有對應的數據的時候建立一個就好,若是有的時候只要將當前數組的下標交給臨時變量tempNode就好,所通過字符的path加1,將最後一個字符所對應的end加1,將其標記爲一個此字符是一個單詞的結尾便可.url

查找
/* *查找數據 */
func searchStr(str string,root *Node) bool {
	if len(str) == 0{
		return false
	}
	tempNode := root
	for _,value := range str{

		if tempNode.children[value - 'a'] == nil{
			return false
		}
		tempNode = tempNode.children[value - 'a']
	}
	if tempNode.end != 0{
		return true
	}
	return false
}
複製代碼

一樣,在查找數據的時候也是將須要查找的字符串和前綴樹的ROOT傳入,字符串的判空處理也是必作的,這個裏面的tempNode能夠有也能夠沒有,我寫tempNode能夠是說是個人一個編碼的習慣,一樣,在查找單詞的時候也是要遍歷這個字符串(在插入的時候我就已經解釋過了我這裏爲啥和流程中寫的不同,沒有把字符串轉成字符數組),在for循環裏面第一個if若是第一個字符沒有在前綴樹中找到,那麼就視爲所要查找的字符串沒有出如今這個前綴樹裏面,則將當前的字符節點交給臨時變量tempNode,當整個循環遍歷完成以後,也就說明我要查找的字符串中的每個字符都在這顆前綴樹裏面並連續着.這個時候若是最後一個單詞的end屬性爲大於0的一個數,那麼這個要查找的字符串就必定在這顆前綴樹裏面,返回truespa

findstr

統計以XXX開頭的單詞個數

這個前綴樹很強大,上面的解釋也說到過,能夠對文本的統計

strArgs:=[]string{"qQYgMU","FFpdCl","nyyJmh","XJCebb","OrCiHb","xvDdzZ","nyCebF","hi","hello","nyyJmn"}
複製代碼

在前綴樹裏面插入了這個數組裏面的字符串,我如今要統計以n開頭的單詞有幾個?如何處理呢?

這裏就用到了在結構體中定義的Path屬性了,在插入的時候說過當有一個字符通過這個path就會加1,因此我只須要找到所要查找前綴的最後一個單詞拿到了它的path屬性就能夠知道以這個字符串開頭的單詞有幾個

/* *查找以XX開頭的數據有幾個 */
func searchPrefixCount(str string,root *Node) int{
	if len(str) == 0{
		return -1
	}
	tempNode := root
	for _,value := range str{
		if tempNode.children[value - 'a'] == nil {
			return 0
		}
		tempNode = tempNode.children[value - 'a']
		return tempNode.path
	}
	return -1
}
複製代碼

image-20210622183656757

刪除數據

刪除數據的時候一樣也是要遍歷字符串,不過在此以前應該先查找一次這顆樹裏面有沒有要刪除的字符串,若是沒有就直接return就好

/* * 刪除數據 */
func delStr(str string,root *Node) bool {
	if len(str) == 0{
		return false
	}
	if !searchStr(strings.ToLower(str),root) {
		return false
	}
	tempNode := root
	for _,value := range str{
		if tempNode.children[value - 'a'].path > 1 {
			tempNode.children[value - 'a'].path--
			tempNode = tempNode.children[value - 'a']
		}else{
			tempNode.children[value - 'a'] = nil
			return true
		}
	}
	return false
}
複製代碼

path是當有字符通過的時候加一,那麼在刪除數據的時候只要查找到字符將這個字符串所通過的字符的path減1, 我這裏還加了一個else,當path等於1的時候也就是說明當前所要刪除的字符串是最後一個通過此字符的字符串,這裏直接將其置空,等系統回收就行了

image-20210622184820176

相關文章
相關標籤/搜索