- Software Letters
- Posts
- Mastering Data Structures with 50 Coding Challenges: Trees, Graphs, Stacks, and Queues - Part 2
Mastering Data Structures with 50 Coding Challenges: Trees, Graphs, Stacks, and Queues - Part 2
An In-Depth Guide to Essential Data Structures for Efficient Programming and Problem-Solving
In computer science, understanding and effectively utilizing data structures is crucial for designing efficient algorithms and solving complex problems. Among the fundamental data structures are trees, graphs, stacks, and queues. Each of these structures serves unique purposes and has distinct characteristics that make them suitable for different types of applications and scenarios.
This guide delves into these essential data structures, providing clear definitions, characteristics, and practical examples in Go (Golang). By exploring trees, graphs, stacks, and queues, you'll gain a comprehensive understanding of their functionalities and how to implement them. Additionally, we've included 50 coding challenges designed to test and enhance your knowledge of these data structures. These challenges will enable you to improve your problem-solving skills and tackle a wide range of computational problems with confidence.
26. Graph BFS
Explanation: Implement Breadth-First Search (BFS) for a graph.
Source Code:
package main
import (
"fmt"
)
type Graph struct {
vertices int
edges map[int][]int
}
func NewGraph(vertices int) *Graph {
return &Graph{
vertices: vertices,
edges: make(map[int][]int),
}
}
func (g *Graph) AddEdge(v, w int) {
g.edges[v] = append(g.edges[v], w)
}
func (g *Graph) BFS(start int) []int {
visited := make([]bool, g.vertices)
queue := []int{start}
visited[start] = true
result := []int{}
for len(queue) > 0 {
vertex := queue[0]
queue = queue[1:]
result = append(result, vertex)
for _, neighbor := range g.edges[vertex] {
if !visited[neighbor] {
queue = append(queue, neighbor)
visited[neighbor] = true
}
}
}
return result
}
func main() {
g := NewGraph(6)
g.AddEdge(0, 1)
g.AddEdge(0, 2)
g.AddEdge(1, 3)
g.AddEdge(1, 4)
g.AddEdge(2, 5)
fmt.Println(g.BFS(0)) // Output: [0 1 2 3 4 5]
}
27. Graph DFS
Explanation: Implement Depth-First Search (DFS) for a graph.
Source Code:
package main
import (
"fmt"
)
type Graph struct {
vertices int
edges map[int][]int
}
func NewGraph(vertices int) *Graph {
return &Graph{
vertices: vertices,
edges: make(map[int][]int),
}
}
func (g *Graph) AddEdge(v, w int) {
g.edges[v] = append(g.edges[v], w)
}
func (g *Graph) DFS(start int) []int {
visited := make([]bool, g.vertices)
result := []int{}
var dfs func(v int)
dfs = func(v int) {
visited[v] = true
result = append(result, v)
for _, neighbor := range g.edges[v] {
if !visited[neighbor] {
dfs(neighbor)
}
}
}
dfs(start)
return result
}
func main() {
g := NewGraph(6)
g.AddEdge(0, 1)
g.AddEdge(0, 2)
g.AddEdge(1, 3)
g.AddEdge(1, 4)
g.AddEdge(2, 5)
fmt.Println(g.DFS(0)) // Output: [0 1 3 4 2 5]
}
28. Detect Cycle in Graph
Explanation: Write a function to detect if a graph contains a cycle.
Source Code:
package main
import (
"fmt"
)
type Graph struct {
vertices int
edges map[int][]int
}
func NewGraph(vertices int) *Graph {
return &Graph{
vertices: vertices,
edges: make(map[int][]int),
}
}
func (g *Graph) AddEdge(v, w int) {
g.edges[v] = append(g.edges[v], w)
}
func (g *Graph) isCyclicUtil(v int, visited []bool, recStack []bool) bool {
if visited[v] == false {
visited[v] = true
recStack[v] = true
for _, neighbor := range g.edges[v] {
if !visited[neighbor] && g.isCyclicUtil(neighbor, visited, recStack) {
return true
} else if recStack[neighbor] {
return true
}
}
}
recStack[v] = false
return false
}
func (g *Graph) isCyclic() bool {
visited := make([]bool, g.vertices)
recStack := make([]bool, g.vertices)
for i := 0; i < g.vertices; i++ {
if g.isCyclicUtil(i, visited, recStack) {
return true
}
}
return false
}
func main() {
g := NewGraph(4)
g.AddEdge(0, 1)
g.AddEdge(1, 2)
g.AddEdge(2, 0)
g.AddEdge(2, 3)
fmt.Println(g.isCyclic()) // Output: true
}
Heaps and Priority Queues
29. Implement Min-Heap
Explanation: Implement a min-heap data structure.
Source Code:
package main
import (
"fmt"
)
type MinHeap struct {
array []int
}
func (h *MinHeap) insert(key int) {
h.array = append(h.array, key)
h.heapifyUp(len(h.array) - 1)
}
func (h *MinHeap) extractMin() int {
if len(h.array) == 0 {
return -1 // or some indication that the heap is empty
}
root := h.array[0]
lastIndex := len(h.array) - 1
h.array[0] = h.array[lastIndex]
h.array = h.array[:lastIndex]
h.heapifyDown(0)
return root
}
func (h *MinHeap) heapifyUp(index int) {
for h.array[parent(index)] > h.array[index] {
h.swap(parent(index), index)
index = parent(index)
}
}
func (h *MinHeap) heapifyDown(index int) {
lastIndex := len(h.array) - 1
l, r := left(index), right(index)
childToCompare := 0
for l <= lastIndex {
if l == lastIndex {
childToCompare = l
} else if h.array[l] < h.array[r] {
childToCompare = l
} else {
childToCompare = r
}
if h.array[index] > h.array[childToCompare] {
h.swap(index, childToCompare)
index = childToCompare
l, r = left(index), right(index)
} else {
return
}
}
}
func parent(i int) int {
return (i - 1) / 2
}
func left(i int) int {
return 2*i + 1
}
func right(i int) int {
return 2*i + 2
}
func (h *MinHeap) swap(i1, i2 int) {
h.array[i1], h.array[i2] = h.array[i2], h.array[i1]
}
func main() {
h := &MinHeap{}
h.insert(3)
h.insert(1)
h.insert(6)
h.insert(5)
h.insert(2)
h.insert(4)
fmt.Println(h.extractMin()) // Output: 1
fmt.Println(h.extractMin()) // Output: 2
fmt.Println(h.extractMin()) // Output: 3
}
30. Kth Largest Element
Explanation: Write a function to find the Kth largest element in an array using a min-heap.
Source Code:
package main
import (
"container/heap"
"fmt"
)
type MinHeap []int
func (h MinHeap) Len() int { return len(h) }
func (h MinHeap) Less(i, j int) bool { return h[i] < h[j] }
func (h MinHeap) Swap(i, j int) { h[i], h[j] = h[j], h[i] }
func (h *MinHeap) Push(x interface{}) { *h = append(*h, x.(int)) }
func (h *MinHeap) Pop() interface{} {
old := *h
n := len(old)
x := old[n-1]
*h = old[0 : n-1]
return x
}
func findKthLargest(nums []int, k int) int {
h := &MinHeap{}
heap.Init(h)
for _, num := range nums {
heap.Push(h, num)
if h.Len() > k {
heap.Pop(h)
}
}
return heap.Pop(h).(int)
}
func main() {
nums := []int{3, 2, 1, 5, 6, 4}
k := 2
fmt.Println(findKthLargest(nums, k)) // Output: 5
}
31. Merge K Sorted Lists
Explanation: Write a function to merge K sorted linked lists using a priority queue.
Source Code:
package main
import (
"container/heap"
"fmt"
)
type ListNode struct {
Val int
Next *ListNode
}
type MinHeap []*ListNode
func (h MinHeap) Len() int { return len(h) }
func (h MinHeap) Less(i, j int) bool { return h[i].Val < h[j].Val }
func (h MinHeap) Swap(i, j int) { h[i], h[j] = h[j], h[i] }
func (h *MinHeap) Push(x interface{}) { *h = append(*h, x.(*ListNode)) }
func (h *MinHeap) Pop() interface{} {
old := *h
n := len(old)
x := old[n-1]
*h = old[0 : n-1]
return x
}
func mergeKLists(lists []*ListNode) *ListNode {
h := &MinHeap{}
heap.Init(h)
for _, list := range lists {
if list != nil {
heap.Push(h, list)
}
}
dummy := &ListNode{}
current := dummy
for h.Len() > 0 {
minNode := heap.Pop(h).(*ListNode)
current.Next = minNode
current = current.Next
if minNode.Next != nil {
heap.Push(h, minNode.Next)
}
}
return dummy.Next
}
func printList(head *ListNode) {
for head != nil {
fmt.Printf("%d ", head.Val)
head = head.Next
}
fmt.Println()
}
func main() {
list1 := &ListNode{Val: 1, Next: &ListNode{Val: 4, Next: &ListNode{Val: 5}}}
list2 := &ListNode{Val: 1, Next: &ListNode{Val: 3, Next: &ListNode{Val: 4}}}
list3 := &ListNode{Val: 2, Next: &ListNode{Val: 6}}
lists := []*ListNode{list1, list2, list3}
mergedList := mergeKLists(lists)
printList(mergedList) // Output: 1 1 2 3 4 4 5 6
}
32. Top K Frequent Elements
Explanation: Write a function to find the top K frequent elements in an array using a heap.
Source Code:
package main
import (
"container/heap"
"fmt"
)
type Element struct {
val int
count int
}
type MaxHeap []Element
func (h MaxHeap) Len() int { return len(h) }
func (h MaxHeap) Less(i, j int) bool { return h[i].count > h[j].count }
func (h MaxHeap) Swap(i, j int) { h[i], h[j] = h[j], h[i] }
func (h *MaxHeap) Push(x interface{}) { *h = append(*h, x.(Element)) }
func (h *MaxHeap) Pop() interface{} {
old := *h
n := len(old)
x := old[n-1]
*h = old[0 : n-1]
return x
}
func topKFrequent(nums []int, k int) []int {
frequency := make(map[int]int)
for _, num := range nums {
frequency[num]++
}
h := &MaxHeap{}
heap.Init(h)
for val, count := range frequency {
heap.Push(h, Element{val, count})
}
result := []int{}
for i := 0; i < k; i++ {
result = append(result, heap.Pop(h).(Element).val)
}
return result
}
func main() {
nums := []int{1, 1, 1, 2, 2, 3}
k := 2
fmt.Println(topKFrequent(nums, k)) // Output: [1 2]
}
33. Median Finder
Explanation: Implement a data structure that supports the insertion of numbers and returns the median of the current numbers.
Source Code:
package main
import (
"container/heap"
"fmt"
)
type MaxHeap []int
type MinHeap []int
func (h MaxHeap) Len() int { return len(h) }
func (h MaxHeap) Less(i, j int) bool { return h[i] > h[j] }
func (h MaxHeap) Swap(i, j int) { h[i], h[j] = h[j], h[i] }
func (h *MaxHeap) Push(x interface{}) { *h = append(*h, x.(int)) }
func (h *MaxHeap) Pop() interface{} {
old := *h
n := len(old)
x := old[n-1]
*h = old[0 : n-1]
return x
}
func (h MinHeap) Len() int { return len(h) }
func (h MinHeap) Less(i, j int) bool { return h[i] < h[j] }
func (h MinHeap) Swap(i, j int) { h[i], h[j] = h[j], h[i] }
func (h *MinHeap) Push(x interface{}) { *h = append(*h, x.(int)) }
func (h *MinHeap) Pop() interface{} {
old := *h
n := len(old)
x := old[n-1]
*h = old[0 : n-1]
return x
}
type MedianFinder struct {
maxHeap *MaxHeap
minHeap *MinHeap
}
func Constructor() MedianFinder {
maxHeap := &MaxHeap{}
minHeap := &MinHeap{}
heap.Init(maxHeap)
heap.Init(minHeap)
return MedianFinder{maxHeap, minHeap}
}
func (mf *MedianFinder) AddNum(num int) {
heap.Push(mf.maxHeap, num)
heap.Push(mf.minHeap, heap.Pop(mf.maxHeap))
if mf.maxHeap.Len() < mf.minHeap.Len() {
heap.Push(mf.maxHeap, heap.Pop(mf.minHeap))
}
}
func (mf *MedianFinder) FindMedian() float64 {
if mf.maxHeap.Len() > mf.minHeap.Len() {
return float64((*mf.maxHeap)[0])
}
return float64((*mf.maxHeap)[0]+(*mf.minHeap)[0]) / 2.0
}
func main() {
mf := Constructor()
mf.AddNum(1)
mf.AddNum(2)
fmt.Println(mf.FindMedian()) // Output: 1.5
mf.AddNum(3)
fmt.Println(mf.FindMedian()) // Output: 2
}
Hash Tables
34. Two Sum
Explanation: Write a function to find two numbers in an array that add up to a target value using a hash map.
Source Code:
package main
import (
"fmt"
)
func twoSum(nums []int, target int) []int {
m := make(map[int]int)
for i, num := range nums {
if idx, ok := m[target-num]; ok {
return []int{idx, i}
}
m[num] = i
}
return nil
}
func main() {
nums := []int{2, 7, 11, 15}
target := 9
fmt.Println(twoSum(nums, target)) // Output: [0 1]
}
35. Group Anagrams
Explanation: Write a function to group anagrams from a list of strings.
Source Code:
package main
import (
"fmt"
"sort"
"strings"
)
func groupAnagrams(strs []string) [][]string {
m := make(map[string][]string)
for _, str := range strs {
sortedStr := sortString(str)
m[sortedStr] = append(m[sortedStr], str)
}
result := [][]string{}
for _, v := range m {
result = append(result, v)
}
return result
}
func sortString(s string) string {
sortedStr := strings.Split(s, "")
sort.Strings(sortedStr)
return strings.Join(sortedStr, "")
}
func main() {
strs := []string{"eat", "tea", "tan", "ate", "nat", "bat"}
fmt.Println(groupAnagrams(strs)) // Output: [[eat tea ate] [tan nat] [bat]]
}
36. Longest Substring Without Repeating Characters
Explanation: Write a function to find the length of the longest substring without repeating characters.
Source Code:
package main
import (
"fmt"
)
func lengthOfLongestSubstring(s string) int {
m := make(map[byte]int)
maxLen, start := 0, 0
for i := 0; i < len(s); i++ {
if idx, ok := m[s[i]]; ok && idx >= start {
start = idx + 1
}
m[s[i]] = i
if i-start+1 > maxLen {
maxLen = i - start + 1
}
}
return maxLen
}
func main() {
s := "abcabcbb"
fmt.Println(lengthOfLongestSubstring(s)) // Output: 3
}
37. Subarray Sum Equals K
Explanation: Write a function to find the number of subarrays that sum to a given value using a hash map.
Source Code:
package main
import (
"fmt"
)
func subarraySum(nums []int, k int) int {
count, sum := 0, 0
m := make(map[int]int)
m[0] = 1
for _, num := range nums {
sum += num
if _, ok := m[sum-k]; ok {
count += m[sum-k]
}
m[sum]++
}
return count
}
func main() {
nums := []int{1, 1, 1}
k := 2
fmt.Println(subarraySum(nums, k)) // Output: 2
}
38. Find All Duplicates
Explanation: Write a function to find all duplicate elements in an array using a hash map.
Source Code:
package main
import (
"fmt"
)
func findDuplicates(nums []int) []int {
m := make(map[int]int)
duplicates := []int{}
for _, num := range nums {
m[num]++
if m[num] == 2 {
duplicates = append(duplicates, num)
}
}
return duplicates
}
func main() {
nums := []int{4, 3, 2, 7, 8, 2, 3, 1}
fmt.Println(findDuplicates(nums)) // Output: [2 3]
}
Strings
39. Longest Palindromic Substring
Explanation: Write a function to find the longest palindromic substring in a given string.
Source Code:
package main
import (
"fmt"
)
func longestPalindrome(s string) string {
if len(s) < 2 {
return s
}
start, maxLen := 0, 1
for i := 0; i < len(s); i++ {
expandAroundCenter(s, i, i, &start, &maxLen)
expandAroundCenter(s, i, i+1, &start, &maxLen)
}
return s[start : start+maxLen]
}
func expandAroundCenter(s string, left, right int, start, maxLen *int) {
for left >= 0 && right < len(s) && s[left] == s[right] {
left--
right++
}
if right-left-1 > *maxLen {
*start = left + 1
*maxLen = right - left - 1
}
}
func main() {
s := "babad"
fmt.Println(longestPalindrome(s)) // Output: "bab" or "aba"
}
40. String Compression
Explanation: Write a function to perform basic string compression using the counts of repeated characters.
Source Code:
package main
import (
"fmt"
"strconv"
)
func compressString(s string) string {
if len(s) == 0 {
return ""
}
compressed := ""
count := 1
for i := 1; i < len(s); i++ {
if s[i] == s[i-1] {
count++
} else {
compressed += string(s[i-1]) + strconv.Itoa(count)
count = 1
}
}
compressed += string(s[len(s)-1]) + strconv.Itoa(count)
if len(compressed) >= len(s) {
return s
}
return compressed
}
func main() {
s := "aabcccccaaa"
fmt.Println(compressString(s)) // Output: "a2b1c5a3"
}
41. Isomorphic Strings
Explanation: Write a function to determine if two strings are isomorphic.
Source Code:
package main
import (
"fmt"
)
func isIsomorphic(s string, t string) bool {
if len(s) != len(t) {
return false
}
mappingS := make(map[byte]byte)
mappingT := make(map[byte]byte)
for i := 0; i < len(s); i++ {
charS, charT := s[i], t[i]
if mappingS[charS] > 0 && mappingS[charS] != charT {
return false
}
if mappingT[charT] > 0 && mappingT[charT] != charS {
return false
}
mappingS[charS] = charT
mappingT[charT] = charS
}
return true
}
func main() {
s := "egg"
t := "add"
fmt.Println(isIsomorphic(s, t)) // Output: true
}
42. Word Ladder
Explanation: Write a function to find the shortest transformation sequence from one word to another.
Source Code:
package main
import (
"fmt"
)
func ladderLength(beginWord string, endWord string, wordList []string) int {
wordSet := make(map[string]bool)
for _, word := range wordList {
wordSet[word] = true
}
if !wordSet[endWord] {
return 0
}
queue := []string{beginWord}
steps := 1
for len(queue) > 0 {
for i := len(queue); i > 0; i-- {
word := queue[0]
queue = queue[1:]
if word == endWord {
return steps
}
for j := 0; j < len(word); j++ {
for k := 'a'; k <= 'z'; k++ {
newWord := word[:j] + string(k) + word[j+1:]
if wordSet[newWord] {
queue = append(queue, newWord)
delete(wordSet, newWord)
}
}
}
}
steps++
}
return 0
}
func main() {
beginWord := "hit"
endWord := "cog"
wordList := []string{"hot", "dot", "dog", "lot", "log", "cog"}
fmt.Println(ladderLength(beginWord, endWord, wordList)) // Output: 5
}
43. Valid Anagram
Explanation: Write a function to determine if two strings are anagrams of each other.
Source Code:
package main
import (
"fmt"
)
func isAnagram(s string, t string) bool {
if len(s) != len(t) {
return false
}
count := make(map[rune]int)
for _, char := range s {
count[char]++
}
for _, char := range t {
count[char]--
if count[char] < 0 {
return false
}
}
return true
}
func main() {
s := "anagram"
t := "nagaram"
fmt.Println(isAnagram(s, t)) // Output: true
}
Miscellaneous
44. LRU Cache
Explanation: Implement an LRU (Least Recently Used) cache.
Source Code:
package main
import (
"container/list"
"fmt"
)
type LRUCache struct {
capacity int
cache map[int]*list.Element
list *list.List
}
type Pair struct {
key, value int
}
func Constructor(capacity int) LRUCache {
return LRUCache{
capacity: capacity,
cache: make(map[int]*list.Element),
list: list.New(),
}
}
func (c *LRUCache) Get(key int) int {
if node, ok := c.cache[key]; ok {
c.list.MoveToFront(node)
return node.Value.(Pair).value
}
return -1
}
func (c *LRUCache) Put(key, value int) {
if node, ok := c.cache[key]; ok {
c.list.MoveToFront(node)
node.Value = Pair{key, value}
} else {
if c.list.Len() == c.capacity {
node := c.list.Back()
c.list.Remove(node)
delete(c.cache, node.Value.(Pair).key)
}
node := c.list.PushFront(Pair{key, value})
c.cache[key] = node
}
}
func main() {
cache := Constructor(2)
cache.Put(1, 1)
cache.Put(2, 2)
fmt.Println(cache.Get(1)) // Output: 1
cache.Put(3, 3)
fmt.Println(cache.Get(2)) // Output: -1
cache.Put(4, 4)
fmt.Println(cache.Get(1)) // Output: -1
fmt.Println(cache.Get(3)) // Output: 3
fmt.Println(cache.Get(4)) // Output: 4
}
45. Design a Hit Counter
Explanation: Design a hit counter that counts the number of hits received in the past 5 minutes.
Source Code:
package main
import (
"fmt"
"time"
)
type HitCounter struct {
timestamps []int
}
func Constructor() HitCounter {
return HitCounter{}
}
func (c *HitCounter) Hit(timestamp int) {
c.timestamps = append(c.timestamps, timestamp)
}
func (c *HitCounter) GetHits(timestamp int) int {
startTime := timestamp - 300
for len(c.timestamps) > 0 && c.timestamps[0] <= startTime {
c.timestamps = c.timestamps[1:]
}
return len(c.timestamps)
}
func main() {
counter := Constructor()
now := int(time.Now().Unix())
counter.Hit(now)
counter.Hit(now + 1)
counter.Hit(now + 300)
counter.Hit(now + 301)
fmt.Println(counter.GetHits(now + 301)) // Output: 3
}
46. Implement Trie (Prefix Tree)
Explanation: Implement a trie for storing and searching a set of strings.
Source Code:
package main
import (
"fmt"
)
type TrieNode struct {
children map[rune]*TrieNode
isEnd bool
}
type Trie struct {
root *TrieNode
}
func Constructor() Trie {
return Trie{root: &TrieNode{children: make(map[rune]*TrieNode)}}
}
func (t *Trie) Insert(word string) {
node := t.root
for _, char := range word {
if _, ok := node.children[char]; !ok {
node.children[char] = &TrieNode{children: make(map[rune]*TrieNode)}
}
node = node.children[char]
}
node.isEnd = true
}
func (t *Trie) Search(word string) bool {
node := t.root
for _, char := range word {
if _, ok := node.children[char]; !ok {
return false
}
node = node.children[char]
}
return node.isEnd
}
func (t *Trie) StartsWith(prefix string) bool {
node := t.root
for _, char := range prefix {
if _, ok := node.children[char]; !ok {
return false
}
node = node.children[char]
}
return true
}
func main() {
trie := Constructor()
trie.Insert("apple")
fmt.Println(trie.Search("apple")) // Output: true
fmt.Println(trie.Search("app")) // Output: false
fmt.Println(trie.StartsWith("app")) // Output: true
trie.Insert("app")
fmt.Println(trie.Search("app")) // Output: true
}
47. Design Tic-Tac-Toe
Explanation: Design a Tic-Tac-Toe game that is played between two players on a 3x3 grid.
Source Code:
package main
import (
"fmt"
)
type TicTacToe struct {
board [][]int
}
func Constructor(n int) TicTacToe {
board := make([][]int, n)
for i := range board {
board[i] = make([]int, n)
}
return TicTacToe{board: board}
}
func (this *TicTacToe) Move(row int, col int, player int) int {
this.board[row][col] = player
if this.checkWin(row, col, player) {
return player
}
return 0
}
func (this *TicTacToe) checkWin(row int, col int, player int) bool {
n := len(this.board)
rowWin, colWin, diagWin, antiDiagWin := true, true, true, true
for i := 0; i < n; i++ {
if this.board[row][i] != player {
rowWin = false
}
if this.board[i][col] != player {
colWin = false
}
if this.board[i][i] != player {
diagWin = false
}
if this.board[i][n-i-1] != player {
antiDiagWin = false
}
}
return rowWin || colWin || diagWin || antiDiagWin
}
func main() {
game := Constructor(3)
fmt.Println(game.Move(0, 0, 1)) // Output: 0
fmt.Println(game.Move(0, 2, 2)) // Output: 0
fmt.Println(game.Move(2, 2, 1)) // Output: 0
fmt.Println(game.Move(1, 1, 2)) // Output: 0
fmt.Println(game.Move(2, 0, 1)) // Output: 0
fmt.Println(game.Move(1, 0, 2)) // Output: 0
fmt.Println(game.Move(2, 1, 1)) // Output: 1 (player 1 wins)
}
48. Implement Skiplist
Explanation: Implement a skiplist for efficient searching.
Source Code:
package main
import (
"fmt"
"math/rand"
)
const MaxLevel = 16
type SkiplistNode struct {
val int
next []*SkiplistNode
}
type Skiplist struct {
head *SkiplistNode
level int
}
func Constructor() Skiplist {
return Skiplist{head: &SkiplistNode{next: make([]*SkiplistNode, MaxLevel)}, level: 1}
}
func (s *Skiplist) Search(target int) bool {
curr := s.head
for i := s.level - 1; i >= 0; i-- {
for curr.next[i] != nil && curr.next[i].val < target {
curr = curr.next[i]
}
}
curr = curr.next[0]
return curr != nil && curr.val == target
}
func (s *Skiplist) Add(num int) {
update := make([]*SkiplistNode, MaxLevel)
curr := s.head
for i := s.level - 1; i >= 0; i-- {
for curr.next[i] != nil && curr.next[i].val < num {
curr = curr.next[i]
}
update[i] = curr
}
level := randomLevel()
if level > s.level {
for i := s.level; i < level; i++ {
update[i] = s.head
}
s.level = level
}
newNode := &SkiplistNode{val: num, next: make([]*SkiplistNode, level)}
for i := 0; i < level; i++ {
newNode.next[i] = update[i].next[i]
update[i].next[i] = newNode
}
}
func (s *Skiplist) Erase(num int) bool {
update := make([]*SkiplistNode, MaxLevel)
curr := s.head
for i := s.level - 1; i >= 0; i-- {
for curr.next[i] != nil && curr.next[i].val < num {
curr = curr.next[i]
}
update[i] = curr
}
curr = curr.next[0]
if curr == nil || curr.val != num {
return false
}
for i := 0; i < s.level; i++ {
if update[i].next[i] != curr {
break
}
update[i].next[i] = curr.next[i]
}
for s.level > 1 && s.head.next[s.level-1] == nil {
s.level--
}
return true
}
func randomLevel() int {
level := 1
for rand.Float32() < 0.5 && level < MaxLevel {
level++
}
return level
}
func main() {
skiplist := Constructor()
skiplist.Add(1)
skiplist.Add(2)
skiplist.Add(3)
fmt.Println(skiplist.Search(0)) // Output: false
skiplist.Add(4)
fmt.Println(skiplist.Search(1)) // Output: true
fmt.Println(skiplist.Erase(0)) // Output: false
fmt.Println(skiplist.Erase(1)) // Output: true
fmt.Println(skiplist.Search(1)) // Output: false
}
49. LFU Cache
Explanation: Implement an LFU (Least Frequently Used) cache.
Source Code:
package main
import (
"container/heap"
"fmt"
)
type LFUCache struct {
capacity int
lookup map[int]*Node
freqHeap *FrequencyHeap
}
type Node struct {
key int
value int
frequency int
index int
}
type FrequencyHeap []*Node
func (h FrequencyHeap) Len() int { return len(h) }
func (h FrequencyHeap) Less(i, j int) bool { return h[i].frequency < h[j].frequency }
func (h FrequencyHeap) Swap(i, j int) {
h[i], h[j] = h[j], h[i]
h[i].index, h[j].index = i, j
}
func (h *FrequencyHeap) Push(x interface{}) {
n := len(*h)
node := x.(*Node)
node.index = n
*h = append(*h, node)
}
func (h *FrequencyHeap) Pop() interface{} {
old := *h
n := len(old)
node := old[n-1]
node.index = -1
*h = old[0 : n-1]
return node
}
func (h *FrequencyHeap) update(node *Node) {
heap.Fix(h, node.index)
}
func Constructor(capacity int) LFUCache {
h := &FrequencyHeap{}
heap.Init(h)
return LFUCache{
capacity: capacity,
lookup: make(map[int]*Node),
freqHeap: h,
}
}
func (c *LFUCache) Get(key int) int {
if node, ok := c.lookup[key]; ok {
node.frequency++
c.freqHeap.update(node)
return node.value
}
return -1
}
func (c *LFUCache) Put(key, value int) {
if c.capacity == 0 {
return
}
if node, ok := c.lookup[key]; ok {
node.value = value
node.frequency++
c.freqHeap.update(node)
return
}
if len(c.lookup) == c.capacity {
node := heap.Pop(c.freqHeap).(*Node)
delete(c.lookup, node.key)
}
node := &Node{key: key, value: value, frequency: 1}
c.lookup[key] = node
heap.Push(c.freqHeap, node)
}
func main() {
cache := Constructor(2)
cache.Put(1, 1)
cache.Put(2, 2)
fmt.Println(cache.Get(1)) // Output: 1
cache.Put(3, 3)
fmt.Println(cache.Get(2)) // Output: -1
fmt.Println(cache.Get(3)) // Output: 3
cache.Put(4, 4)
fmt.Println(cache.Get(1)) // Output: -1
fmt.Println(cache.Get(3)) // Output: 3
fmt.Println(cache.Get(4)) // Output: 4
}
50. Maximal Square
Explanation: Write a function to find the largest square containing only 1's in a binary matrix.
Source Code:
package main
import (
"fmt"
)
func maximalSquare(matrix [][]byte) int {
if len(matrix) == 0 {
return 0
}
rows, cols := len(matrix), len(matrix[0])
maxSide := 0
dp := make([][]int, rows)
for i := range dp {
dp[i] = make([]int, cols)
}
for i := 0; i < rows; i++ {
for j := 0; j < cols; j++ {
if matrix[i][j] == '1' {
if i == 0 || j == 0 {
dp[i][j] = 1
} else {
dp[i][j] = min(dp[i-1][j-1], dp[i-1][j], dp[i][j-1]) + 1
}
if dp[i][j] > maxSide {
maxSide = dp[i][j]
}
}
}
}
return maxSide * maxSide
}
func min(a, b, c int) int {
if a < b && a < c {
return a
} else if b < c {
return b
}
return c
}
func main() {
matrix := [][]byte{
{'1', '0', '1', '0', '0'},
{'1', '0', '1', '1', '1'},
{'1', '1', '1', '1', '1'},
{'1', '0', '0', '1', '0'},
}
fmt.Println(maximalSquare(matrix)) // Output: 4
}
These exercises provide a comprehensive set of challenges to help practice and improve your skills in data structures and algorithms using Go.