# README
Learn Data Structures and Algorithms with Golang
Welcome to the repository for the exercises from the book Learn Data Structures and Algorithms with Golang by Bhagvan Kommadi. This repository contains solutions for each chapter of the book.
Table of Contents
-
Section 1: Introduction to Data Structures and Algorithms and the Go Language
-
Chapter 1: Data Structures and Algorithms
- List
- Tuples
- Heap
- Adapter
- Bridge
- Composite
- Decorator
- Facade
- Flyweight
- Private Class
- Proxy
- Flow chart
- Pseudo code
- Complexity and performance analysis
- Complexity
- Linear Complexity
- Quadratic Complexity
- Cubic Complexity
- Logarithmic Complexity
- Brute force algorithm
- Divede and Conquer Algorithms
- Backtracking algorithms
-
Chapter 2: Getting Started with Go for Data Structures and Algorithms Technical requirements
-
Hello World !
This is the traditional "Hello World" program in Go. It is the first program that most people write when they are learning a new programming language. The program prints the string "Hello, World!" to the console.
You can run the program below by copying the code into a file named hello-world.go
and running go run hello-world.go
in your terminal.
package main
import "fmt"
func main() {
fmt.Println("Hello, World!")
}
Section 1: Introduction to Data Structures and Algorithms and the Go Language
We will be introducing the abstract data types, definition, and classification of data structures. Readers will be well-versed with performance analysis of algorithms and choosing appropriate data structures for structural design patterns after reading this part.
Chapter 1: Data Structures and Algorithms
Data Structures and Algorithms, focuses on the definition of abstract data types, classifying data structures into linear, non-linear, homogeneous, heterogeneous, and dynamic types. Abstract data types, such as container, list, set, map, graph, stack, and queue, are presented in this chapter. This chapter also covers the performance analysis of data structures, as well as the correct choice of data structures and structural design patterns
List
package main
import (
"container/list"
"fmt"
)
func main() {
var intList list.List
intList.PushBack(11)
intList.PushBack(23)
intList.PushBack(34)
for element := intList.Front(); element != nil; element = element.Next() {
fmt.Println(element.Value.(int))
}
}
Tuples
package main
import (
"fmt"
)
func powerSeries(a int) (int, int) {
return a * a, a * a * a
}
func powerSeriesNamed(a int) (square int, cube int) {
square = a * a
cube = a * a * a
return
}
func powerSeriesError(a int) (int, int, error) {
var square int = a * a
var cube int = a * a * a
return square, cube, nil
}
func main() {
var square int
var cube int
square, cube = powerSeries(3)
fmt.Println("Square", square, "Cube", cube)
fmt.Println(powerSeriesNamed(4))
fmt.Println(powerSeriesError(5))
}
Heap
package main
import (
"container/heap"
"fmt"
)
type IntegerHeap []int
func (iheap IntegerHeap) Len() int { return len(iheap) }
func (iheap IntegerHeap) Less(i, j int) bool { return iheap[i] < iheap[j] }
func (iheap IntegerHeap) Swap(i, j int) { iheap[i], iheap[j] = iheap[j], iheap[i] }
func (iheap *IntegerHeap) Push(heapintf interface{}) {
*iheap = append(*iheap, heapintf.(int))
}
func (iheap *IntegerHeap) Pop() interface{} {
var n int
var x1 int
var previous IntegerHeap = *iheap
n = len(previous)
x1 = previous[n-1]
*iheap = previous[0 : n-1]
return x1
}
func main() {
var intHeap *IntegerHeap = &IntegerHeap{1, 4, 5}
heap.Init(intHeap)
heap.Push(intHeap, 2)
fmt.Printf("minimum: %d\n", (*intHeap)[0])
for intHeap.Len() > 0 {
fmt.Printf("%d \n", heap.Pop(intHeap))
}
}
Adapter
Read more about the Adapter pattern:
Refactoring.guru
GolangByExample.com
package main
import "fmt"
type IProcess interface {
process()
}
type Adapter struct {
adaptee Adaptee
}
func (adapter Adapter) process() {
fmt.Println("Adapter process")
adapter.adaptee.convert()
}
type Adaptee struct {
adapterType int
}
func (adaptee Adaptee) convert() {
fmt.Println("Adaptee convert method")
}
func main() {
var processor IProcess = Adapter{}
processor.process()
}
Bridge
Read more about the Bridge pattern:
Refactoring.guru
GolangByExample.com
package main
import "fmt"
type IDrawShape interface {
drawShape(x [5]float32, y [5]float32)
}
type DrawShape struct{}
func (drawShape DrawShape) drawShape(x [5]float32, y [5]float32) {
fmt.Println("Drawing Shape")
}
type IContour interface {
drawContour(x [5]float32, y [5]float32)
resizeByFactor(factor int)
}
type Contour struct {
x [5]float32
y [5]float32
shape DrawShape
factor int
}
func (contour Contour) drawContour(x [5]float32, y [5]float32) {
fmt.Println("Drawing Contour")
contour.shape.drawShape(contour.x, contour.y)
}
func (contour Contour) resizeByFactor(factor int) {
contour.factor = factor
}
func main() {
var x = [5]float32{1, 2, 3, 4, 5}
var y = [5]float32{1, 2, 3, 4, 5}
var contour IContour = Contour{x: x, y: y}
contour.drawContour(x, y)
contour.resizeByFactor(2)
}
Composite
Read more about the Composite pattern:
Refactoring.guru
GolangByExample.com
package main
import "fmt"
type IComposite interface {
perform()
}
type Branch struct {
leafs []Leaflet
name string
branches []Branch
}
func (branch *Branch) perform() {
fmt.Println("Branch: " + branch.name)
for _, leaf := range branch.leafs {
leaf.perform()
}
for _, branch := range branch.branches {
branch.perform()
}
}
func (branch *Branch) addLeaf(leaf Leaflet) {
branch.leafs = append(branch.leafs, leaf)
}
func (branch *Branch) addBranch(newBranch Branch) {
branch.branches = append(branch.branches, newBranch)
}
func (branch *Branch) getLeaflets() []Leaflet {
return branch.leafs
}
type Leaflet struct {
name string
}
func (leaf *Leaflet) perform() {
fmt.Println("Leaflet: " + leaf.name)
}
func main() {
var branch = &Branch{name: "branch 1"}
var leaf1 = Leaflet{name: "leaf 1"}
var leaf2 = Leaflet{name: "leaf 2"}
var branch2 = Branch{name: "branch 2"}
branch.addLeaf(leaf1)
branch.addLeaf(leaf2)
branch.addBranch(branch2)
branch.perform()
}
Decorator
Read more about the Decorator pattern:
Refactoring.guru
GolangByExample.com
package main
import "fmt"
type IProcess interface {
process()
}
type Process struct{}
func (process Process) process() {
fmt.Println("Process process")
}
type ProcessDecorator struct {
processInstance *Process
}
func (decorator *ProcessDecorator) process() {
if decorator.processInstance == nil {
fmt.Println("ProcessDecorator process")
} else {
fmt.Printf("ProcessDecorator process and ")
decorator.processInstance.process()
}
}
func main() {
var process = &Process{}
var decorator = &ProcessDecorator{}
decorator.process()
decorator.processInstance = process
decorator.process()
}
Facade
Read more about the Facade pattern:
Refactoring.guru
GolangByExample.com
package main
import "fmt"
type Account struct {
id string
accountType string
}
func (account *Account) create(accountType string) *Account {
fmt.Println("Account creation with type")
account.accountType = accountType
return account
}
func (account *Account) getById(id string) *Account {
fmt.Println("Getting account by id")
return account
}
func (account *Account) deleteById(id string) {
fmt.Println("Delete account by id")
}
type Customer struct {
id string
name string
}
func (customer *Customer) create(name string) *Customer {
fmt.Println("Customer creation")
customer.name = name
return customer
}
type Transaction struct {
id string
amount float32
srcAccountId string
destAccountId string
}
func (transaction *Transaction) create(srcAccountId, destAccountId string, amount float32) *Transaction {
fmt.Println("Transaction creation")
transaction.srcAccountId = srcAccountId
transaction.destAccountId = destAccountId
transaction.amount = amount
return transaction
}
type BranchManagerFacade struct {
account *Account
customer *Customer
transaction *Transaction
}
func NewBranchManagerFacade() *BranchManagerFacade {
return &BranchManagerFacade{&Account{}, &Customer{}, &Transaction{}}
}
func (facade *BranchManagerFacade) createCustomerAccount(customerName, accountType string) (*Customer, *Account) {
var customer = facade.customer.create(customerName)
var account = facade.account.create(accountType)
return customer, account
}
func (facade *BranchManagerFacade) createTransaction(srcAccountId, destAccountId string, amount float32) *Transaction {
var transaction = facade.transaction.create(srcAccountId, destAccountId, amount)
return transaction
}
func main() {
var facade = NewBranchManagerFacade()
var customer *Customer
var account *Account
customer, account = facade.createCustomerAccount("John Doe", "savings")
fmt.Println(customer.name)
fmt.Println(account.accountType)
var transaction = facade.createTransaction("1", "2", 100)
fmt.Println(transaction.amount)
}
Flyweight
Read more about the Flyweight pattern:
Refactoring.guru
GolangByExample.com
package main
import "fmt"
type DataTransferObject interface {
getId() string
}
type Customer struct {
id string
name string
ssn string
}
func (customer Customer) getId() string {
return customer.id
}
type Employee struct {
id string
name string
}
func (employee Employee) getId() string {
return employee.id
}
type Manager struct {
id string
name string
dept string
}
func (manager Manager) getId() string {
return manager.id
}
type Address struct {
id string
streetLine1 string
streetLine2 string
state string
city string
}
func (address Address) getId() string {
return address.id
}
type DataTransferObjectFactory struct {
pool map[string]DataTransferObject
}
func (factory DataTransferObjectFactory) getDataTransferObject(dtoType string) DataTransferObject {
var dto = factory.pool[dtoType]
if dto == nil {
fmt.Println("new DTO of dtoType: " + dtoType)
switch dtoType {
case "customer":
factory.pool[dtoType] = Customer{id: "1"}
case "employee":
factory.pool[dtoType] = Employee{id: "2"}
case "manager":
factory.pool[dtoType] = Manager{id: "3"}
case "address":
factory.pool[dtoType] = Address{id: "4"}
}
dto = factory.pool[dtoType]
}
return dto
}
func main() {
var factory = DataTransferObjectFactory{pool: make(map[string]DataTransferObject)}
var customer DataTransferObject = factory.getDataTransferObject("customer")
fmt.Println("Customer ", customer.getId())
var employee DataTransferObject = factory.getDataTransferObject("employee")
fmt.Println("Employee ", employee.getId())
var manager DataTransferObject = factory.getDataTransferObject("manager")
fmt.Println("Manager ", manager.getId())
var address DataTransferObject = factory.getDataTransferObject("address")
fmt.Println("Address ", address.getId())
}
Private Class
package main
import (
"encoding/json"
"fmt"
)
type AccountDetails struct {
id string
accountType string
}
type Account struct {
details *AccountDetails
CustomerName string
}
func (account *Account) setDetails(id, accountType string) {
account.details = &AccountDetails{id, accountType}
}
func (account *Account) getId() string {
return account.details.id
}
func (account *Account) getAccountType() string {
return account.details.accountType
}
func main() {
var account *Account = &Account{CustomerName: "John Smith"}
account.setDetails("4532", "current")
jsonAccount, _ := json.Marshal(account)
fmt.Println("Private Class hidden:", string(jsonAccount))
fmt.Println("Account ID:", account.getId())
fmt.Println("Account Type:", account.getAccountType())
}
Proxy
Read more about the Proxy pattern:
Refaactoring.guru
GolangByExample.com
package main
import "fmt"
type RealObject struct {
}
type IRealObject interface {
performActin()
}
func (realObject *RealObject) performAction() {
fmt.Println("RealObject performAction()")
}
type VirtualProxy struct {
realObject *RealObject
}
func (virtualProxy *VirtualProxy) performAction() {
if virtualProxy.realObject == nil {
virtualProxy.realObject = &RealObject{}
}
fmt.Println("VirtualProxy performAction()")
virtualProxy.realObject.performAction()
}
func main() {
var object VirtualProxy = VirtualProxy{}
object.performAction()
}
Flow chart
Example of Flow chart
Pseudo code
Example of pseudo code
maximum(arr) {
n <- len(arr)
max <- arr[0]
for k <-0, n do {
if arr[k] > max {
max <-arr[k]
}
}
return max
}
Complexity and performance analysis
The complexity is how the algorithm scales when the number of input parameters increases.
Performance is a measure of time, space, memory, and other parameters
Complexity
package main
import "fmt"
func main() {
var (
m [10]int
k int
)
for k = 0; k < 10; k++ {
m[k] = k + 200
fmt.Printf("Element [%d] = %d\n", k, m[k])
}
}
BigO notation
T(n)=O(n)
Using Big O notation, the constant time O(1), linear time O(n), logarithmic time O(logn), cubic time O(n^3) and quadratic time O(n^2) complexity are different complexity types for an algorithm.
Linear complexity
package main
import "fmt"
func main() {
var (
m [10]int
k int
)
for k = 0; k < 10; k++ {
m[k] = k * 200
fmt.Printf("Element[%d] = %d\n", k, m[k])
}
}
Quadratic complexit
package main
import "fmt"
func main() {
var (
k, l int
)
for k = 1; k <= 10; k++ {
fmt.Println("Multiplication Table", k)
for l = 1; l <= 10; l++ {
var x int = l * k
fmt.Println(x)
}
}
}
Cubic Complexity
package main
import "fmt"
func main() {
var k, l, m int
var arr [10][10][10]int
for k = 0; k < 10; k++ {
for l = 0; l < 10; l++ {
for m = 0; m < 10; m++ {
arr[k][l][m] = 1
fmt.Println("Element value ", k, l, m, arr[k][l][m])
}
}
}
}
Logarithmic complexity
package main
import "fmt"
type Tree struct {
LeftNode *Tree
Value int
RightNode *Tree
}
func (tree *Tree) insert(m int) {
if tree != nil {
if tree.LeftNode == nil {
tree.LeftNode = &Tree{nil, m, nil}
} else {
if tree.RightNode == nil {
tree.RightNode = &Tree{nil, m, nil}
} else {
if tree.LeftNode != nil {
tree.LeftNode.insert(m)
} else {
tree.RightNode.insert(m)
}
}
}
} else {
tree = &Tree{nil, m, nil}
}
}
func print(tree *Tree) {
if tree != nil {
fmt.Println("Value", tree.Value)
fmt.Printf("Tree Node Left")
print(tree.LeftNode)
fmt.Printf("Tree Node Right")
print(tree.RightNode)
} else {
fmt.Printf("Nil\n")
}
}
func main() {
var tree *Tree = &Tree{nil, 1, nil}
print(tree)
tree.insert(3)
print(tree)
tree.insert(5)
print(tree)
tree.LeftNode.insert(7)
print(tree)
}
Brute force algorithms
package main
import "fmt"
func findElement(arr [10]int, k int) bool {
var i int
for i = 0; i < 10; i++ {
if arr[i] == k {
return true
}
}
return false
}
func main() {
var arr = [10]int{1, 4, 7, 8, 3, 9, 2, 4, 1, 9}
var check bool = findElement(arr, 10)
fmt.Println(check)
var check2 bool = findElement(arr, 9)
fmt.Println(check2)
}
Divede and Conquer Algorithms
package main
import "fmt"
func fibonacci(k int) int {
if k <= 1 {
return 1
}
return fibonacci(k-1) + fibonacci(k-2)
}
func main() {
var i int = 5
for i = 0; i < 8; i++ {
var fib = fibonacci(i)
fmt.Println(fib)
}
}
Backtracking algorithms
package main
import "fmt"
func findElementWithSum(arr [10]int, combinations [19]int, size int, k int, addValue int, l int, m int) int {
var num int = 0
if addValue > k {
return -1
}
if addValue == k {
num++
var p int = 0
for p = 0; p < m; p++ {
fmt.Printf("%d, ", arr[combinations[p]])
}
fmt.Println(" ")
}
var i int
for i = l; i < size; i++ {
combinations[m] = l
findElementWithSum(arr, combinations, size, k, addValue+arr[i], l, m+1)
l++
}
return num
}
func main() {
var arr = [10]int{1, 4, 7, 8, 3, 9, 2, 4, 1, 8}
var addedSum = 18
var combinations [19]int
findElementWithSum(arr, combinations, 10, addedSum, 0, 0, 0)
}
Chapter 2: Getting Started with Go for Data Structures and Algorithms Technical requirements
Getting Started with Go for Data Structures and Algorithms, covers Go-specific data structures, such as arrays, slices, two-dimensional slices, maps, structs, and channels. Variadic functions, deferred function calls, and panic and recover operations are introduced. Slicing operations, such as enlarging using append and copy, assigning parts, appending a slice, and appending part of a slice, are also presented in this chapter.
Arrays
package main
import "fmt"
func main() {
var arr = [5]int{1, 2, 3, 4, 5}
var i int
for i = 0; i < len(arr); i++ {
fmt.Println("printing elaments ", arr[i])
}
for i, value := range arr {
fmt.Println("range ", i, value)
}
for _, value := range arr {
fmt.Println("blank range", value)
}
}
The len function
package main
import "fmt"
func main() {
var slice = []int{1, 3, 5, 6}
slice = append(slice, 8)
fmt.Println("Capacity", cap(slice))
fmt.Println("Length", len(slice))
}
Slice function
package main
import "fmt"
func twiceValue(slice []int) {
var i int
var value int
for i, value = range slice {
slice[i] = 2 * value
}
}
func main() {
var slice = []int{1, 3, 5, 6}
twiceValue(slice)
var i int
for i = 0; i < len(slice); i++ {
fmt.Println("new slice value ", slice[i])
}
}
Two dimensional slices
package main
import "fmt"
func main() {
var twoDArray [8][8]int
twoDArray[3][6] = 18
twoDArray[7][4] = 3
fmt.Println(twoDArray)
}
package main
import "fmt"
func main() {
var (
rows int
cols int
)
rows = 7
cols = 9
var twoDSlices = make([][]int, rows)
var i int
for i = range twoDSlices {
twoDSlices[i] = make([]int, cols)
}
fmt.Println(twoDSlices)
}
package main
import "fmt"
func main() {
var arr = []int{5, 6, 7, 8, 9}
var slice1 = arr[:3]
fmt.Println("slice1", slice1)
var slice2 = arr[1:5]
fmt.Println("slice2", slice2)
var slice3 = append(slice2, 12)
fmt.Println("slice3", slice3)
}
Maps
package main
import "fmt"
func main() {
var languages = map[int]string{
3: "English",
4: "French",
5: "Spanish",
}
var products = make(map[int]string)
products[1] = "chair"
products[2] = "table"
var i int
var value string
for i, value = range languages {
println("language ", i, ":", value)
}
fmt.Println("product with key 2", products[2])
delete(products, 1)
fmt.Println(products)
}
Databasa operations
The GetCustomer function
func GetCustomers() []Customer {
var database *sql.DB = GetConnection()
var (
err error
rows *sql.Rows
)
rows, err = database.Query("SELECT * FROM customers ORDER BY CustomerId DESC")
if err != nil {
panic(err.Error())
}
var (
customer = Customer{}
customers = []Customer{}
)
for rows.Next() {
var (
customerId int
customerName string
ssn string
)
err = rows.Scan(
&customerId,
&customerName,
&ssn,
)
if err != nil {
panic(err.Error())
}
customer.CustomerId = customerId
customer.CustomerName = customerName
customer.SSN = ssn
customers = append(customers, customer)
}
defer database.Close()
return customers
}
The GetCustomerById function
func GetCustomerById(customerId int) Customer {
var (
database *sql.DB = GetConnection()
err error
rows *sql.Rows
)
rows, err = database.Query("SELECT * FROM customers WHERE CustomerId = ?", customerId)
if err != nil {
panic(err.Error())
}
var customer Customer = Customer{}
for rows.Next() {
var (
customerId int
customerName string
ssn string
)
err = rows.Scan(
&customerId,
&customerName,
&ssn,
)
if err != nil {
panic(err.Error())
}
customer.CustomerId = customerId
customer.CustomerName = customerName
customer.SSN = ssn
}
defer database.Close()
return customer
}
The InsertCustomer function
func InsertCustomer(customer Customer) {
var (
database *sql.DB = GetConnection()
err error
insert *sql.Stmt
)
insert, err = database.Prepare("INSERT INTO customers (CustomerName, SSN) VALUES (?, ?)")
if err != nil {
panic(err.Error())
}
insert.Exec(customer.CustomerName, customer.SSN)
defer database.Close()
}
The UpdateCustomer function
func UpdateCustomer(customer Customer) {
var (
database *sql.DB = GetConnection()
err error
update *sql.Stmt
)
update, err = database.Prepare("UPDATE customers SET CustomerName = ?, SSN = ? WHERE CustomerId = ?")
if err != nil {
panic(err.Error())
}
update.Exec(customer.CustomerName, customer.SSN, customer.CustomerId)
defer database.Close()
}
The DeleteCustomer function
func DeleteCustomer(csutomer Customer) {
var (
database *sql.DB = GetConnection()
err error
delete *sql.Stmt
)
delete, err = database.Prepare("DELETE FROM customers WHERE CustomerId = ?")
if err != nil {
panic(err.Error())
}
delete.Exec(csutomer.CustomerId)
defer database.Close()
}
CRUD web forms
HTML Template
package main
import (
"html/template"
"log"
"net/http"
)
func Home(writer http.ResponseWriter, reader *http.Request) {
var template_html *template.Template = template.Must(template.ParseFiles("./Chapter02/06-WebForms/main.html"))
template_html.Execute(writer, nil)
}
func main() {
log.Println("Server started on: http://localhost:8000")
http.HandleFunc("/", Home)
http.ListenAndServe(":8000", nil)
}
<html>
<body>
<p>Welcome to Web Forms</p>
</body>
</html>
Create function
func Create(writer http.ResponseWriter, request *http.Request) {
template_html.ExecuteTemplate(writer, "Create", nil)
}
Insert function
func Insert(writer http.ResponseWriter, request *http.Request) {
var (
customer Customer
customers []Customer
)
customer.CustomerName = request.FormValue("customername")
customer.SSN = request.FormValue("ssn")
InsertCustomer(customer)
customers = GetCustomers()
template_html.ExecuteTemplate(writer, "Home", customers)
}
Alter function
func Alter(writer http.ResponseWriter, request *http.Request) {
var (
customer Customer
customerId int
customerIdStr string
customers []Customer
)
customerIdStr = request.FormValue("id")
fmt.Sscanf(customerIdStr, "%d", customerId)
customer.CustomerId = customerId
customer.CustomerName = request.FormValue("customername")
customer.SSN = request.FormValue("ssn")
UpdateCustomer(customer)
customers = GetCustomers()
template_html.ExecuteTemplate(writer, "Home", customers)
}
Update function
func Update(writer http.ResponseWriter, request *http.Request) {
var (
customerId int
customerIdStr string
customer Customer
)
customerIdStr = request.FormValue("id")
fmt.Sscanf(customerIdStr, "%d", &customerId)
customer = GetCustomerById(customerId)
template_html.ExecuteTemplate(writer, "Update", customer)
}
Delete function
func Delete(writer http.ResponseWriter, request *http.Request) {
var (
customerId int
customerIdStr string
customer Customer
customers []Customer
)
customerIdStr = request.FormValue("id")
fmt.Sscanf(customerIdStr, "%d", &customerId)
customer = GetCustomerById(customerId)
DeleteCustomer(customer)
customers = GetCustomers()
template_html.ExecuteTemplate(writer, "Home", customers)
}
View function
func View(writer http.ResponseWriter, request *http.Request) {
var (
customerId int
customerIdStr string
customer Customer
customers []Customer
)
customerIdStr = request.FormValue("id")
fmt.Sscanf(customerIdStr, "%d", &customerId)
customer = GetCustomerById(customerId)
fmt.Println(customer)
customers = GetCustomers()
customers = append(customers, customer)
template_html.ExecuteTemplate(writer, "View", customers)
}
The main function
func main() {
log.Println("Server started on: http://localhost:8000")
http.HandleFunc("/", Home)
http.HandleFunc("/alter", Alter)
http.HandleFunc("/create", Create)
http.HandleFunc("/update", Update)
http.HandleFunc("/view", View)
http.HandleFunc("/insert", Insert)
http.HandleFunc("/delete", Delete)
http.ListenAndServe(":8000", nil)
}
Header template
{{ define "Header" }}
<!DOCTYPLE html>
<html>
<head>
<title>CRM</title>
<meta charset="UTF-8" />
</head>
<body>
<h1>Customer Management - CRM</h1>
{{ end }}
Home template
{{ define "Home" }}
{{ template "Header"}}
{{ template "Menu"}}
<h2>Customers</h2>
<table border="1">
<thead>
<tr>
<td>CustomerId</td>
<td>CustomerName</td>
<td>SSN</td>
<td>Update</td>
<td>Delete</td>
</tr>
</thead>
<tbody>
{{ range . }}
<tr>
<td>{{ .CustomerId }}</td>
<td>{{ .CustomerName }}</td>
<td>{{ .SSN }}</td>
<td><a href="/view?id={{ .CustomerId }}">View</a></td>
<td><a href="/update?id={{ .CustomerId }}">Update</a></td>
<td><a href="/delete?id={{ .CustomerId }}">Delete</a></td>
</tr>
{{ end }}
</tbody>
</table>
{{ template "Footer"}}
{{ end }}
Footer template
{{ define "Footer" }}
<!DICTYPLE html>
</body>
</html>
{{ end }}
Menu template
{{ define "Menu" }}
<a href="/">Home</a> | <a href="/create">Create Customer</a>
{{ end }}
Create template
{{ define "Create" }}
{{ template "Header"}}
{{ template "Menu"}}
<br>
<h1>Create Customer</h1>
<br>
<br>
<form method="POST" action="/insert">
Customer Name:
<input type="text" name="customername" placeholder="Customer name" autofocus/>
<br>
<br>
SSN:
<input type="text" name="ssn" placeholder="ssn"/>
<br>
<br>
<input type="submit" value="Create Customer" />
</form>
{{ template "Footer"}}
{{ end }}
Update template
{{ define "Update" }}
{{ template "Header"}}
{{ template "Menu"}}
<br>
<h1>Update Customer</h1>
<br>
<br>
<form method="POST" action="/alter">
<input type="hidden" name="id" value="{{ .CustomerId }}" />
Customer Name:
<input type="text" name="customername" placeholder="Customer name" value="{{ .CustomerName }}" autofocus/>
<br>
<br>
SSN:
<input type="text" name="ssn" placeholder="ssn" value="{{ .SSN }}"/>
<br>
<br>
<input type="submit" value="Update Customer" />
</form>
{{ template "Footer"}}
{{ end }}
View template
{{ define "View" }}
{{ template "Header"}}
{{ template "Menu"}}
<br>
<h1>View Customer</h1>
<br>
<br>
<table border="1">
<tr>
<td>CustomerId</td>
<td>CustomerName</td>
<td>SSN</td>
<td>Update</td>
<td>Delete</td>
</tr>
{{ if . }}
{{ range . }}
<tr>
<td>{{ .CustomerId }}</td>
<td>{{ .CustomerName }}</td>
<td>{{ .SSN }}</td>
<td><a href="/update?id={{ .CustomerId }}">Update</a></td>
<td><a href="/delete?id={{ .CustomerId }}">Delete</a></td>
</tr>
{{ end }}
{{ end }}
</table>
{{ template "Footer"}}
{{ end }}
Section 2: Basic Data Structures and Algorithms using Go
We will talk about data structures, including linear, non-linear, homogeneous, heterogeneous, and dynamic types, as well as classic algorithms. This section covers different types of lists, trees, arrays, sets, dictionaries, tuples, heaps, queues, and stacks, along with sorting, recursion, searching, and hashing algorithms.
Chapter 3: Linear Data Structures
Linear Data Structures, covers linear data structures such as lists, sets, tuples, stacks, and heaps. The operations related to these types, including insertion, deletion, updating, reversing, and merging are shown with various code samples. In this chapter, we present the complexity analysis of various data structure operations that display accessing, search, insertion, and deletion times.
Lists
Linked list
package main
import "fmt"
type Node struct {
property int
nextNode *Node
}
type LinkedList struct {
headNode *Node
}
func (linkedList *LinkedList) AddToHead(property int) {
var node = &Node{}
node.property = property
node.nextNode = nil
if linkedList.headNode != nil {
node.nextNode = linkedList.headNode
}
linkedList.headNode = node
}
func (linkedList *LinkedList) IterateList() {
for node := linkedList.headNode; node != nil; node = node.nextNode {
fmt.Println(node.property)
}
}
func (linkedList *LinkedList) LastNode() *Node {
var lastNode *Node
for node := linkedList.headNode; node != nil; node = node.nextNode {
if node.nextNode == nil {
lastNode = node
}
}
return lastNode
}
func (linkedList *LinkedList) AddToEnd(property int) {
var node *Node = &Node{}
node.property = property
node.nextNode = nil
var lastNode *Node = linkedList.LastNode()
if lastNode != nil {
lastNode.nextNode = node
}
}
func (linkedList *LinkedList) NodeWithValue(property int) *Node {
var (
nodeWith *Node
)
for node := linkedList.headNode; node != nil; node = node.nextNode {
if node.property == property {
nodeWith = node
break
}
}
return nodeWith
}
func (linkedList *LinkedList) AddAfter(nodeProperty, property int) {
var (
node = &Node{}
nodeWith *Node
)
node.property = property
node.nextNode = nil
nodeWith = linkedList.NodeWithValue(nodeProperty)
if nodeWith != nil {
node.nextNode = nodeWith.nextNode
nodeWith.nextNode = node
}
}
func main() {
var linkedList LinkedList = LinkedList{}
linkedList.AddToHead(1)
linkedList.AddToHead(3)
linkedList.AddToEnd(5)
linkedList.AddAfter(1, 7)
linkedList.IterateList()
}
Doubly linked list
package main
import "fmt"
type Node struct {
property int
nextNode *Node
previousNode *Node
}
type LinkedList struct {
headNode *Node
}
func (linkedList *LinkedList) NodeBetweenValues(firstProperty int, secondProperty int) *Node {
var (
nodeWith *Node
)
for node := linkedList.headNode; node != nil; node = node.nextNode {
if node.previousNode != nil && node.nextNode != nil {
if node.previousNode.property == firstProperty &&
node.nextNode.property == secondProperty {
nodeWith = node
break
}
}
}
return nodeWith
}
func (linkedList *LinkedList) AddToHead(property int) {
var (
node = &Node{}
)
node.property = property
node.nextNode = nil
if linkedList.headNode != nil {
node.nextNode = linkedList.headNode
linkedList.headNode.previousNode = node
}
linkedList.headNode = node
}
func (linkedList *LinkedList) NodeWithValue(property int) *Node {
var (
nodeWith *Node
)
for node := linkedList.headNode; node != nil; node = node.nextNode {
if node.property == property {
nodeWith = node
break
}
}
return nodeWith
}
func (linkedList *LinkedList) AddAfter(nodeProperty, property int) {
var (
node *Node = &Node{}
nodeWith *Node
)
node.property = property
node.nextNode = nil
nodeWith = linkedList.NodeWithValue(nodeProperty)
if nodeWith != nil {
node.nextNode = nodeWith.nextNode
node.previousNode = nodeWith
nodeWith.nextNode = node
}
}
func (linkedList *LinkedList) LastNode() *Node {
var (
lastNode *Node
)
for node := linkedList.headNode; node != nil; node = node.nextNode {
if node.nextNode == nil {
lastNode = node
break
}
}
return lastNode
}
func (linkedList *LinkedList) AddToEnd(property int) {
var (
node *Node = &Node{}
lastNode *Node
)
node.property = property
node.nextNode = nil
lastNode = linkedList.LastNode()
if lastNode != nil {
lastNode.nextNode = node
node.previousNode = lastNode
}
}
func main() {
var linkedList LinkedList = LinkedList{}
linkedList.AddToHead(1)
linkedList.AddToHead(3)
linkedList.AddToEnd(5)
linkedList.AddAfter(1, 7)
fmt.Println(linkedList.headNode.property)
var node *Node = linkedList.NodeBetweenValues(1, 5)
fmt.Println(node.property)
}
Sets
package main
import "fmt"
type Set struct {
integerMap map[int]bool
}
func (set *Set) New() {
set.integerMap = make(map[int]bool)
}
func (set *Set) AddElement(element int) {
if !set.integerMap[element] {
set.integerMap[element] = true
}
}
func (set *Set) DeleteElement(element int) {
delete(set.integerMap, element)
}
func (set *Set) ContainsElement(element int) bool {
var exists bool
_, exists = set.integerMap[element]
return exists
}
func main() {
var set *Set = &Set{}
set.New()
set.AddElement(1)
set.AddElement(2)
fmt.Println(set)
fmt.Println(set.ContainsElement(1))
}
func (set *Set) Intersect(anotherSet *Set) *Set {
var intersectSet *Set = &Set{}
intersectSet.New()
for value := range set.integerMap {
if anotherSet.ContainsElement(value) {
intersectSet.AddElement(value)
}
}
return intersectSet
}
func (set *Set) Union(anotherSet *Set) *Set {
var unionSet *Set = &Set{}
unionSet.New()
for value := range set.integerMap {
unionSet.AddElement(value)
}
for value := range anotherSet.integerMap {
unionSet.AddElement(value)
}
return unionSet
}
func main() {
var (
set *Set = &Set{}
anotherSet *Set = &Set{}
)
set.New()
anotherSet.New()
set.AddElement(1)
set.AddElement(2)
fmt.Println("initial set", set)
fmt.Println(set.ContainsElement(1))
anotherSet.AddElement(2)
anotherSet.AddElement(4)
anotherSet.AddElement(5)
fmt.Println(set.Intersect(anotherSet))
fmt.Println(set.Union(anotherSet))
}
Tuples
package main
import "fmt"
func h(x int, y int) int {
return x * y
}
func g(l int, m int) (x int, y int) {
x = 2 * l
y = 4 * m
return
}
func main() {
fmt.Println(h(g(1, 2)))
}
Queues
Simple queue
package main
import "fmt"
type Queue []*Order
type Order struct {
priority int
quantity int
product string
customerName string
}
func (o *Order) New(priority int, quantity int, product string, customerName string) {
o.priority = priority
o.quantity = quantity
o.product = product
o.customerName = customerName
}
func (queue *Queue) Add(order *Order) {
if len(*queue) == 0 {
*queue = append(*queue, order)
} else {
var (
appended bool = false
)
for i, addedOrder := range *queue {
if order.priority > addedOrder.priority {
*queue = append((*queue)[:i], append(Queue{order}, (*queue)[i:]...)...)
appended = true
break
}
}
if !appended {
*queue = append(*queue, order)
}
}
}
func main() {
var (
queue Queue = make(Queue, 0)
order1 *Order = &Order{}
priority1 int = 2
quantity1 int = 20
product1 string = "Computer"
custoemrName1 string = "Greg White"
)
order1.New(priority1, quantity1, product1, custoemrName1)
var (
order2 *Order = &Order{}
priority2 int = 1
quantity2 int = 10
product2 string = "Monitor"
custoemrName2 string = "John Smith"
)
order2.New(priority2, quantity2, product2, custoemrName2)
queue.Add(order1)
queue.Add(order2)
for i := 0; i < len(queue); i++ {
fmt.Println(queue[i])
}
}
Synchronized queue
package main
import (
"fmt"
"time"
"math/rand"
)
const (
messagePassStart = iota
messageTicketStart
messagePassEnd
messageTicketEnd
)
type Queue struct {
waitPass int
waitTicket int
playPass bool
playTicket bool
queuePass chan int
queueTicket chan int
message chan int
}
func (queue *Queue) New() {
queue.message = make(chan int)
queue.queuePass = make(chan int)
queue.queueTicket = make(chan int)
go func() {
var (
message int
)
for {
select {
case message = <-queue.message:
switch message {
case messagePassStart:
queue.waitPass++
case messagePassEnd:
queue.playPass = false
case messageTicketStart:
queue.waitTicket++
case messageTicketEnd:
queue.playTicket = false
}
if queue.waitPass > 0 && queue.waitTicket > 0 &&
!queue.playPass && !queue.playTicket {
queue.playPass = true
queue.playTicket = true
queue.waitPass--
queue.waitTicket--
queue.queuePass <- 1
queue.queueTicket <- 1
}
}
}
}()
}
func (queue *Queue) StartTicketIssue() {
queue.message <- messageTicketStart
<-queue.queueTicket
}
func (queue *Queue) EndTicketIssue() {
queue.message <- messageTicketEnd
}
func ticketIssue(queue *Queue) {
for {
time.Sleep(time.Duration(rand.Intn(10000)) * time.Millisecond)
queue.StartTicketIssue()
fmt.Println("Ticket Issue starts")
time.Sleep(time.Duration(rand.Intn(2000)) * time.Millisecond)
fmt.Println("Ticket Issue ends")
queue.EndTicketIssue()
}
}
func (queue *Queue) StartPass() {
queue.message <- messagePassStart
<-queue.queuePass
}
func (queue *Queue) EndPass() {
queue.message <- messagePassEnd
}
func passenger(queue *Queue) {
for {
time.Sleep(time.Duration(rand.Intn(10000)) * time.Millisecond)
queue.StartPass()
fmt.Println("Passenger starts")
time.Sleep(time.Duration(rand.Intn(2000)) * time.Millisecond)
fmt.Println("Passenger ends")
queue.EndPass()
}
}
func main() {
var queue *Queue = &Queue{}
queue.New()
fmt.Println(queue)
for i := 0; i < 10; i++ {
go passenger(queue)
}
for i := 0; i < 5; i++ {
go ticketIssue(queue)
}
select {}
}
Stacks
package main
import (
"fmt"
"strconv"
)
type Element struct {
Value int
}
type Stack struct {
elements []*Element
elementCount int
}
func (element *Element) String() string {
return strconv.Itoa(element.Value)
}
func (stack *Stack) New() {
stack.elements = make([]*Element, 0)
}
func (stack *Stack) Push(element *Element) {
stack.elements = append(stack.elements[:stack.elementCount], element)
stack.elementCount++
}
func (stack *Stack) Pop() *Element {
if stack.elementCount == 0 {
return nil
}
var (
length int = len(stack.elements)
element *Element = stack.elements[length-1]
)
if length > 1 {
stack.elements = stack.elements[:length-1]
} else {
stack.elements = stack.elements[0:]
}
stack.elementCount = len(stack.elements)
return element
}
func main() {
var (
stack *Stack = &Stack{}
element1 *Element = &Element{3}
element2 *Element = &Element{5}
element3 *Element = &Element{7}
element4 *Element = &Element{9}
)
stack.New()
stack.Push(element1)
stack.Push(element2)
stack.Push(element3)
stack.Push(element4)
fmt.Println(stack.Pop(), stack.Pop(), stack.Pop(), stack.Pop())
}
Contributing
Contributions are welcome! If you have a solution to an exercise that is different from the one provided, feel free to open a pull request.
- Fork the repository.
- Create a new branch (
git checkout -b feature/exercise-solution
). - Commit your changes (
git commit -m 'Add solution for Exercise X.Y'
). - Push to the branch (
git push origin feature/exercise-solution
). - Open a pull request.
License
This project is licensed under the MIT License - see the LICENSE file for details.