10.1 Data Structures (Queue Stack Lists)

در این قسمت به بررسی مواردی مهم از ساختمان داده در زبان گو می‌پردازیم و این آموزش مناسب افرادی هست که با مباحث ساختمان داده آشنایی داشته باشند.

10.1.1 Queue in Golang #

یک صف (queue) ساده را می توان با استفاده از GO به کمک موارد زیر پیاده سازی کرد:

  1. container/list package
  2. slice

یک صف عملیات زیر را انجام می‌دهد:

  1. Enqueue
  2. Dequeue
  3. Front
  4. Size
  5. Empty

10.1.1.1 List Implementation #

پیاده سازی صف به کمک لیست‌ها

 1package main
 2
 3import (
 4    "container/list"
 5    "fmt"
 6)
 7
 8type customQueue struct {
 9    queue *list.List
10}
11
12func (c *customQueue) Enqueue(value string) {
13    c.queue.PushBack(value)
14}
15
16func (c *customQueue) Dequeue() error {
17    if c.queue.Len() > 0 {
18        ele := c.queue.Front()
19        c.queue.Remove(ele)
20    }
21    return fmt.Errorf("Pop Error: Queue is empty")
22}
23
24func (c *customQueue) Front() (string, error) {
25    if c.queue.Len() > 0 {
26        if val, ok := c.queue.Front().Value.(string); ok {
27            return val, nil
28        }
29        return "", fmt.Errorf("Peep Error: Queue Datatype is incorrect")
30    }
31    return "", fmt.Errorf("Peep Error: Queue is empty")
32}
33
34func (c *customQueue) Size() int {
35    return c.queue.Len()
36}
37
38func (c *customQueue) Empty() bool {
39    return c.queue.Len() == 0
40}
41
42func main() {
43    customQueue := &customQueue{
44        queue: list.New(),
45    }
46    fmt.Printf("Enqueue: A\n")
47    customQueue.Enqueue("A")
48    fmt.Printf("Enqueue: B\n")
49    customQueue.Enqueue("B")
50    fmt.Printf("Size: %d\n", customQueue.Size())
51    for customQueue.Size() > 0 {
52        frontVal, _ := customQueue.Front()
53        fmt.Printf("Front: %s\n", frontVal)
54        fmt.Printf("Dequeue: %s\n", frontVal)
55        customQueue.Dequeue()
56    }
57    fmt.Printf("Size: %d\n", customQueue.Size())
58}

خروجی برنامه بالا:

1Enqueue: A
2Enqueue: B
3Size: 2
4Front: A
5Dequeue: A
6Front: B
7Dequeue: B
8Size: 0

10.1.1.2 Slice Implementation #

پیاده سازی صف به کمک slice

 1package main
 2
 3import (
 4	"fmt"
 5	"sync"
 6)
 7
 8type customQueue struct {
 9	queue []string
10	lock  sync.RWMutex
11}
12
13func (c *customQueue) Enqueue(name string) {
14	c.lock.Lock()
15	defer c.lock.Unlock()
16	c.queue = append(c.queue, name)
17}
18
19func (c *customQueue) Dequeue() error {
20	if len(c.queue) > 0 {
21		c.lock.Lock()
22		defer c.lock.Unlock()
23		c.queue = c.queue[1:]
24		return nil
25	}
26	return fmt.Errorf("Pop Error: Queue is empty")
27}
28
29func (c *customQueue) Front() (string, error) {
30	if len(c.queue) > 0 {
31		c.lock.Lock()
32		defer c.lock.Unlock()
33		return c.queue[0], nil
34	}
35	return "", fmt.Errorf("Peep Error: Queue is empty")
36}
37
38func (c *customQueue) Size() int {
39	return len(c.queue)
40}
41
42func (c *customQueue) Empty() bool {
43	return len(c.queue) == 0
44}
45
46func main() {
47	customQueue := &customQueue{
48		queue: make([]string, 0),
49	}
50
51	fmt.Printf("Enqueue: A\n")
52	customQueue.Enqueue("A")
53	fmt.Printf("Enqueue: B\n")
54	customQueue.Enqueue("B")
55	fmt.Printf("Len: %d\n", customQueue.Size())
56
57	for customQueue.Size() > 0 {
58		frontVal, _ := customQueue.Front()
59		fmt.Printf("Front: %s\n", frontVal)
60		fmt.Printf("Dequeue: %s\n", frontVal)
61		customQueue.Dequeue()
62	}
63	fmt.Printf("Len: %d\n", customQueue.Size())
64}

خروجی برنامه بالا:

Enqueue: A
Enqueue: B
Size: 2
Front: A
Dequeue: A
Front: B
Dequeue: B
Size: 0

10.1.2 Stack in Golang #

یک پشته (Stack) ساده را می توان با استفاده از GO به کمک موارد زیر پیاده سازی کرد:

  1. container/list package
  2. slice

یک stack عملیات زیر را انجام می‌دهد:

  1. Push
  2. Pop
  3. Front
  4. Size
  5. Empty

10.1.2.1 List Implementation #

پیاده سازی پشته به کمک لیست‌ها

 1package main
 2
 3import (
 4    "container/list"
 5    "fmt"
 6)
 7
 8type customStack struct {
 9    stack *list.List
10}
11
12func (c *customStack) Push(value string) {
13    c.stack.PushFront(value)
14}
15
16func (c *customStack) Pop() error {
17    if c.stack.Len() > 0 {
18        ele := c.stack.Front()
19        c.stack.Remove(ele)
20    }
21    return fmt.Errorf("Pop Error: Stack is empty")
22}
23
24func (c *customStack) Front() (string, error) {
25    if c.stack.Len() > 0 {
26        if val, ok := c.stack.Front().Value.(string); ok {
27            return val, nil
28        }
29        return "", fmt.Errorf("Peep Error: Stack Datatype is incorrect")
30    }
31    return "", fmt.Errorf("Peep Error: Stack is empty")
32}
33
34func (c *customStack) Size() int {
35    return c.stack.Len()
36}
37
38func (c *customStack) Empty() bool {
39    return c.stack.Len() == 0
40}
41
42func main() {
43    customStack := &customStack{
44        stack: list.New(),
45    }
46    fmt.Printf("Push: A\n")
47    customStack.Push("A")
48    fmt.Printf("Push: B\n")
49    customStack.Push("B")
50    fmt.Printf("Size: %d\n", customStack.Size())
51    for customStack.Size() > 0 {
52        frontVal, _ := customStack.Front()
53        fmt.Printf("Front: %s\n", frontVal)
54        fmt.Printf("Pop: %s\n", frontVal)
55        customStack.Pop()
56    }
57    fmt.Printf("Size: %d\n", customStack.Size())
58}

خروجی برنامه بالا:

1Push: A
2Push: B
3Size: 2
4Front: B
5Pop: B
6Front: A
7Pop: A
8Size: 0

10.1.2.2 Slice Implementation #

پیاده سازی پشته به کمک slice

 1package main
 2
 3import (
 4    "fmt"
 5    "sync"
 6)
 7
 8type customStack struct {
 9    stack []string
10    lock  sync.RWMutex
11}
12
13func (c *customStack) Push(name string) {
14    c.lock.Lock()
15    defer c.lock.Unlock()
16    c.stack = append(c.stack, name)
17}
18
19func (c *customStack) Pop() error {
20    len := len(c.stack)
21    if len > 0 {
22        c.lock.Lock()
23        defer c.lock.Unlock()
24        c.stack = c.stack[:len-1]
25        return nil
26    }
27    return fmt.Errorf("Pop Error: Stack is empty")
28}
29
30func (c *customStack) Front() (string, error) {
31    len := len(c.stack)
32    if len > 0 {
33        c.lock.Lock()
34        defer c.lock.Unlock()
35        return c.stack[len-1], nil
36    }
37    return "", fmt.Errorf("Peep Error: Stack is empty")
38}
39
40func (c *customStack) Size() int {
41    return len(c.stack)
42}
43
44func (c *customStack) Empty() bool {
45    return len(c.stack) == 0
46}
47
48func main() {
49    customStack := &customStack{
50        stack: make([]string, 0),
51    }
52    fmt.Printf("Push: A\n")
53    customStack.Push("A")
54    fmt.Printf("Push: B\n")
55    customStack.Push("B")
56    fmt.Printf("Size: %d\n", customStack.Size())
57    for customStack.Size() > 0 {
58        frontVal, _ := customStack.Front()
59        fmt.Printf("Front: %s\n", frontVal)
60        fmt.Printf("Pop: %s\n", frontVal)
61        customStack.Pop()
62    }
63    fmt.Printf("Size: %d\n", customStack.Size())
64}

خروجی برنامه بالا:

1Push: A
2Push: B
3Size: 2
4Front: B
5Pop: B
6Front: A
7Pop: A
8Size: 0

10.1.3 Set implementation in Golang #

مجموعه (set) یک ساختار داده ای است که عناصر را بدون نظم خاصی در خود نگه می دارد. یک عنصر فقط یک بار در یک مجموعه ظاهر می شود.

Set را می توان با استفاده از map در GO پیاده سازی کرد. ما از map[string]struct{} برای مجموعه استفاده خواهیم کرد زیرا struct{} هیچ حافظه ای اشغال نمی کند، بنابراین از نظر ذخیره سازی کارآمدتر است.
در زیر مثال ساده مجموعه (set) که دارای عملیات زیر است را داریم:

  1. Add
  2. Remove
  3. Exists
 1package main
 2
 3import (
 4    "fmt"
 5)
 6
 7//MakeSet initialize the set
 8func makeSet() *customSet {
 9    return &customSet{
10        container: make(map[string]struct{}),
11    }
12}
13
14type customSet struct {
15    container map[string]struct{}
16}
17
18func (c *customSet) Exists(key string) bool {
19    _, exists := c.container[key]
20    return exists
21}
22
23func (c *customSet) Add(key string) {
24    c.container[key] = struct{}{}
25}
26
27func (c *customSet) Remove(key string) error {
28    _, exists := c.container[key]
29    if !exists {
30        return fmt.Errorf("Remove Error: Item doesn't exist in set")
31    }
32    delete(c.container, key)
33    return nil
34}
35
36func (c *customSet) Size() int {
37    return len(c.container)
38}
39
40func main() {
41    customSet := makeSet()
42    fmt.Printf("Add: B\n")
43    customSet.Add("A")
44    fmt.Printf("Add: B\n")
45    customSet.Add("B")
46    fmt.Printf("Size: %d\n", customSet.Size())
47    fmt.Printf("A Exists?: %t\n", customSet.Exists("A"))
48    fmt.Printf("B Exists?: %t\n", customSet.Exists("B"))
49    fmt.Printf("C Exists?: %t\n", customSet.Exists("C"))
50    fmt.Printf("Remove: B\n")
51    customSet.Remove("B")
52    fmt.Printf("B Exists?: %t\n", customSet.Exists("B"))
53}

خروجی برنامه بالا:

1Add: B
2Add: B
3Size: 2
4A Exists?: true
5B Exists?: true
6C Exists?: false
7Remove: B
8B Exists?: false

10.1.4 Linked List in Golang #

لیست منفرد یک نوع ساده از لیست پیوندی است که امکان پیمایش در یک جهت یعنی جلو را فراهم می کند. هر گره در لیست پیوندی شامل بخش داده و اشاره گر به گره بعدی در لیست پیوند شده است.

لیست پیوندی اجرا شده در مثال زیر از عملیات زیر پشتیبانی می کند.

  1. AddFront
  2. AddBack
  3. RemoveFront
  4. RemoveBack
  5. Traverse
  6. Front
  7. Size
  1package main
  2
  3import "fmt"
  4
  5type ele struct {
  6    name string
  7    next *ele
  8}
  9
 10type singleList struct {
 11    len  int
 12    head *ele
 13}
 14
 15func initList() *singleList {
 16    return &singleList{}
 17}
 18
 19func (s *singleList) AddFront(name string) {
 20    ele := &ele{
 21        name: name,
 22    }
 23    if s.head == nil {
 24        s.head = ele
 25    } else {
 26        ele.next = s.head
 27        s.head = ele
 28    }
 29    s.len++
 30    return
 31}
 32
 33func (s *singleList) AddBack(name string) {
 34    ele := &ele{
 35        name: name,
 36    }
 37    if s.head == nil {
 38        s.head = ele
 39    } else {
 40        current := s.head
 41        for current.next != nil {
 42            current = current.next
 43        }
 44        current.next = ele
 45    }
 46    s.len++
 47    return
 48}
 49
 50func (s *singleList) RemoveFront() error {
 51    if s.head == nil {
 52        return fmt.Errorf("List is empty")
 53    }
 54    s.head = s.head.next
 55    s.len--
 56    return nil
 57}
 58
 59func (s *singleList) RemoveBack() error {
 60    if s.head == nil {
 61        return fmt.Errorf("removeBack: List is empty")
 62    }
 63    var prev *ele
 64    current := s.head
 65    for current.next != nil {
 66        prev = current
 67        current = current.next
 68    }
 69    if prev != nil {
 70        prev.next = nil
 71    } else {
 72        s.head = nil
 73    }
 74    s.len--
 75    return nil
 76}
 77
 78func (s *singleList) Front() (string, error) {
 79    if s.head == nil {
 80        return "", fmt.Errorf("Single List is empty")
 81    }
 82    return s.head.name, nil
 83}
 84
 85func (s *singleList) Size() int {
 86    return s.len
 87}
 88
 89func (s *singleList) Traverse() error {
 90    if s.head == nil {
 91        return fmt.Errorf("TranverseError: List is empty")
 92    }
 93    current := s.head
 94    for current != nil {
 95        fmt.Println(current.name)
 96        current = current.next
 97    }
 98    return nil
 99}
100
101func main() {
102     singleList := initList()
103    fmt.Printf("AddFront: A\n")
104    singleList.AddFront("A")
105    fmt.Printf("AddFront: B\n")
106    singleList.AddFront("B")
107    fmt.Printf("AddBack: C\n")
108    singleList.AddBack("C")
109
110    fmt.Printf("Size: %d\n", singleList.Size())
111   
112    err := singleList.Traverse()
113    if err != nil {
114        fmt.Println(err.Error())
115    }
116    
117    fmt.Printf("RemoveFront\n")
118    err = singleList.RemoveFront()
119    if err != nil {
120        fmt.Printf("RemoveFront Error: %s\n", err.Error())
121    }
122    
123    fmt.Printf("RemoveBack\n")
124    err = singleList.RemoveBack()
125    if err != nil {
126        fmt.Printf("RemoveBack Error: %s\n", err.Error())
127    }
128    
129    fmt.Printf("RemoveBack\n")
130    err = singleList.RemoveBack()
131    if err != nil {
132        fmt.Printf("RemoveBack Error: %s\n", err.Error())
133    }
134    
135    fmt.Printf("RemoveBack\n")
136    err = singleList.RemoveBack()
137    if err != nil {
138        fmt.Printf("RemoveBack Error: %s\n", err.Error())
139    }
140    
141    err = singleList.Traverse()
142    if err != nil {
143        fmt.Println(err.Error())
144    }
145    
146   fmt.Printf("Size: %d\n", singleList.Size())
147}

خروجی برنامه بالا:

 1AddFront: A
 2AddFront: B
 3AddBack: C
 4Size: 3
 5B
 6A
 7C
 8RemoveFront
 9RemoveBack
10RemoveBack
11RemoveBack
12RemoveBack Error: removeBack: List is empty
13TranverseError: List is empty
14Size: 0

10.1.5 Doubly Linked List in Go #

یک لیست مضاعف (Doubly Linked) شامل سه قسمت در گره خود است.

  • فیلد داده.
  • یک اشاره گر بعدی به گره بعدی در لیست اشاره می کند.
  • یک اشاره گر قبلی که به گره قبلی در لیست اشاره می کند.

در اینجا فیلدهای «داده‌ها» و «بعدی» مانند لیست‌های پیوندی منفرد هستند. فیلد اشاره گر «قبلی» ویژگی جدیدی است که لیست پیوندی را به لیست پیوندی دوگانه تبدیل می کند.

در زیر نمونه ای از یک لیست با پیوند دوگانه آورده شده است. اشاره گر قبلی گره head (start) به Null اشاره می کند. به طور مشابه، اشاره گر Next آخرین گره به Null اشاره می کند.

array برای پیاده‌سازی یک  doubly linked list در زبان Go، یک ساختار گره با داده‌ها، اشاره‌گر قبلی و اشاره‌گر بعدی، روش‌هایی برای افزودن گره‌ها در  doubly linked list (از قسمت جلویی یا از انتهای هر دو) و روش‌هایی برای پیمایش به جلو/عقب ایجاد کنید.

  1package main
  2
  3import "fmt"
  4
  5type node struct {
  6	data string
  7	prev *node
  8	next *node
  9}
 10
 11type doublyLinkedList struct {
 12	len  int
 13	tail *node
 14	head *node
 15}
 16
 17func initDoublyList() *doublyLinkedList {
 18	return &doublyLinkedList{}
 19}
 20
 21func (d *doublyLinkedList) AddFrontNodeDLL(data string) {
 22	newNode := &node{
 23		data: data,
 24	}
 25	if d.head == nil {
 26		d.head = newNode
 27		d.tail = newNode
 28	} else {
 29		newNode.next = d.head
 30		d.head.prev = newNode
 31		d.head = newNode
 32	}
 33	d.len++
 34	return
 35}
 36
 37func (d *doublyLinkedList) AddEndNodeDLL(data string) {
 38	newNode := &node{
 39		data: data,
 40	}
 41	if d.head == nil {
 42		d.head = newNode
 43		d.tail = newNode
 44	} else {
 45		currentNode := d.head
 46		for currentNode.next != nil {
 47			currentNode = currentNode.next
 48		}
 49		newNode.prev = currentNode
 50		currentNode.next = newNode
 51		d.tail = newNode
 52	}
 53	d.len++
 54	return
 55}
 56func (d *doublyLinkedList) TraverseForward() error {
 57	if d.head == nil {
 58		return fmt.Errorf("TraverseError: List is empty")
 59	}
 60	temp := d.head
 61	for temp != nil {
 62		fmt.Printf("value = %v, prev = %v, next = %v\n", temp.data, temp.prev, temp.next)
 63		temp = temp.next
 64	}
 65	fmt.Println()
 66	return nil
 67}
 68
 69func (d *doublyLinkedList) TraverseReverse() error {
 70	if d.head == nil {
 71		return fmt.Errorf("TraverseError: List is empty")
 72	}
 73	temp := d.tail
 74	for temp != nil {
 75		fmt.Printf("value = %v, prev = %v, next = %v\n", temp.data, temp.prev, temp.next)
 76		temp = temp.prev
 77	}
 78	fmt.Println()
 79	return nil
 80}
 81
 82func (d *doublyLinkedList) Size() int {
 83	return d.len
 84}
 85func main() {
 86	doublyList := initDoublyList()
 87	fmt.Printf("Add Front Node: C\n")
 88	doublyList.AddFrontNodeDLL("C")
 89	fmt.Printf("Add Front Node: B\n")
 90	doublyList.AddFrontNodeDLL("B")
 91	fmt.Printf("Add Front Node: A\n")
 92	doublyList.AddFrontNodeDLL("A")
 93	fmt.Printf("Add End Node: D\n")
 94	doublyList.AddEndNodeDLL("D")
 95	fmt.Printf("Add End Node: E\n")
 96	doublyList.AddEndNodeDLL("E")
 97
 98	fmt.Printf("Size of doubly linked ist: %d\n", doublyList.Size())
 99
100	err := doublyList.TraverseForward()
101	if err != nil {
102		fmt.Println(err.Error())
103	}
104
105	err = doublyList.TraverseReverse()
106	if err != nil {
107		fmt.Println(err.Error())
108	}
109}

خروجی مورد انتظار برابر حالت زیر است:

 1Add Front Node: C
 2Add Front Node: B
 3Add Front Node: A
 4Add End Node: D
 5Add End Node: E
 6Size of doubly linked ist: 5
 7value = A, prev = , next = &{B 0xc000070060 0xc000070020}
 8value = B, prev = &{A  0xc000070040}, next = &{C 0xc000070040 0xc000070080}
 9value = C, prev = &{B 0xc000070060 0xc000070020}, next = &{D 0xc000070020 0xc0000700a0}
10value = D, prev = &{C 0xc000070040 0xc000070080}, next = &{E 0xc000070080 }
11value = E, prev = &{D 0xc000070020 0xc0000700a0}, next = 
12
13value = E, prev = &{D 0xc000070020 0xc0000700a0}, next = 
14value = D, prev = &{C 0xc000070040 0xc000070080}, next = &{E 0xc000070080 }
15value = C, prev = &{B 0xc000070060 0xc000070020}, next = &{D 0xc000070020 0xc0000700a0}
16value = B, prev = &{A  0xc000070040}, next = &{C 0xc000070040 0xc000070080}
17value = A, prev = , next = &{B 0xc000070060 0xc000070020}

10.1.6 Tree in Go #

درخت به عنوان یک ساختمان داده غیرخطی تعریف می‌شود که از مجموعه‌ای از گره‌ها تشکیل شده است، و این گره‌ها توسط یال‌ها به یکدیگر متصل شده‌اند

خواص یک درخت:

  • درخت از یک گره ریشه و صفر یا چند درخت فرعی متصل به آن تشکیل شده است
  • گره ریشه بالاترین گره درخت است
  • گره‌های برگ گره‌هایی هستند که هیچ فرزندی ندارند
  • عمق یک گره تعداد یال‌ها بین ریشه و خودش است
  • ارتفاع یک گره تعداد یال‌ها بین خودش و دورترین گره برگ در زیردرخت خود است

‍‍

  1package main
  2
  3import "fmt" 
  4
  5// Tree represents a tree structure. 
  6type Tree struct { 
  7	root *TreeNode 
  8}
  9
 10// TreeNode represents a node in the tree.
 11type TreeNode struct {
 12	data int
 13	children []*TreeNode
 14}
 15
 16// insertTree adds a new node with the given data as the root node of the tree.
 17func (tree *Tree) insertTree(data int) {
 18	if tree.root == nil { 
 19		tree.root = &TreeNode{data: data} 
 20	} 
 21}
 22
 23// InsertNode adds a new node with the given data as a child of the specified node.
 24func (node *TreeNode) insertNode(data int) *TreeNode {
 25	newNode := &TreeNode{data: data}
 26	node.children = append(node.children, newNode)
 27	return newNode
 28}
 29
 30// deleteTree removes the specified node, starting from the root of the tree.
 31func (tree *Tree) deleteFromRoot(nodeToDelete *TreeNode) {
 32	if tree.root != nil {
 33		tree.root = tree.root.deleteNode(nodeToDelete)
 34	}
 35}
 36
 37// deleteNode recursively removes the specified node and its descendants from the current node's children.
 38func (node *TreeNode) deleteNode(nodeToDelete *TreeNode) *TreeNode {
 39	var updatedChildren []*TreeNode
 40	for _, child := range node.children {
 41		if child != nodeToDelete {
 42			updatedChildren = append(updatedChildren, child.deleteNode(nodeToDelete))
 43		}
 44}
 45
 46	node.children = updatedChildren
 47	return node
 48}
 49
 50// searchFromRoot searches for a node with the specified data starting from the tree's root.
 51func (tree *Tree) searchFromRoot(data int) *TreeNode {
 52	if tree.root != nil {
 53		node := tree.root.searchFromNode(data)
 54		return node
 55	}
 56	
 57	return nil
 58}
 59
 60// searchFromNode searches for a node with the specified data starting from the current node.
 61func (node *TreeNode) searchFromNode(data int) *TreeNode {
 62	if node.data == data {
 63		return node
 64	}
 65	for _, child := range node.children {
 66		if foundNode := child.searchFromNode(data); foundNode != nil {
 67			return foundNode
 68		}
 69	}
 70
 71	return nil
 72}
 73
 74// traverseFromRoot initiates a traversal of the tree starting from the root node.
 75func (tree *Tree) traverseFromRoot() {
 76	if tree.root != nil {
 77		tree.root.traverse()
 78	}
 79}
 80
 81// traverse performs a recursive traversal starting from the current node.
 82func (node *TreeNode) traverse() {
 83	if node == nil {
 84		return
 85	}
 86	
 87	fmt.Printf("%d ", node.data)
 88	for _, child := range node.children {
 89		child.traverse()
 90	}
 91}
 92
 93func main() {
 94	// Creating a Tree instance
 95	tree := Tree{}
 96	
 97	// Inserting nodes
 98	tree.insertTree(1)
 99	tree.root.insertNode(2)
100	node3 := tree.root.insertNode(3)
101	node4 := tree.root.insertNode(4)
102	node3.insertNode(5)
103	node3.insertNode(6)
104	node4.insertNode(7)
105
106	// Traversing and printing nodes
107	fmt.Println("Traverse from root:")
108	tree.root.traverse()
109	
110	// Searching for node
111	fmt.Println("\nSearch for node 3:")
112	node := tree.searchFromRoot(3)
113	if node != nil {
114		fmt.Println("node found")
115	} else {
116		fmt.Println("node not found")
117	}
118	
119	fmt.Println("Search for node 8:")
120	node8 := tree.searchFromRoot(8)
121	if node8 != nil {
122		fmt.Println("node found")
123	} else {
124		fmt.Println("node not found")
125	}
126	
127	// Deleting a node
128	fmt.Println("After deleting node 3:")
129	tree.deleteFromRoot(node3)
130	tree.root.traverse()
131}

خروجی برنامه بالا:

1Traverse from root:
21 2 3 5 6 4 7 
3Search for node 3
4node found
5Search for node 8
6node not found
7After deleting node 3:
81 2 4 7

10.1.7 Binary Tree in Go #

درخت دودویی، نوعی ساختار داده‌ای درخت است که هر گره آن می‌تواند حداکثر دو فرزند (یک فرزند چپ و یک فرزند راست) داشته باشد

  1package main
  2
  3import "fmt"
  4
  5// BinaryTree represents a binary tree.
  6type BinaryTree struct {
  7	root *BinaryNode
  8}
  9
 10// BinaryNode represents a node in the binary tree.
 11type BinaryNode struct {
 12	data int
 13	left *BinaryNode
 14	right *BinaryNode
 15}
 16
 17// insertFromRoot inserts a new node with the given data into the tree.
 18func (tree *BinaryTree) insertFromRoot(data int) *BinaryTree {
 19	if tree.root != nil {
 20		tree.root.insertNode(data)
 21	} else {
 22		tree.root = &BinaryNode{data: data}
 23	}
 24	
 25	return tree
 26}
 27
 28// insertNode inserts a new node with the given data into the subtree rooted at the current node using level-order traversal.
 29func (node *BinaryNode) insertNode(data int) *BinaryNode {
 30	var tempNode *BinaryNode
 31	queue := []*BinaryNode{node}
 32	
 33	for len(queue) > 0 {
 34		tempNode, queue = queue[0], queue[1:]
 35		
 36		if tempNode.left == nil {
 37			tempNode.left = &BinaryNode{data: data}
 38			break
 39		}
 40		queue = append(queue, tempNode.left)	
 41		
 42		if tempNode.right == nil {
 43			tempNode.right = &BinaryNode{data: data}
 44			break
 45		}
 46		queue = append(queue, tempNode.right)
 47	}
 48
 49	return node
 50}
 51
 52// deleteFromRoot deletes a specific node from the binary tree starting from the root.
 53func (tree *BinaryTree) deleteFromRoot(nodeToDelete *BinaryNode) {
 54	if tree.root != nil {
 55		tree.root.deleteNode(nodeToDelete)
 56	}
 57}
 58
 59// deletetNode attempts to delete a specific node from the subtree rooted at the current node.
 60func (node *BinaryNode) deleteNode(nodeToDelete *BinaryNode) *BinaryNode {
 61	var keyNode, lastNode, tempNode *BinaryNode
 62	queue := []*BinaryNode{node}	
 63	
 64	for len(queue) > 0 {
 65		tempNode, queue = queue[0], queue[1:]
 66	
 67		if tempNode == nodeToDelete {		
 68			keyNode = tempNode
 69		}
 70
 71		if tempNode.left != nil {
 72			lastNode, queue = tempNode, append(queue, tempNode.left)
 73		}  
 74
 75		if tempNode.right != nil {
 76			lastNode, queue = tempNode, append(queue, tempNode.right)
 77		}
 78	}
 79
 80  
 81
 82	if keyNode != nil {
 83		keyNode.data = tempNode.data
 84		
 85		if lastNode.right == tempNode {
 86			lastNode.right = nil
 87		} else {
 88			lastNode.left = nil
 89		}
 90	}
 91
 92	return node
 93}
 94  
 95// searchFromRoot searches for a node with the given data in the binary tree starting from the root.
 96func (tree *BinaryTree) searchFromRoot(data int) *BinaryNode {
 97	if tree.root != nil {
 98		return tree.root.searchFromNode(data)
 99	}
100	
101	return nil
102}
103
104// searchFromNode performs a level-order traversal to find a node with the given data
105func (node *BinaryNode) searchFromNode(data int) *BinaryNode {
106	var tempNode *BinaryNode
107	queue := []*BinaryNode{node}
108
109	for len(queue) > 0 {
110		tempNode, queue = queue[0], queue[1:]
111		
112		if tempNode.data == data {
113			return tempNode
114		}
115
116		if tempNode.left != nil {
117			queue = append(queue, tempNode.left)
118		}
119
120		if tempNode.right != nil {
121			queue = append(queue, tempNode.right)
122		}
123	}
124	
125	return nil
126}
127
128// printTreeInOrder prints the values of nodes in the binary tree starting from root using an in-order traversal.
129func (tree *BinaryTree) printTreeInOrder() {
130	if tree.root != nil {
131		tree.root.printSubTreeInOrder()
132	}
133}
134
135// printTreePreOrder prints the values of nodes in the binary tree starting from root using a pre-order traversal.
136func (tree *BinaryTree) printTreePreOrder() {
137	if tree.root != nil {
138		tree.root.printSubTreePreOrder()
139	}
140}
141
142// printTreePostOrder prints the values of nodes in the binary tree starting from root using a post-order traversal.
143func (tree *BinaryTree) printTreePostOrder() {
144	if tree.root != nil {
145		tree.root.printSubTreePostOrder()
146	}
147}
148
149// printSubTreeInOrder prints the values of nodes in the subtree rooted at the given node using an in-order traversal.
150func (node *BinaryNode) printSubTreeInOrder() {
151	if node != nil {
152		node.left.printSubTreeInOrder()
153		fmt.Printf("%d ", node.data)
154		node.right.printSubTreeInOrder()
155	}
156}
157
158// printSubTreePreOrder prints the values of nodes in the subtree rooted at the given node using a pre-order traversal.
159func (node *BinaryNode) printSubTreePreOrder() {
160	if node != nil {
161		fmt.Printf("%d ", node.data)
162		node.left.printSubTreePreOrder()
163		node.right.printSubTreePreOrder()
164	}
165}
166
167// printSubTreePostOrder prints the values of nodes in the subtree rooted at the given node using a post-order traversal.
168func (node *BinaryNode) printSubTreePostOrder() {
169	if node != nil {
170		node.left.printSubTreePostOrder()
171		node.right.printSubTreePostOrder()
172		fmt.Printf("%d ", node.data)
173	}
174}
175
176func main() {
177	// Create a new binary tree
178	tree := BinaryTree{}
179		
180	// Insert nodes
181	tree.insertFromRoot(1)
182	tree.insertFromRoot(2)
183	tree.insertFromRoot(3)
184	tree.insertFromRoot(4)
185	tree.insertFromRoot(5)
186	tree.insertFromRoot(6)
187	tree.insertFromRoot(7)	  
188	
189	fmt.Println("In-Order Traversal:")
190	tree.printTreeInOrder()
191	
192	fmt.Println("\nPre-Order Traversal:")
193	tree.printTreePreOrder()
194	
195	fmt.Println("\nPost-Order Traversal:")
196	tree.printTreePostOrder()
197	
198	// Search for a node
199	fmt.Println("\n\nSearching for node with data 6:")
200	nodeToSearch := tree.searchFromRoot(6)
201	
202	if nodeToSearch != nil {
203		fmt.Println("found node with data 6:", nodeToSearch.data)
204	} else {
205		fmt.Println("node with data 6 not found.")
206	}
207	
208	fmt.Println("\nSearching for node with data 9:")
209	nodeToSearch = tree.searchFromRoot(9)
210	
211	if nodeToSearch != nil {
212		fmt.Println("found node with data 9:", nodeToSearch.data)
213	} else {
214		fmt.Println("node with data 9 not found.")
215	}
216	
217	fmt.Println("\nDeleting node with data 4:")
218	nodeToDelete := tree.searchFromRoot(4)
219	
220	if nodeToDelete != nil {
221		fmt.Println("deleted node with data 4.")
222		tree.deleteFromRoot(nodeToDelete)
223	} else {
224		fmt.Println("node with data 4 not found.")
225	}
226	
227	fmt.Println("\nIn-Order Traversal after deletion:")
228	tree.printTreeInOrder()
229}

خروجی کد بالا:

 1In-Order Traversal:
 24 2 5 1 6 3 7 
 3Pre-Order Traversal:
 41 2 4 5 3 6 7 
 5Post-Order Traversal:
 64 5 2 6 7 3 1 
 7
 8Searching for node with data 6:
 9found node with data 6: 6
10
11Searching for node with data 9:
12node with data 9 not found.
13
14Deleting node with data 4:
15deleted node with data 4.
16
17In-Order Traversal after deletion:
187 2 5 1 6 3

10.1.8 Binary Search Tree in Go #

درخت جستجو دودویی یک نوع از درخت دودویی است که ب هر گره دارای یک مقدار داده و دو زیردرخت (زیردرخت چپ و زیردرخت راست) می‌باشد. در این درخت، داده‌های کوچکتر از مقدار دادهٔ گره مورد نظر در زیردرخت چپ قرار می‌گیرند و داده‌های بزرگتر در زیردرخت راست قرار می‌گیرند . درخت جستجو دودویی به گونه ای طراحی شده است که عملیات جستجو، افزودن و حذف به طور موثر انجام شود

  1package main
  2
  3import "fmt"
  4
  5// BinarySearchTree represents a binary search tree.
  6type BinarySearchTree struct {
  7	root *BinarySearchNode
  8}
  9
 10// BinarySearchNode represents a node in the binary search tree.
 11type BinarySearchNode struct {
 12	data int
 13	left *BinarySearchNode
 14	right *BinarySearchNode
 15}
 16
 17// insertFromRoot inserts a new node with the given data into the binary search tree, starting from the root of the tree.
 18func (tree *BinarySearchTree) insertFromRoot(data int) *BinarySearchTree {
 19	if tree.root != nil {
 20		tree.root.insertNode(data)
 21	} else {
 22		tree.root = &BinarySearchNode{data: data}
 23	}
 24	
 25	return tree
 26}
 27
 28// insertNode inserts a new node with the given data into the binary search tree rooted at the current node.
 29func (node *BinarySearchNode) insertNode(data int) *BinarySearchNode {
 30	if node == nil {
 31		return &BinarySearchNode{data: data}
 32	} else if data == node.data {
 33		return node
 34	} else if data > node.data {
 35		node.right = node.right.insertNode(data)
 36	} else {
 37		node.left = node.left.insertNode(data)
 38	}
 39
 40	return node
 41}
 42
 43// deleteFromRoot deletes a specific node from the binary search tree starting from the root node.
 44func (tree *BinarySearchTree) deleteFromRoot(nodeToDelete *BinarySearchNode) *BinarySearchNode {
 45	if tree.root != nil {
 46		return tree.root.left.deleteNode(nodeToDelete)
 47	}
 48	
 49	return nil
 50} 
 51
 52// deleteNode recursively deletes a specific node from the subtree rooted at the current node.
 53func (node *BinarySearchNode) deleteNode(nodeToDelete *BinarySearchNode) *BinarySearchNode {
 54	if node == nil {
 55	return nil
 56	}
 57	
 58	if nodeToDelete.data < node.data {
 59		node.left = node.left.deleteNode(nodeToDelete)
 60	} else if nodeToDelete.data > node.data {
 61		node.right = node.right.deleteNode(nodeToDelete)
 62	} else {
 63	
 64	if node.left == nil {
 65		return node.right
 66	} else if node.right == nil {
 67		return node.left
 68	}
 69
 70	minNode := node.right.findMin()
 71	node.data = minNode.data
 72	node.right = node.right.deleteNode(nodeToDelete)
 73	}
 74	
 75	return node
 76
 77}
 78
 79// findMin returns the minimum node value in the subtree rooted at the current node.
 80func (node *BinarySearchNode) findMin() *BinarySearchNode {
 81	for node.left != nil {
 82		node = node.left
 83	}
 84
 85	return node
 86}
 87
 88// searchFromRoot searches for a node with the specified data in the binary search tree starting from the root node.
 89func (tree *BinarySearchTree) searchFromRoot(data int) *BinarySearchNode {
 90	if tree.root != nil {
 91		return tree.root.searchNode(data)
 92	}
 93	
 94	return nil
 95}
 96
 97  
 98
 99// searchNode recursively searches for a node with the specified data in the subtree rooted at the current node.
100func (node *BinarySearchNode) searchNode(data int) *BinarySearchNode {
101	if node == nil {
102		return nil
103	}
104
105	if node.data == data {
106		return node
107	} else if data > node.data {
108		return node.right.searchNode(data)
109	} else if data < node.data {
110		return node.left.searchNode(data)
111	}
112
113	return nil
114}
115
116// printTreeInOrder prints the values of nodes in the binary search tree starting from root using an in-order traversal.
117func (tree *BinarySearchTree) printTreeInOrder() {
118	if tree.root != nil {
119		tree.root.printSubTreeInOrder()
120	}
121}
122
123// printTreePreOrder prints the values of nodes in the binary search tree starting from root using a pre-order traversal.
124
125func (tree *BinarySearchTree) printTreePreOrder() {
126	if tree.root != nil {
127		tree.root.printSubTreePreOrder()
128	}
129}
130
131// printTreePostOrder prints the values of nodes in the binary search tree starting from root using a post-order traversal.
132func (tree *BinarySearchTree) printTreePostOrder() {
133	if tree.root != nil {
134		tree.root.printSubTreePostOrder()
135	}
136}
137
138// printSubTreeInOrder prints the values of nodes in the subtree rooted at the given node using an in-order traversal.
139func (node *BinarySearchNode) printSubTreeInOrder() {
140	if node != nil {
141		node.left.printSubTreeInOrder()
142		fmt.Printf("%d ", node.data)
143		node.right.printSubTreeInOrder()
144	}
145}
146
147// printSubTreePreOrder prints the values of nodes in the subtree rooted at the given node using a pre-order traversal.
148func (node *BinarySearchNode) printSubTreePreOrder() {
149	if node != nil {
150		fmt.Printf("%d ", node.data)
151		node.left.printSubTreePreOrder()
152		node.right.printSubTreePreOrder()
153	}
154}
155
156// printSubTreePostOrder prints the values of nodes in the subtree rooted at the given node using a post-order traversal.
157func (node *BinarySearchNode) printSubTreePostOrder() {
158	if node != nil {
159		node.left.printSubTreePostOrder()
160		node.right.printSubTreePostOrder()
161		fmt.Printf("%d ", node.data)
162	}
163}
164
165func main() {
166	
167	// Create a BinarySearchTree
168	bst := &BinarySearchTree{}
169	bst.insertFromRoot(5).insertFromRoot(3).insertFromRoot(7).	
170	insertFromRoot(2).insertFromRoot(4)
171	
172	fmt.Println("In-order traversal:")
173	bst.printTreeInOrder()
174	
175	fmt.Println("\nPre-order traversal:")
176	bst.printTreePreOrder()
177
178	fmt.Println("\nPost-order traversal:")
179	bst.printTreePostOrder()
180
181	// Search for a node
182	fmt.Println("\n Searching for node with data 1")
183	searchNode := bst.searchFromRoot(1)
184	if searchNode != nil {
185		fmt.Printf("Node %d found.\n", searchNode.data)
186	} else {
187		fmt.Println("Node not found.")
188	}
189
190	// Search for a node
191	fmt.Println("Searching for node with data 3")
192	searchNode = bst.searchFromRoot(3)
193	if searchNode != nil {	
194		fmt.Printf("Node %d found.\n", searchNode.data)
195	} else {
196		fmt.Println("Node not found.")
197	}
198	
199	// Delete a node
200	bst.deleteFromRoot(searchNode)
201	fmt.Println("In-order traversal after deleting 3:")
202	bst.printTreeInOrder()
203}

خروجی کد بالا:

 1In-order traversal:
 22 3 4 5 7 
 3Pre-order traversal:
 45 3 2 4 7 
 5Post-order traversal:
 62 4 3 7 5 
 7Searching for node with data 1
 8Node not found.
 9Searching for node with data 3
10Node 3 found.
11In-order traversal after deleting 3:
122 4 5 7