通过示例学习-Go-语言-2023-二十一-
通过示例学习 Go 语言 2023(二十一)
Go 语言中的最长递增子序列程序
来源:
golangbyexample.com/longest-increasing-subsequence-golang/
目录
-
概述
-
程序
概述
目标是找到给定输入数组中的最长递增子序列。它是给定序列中最长的子序列,使得每个元素都大于其前一个元素。
例如
Input: [1,5,7,6]
The longest subsequence is [1,5,6] which is of length 3
Output: 3
另一个例子
Input: [3,2,1]
The longest subsequence is either {3}, {2} or {1}. Each is of length 1
Output: 1
最长递增子序列是一个动态规划问题。假设输入数组仅命名为 input。假设 lis 是一个数组,其中 lis[i] 是索引 i 处的最长递增子序列的长度。
然后
-
lis[0] = 1
-
lis[i] = max(lis[j]) + 1 其中 0 <= j < i 且 input[i] > input[j]
-
lis[i] = 1 如果没有这样的 j
程序
这里是相同程序的代码。
package main
import "fmt"
func lengthOfLIS(nums []int) int {
lenNums := len(nums)
lis := make([]int, lenNums)
for i := 0; i < lenNums; i++ {
lis[i] = 1
}
for i := 1; i < lenNums; i++ {
for j := 0; j < i; j++ {
if nums[i] > nums[j] && lis[i] < (lis[j]+1) {
lis[i] = lis[j] + 1
}
}
}
max := 0
for i := 0; i < lenNums; i++ {
if lis[i] > max {
max = lis[i]
}
}
return max
}
func main() {
output := lengthOfLIS([]int{1, 5, 7, 6})
fmt.Println(output)
output = lengthOfLIS([]int{3, 2, 1})
fmt.Println(output)
}
输出
3
1
注意: 请查看我们的 Golang 高级教程。本系列教程内容详尽,我们试图通过示例覆盖所有概念。本教程适合那些希望获得 Golang 专业知识和扎实理解的人 – Golang 高级教程
如果你有兴趣了解所有设计模式如何在 Golang 中实现。如果是的话,这篇文章就是为你准备的 – 所有设计模式 Golang
Go 语言中的字符串最长回文子串
来源:
golangbyexample.com/longest-palindromic-substring-go/
目录
-
概述
-
程序
概述
目标是在字符串中找到最大的回文子串。例如,假设输入字符串为
cabae
输出应该是aba,它是最大的回文子串。
我们将使用动态编程来解决这个问题。我们将使用两个矩阵。每个矩阵的大小为len*len,其中len是输入字符串的大小。
-
第一个矩阵将存储索引“i”和“j”之间的子串是否为回文。
-
第二个矩阵将存储“i”和“j”之间的最长回文子串。
以下是最优子结构。
- 如果string[i] == string[j]且i+1到j-1的子串是回文,则
len(i,j) = 2 + len(i+1, j-1)
- 如果string[i] != string[j],那么
len(i,j) = max(len(i+1,j), len(i,j-1)
程序
以下是相应的程序。
package main
import (
"fmt"
"math"
"unicode/utf8"
)
func main() {
output := longestPalindrome("cabae")
fmt.Println(output)
}
func longestPalindrome(s string) string {
stringLength := utf8.RuneCountInString(s)
isPalindromeMatrix := make([][]int, stringLength)
for i := range isPalindromeMatrix {
isPalindromeMatrix[i] = make([]int, stringLength)
}
for i, outer := range isPalindromeMatrix {
for j := range outer {
if i == j {
isPalindromeMatrix[i][j] = 1
}
}
}
palindromeLengthMatrix := make([][]int, stringLength)
for i := range palindromeLengthMatrix {
palindromeLengthMatrix[i] = make([]int, stringLength)
}
for i, outer := range palindromeLengthMatrix {
for j := range outer {
if i == j {
palindromeLengthMatrix[i][j] = 1
}
}
}
for len := 2; len <= stringLength; len++ {
for i := 0; i <= stringLength-len; i++ {
j := i + len - 1
if s[i] == s[j] {
if len == 2 {
isPalindromeMatrix[i][j] = 1
palindromeLengthMatrix[i][j] = 2
} else {
if isPalindromeMatrix[i+1][j-1] == 1 {
isPalindromeMatrix[i][j] = 1
palindromeLengthMatrix[i][j] = 2 + palindromeLengthMatrix[i+1][j-1]
} else {
isPalindromeMatrix[i][j] = -1
palindromeLengthMatrix[i][j] = int(math.Max(float64(palindromeLengthMatrix[i+1][j]), float64(palindromeLengthMatrix[i][j-1])))
}
}
} else {
isPalindromeMatrix[i][j] = -1
}
}
}
max_row_index := 0
max_column_index := 0
max := 0
for i, outer := range palindromeLengthMatrix {
for j := range outer {
if palindromeLengthMatrix[i][j] > max && isPalindromeMatrix[i][j] == 1 {
max = palindromeLengthMatrix[i][j]
max_row_index = i
max_column_index = j
}
}
}
return s[max_row_index : max_column_index+1]
}
输出
aba
```*
<!--yml
分类:未分类
日期:2024-10-13 06:41:56
-->
# 在 Go (Golang)中找到最长无重复字符的子字符串程序。
> 来源:[`golangbyexample.com/longest-substring-without-repeating-characters-golang/`](https://golangbyexample.com/longest-substring-without-repeating-characters-golang/)
目录
+ 概述
+ 程序
## **概述**
给定一个字符串,我们必须找到其中最长的无重复字符的子字符串。例如,如果字符串是
```go
abbabcda
那么答案将是“abcd”,长度应为 4。
我们使用哈希表和三个变量。
-
哈希表跟踪任何字符的最后索引位置。
-
longestSubstringLength – 这个变量存储到目前为止最长的无重复字符的子字符串长度。
-
currentSubstringLength – 这个变量存储无重复字符的当前子字符串长度。
-
start – 这表示当前无重复字符子字符串的起始位置。
我们遍历字符串,并检查当前字符的哈希表。在以下两个条件下,我们简单地增加currentSubstringLength。
-
如果当前字符的条目不存在,则说明当前字符之前未出现过。
-
如果条目存在并且当前字符之前已出现,但不属于当前长度。
否则
- 我们重置起始位置和currentSubstringLength以将当前字符包括在当前长度中。在重置之前,我们检查currentSubstringLength是否大于longestSubstringLength。如果是,则将longestSubstringLength设置为currentSubstringLength。
让我们看一下相应的程序。
程序
package main
import "fmt"
func main() {
len := lengthOfLongestSubstring("abbabcda")
fmt.Println(len)
}
func lengthOfLongestSubstring(s string) int {
charLastIndex := make(map[rune]int)
longestSubstringLength := 0
currentSubstringLength := 0
start := 0
for index, character := range s {
lastIndex, ok := charLastIndex[character]
if !ok || lastIndex < index-currentSubstringLength {
currentSubstringLength++
} else {
if currentSubstringLength > longestSubstringLength {
longestSubstringLength = currentSubstringLength
}
start = lastIndex + 1
currentSubstringLength = index - start + 1
}
charLastIndex[character] = index
}
if currentSubstringLength > longestSubstringLength {
longestSubstringLength = currentSubstringLength
}
return longestSubstringLength
}
输出
4
```*
<!--yml
类别:未分类
日期:2024-10-13 06:43:24
-->
# 在 Go 语言中查找字符串内最长有效括号子串
> 来源:[`golangbyexample.com/longest-valid-parentheses-substring-go/`](https://golangbyexample.com/longest-valid-parentheses-substring-go/)
目录
+ 概述
+ 程序
## **概述**
目标是找到字符串内最长有效括号子串的长度
例如
```go
Input: ()(()
Output: 2
Input: ()()())
Output: 6
程序
package main
import (
"fmt"
"sync"
)
func main() {
valid := longestValidParentheses("()(()")
fmt.Println(valid)
valid = longestValidParentheses("()()())")
fmt.Println(valid)
valid = longestValidParentheses("(()")
fmt.Println(valid)
valid = longestValidParentheses("(()(()")
fmt.Println(valid)
valid = longestValidParentheses("()()")
fmt.Println(valid)
valid = longestValidParentheses("(()()")
fmt.Println(valid)
valid = longestValidParentheses("((()))(()")
fmt.Println(valid)
valid = longestValidParentheses(")(((((()())()()))()(()))(")
fmt.Println(valid)
valid = longestValidParentheses("")
fmt.Println(valid)
}
type customStack struct {
stack []int
lock sync.RWMutex
}
func (c *customStack) Push(name int) {
c.lock.Lock()
defer c.lock.Unlock()
c.stack = append(c.stack, name)
}
func (c *customStack) Pop() (int, error) {
len := len(c.stack)
if len > 0 {
c.lock.Lock()
defer c.lock.Unlock()
val := c.stack[len-1]
c.stack = c.stack[:len-1]
return val, nil
}
return 0, fmt.Errorf("Pop Error: Stack is empty")
}
func (c *customStack) Front() (int, error) {
len := len(c.stack)
if len > 0 {
c.lock.Lock()
defer c.lock.Unlock()
return c.stack[len-1], nil
}
return 0, fmt.Errorf("Peep Error: Stack is empty")
}
func (c *customStack) Size() int {
return len(c.stack)
}
func (c *customStack) Empty() bool {
return len(c.stack) == 0
}
func (c *customStack) Flush() {
c.stack = []int{}
}
func longestValidParentheses(s string) int {
customStack := &customStack{
stack: []int{},
}
longestValidParenthesisLength := 0
currentValidParenthesisLength := 0
customStack.Push(-1)
for i, val := range s {
if val == '(' {
customStack.Push(i)
} else if val == ')' {
customStack.Pop()
if customStack.Size() == 0 {
customStack.Push(i)
} else {
index, _ := customStack.Front()
currentValidParenthesisLength = i - index
if currentValidParenthesisLength > longestValidParenthesisLength {
longestValidParenthesisLength = currentValidParenthesisLength
}
}
}
}
if currentValidParenthesisLength > longestValidParenthesisLength {
longestValidParenthesisLength = currentValidParenthesisLength
}
return longestValidParenthesisLength
}
输出
2
6
2
2
4
4
6
22
0
注意: 请查看我们的 Golang 高级教程。本系列的教程内容详尽,力求用示例覆盖所有概念。本教程适合那些希望获得 Golang 专业知识和扎实理解的人 – Golang 高级教程
如果你有兴趣了解如何在 Golang 中实现所有设计模式。如果是的话,这篇文章就是为你准备的 – 所有设计模式 Golang
Go (Golang)中的 LRU 缓存实现。
来源:
golangbyexample.com/lru-cache-implementation-golang/
目录。
概述
+ 实现细节
+ Set(key int, value int)")
+ Get(key int) ")
-
程序
-
结论
概述
目标是实现一个缓存。
-
它应该支持Set和Get操作。
-
Set和Get的时间复杂度为 O(1)。
-
假设缓存的最大容量为 3。当缓存已满并且需要插入一个新键时,则需要从缓存中删除现有条目之一。
-
删除应基于驱逐算法——LRU。
实现细节
-
我们将使用映射和双向链表来存储所有内容。使用映射和双向链表的目的是确保即使在驱逐情况下,get和set操作也是 O(1)。
-
映射的键为字符串类型,值为指向双向链表中节点的指针类型。
-
双向链表的每个节点将包含键和值。每个节点还将有一个指向双向链表中前一个节点的指针和一个指向下一个节点的指针。
让我们看看Get和Set如何在 O(1)时间内工作。
Set(key int, value int)
对于任何 set 操作,它将首先创建一个包含给定键和值的双向链表节点。然后在映射中以输入键作为键,节点地址作为值进行条目插入。一旦节点创建,就有两种情况。
-
缓存未满——在这种情况下,它将控制权传递给当前的驱逐算法 LRU。LRU 算法将在双向链表的末尾插入该节点。前面的节点是最近最少使用的节点。这里每个操作都是 O(1)。
-
缓存已满——在这种情况下,它将控制权传递给当前的驱逐算法 LRU。它将驱逐前面的最近最少使用的节点。一旦该节点被驱逐,就会在末尾插入新节点。这里每个操作都是 O(1)。
Get(key int)
对于任何 Get 操作,它首先检查映射中给定的键是否存在。如果存在,则获取映射中该键指向的节点的地址。然后从节点中获取值。接下来,它将控制权传递给当前的驱逐算法 LRU。LRU 算法将在双向链表的末尾移动当前节点。前面的节点是最近最少使用的节点,保持该节点在末尾。这里每个操作都是 O(1)。
程序
这是用 Go 编程语言编写的完整工作代码,供有兴趣的人参考。
doublylinklist.go
package main
import "fmt"
type node struct {
key string
value string
prev *node
next *node
}
type doublyLinkedList struct {
len int
tail *node
head *node
}
func initDoublyList() *doublyLinkedList {
return &doublyLinkedList{}
}
func (d *doublyLinkedList) AddToFront(key, value string) {
newNode := &node{
key: key,
value: value,
}
if d.head == nil {
d.head = newNode
d.tail = newNode
} else {
newNode.next = d.head
d.head.prev = newNode
d.head = newNode
}
d.len++
return
}
func (d *doublyLinkedList) RemoveFromFront() {
if d.head == nil {
return
} else if d.head == d.tail {
d.head = nil
d.tail = nil
} else {
d.head = d.head.next
}
d.len--
}
func (d *doublyLinkedList) AddToEnd(node *node) {
newNode := node
if d.head == nil {
d.head = newNode
d.tail = newNode
} else {
currentNode := d.head
for currentNode.next != nil {
currentNode = currentNode.next
}
newNode.prev = currentNode
currentNode.next = newNode
d.tail = newNode
}
d.len++
}
func (d *doublyLinkedList) Front() *node {
return d.head
}
func (d *doublyLinkedList) MoveNodeToEnd(node *node) {
prev := node.prev
next := node.next
if prev != nil {
prev.next = next
}
if next != nil {
next.prev = prev
}
if d.tail == node {
d.tail = prev
}
if d.head == node {
d.head = next
}
node.next = nil
node.prev = nil
d.len--
d.AddToEnd(node)
}
func (d *doublyLinkedList) TraverseForward() error {
if d.head == nil {
return fmt.Errorf("TraverseError: List is empty")
}
temp := d.head
for temp != nil {
fmt.Printf("key = %v, value = %v, prev = %v, next = %v\n", temp.key, temp.value, temp.prev, temp.next)
temp = temp.next
}
fmt.Println()
return nil
}
func (d *doublyLinkedList) Size() int {
return d.len
}
evictionAlgorithm.go
package main
type evictionAlgo interface {
evict(c *Cache) string
get(node *node, c *Cache)
set(node *node, c *Cache)
set_overwrite(node *node, value string, c *Cache)
}
func createEvictioAlgo(algoType string) evictionAlgo {
if algoType == "fifo" {
return &fifo{}
} else if algoType == "lru" {
return &lru{}
}
return nil
}
lru.go
package main
import "fmt"
type lru struct {
}
func (l *lru) evict(c *Cache) string {
key := c.doublyLinkedList.Front().key
fmt.Printf("Evicting by lru strtegy. Evicted Node Key: %s: ", key)
c.doublyLinkedList.RemoveFromFront()
return key
}
func (l *lru) get(node *node, c *Cache) {
fmt.Println("Shuffling doubly linked list due to get operation")
c.doublyLinkedList.MoveNodeToEnd(node)
}
func (l *lru) set(node *node, c *Cache) {
fmt.Println("Shuffling doubly linked list due to set operation")
c.doublyLinkedList.AddToEnd(node)
}
func (l *lru) set_overwrite(node *node, value string, c *Cache) {
fmt.Println("Shuffling doubly linked list due to set_overwrite operation")
node.value = value
c.doublyLinkedList.MoveNodeToEnd(node)
}
fifo.go
package main
import "fmt"
type fifo struct {
}
func (l *fifo) evict(c *Cache) string {
fmt.Println("Evicting by fifo strtegy")
key := c.doublyLinkedList.Front().key
c.doublyLinkedList.RemoveFromFront()
return key
}
func (l *fifo) get(node *node, c *Cache) {
fmt.Println("Shuffling doubly linked list due to get operation")
}
func (l *fifo) set(node *node, c *Cache) {
fmt.Println("Shuffling doubly linked list due to set operation")
c.doublyLinkedList.AddToEnd(node)
}
func (l *fifo) set_overwrite(node *node, value string, c *Cache) {
fmt.Println("Shuffling doubly linked list due to set_overwrite operation")
}
cache.go
package main
import "fmt"
type Cache struct {
doublyLinkedList *doublyLinkedList
storage map[string]*node
evictionAlgo evictionAlgo
capacity int
maxCapacity int
}
func initCache(evictionAlgo evictionAlgo, maxCapacity int) Cache {
storage := make(map[string]*node)
return Cache{
doublyLinkedList: &doublyLinkedList{},
storage: storage,
evictionAlgo: evictionAlgo,
capacity: 0,
maxCapacity: maxCapacity,
}
}
func (this *Cache) setEvictionAlgo(e evictionAlgo) {
this.evictionAlgo = e
}
func (this *Cache) set(key, value string) {
node_ptr, ok := this.storage[key]
if ok {
this.evictionAlgo.set_overwrite(node_ptr, value, this)
return
}
if this.capacity == this.maxCapacity {
evictedKey := this.evict()
delete(this.storage, evictedKey)
}
node := &node{key: key, value: value}
this.storage[key] = node
this.evictionAlgo.set(node, this)
this.capacity++
}
func (this *Cache) get(key string) string {
node_ptr, ok := this.storage[key]
if ok {
this.evictionAlgo.get(node_ptr, this)
return (*node_ptr).value
}
return ""
}
func (this *Cache) evict() string {
key := this.evictionAlgo.evict(this)
this.capacity--
return key
}
func (this *Cache) print() {
for k, v := range this.storage {
fmt.Printf("key :%s value: %s\n", k, (*v).value)
}
this.doublyLinkedList.TraverseForward()
}
main.go
package main
import "fmt"
func main() {
lru := createEvictioAlgo("lru")
cache := initCache(lru, 3)
cache.set("a", "1")
cache.print()
cache.set("b", "2")
cache.print()
cache.set("c", "3")
cache.print()
value := cache.get("a")
fmt.Printf("key: a, value: %s\n", value)
cache.print()
cache.set("d", "4")
cache.print()
cache.set("e", "5")
cache.print()
}
输出
Shuffling doubly linked list due to set operation
key :a value: 1
key = a, value = 1, prev = <nil>, next = <nil>Shuffling doubly linked list due to set operation
key :a value: 1
key :b value: 2
key = a, value = 1, prev = <nil>, next = &{b 2 0xc00007e1e0 <nil>}
key = b, value = 2, prev = &{a 1 <nil>0xc00007e210}, next = <nil>Shuffling doubly linked list due to set operation
key :a value: 1
key :b value: 2
key :c value: 3
key = a, value = 1, prev = <nil>, next = &{b 2 0xc00007e1e0 0xc00007e2a0}
key = b, value = 2, prev = &{a 1 <nil>0xc00007e210}, next = &{c 3 0xc00007e210 <nil>}
key = c, value = 3, prev = &{b 2 0xc00007e1e0 0xc00007e2a0}, next = <nil>Shuffling doubly linked list due to get operation
key: a, value: 1
key :a value: 1
key :b value: 2
key :c value: 3
key = b, value = 2, prev = <nil>, next = &{c 3 0xc00007e210 0xc00007e1e0}
key = c, value = 3, prev = &{b 2 <nil>0xc00007e2a0}, next = &{a 1 0xc00007e2a0 <nil>}
key = a, value = 1, prev = &{c 3 0xc00007e210 0xc00007e1e0}, next = <nil>Evicting by lru strtegy. Evicted Node Key: %s: b
Shuffling doubly linked list due to set operation
key :d value: 4
key :c value: 3
key :a value: 1
key = c, value = 3, prev = &{b 2 <nil>0xc00007e2a0}, next = &{a 1 0xc00007e2a0 0xc00007e450}
key = a, value = 1, prev = &{c 3 0xc00007e210 0xc00007e1e0}, next = &{d 4 0xc00007e1e0 <nil>}
key = d, value = 4, prev = &{a 1 0xc00007e2a0 0xc00007e450}, next = <nil>Evicting by lru strtegy. Evicted Node Key: %s: c
Shuffling doubly linked list due to set operation
key :a value: 1
key :d value: 4
key :e value: 5
key = a, value = 1, prev = &{c 3 0xc00007e210 0xc00007e1e0}, next = &{d 4 0xc00007e1e0 0xc00007e570}
key = d, value = 4, prev = &{a 1 0xc00007e2a0 0xc00007e450}, next = &{e 5 0xc00007e450 <nil>}
key = e, value = 5, prev = &{d 4 0xc00007e1e0 0xc00007e570}, next =</nil></nil></nil></nil></nil></nil></nil></nil></nil></nil></nil></nil></nil></nil></nil></nil></nil></nil>
结论
以上就是在 Golang 中实现 LRU 缓存的所有内容。希望你喜欢这篇文章。请在评论中分享反馈。
- lru*
在 Go (Golang)中找到数组中的多数元素。
来源:
golangbyexample.com/majority-element-array-golang/
。
目录
-
概述
-
程序
概述
目标是找到给定数组中的多数元素。多数元素是指在给定数组中出现次数超过 n/2 的元素,其中 n 是数组的长度。
示例 1
Input: [2, 1, 2, 2, 3]
Output: 2
示例 2
Input: [1]
Output: 1
程序
下面是相同内容的程序。
package main
import "fmt"
func majorityElement(nums []int) int {
lenNums := len(nums)
if lenNums == 1 {
return nums[0]
}
numsMap := make(map[int]int)
for i := 0; i < lenNums; i++ {
_, ok := numsMap[nums[i]]
if ok {
numsMap[nums[i]] = numsMap[nums[i]] + 1
if numsMap[nums[i]] > lenNums/2 {
return nums[i]
}
} else {
numsMap[nums[i]] = 1
}
}
return 0
}
func main() {
output := majorityElement([]int{2, 1, 2, 2, 3})
fmt.Println(output)
output = majorityElement([]int{1})
fmt.Println(output)
}
输出:
2
1
注意: 请查看我们的 Golang 进阶教程。本系列的教程内容详尽,我们尝试用例子覆盖所有概念。本教程适合那些希望获得专业知识和对 Golang 有扎实理解的人—— Golang 进阶教程。
如果你有兴趣了解如何在 Golang 中实现所有设计模式。如果是,那么这篇文章适合你—— 所有设计模式 Golang。
此外,请查看我们的系统设计教程系列—— 系统设计教程系列。
在 Go (Golang) 中复制文件
来源:
golangbyexample.com/copy-file-go/
在本文中,我们将看到两种复制文件的方法。
目录
** 第一种方法
- 第二种方法
第一种方法
io.Copy() 可用于将文件从源(src)复制到目标(dest)。成功复制时,将返回 err != nil 而非 err == EOF。
以下是该函数的签名。它返回写入目标的字节数。
func Copy(dst Writer, src Reader) (written int64, err error)
代码
首先创建一个名为 “original.txt” 的文件,并在其中写入一些内容。
package main
import (
"fmt"
"io"
"log"
"os"
)
// Copy a file
func main() {
// Open original file
original, err := os.Open("original.txt")
if err != nil {
log.Fatal(err)
}
defer original.Close()
// Create new file
new, err := os.Create("new.txt")
if err != nil {
log.Fatal(err)
}
defer new.Close()
//This will copy
bytesWritten, err := io.Copy(new, original)
if err != nil {
log.Fatal(err)
}
fmt.Printf("Bytes Written: %d\n", bytesWritten)
}
输出:
Bytes Written:
第二种方法
如果文件内容较少,我们也可以先读取文件内容,然后将所有内容写入新文件。见下面的代码。
在这里同样首先创建一个名为 “original.txt” 的文件,并在其中写入一些内容。
package main
import (
"io/ioutil"
"log"
)
// Copy a file
func main() {
//Read all the contents of the original file
bytesRead, err := ioutil.ReadFile("original.txt")
if err != nil {
log.Fatal(err)
}
//Copy all the contents to the desitination file
err = ioutil.WriteFile("new.txt", bytesRead, 0755)
if err != nil {
log.Fatal(err)
}
}
输出
new.txt file will have same content as original.txt
- 复制* 文件* go* golang*
从 go.mod 文件手动下载依赖项(Go)
来源:
golangbyexample.com/download-dependency-golang/
目录
-
概述
-
示例
概述
可以使用下面的命令下载 go.mod 文件中存在的依赖项。
go mod download
该命令用于在应用程序运行之前预下载所有依赖项。go build 和 go install 也将下载依赖项并构建二进制文件。go run 也将下载并运行二进制文件。go mod download 命令用于在不构建或运行的情况下预下载依赖项。
示例
让我们先创建一个模块。
go mod init learn
让我们也将直接依赖添加到 go.mod 文件中。
require github.com/pborman/uuid v1.2.1
有了这个依赖,go.mod 文件将如下所示。
module learn
go 1.14
require github.com/pborman/uuid v1.2.1
现在我们需要下载新添加的依赖。为此,我们可以使用下面的命令
go mod download
该命令将下载 github.com/pborman/uuid 模块以及它的所有依赖项。它还将更新 go.sum 文件,包含所有直接和间接依赖项的校验和和版本。现在让我们检查一下 go.sum 文件。
执行 cat **go.sum**
github.com/google/uuid v1.0.0 h1:b4Gk+7WdP/d3HZH8EJsZpvV7EtDOgaZLtnaNGIu1adA=
github.com/google/uuid v1.0.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/pborman/uuid v1.2.1 h1:+ZZIw58t/ozdjRaXh/3awHfmWRbzYxJoAdNJxe/3pvw=
github.com/pborman/uuid v1.2.1/go.mod h1:X/NO0urCmaxf9VXbdlT7C2Yzkj2IKimNn4k+gtPdI/k=
go.sum 文件列出了模块所需的直接和间接依赖项的校验和。github.com/google/uuid 被 github.com/pborman/uuid 内部使用。它是该模块的间接依赖,因此记录在 go.sum 文件中。
Go 语言中的映射
来源:
golangbyexample.com/maps-in-golang/
这是 Go 语言综合教程系列的第十九章。请参阅此链接获取系列的其他章节 – Go 语言综合教程系列
下一个教程 – 方法
上一个教程 – 切片
现在让我们看看当前的教程。以下是当前教程的目录。
目录
-
概述
-
映射中允许的键类型
-
映射中允许的值类型
-
创建映射
-
使用 map[<key_type>]<value_type> 格式
-
使用 Make
-
-
映射操作
-
添加键值对
-
更新键值对
-
获取与键对应的值
-
删除键值对
-
检查键是否存在
-
映射上的函数
-
-
零值
-
映射是引用数据类型
-
遍历映射
-
映射不安全用于并发使用
-
结论
概述
映射是 Go 语言内置的数据类型,类似于哈希表,用于将键映射到值。映射是一个无序集合,其中每个键是唯一的,而值可以对两个或多个不同的键相同。使用映射的优点是提供快速的检索、搜索、插入和删除操作。
映射是引用数据类型。当你将一个映射赋值给另一个映射时,它们都引用相同的基础映射。以下是映射的格式
map[key_type]value_type
key_type 和 value_type 可以是不同类型或相同类型。在以下示例中,键类型是 string,值类型是 int。
map[string]int
映射中允许的键类型
映射键可以是任何可比较的类型。根据 Go 规范,一些可比较的类型是
-
布尔型
-
数值型
-
字符串,
-
指针
-
通道
-
接口类型
-
结构体 – 如果所有字段类型是可比较的
-
数组 – 如果数组元素值的类型是可比较的
根据 Go 规范,一些不可比较的类型不能作为映射中的键。
-
切片。
-
地图。
-
函数。
参考 – golang.org/ref/spec#Comparison_operators
地图中的允许值类型
值可以是地图中的任何类型。
创建一个地图
-
使用 map[<key_type>]<value_type>{}格式也称为地图字面量。
-
使用 make。
让我们逐一看看上述每种方法。
使用 map[<key_type>]<value_type>格式
创建地图的最常见方法之一是使用地图字面量:
map[key_type]value_type{}
以上是一个示例,其中键类型为字符串,值类型为整数。
employeeSalary := map[string]int{}
地图也可以创建为一些已初始化的键值。
employeeSalary := map[string]int{
"John": 1000
"Sam": 2000
}
也可以向地图添加新的键值对。
employeeSalary["Tom"] = 2000
让我们看一个程序。
package main
import "fmt"
func main() {
//Declare
employeeSalary := map[string]int{}
fmt.Println(employeeSalary)
//Intialize using map lieteral
employeeSalary = map[string]int{
"John": 1000,
"Sam": 1200,
}
//Adding a key value
employeeSalary["Tom"] = 2000
fmt.Println(employeeSalary)
}
输出
map[]
map[John:1000 Sam:1200 Tom:2000]
在上面的程序中,我们创建了一个初始化为某些值的地图字面量。然后我们在其中添加了另一个键值对。接着我们使用 fmt.Println 打印它,以map[key:value key:value]格式打印所有的键值对。
地图也可以使用 var 关键字声明,但它默认创建一个 nil 地图,因为地图的零值是 nil。向该地图添加任何键值对将导致 panic。让我们看一个例子。
package main
func main() {
var employeeSalary map[string]int
employeeSalary["Tom"] = 2000
}
输出
panic: assignment to entry in nil map
上述程序因地图为 nil 而引发 panic。
使用var关键字声明地图的一个用例是,当需要将已有的地图赋值给它或当我们想要赋值函数的结果时。
使用 Make
这是另一种创建地图的方法。内置函数make可以用来创建地图。它返回一个已初始化的地图。因此,可以向其添加键值对。
package main
import "fmt"
func main() {
//Declare
employeeSalary := make(map[string]int)
//Adding a key value
employeeSalary["Tom"] = 2000
fmt.Println(employeeSalary)
}
输出
map[Tom:2000]
在上面的程序中,我们使用 make 函数创建了一个地图。然后我们在其中添加了一个键值对。接着我们使用fmt.Println打印它,打印所有的键值对。
地图操作
以下操作适用于地图。
-
添加一个键值对。
-
更新一个键。
-
获取与键对应的值。
-
删除一个键值对。
-
检查键是否存在。
添加一个键值对
以下是向地图添加键值对的格式。
mapName[key] = value
让我们看一个例子。
package main
import "fmt"
func main() {
//Declare
employeeSalary := make(map[string]int)
//Adding a key value
employeeSalary["Tom"] = 2000
fmt.Pr
输出
map[Tom:2000]
还要注意,向 nil 地图添加内容将导致 panic。
更新一个键值对
当尝试向已经存在的地图添加一个键时,新值将覆盖旧值。这类似于在地图中更新一个键。让我们看一个例子。
package main
import "fmt"
func main() {
//Declare
employeeSalary := make(map[string]int)
//Adding a key value
fmt.Println("Before update")
employeeSalary["Tom"] = 2000
fmt.Println(employeeSalary)
fmt.Println("After update")
employeeSalary["Tom"] = 3000
fmt.Println(employeeSalary)
}
输出
Before update
map[Tom:2000]
After update
map[Tom:3000]
在上面的程序中,写入相同的键“Tom”并赋新值3000时,将覆盖现有值2000。当我们再次打印地图时,打印的值为 3000。
获取与键对应的值
以下是检索与键对应的值的格式。
val := mapName[key]
让我们看一个程序。
package main
import "fmt"
func main() {
//Declare
employeeSalary := make(map[string]int)
//Adding a key value
employeeSalary["Tom"] = 2000
//Retrieve a value
salary := employeeSalary["Tom"]
fmt.Printf("Salary: %d", salary)
}
删除一个键值对
以下是删除与键对应的值的格式。
delete(map_name, key)
package main
import "fmt"
func main() {
//Declare
employeeSalary := make(map[string]int)
//Adding a key value
fmt.Println("Adding key")
employeeSalary["Tom"] = 2000
fmt.Println(employeeSalary)
fmt.Println("\nDeleting key")
delete(employeeSalary, "Tom")
fmt.Println(employeeSalary)
}
输出
Adding key
map[Tom:2000]
Deleting key
map[]
在上面的程序中,我们删除了键,当我们再次打印地图时,该键不存在。
检查键是否存在
以下是检查地图中是否存在某个键的格式。
val, ok := mapName[key]
有两种情况。
-
如果键存在,val变量将是地图中该键的值,ok变量将为 true。
-
如果键不存在,val变量将是值类型的默认零值,ok变量为 false
让我们看一个例子
package main
import "fmt"
func main() {
//Declare
employeeSalary := make(map[string]int)
//Adding a key value
employeeSalary["Tom"] = 2000
fmt.Println("Key exists case")
val, ok := employeeSalary["Tom"]
fmt.Printf("Val: %d, ok: %t\n", val, ok)
fmt.Println("Key doesn't exists case")
val, ok = employeeSalary["Sam"]
fmt.Printf("Val: %d, ok: %t\n", val, ok)
}
输出
Key exists case
Val: 2000, ok: true
Key doesn't exists case
Val: 0, ok: false
在上述程序中,当键存在时,val 变量被设置为实际值,这里是 2000,ok 变量为 true。当键不存在时,val变量被设置为 0,这是 int 类型的默认零值,ok变量为 false。这个ok变量是检查键是否存在于映射中的最佳方式。
如果我们只想检查一个键是否存在,而不需要值,则可以使用空标识符,即“_”来代替值。
_, ok = employeeSalary["Sam"]
映射上的函数
下面是可以在映射上使用的内置函数
- len()函数
len()函数
len()函数可以用于获取映射的长度,即映射中存在的键值对的数量。下面是使用此函数的格式。
len(mapName)
让我们看一个程序
package main
import "fmt"
func main() {
//Declare
employeeSalary := make(map[string]int)
//Adding a key value
employeeSalary["Tom"] = 2000
employeeSalary["Sam"] = 1200
lenOfMap := len(employeeSalary)
fmt.Println(lenOfMap)
}
输出
2
零值
映射的零值是 nil。当我们使用var关键字声明一个映射时,这一点也得到了证明。请参见下面的程序。
package main
import "fmt"
func main() {
var employeeSalary map[string]int
if employeeSalary == nil {
fmt.Println("employeeSalary map is nil")
}
}
输出
employeeSalary map is nil
映射是引用数据类型
映射是引用数据类型。因此,将一个映射分配给一个新变量时,这两个变量都引用同一个映射。一个映射的任何更改都会反映在另一个映射中,反之亦然。
package main
import "fmt"
func main() {
//Declare
employeeSalary := make(map[string]int)
//Adding a key value
employeeSalary["Tom"] = 2000
employeeSalary["Sam"] = 1200
eS := employeeSalary
//Change employeeSalary
employeeSalary["John"] = 3000
fmt.Println("Changing employeeSalary Map")
fmt.Printf("employeeSalary: %v\n", employeeSalary)
fmt.Printf("eS: %v\n", eS)
//Change eS
employeeSalary["John"] = 4000
fmt.Println("\nChanging eS Map")
fmt.Printf("employeeSalary: %v\n", employeeSalary)
fmt.Printf("eS: %v\n", eS)
}
在上述程序中,eS 是一个新映射变量,我们将现有的employeeSalary映射分配给它。
-
首先,我们在employeeSalary映射中添加一个新键。这个更改同时反映在employeeSalary和eS映射中
-
其次,我们更新了eS映射中的一个现有键。这个更改同样反映在employeeSalary和eS映射中。
这表明映射是引用数据类型
遍历映射
range 操作符可以用于在 Go 中遍历映射
首先让我们定义一个映射
sample := map[string]string{
"a": "x",
"b": "y",
}
- 迭代所有键和值
for k, v := range sample {
fmt.Printf("key :%s value: %s\n", k, v)
}
输出:
key :a value: x
key :b value: y
- 仅迭代键
for k := range sample {
fmt.Printf("key :%s\n", k)
}
输出:
key :a
key :b
- 仅迭代值
for _, v := range sample {
fmt.Printf("value :%s\n", v)
}
输出:
value :x
value :y
- 获取所有键的列表
keys := getAllKeys(sample)
fmt.Println(keys)
func getAllKeys(sample map[string]string) []string {
var keys []string
for k := range sample {
keys = append(keys, k)
}
return keys
}
输出:
[a b]
映射在并发使用时不安全
golang 映射在并发使用时是不安全的。
有错误的代码: 下面是一段有错误的代码。如果发生并发读取和写入映射,可能会导致崩溃。
package main
var (
allData = make(map[string]string)
)
func get(key string) string {
return allData[key]
}
func set(key string, value string) {
allData[key] = value
}
func main() {
go set("a", "Some Data 1")
go set("b", "Some Data 2")
go get("a")
go get("b")
go get("a")
}
可能的输出:
fatal error: concurrent map read and map write
正确的代码:
我们可以使用锁来允许并发访问映射
package main
import (
"fmt"
"sync"
)
var (
allData = make(map[string]string)
rwm sync.RWMutex
)
func get(key string) string {
rwm.RLock()
defer rwm.RUnlock()
return allData[key]
}
func set(key string, value string) {
rwm.Lock()
defer rwm.Unlock()
allData[key] = value
}
func main() {
set("a", "Some Data")
result := get("a")
fmt.Println(result)
}
输出
Some data
结论
这就是关于 golang 中映射的全部内容。我们学习了如何创建映射、映射上的操作、映射中定义的一些函数,如 Glen(),以及如何遍历映射,最后但同样重要的是,映射在并发使用时是不安全的。希望你喜欢这篇文章。请在评论中分享反馈/改进/错误。
下一篇教程 – 方法
上一篇教程 – 切片
Go(Golang)中两个数字的最大值
来源:
golangbyexample.com/max-of-two-numbers-go/
目录
-
概述
-
代码:
概述
GO 的math包提供了一个Max方法,可以用来获取两个数字的最大值。
以下是函数的签名。它接收两个浮点数作为输入,并返回一个浮点数。
func Max(x, y float64) float64
另外,Max函数的一些特殊情况是
-
Max(x, +Inf) = Max(+Inf, x) = +Inf
-
Max(x, NaN) = Max(NaN, x) = NaN
-
Max(+0, ±0) = Max(±0, +0) = +0
-
Max(-0, -0) = -0
代码:
package main
import (
"fmt"
"math"
)
func main() {
max := math.Max(2, 3)
fmt.Println(max)
max = math.Max(-2.1, -3.3)
fmt.Println(max)
}
输出:
3
-2.1
Golang 中的最大堆
来源:
golangbyexample.com/maxheap-in-golang/
目录
** 介绍
-
对最大堆的操作
-
实现
介绍
最大堆是一个完整的二叉树,其中父节点的值大于或等于其左子节点和右子节点的值。完整的二叉树是一个除了最后一层外,所有层都是满的二叉树。
我们使用数组来表示最大堆。根元素是 arr[0]。对于索引 i,我们有
-
左子节点 – 2*i + 1
-
右子节点 – 2*i + 2
以下是最大堆的表示

相应的数组将是 [8, 7, 6, 5, 3, 2]
对于索引 0,我们有
-
左子节点 – 2*0 + 1 = 1
-
右子节点 – 2*0 + 2 = 2
因此,arr[0] 即 8 的左子节点是 arr[1] 即 7,右子节点是 arr[2] 即 6
由于每个节点的值大于或等于其子节点的值,因此,根节点的值是最大的值。
对最大堆的操作
-
插入元素 – 需要 O(log n) 的时间。如果插入的值大于其父节点,则需要向上遍历。这个遍历会一直进行,直到插入的值小于其父节点或者插入的值成为根节点。第二种情况发生在插入的值是最大的。
-
移除最大元素 – 需要 O(log n) 的时间。它保存根值,然后用数组中的最后一个值替换根值。接着,它对根节点进行最大堆化,这需要 O(log n) 的时间,因为它向下遍历,直到值大于其父节点。
-
获取最大值 – 需要 O(1) 的时间。返回根值
实现
package main
import "fmt"
type maxheap struct {
heapArray []int
size int
maxsize int
}
func newMaxHeap(maxsize int) *maxheap {
maxheap := &maxheap{
heapArray: []int{},
size: 0,
maxsize: maxsize,
}
return maxheap
}
func (m *maxheap) leaf(index int) bool {
if index >= (m.size/2) && index <= m.size {
return true
}
return false
}
func (m *maxheap) parent(index int) int {
return (index - 1) / 2
}
func (m *maxheap) leftchild(index int) int {
return 2*index + 1
}
func (m *maxheap) rightchild(index int) int {
return 2*index + 2
}
func (m *maxheap) insert(item int) error {
if m.size >= m.maxsize {
return fmt.Errorf("Heap is full")
}
m.heapArray = append(m.heapArray, item)
m.size++
m.upHeapify(m.size - 1)
return nil
}
func (m *maxheap) swap(first, second int) {
temp := m.heapArray[first]
m.heapArray[first] = m.heapArray[second]
m.heapArray[second] = temp
}
func (m *maxheap) upHeapify(index int) {
for m.heapArray[index] > m.heapArray[m.parent(index)] {
m.swap(index, m.parent(index))
index = m.parent(index)
}
}
func (m *maxheap) downHeapify(current int) {
if m.leaf(current) {
return
}
largest := current
leftChildIndex := m.leftchild(current)
rightRightIndex := m.rightchild(current)
//If current is smallest then return
if leftChildIndex < m.size && m.heapArray[leftChildIndex] > m.heapArray[largest] {
largest = leftChildIndex
}
if rightRightIndex < m.size && m.heapArray[rightRightIndex] > m.heapArray[largest] {
largest = rightRightIndex
}
if largest != current {
m.swap(current, largest)
m.downHeapify(largest)
}
return
}
func (m *maxheap) buildMaxHeap() {
for index := ((m.size / 2) - 1); index >= 0; index-- {
m.downHeapify(index)
}
}
func (m *maxheap) remove() int {
top := m.heapArray[0]
m.heapArray[0] = m.heapArray[m.size-1]
m.heapArray = m.heapArray[:(m.size)-1]
m.size--
m.downHeapify(0)
return top
}
func main() {
inputArray := []int{6, 5, 3, 7, 2, 8}
maxHeap := newMaxHeap(len(inputArray))
for i := 0; i < len(inputArray); i++ {
maxHeap.insert(inputArray[i])
}
maxHeap.buildMaxHeap()
fmt.Println("The Max Heap is ")
for i := 0; i < len(inputArray); i++ {
fmt.Println(maxHeap.remove())
}
}
输出:
8
7
6
5
3
2
在 Go (Golang)中数组中递增元素之间的最大差异
来源:
golangbyexample.com/maximum-difference-increasing-elements-in-an-array-golang/
目录
-
概述
-
程序
概述
给定一个数组。目标是找到两个索引 i 和 j 之间的最大差异,使得
-
j > i
-
arr[j] > arr[i]
如果不存在这样的索引,则返回-1
示例 1
Input: intervals = [8, 2, 6, 5]
Output: 4
Explanation: 6-2 = 4
示例 2
Input: intervals = [8, 3, 2, 1]
Output: -1
Explanation: Condition is not satified
程序
这是相应的程序。
package main
import "fmt"
func maximumDifference(nums []int) int {
lenNums := len(nums)
if lenNums == 0 || lenNums == 1 {
return -1
}
minElement := nums[0]
maxDifference := -1
for i := 1; i < lenNums; i++ {
diff := nums[i] - minElement
if diff > maxDifference && diff != 0 {
maxDifference = diff
}
if nums[i] < minElement {
minElement = nums[i]
}
}
return maxDifference
}
func main() {
output := maximumDifference([]int{8, 2, 6, 5})
fmt.Println(output)
output = maximumDifference([]int{8, 3, 2, 1})
fmt.Println(output)
}
输出
4
-1
注意: 请查看我们的 Golang 高级教程。本系列教程内容详尽,尽力涵盖所有概念并附带示例。该教程适合希望获得专业知识和扎实理解的 Golang 学习者 - Golang 高级教程
如果你有兴趣了解如何在 Golang 中实现所有设计模式。如果是的话,这篇文章适合你 - 所有设计模式 Golang
在 Go (Golang) 中,具有相等数量的 0 和 1 的连续子数组的最大长度
来源:
golangbyexample.com/max-length-array-zero-one-golang/
目录
-
概述
-
程序
概述
给定一个仅包含 0 和 1 的数组。目标是找到一个具有相等数量的 0 和 1 的最大长度子数组。让我们通过示例来理解它。
示例 1
Input: intervals = [0, 1]
Output: 2
示例 2
Input: intervals = [0, 1, 1, 0, 1, 1]
Output: 4
以下是我们可以采取的方法
-
我们将创建一个数组 leftSum,其中 leftSum[i] 表示从索引 0 到 i 的数字之和。将 0 视为 -1,将 1 视为 1。
-
现在有两种情况。子数组从索引 0 开始,或者子数组从其他索引开始。
-
从左到右扫描 leftSum 数组。如果 leftSum 中任何索引的值为零,则表示 subarray[0,i] 中包含相同数量的 0 和 1。这将给出如果子数组从索引 0 开始的答案。
-
如果子数组不从零开始。那么再次扫描 leftSum 数组,找到索引 i 和 j,使得 leftSum[i] == leftSum[j]。为了弄清楚这一点,我们将使用一个映射。如果 j-i 的长度大于最大长度,则更新最大长度。
-
最后返回最大长度
程序
这里是相应的程序。
package main
import "fmt"
func findMaxLength(nums []int) int {
lenNums := len(nums)
if lenNums == 0 {
return 0
}
currentSum := 0
sumLeft := make([]int, lenNums)
for i := 0; i < lenNums; i++ {
if nums[i] == 0 {
currentSum = currentSum - 1
} else {
currentSum = currentSum + 1
}
sumLeft[i] = currentSum
}
maxLength := 0
max := 0
min := 0
for i := 0; i < lenNums; i++ {
if sumLeft[i] == 0 {
maxLength = i + 1
}
if sumLeft[i] > max {
max = sumLeft[i]
}
if sumLeft[i] < min {
min = sumLeft[i]
}
}
numMap := make(map[int]int, max-min+1)
for i := 0; i < lenNums; i++ {
index, ok := numMap[sumLeft[i]]
if ok {
currentLength := i - index
if currentLength > maxLength {
maxLength = currentLength
}
} else {
numMap[sumLeft[i]] = i
}
}
return maxLength
}
func main() {
output := findMaxLength([]int{0, 1})
fmt.Println(output)
output = findMaxLength([]int{0, 1, 1, 0, 1, 1})
fmt.Println(output)
}
输出
2
4
注意: 查看我们的 Golang 高级教程。本系列的教程内容详尽,我们尝试用示例覆盖所有概念。本教程适合那些希望获得 Golang 专业知识和深入理解的人 – Golang 高级教程
如果你有兴趣了解所有设计模式如何在 Golang 中实现。如果是这样,那么这篇文章就是为你准备的 – 所有设计模式 Golang
Go 中的最大和子数组程序 (Golang)
来源:
golangbyexample.com/maximum-sum-subarray-golang/
目录
-
概述
-
程序
概述
目标是在给定的输入数组中找到最大子数组。子数组应是连续的,并且至少应包含一个元素。
例如
Input: [4, 5 ,-3]
Maximum Subarray is [4, 5]
Output: 9
另一个示例
Input: [1, 2, -4, 4, 1]
Maximum Subarray is [4, 1]
Output: 5
我们在这里使用 Kadane 算法。在 Kadane 算法中,我们保持两个变量 max 和 currentMax。
-
max 初始化为 IntMin,currentMax 初始化为零。
-
然后我们对数组中的每个元素进行循环。
-
设置 currentMax = currentMax + a[i]。
-
如果 currentMax > max,则将 max 设置为 currentMax。
-
如果 currentMax 小于零,则将 currentMax 重置为零。
-
程序
这里是相同的程序。
package main
import "fmt"
const (
UintSize = 32 << (^uint(0) >> 32 & 1)
MaxInt = 1<<(UintSize-1) - 1 // 1<<31 - 1 or 1<<63 - 1
MinInt = -MaxInt - 1
)
func main() {
input := []int{4, 5, -3}
output := maxSubArray(input)
fmt.Println(output)
input = []int{1, 2, -4, 4, 1}
output = maxSubArray(input)
fmt.Println(output)
}
func maxSubArray(nums []int) int {
lenNums := len(nums)
currentMax := 0
max := MinInt
for i := 0; i < lenNums; i++ {
currentMax = currentMax + nums[i]
if currentMax > max {
max = currentMax
}
if currentMax < 0 {
currentMax = 0
}
}
return max
}
输出
9
5
注意: 查看我们的 Golang 高级教程。本系列教程内容详尽,我们尝试用示例覆盖所有概念。本教程适合那些希望获得专业知识和扎实理解 Golang 的人 - Golang 高级教程。
如果你有兴趣了解如何在 Golang 中实现所有设计模式。如果是,那么这篇文章适合你 - 所有设计模式 Golang。