通过示例学习-Go-语言-2023-十二-
通过示例学习 Go 语言 2023(十二)
Go(Golang)中的工厂设计模式
注意:如果有兴趣了解其他所有设计模式在 GO 中的实现,请查看这个完整参考——Go(Golang)中的所有设计模式
目录
** 介绍:
-
UML 图:
-
映射:
-
示例:
-
完整工作代码:
介绍:
工厂设计模式是一种创建型设计模式,也是最常用的模式之一。该模式提供了一种隐藏创建实例逻辑的方法。
客户端只与工厂结构体交互,并告知需要创建的实例类型。工厂类与相应的具体结构体交互,并返回正确的实例。
在下面的示例中
-
我们有iGun接口,定义了枪应具备的所有方法。
-
存在实现iGun接口的gun结构体。
-
两种具体的枪ak47和maverick。两者都嵌入gun结构,因此也间接实现了iGun的所有方法,因此都是iGun类型。
-
我们有一个gunFactory结构体,它可以创建ak47或maverick类型的枪。
-
main.go作为客户端,而不是直接与ak47或maverick交互,它依赖于gunFactory来创建ak47和maverick的实例。
UML 图:
下面是与上述示例对应的映射 UML 图。
映射:
下面的表格表示 UML 图中的参与者与“示例”中实际实现参与者的映射。
ProductFactory | gunFactory.go |
---|---|
iProduct | iGun.go |
产品 | gun.go |
具体 iProduct 1 | ak47go |
具体 iProduct 1 | maverick.go |
客户端 | main.go |
示例:
iGun.go
package main
type iGun interface {
setName(name string)
setPower(power int)
getName() string
getPower() int
}
gun.go
package main
type gun struct {
name string
power int
}
func (g *gun) setName(name string) {
g.name = name
}
func (g *gun) getName() string {
return g.name
}
func (g *gun) setPower(power int) {
g.power = power
}
func (g *gun) getPower() int {
return g.power
}
ak47.go
package main
type ak47 struct {
gun
}
func newAk47() iGun {
return &ak47{
gun: gun{
name: "AK47 gun",
power: 4,
},
}
}
maverick.go
package main
type maverick struct {
gun
}
func newMaverick() iGun {
return &maverick{
gun: gun{
name: "Maverick gun",
power: 5,
},
}
}
gunFactory.go
package main
import "fmt"
func getGun(gunType string) (iGun, error) {
if gunType == "ak47" {
return newAk47(), nil
}
if gunType == "maverick" {
return newMaverick(), nil
}
return nil, fmt.Errorf("Wrong gun type passed")
}
main.go
package main
import "fmt"
func main() {
ak47, _ := getGun("ak47")
maverick, _ := getGun("maverick")
printDetails(ak47)
printDetails(maverick)
}
func printDetails(g iGun) {
fmt.Printf("Gun: %s", g.getName())
fmt.Println()
fmt.Printf("Power: %d", g.getPower())
fmt.Println()
}
输出:
Gun: AK47 gun
Power: 4
Gun: Maverick gun
Power: 5
完整工作代码:
package main
import "fmt"
type iGun interface {
setName(name string)
setPower(power int)
getName() string
getPower() int
}
type gun struct {
name string
power int
}
func (g *gun) setName(name string) {
g.name = name
}
func (g *gun) getName() string {
return g.name
}
func (g *gun) setPower(power int) {
g.power = power
}
func (g *gun) getPower() int {
return g.power
}
type ak47 struct {
gun
}
func newAk47() iGun {
return &ak47{
gun: gun{
name: "AK47 gun",
power: 4,
},
}
}
type maverick struct {
gun
}
func newMaverick() iGun {
return &maverick{
gun: gun{
name: "Maverick gun",
power: 5,
},
}
}
func getGun(gunType string) (iGun, error) {
if gunType == "ak47" {
return newAk47(), nil
}
if gunType == "maverick" {
return newMaverick(), nil
}
return nil, fmt.Errorf("Wrong gun type passed")
}
func main() {
ak47, _ := getGun("ak47")
maverick, _ := getGun("maverick")
printDetails(ak47)
printDetails(maverick)
}
func printDetails(g iGun) {
fmt.Printf("Gun: %s", g.getName())
fmt.Println()
fmt.Printf("Power: %d", g.getPower())
fmt.Println()
}
输出:
Gun: AK47 gun
Power: 4
Gun: Maverick gun
Power: 5
Go(Golang)中的选择语句中的 Fallthrough 关键字
目录
-
概述
-
代码
概述
选择语句不允许fallthrough关键字选择多个案例。Fallthrough关键字只能在 switch 语句中用于选择多个案例。在准备好的案例中只会随机选择一个案例。它无法执行多个案例,但有一个解决方法。我们可以在 set 语句外部有一个 for 循环。这个 for 循环将根据循环的迭代次数调用选择语句。
让我们看看一个例子。
代码
package main
import "fmt"
func main() {
ch1 := make(chan string)
ch2 := make(chan string)
go goOne(ch1)
go goTwo(ch2)
for i := 0; i < 2; i++ {
select {
case msg1 := <-ch1:
fmt.Println(msg1)
case msg2 := <-ch2:
fmt.Println(msg2)
}
}
}
func goOne(ch chan string) {
ch <- "From goOne goroutine"
}
func goTwo(ch chan string) {
ch <- "From goTwo goroutine"
}
输出
From goOne goroutine
From goTwo goroutine
在上面的程序中,我们在选择语句中放置了一个长度为二的 for 循环。因此,选择语句执行了两次,并打印了每个 case 语句接收到的值。
通过这种方式,我们可以执行多个 case 语句,但请注意,这并不是确定性的。如果在某个特定 case 语句上有多个操作可用,则该 case 在 for 循环的所有迭代中每次都可能被执行。
Go (Golang)中的 Switch 语句中的 Fallthrough 关键字
fallthrough关键字在 golang 的 switch 语句中使用。该关键字用于 switch case 块。如果 case 块中存在fallthrough关键字,则即使当前 case 可能已匹配,它也会将控制权转移到下一个 case。
为了更好地理解这一点,让我们先看一个没有 fallthrough 关键字的示例。
package main
import "fmt"
func main() {
i := 45
switch {
case i < 10:
fmt.Println("i is less than 10")
case i < 50:
fmt.Println("i is less than 50")
case i < 100:
fmt.Println("i is less than 100")
}
}
输出:
i is less than 50
默认情况下,switch 语句从上到下匹配所有 case 语句,并尝试找到第一个与switch 表达式匹配的case 表达式。一旦找到匹配的 case,它将退出,不考虑其他 case。这就是上述示例中发生的情况。尽管 i 小于 100,但该 case 永远不会被执行,因为匹配了第二个 case,之后它退出。
fallthrough 关键字提供了一种绕过此限制的方法。请参见下面的代码,了解fallthrough关键字的示例。在下面的示例中,尽管第二个 case 已匹配,但由于fallthrough关键字,它仍然经过了第三个 case。
package main
import "fmt"
func main() {
i := 45
switch {
case i < 10:
fmt.Println("i is less than 10")
fallthrough
case i < 50:
fmt.Println("i is less than 50")
fallthrough
case i < 100:
fmt.Println("i is less than 100")
}
}
输出
i is less than 50
i is less than 100
fallthrough必须是 switch 块内的最后一个语句。如果不是,编译器将引发错误。
fallthrough statement out of place
以下程序将引发上述错误,因为我们在fmt.Println语句之后有fallthrough语句。
package main
import "fmt"
func main() {
i := 45
switch {
case i < 10:
fmt.Println("i is less than 10")
fallthrough
case i < 50:
fmt.Println("i is less than 50")
fallthrough
fmt.Println("Not allowed")
case i < 100:
fmt.Println("i is less than 100")
}
}
Break 语句
以下是break语句的示例。
package main
import "fmt"
func main() {
switch char := "b"; char {
case "a":
fmt.Println("a")
case "b":
fmt.Println("b")
break
fmt.Println("after b")
default:
fmt.Println("No matching character")
}
}
输出
b
break语句将终止 switch 的执行,下面的行将永远不会被执行。
fmt.Println("after b")
结论
这就是关于 golang 中 switch 语句中 fallthrough 关键字的所有内容。
在 Go (Golang) 中查找所有长度大于二的算术序列
目录
-
概述
-
程序
概述
算术序列是指每个元素之间的差值相等的序列。在此程序中,给定一个整数数组。目标是找到所有长度大于二的算术序列。
通过一个例子来最好地理解这个问题
示例
Input: [2,3,4,5]
Output: 3
在上述数组中我们有三个长度大于 2 的算术片段
-
2,3,4
-
3,4,5
-
2,3,4,5
这是一个动态规划问题,因为它具有最优子结构。假设数组的名称为 input
-
dp[0] = 0
-
dp[1] = 0
-
dp[2] = 1 如果 dp[2] – dp[1] == dp[1] – dp[0]
-
dp[i] = 1 如果 dp[i] – dp[i-1] == dp[i-1] – dp[i-2]
其中 dp[i] 表示长度大于 2 的算术序列直到长度 i+1 的数量
程序
这是相应的程序。
package main
import "fmt"
func numberOfArithmeticSlices(nums []int) int {
lenNums := len(nums)
if lenNums <= 2 {
return 0
}
dp := make([]int, lenNums)
dp[0] = 0
if (nums[2] - nums[1]) == nums[1]-nums[0] {
dp[2] = 1
}
for i := 3; i < lenNums; i++ {
if nums[i]-nums[i-1] == nums[i-1]-nums[i-2] {
dp[i] = dp[i-1] + 1
}
}
output := 0
for i := 2; i < lenNums; i++ {
output = output + dp[i]
}
return output
}
func main() {
output := numberOfArithmeticSlices([]int{2, 3, 4, 5})
fmt.Println(output)
}
输出
3
注意: 请查看我们的 Golang 高级教程。本系列教程内容详尽,我们尝试用例子涵盖所有概念。本教程适合那些希望获得专业知识和扎实理解 Golang 的人 - Golang 高级教程
如果你有兴趣了解如何在 Golang 中实现所有设计模式。如果是的话,这篇文章就是为你准备的 - 所有设计模式 Golang长度大于两个的算术序列
在 Go 中查找数组中的所有重复项 (Golang)
目录
-
概述
-
程序
概述
给定一个数组,其中所有元素的范围在[1, n]内,n 是数组的长度。目标是找到该数组中的所有重复项。
示例
Input: [1, 2, 3, 2, 4, 3]
Output: [2, 3]
这里的想法是利用数字在范围[1, n]内的事实。对于数组中的每个元素,增加其索引处的值 n。因此
-
要获取索引处的值,我们用 value%n
-
最终,如果任何索引的值大于 2*n,那么它是重复的。
程序
这是相同的程序。
package main
import "fmt"
func findDuplicates(nums []int) []int {
lenNums := len(nums)
for i := 0; i < lenNums; i++ {
index := (nums[i] - 1) % lenNums
nums[index] = lenNums + nums[index]
}
k := 0
for i := 0; i < lenNums; i++ {
if nums[i] > 2*lenNums {
nums[k] = i + 1
k++
}
}
return nums[0:k]
}
func main() {
output := findDuplicates([]int{1, 2, 3, 2, 4, 3})
fmt.Println(output)
}
输出
[2 3]
注意: 请查看我们的 Golang 高级教程。该系列的教程内容详尽,我们努力涵盖所有概念及示例。本教程适合那些希望获得专业知识和深入理解 Golang 的人 – Golang 高级教程
如果你有兴趣了解如何在 Golang 中实现所有设计模式。如果是的话,这篇文章适合你 – 所有设计模式 Golang
找到数组中所有和为目标数字的三元组,使用 Go (Golang)
目录
-
概述
-
程序
概述
假设输入是
[1, 2, 3, 4, 1]
那么答案就是
[[1 2 3] [1 4 1] [2 3 1]]
我们可以使用哈希来解决这个问题。它基于以下思想
-
如果两个数字是 x 和 y,而目标和是 target
-
那么另一个数字将是 target–(x+y)
程序
package main
import (
"fmt"
)
func main() {
output := threeSumTarget([]int{1, 2, 3, 4, 1}, 6)
fmt.Println(output)
}
func threeSumTarget(nums []int, target int) [][]int {
numsLength := len(nums)
var results [][]int
for k := 0; k < numsLength-2; k++ {
numsMap := make(map[int]int)
for i := k + 1; i < numsLength; i++ {
if numsMap[target-(nums[i]+nums[k])] > 0 {
result := []int{nums[k], target - (nums[i] + nums[k]), nums[i]}
results = append(results, result)
}
numsMap[nums[i]] = i
}
}
return results
}
输出
[[1 2 3] [1 4 1] [2 3 1]]
在 Go 语言中查找数组中所有和为零的三元组
目录
-
概述
-
程序
概述
假设输入为
[-3, 0, 3, 4, -1, -6]
那么答案将是
[[-3 0 3] [-3 4 -1]]
我们可以使用哈希来解决这个问题。这基于一个想法
-
如果两个数字是 x 和 y
-
另一个数字将是 -(x+y)
程序
package main
import (
"fmt"
)
func main() {
output := threeSumZero([]int{-3, 0, 3, 4, -1, -6})
fmt.Println(output)
}
func threeSumZero(nums []int) [][]int {
numsLength := len(nums)
var results [][]int
for k := 0; k < numsLength-2; k++ {
numsMap := make(map[int]int)
for i := k + 1; i < numsLength; i++ {
if numsMap[0-(nums[i]+nums[k])] > 0 {
result := []int{nums[k], 0 - (nums[i] + nums[k]), nums[i]}
results = append(results, result)
}
numsMap[nums[i]] = i
}
}
return results
}
输出
[[-3 0 3] [-3 4 -1]]
在 Go (Golang) 中查找并删除字符串中的字符
目录
-
概述
-
代码:
概述
GO 的 strings 包提供了一个 ReplaceAll 方法,可以用于将给定子字符串的所有不重叠实例替换为一个新的子字符串。我们可以使用此方法通过将要删除的字符的替换设为空字符串来删除一个字符。
以下是该函数的签名。该函数将在字符串 s 中替换所有不重叠的 old 实例为 new。要删除一个字符,我们可以将该字符作为 old,将 new 作为一个空字符串。
func ReplaceAll(s, old, new string) string
让我们看看工作代码。
代码:
package main
import (
"fmt"
"strings"
)
func main() {
res = strings.ReplaceAll("abcdabxyabr", "a", "")
fmt.Println(res)
}
输出:
bcdbxybr
在 Go (Golang) 中查找并删除子字符串
目录
-
概述
-
代码:
概述
GO 的 strings 包提供了一个 ReplaceAll 方法,可以用来替换给定子字符串的所有不重叠实例为新的子字符串。我们可以使用这个方法通过将要删除的子字符串的替换设为空字符串来删除一个子字符串。
以下是函数的签名。该函数将在字符串 s 中替换所有不重叠的 old 实例为 new。要删除一个子字符串,我们可以将该子字符串作为 old,并将 new 设为空字符串。
func ReplaceAll(s, old, new string) string
让我们来看看工作代码
代码:
package main
import (
"fmt"
"strings"
)
func main() {
res = strings.ReplaceAll("abcabcabcdef", "ab", "")
fmt.Println(res)
}
输出:
cccdef
```*
<!--yml
类别:未分类
日期:2024-10-13 06:13:41
-->
# 在 Go(Golang)中查找并删除数组中的元素
> 来源:[`golangbyexample.com/find-delete-array-golang/`](https://golangbyexample.com/find-delete-array-golang/)
可能有两种情况:
目录
** 修改原数组
+ 请勿修改原数组
# **修改原数组**
+ 继续复制到原数组,跳过需要删除的项。
+ 在最后进行切片。
```go
package main
import "fmt"
func main() {
s := [4]int{1, 2, 3, 1}
after := findAndDelete(s, 1)
fmt.Println(after)
}
func findAndDelete(s [4]int, item int) []int {
index := 0
for _, i := range s {
if i != item {
s[index] = i
index++
}
}
return s[:index]
}
输出:
[2,3]
请勿修改原数组
创建一个新数组并持续插入数据。
package main
import "fmt"
func main() {
before := [4]int{1, 2, 3, 1}
after := findAndDelete(before, 1)
fmt.Println(after)
}
func findAndDelete(s [4]int, itemToDelete int) []int {
var new [4]int
index := 0
for _, i := range s {
if i != itemToDelete {
new[index] = i
index++
}
}
return new[:index]
}
输出
[2,3]
```*
<!--yml
分类:未分类
日期:2024-10-13 06:13:37
-->
# 在 Go (Golang) 中查找并删除切片
> 来源:[`golangbyexample.com/find-delete-slice-golang/`](https://golangbyexample.com/find-delete-slice-golang/)
可能有两种情况:
**修改原始切片**
+ 保持复制到原始切片,跳过需要删除的项
+ 在末尾重新切片
```go
package main
import "fmt"
func main() {
s := []int{"a", "b", "c", "a"}
after := findAndDelete(s, "a")
fmt.Println(after)
}
func findAndDelete(s []int, item int) []int {
index := 0
for _, i := range s {
if i != item {
s[index] = i
index++
}
}
return s[:index]
}
输出:
[2,3]
不要修改原始切片
- 创建一个新切片并持续插入内容。
package main
import "fmt"
func main() {
before := []int{1, 2, 3, 1}
after := findAndDelete(before, 1)
fmt.Println(after)
}
func findAndDelete(s []int, itemToDelete int) []int {
var new = make([]int, len(s))
index := 0
for _, i := range s {
if i != itemToDelete {
new = append(new, i)
index++
}
}
return new[:index]
}
输出:
[2,3]
在 Go(Golang)中查找整数数组中第一个缺失的正整数
来源:
golangbyexample.com/first-missing-postivie-integer-golang/
目录
-
概述
-
程序
概述
目标是找到整数数组中第一个缺失的正整数。
示例
Input: [3, 2, -2]
Output: 1
Input: [7, 8, 9, 11, 12]
Output: 1
Input: [1, 2, 3]
Output: 4
如果数组中存在与该索引相等的数字,我们可以将该索引的值变为负数。对于这个问题,零和负数不重要,所以我们将正数放在一边,零/负数放在另一边。
然后我们仅对正数进行操作,并遵循以下方法。
-
我们遍历数组。对于当前元素x,我们将arr[x-1]的值改为负数。
-
最后,我们遍历并返回仍为正数的索引。如果所有索引都是负数,则返回
total_num_of_positive_int + 1
程序
以下是相同的程序
package main
import (
"fmt"
"math"
)
func main() {
output := firstMissingPositive([]int{3, 2, -2})
fmt.Println(output)
output = firstMissingPositive([]int{-3, -2, -1})
fmt.Println(output)
output = firstMissingPositive([]int{7, 8, 9, 11, 12})
fmt.Println(output)
output = firstMissingPositive([]int{1, 2, -1})
fmt.Println(output)
output = firstMissingPositive([]int{1, 2, 3})
fmt.Println(output)
output = firstMissingPositive([]int{1, 1})
fmt.Println(output)
}
func firstMissingPositive(nums []int) int {
onlyPositiveNumsArray, k := segregate((nums))
for i := 0; i < k; i++ {
value := int(math.Abs(float64(onlyPositiveNumsArray[i])))
if value > 0 && value <= k {
if onlyPositiveNumsArray[value-1] > 0 {
onlyPositiveNumsArray[value-1] = -1 * onlyPositiveNumsArray[value-1]
}
}
}
for i := 0; i < k; i++ {
if onlyPositiveNumsArray[i] > 0 {
return i + 1
}
}
return k + 1
}
func segregate(nums []int) ([]int, int) {
k := 0
for i := 0; i < len(nums); i++ {
if nums[i] > 0 {
nums[k] = nums[i]
k++
}
}
return nums[0:k], k
}
输出
1
1
1
3
4
2
注意: 请查看我们的 Golang 高级教程。本系列教程详细且我们尝试涵盖所有概念及示例。这个教程是为那些希望获得 Golang 专业知识和扎实理解的人准备的 – Golang 高级教程
如果你有兴趣了解如何在 Golang 中实现所有设计模式。如果是的话,那么这篇文章就是为你准备的 – 所有设计模式 Golang
在 Go(Golang)中查找有序数组中目标元素的第一个和最后一个位置
来源:
golangbyexample.com/first-last-position-target-sorted-array-golang/
目录
-
概述
-
程序
概述
目标是找到有序数组中元素的第一个和最后一个位置。
例如
Input: [1, 2, 2, 5]
Target: 2
Output: [1, 2]
Input: [1, 2, 5]
Target: 2
Output: [1, 1]
Input: []
Target: 1
Output: [-1,-1]
策略是
-
在数组中进行二分查找,找到左侧索引。
-
然后在数组中再次进行二分查找,找到正确的索引。
程序
以下是相应的程序
package main
import "fmt"
func main() {
output := searchRange([]int{1, 2, 2, 5}, 2)
fmt.Println(output)
output = searchRange([]int{1, 2, 5}, 2)
fmt.Println(output)
output = searchRange([]int{}, 1)
fmt.Println(output)
}
func searchRange(nums []int, target int) []int {
output := make([]int, 2)
output[0] = findLeftPivot(nums, 0, len(nums)-1, target, len(nums))
output[1] = findRightPivot(nums, 0, len(nums)-1, target, len(nums))
return output
}
func findLeftPivot(nums []int, start, end, target, len int) int {
if start > end {
return -1
}
if start == end && nums[start] == target {
return start
}
mid := (start + end) / 2
if (mid == 0 || nums[mid-1] < nums[mid]) && nums[mid] == target {
return mid
}
if target <= nums[mid] {
return findLeftPivot(nums, start, mid-1, target, len)
}
return findLeftPivot(nums, mid+1, end, target, len)
}
func findRightPivot(nums []int, start, end, target, len int) int {
if start > end {
return -1
}
if start == end && nums[start] == target {
return start
}
mid := (start + end) / 2
if mid+1 <= end && nums[mid] == target && nums[mid] < nums[mid+1] {
return mid
}
if (mid == len-1 || nums[mid] < nums[mid+1]) && nums[mid] == target {
return mid - 1
}
if target >= nums[mid] {
return findRightPivot(nums, mid+1, end, target, len)
}
return findRightPivot(nums, start, mid-1, target, len)
}
输出
[1 2]
[1 1]
[-1 -1]
注意: 查看我们的 Golang 高级教程。本系列的教程内容详尽,我们尝试涵盖所有概念并附上示例。本教程适合希望获得专业知识和扎实理解 Golang 的人 – Golang 高级教程
如果你对理解所有设计模式在 Golang 中的实现感兴趣。如果是的话,这篇文章适合你 – 所有设计模式 Golang
在 Go (Golang) 中查找子字符串的第一次出现的索引
来源:
golangbyexample.com/index-first-instance-substring-golang/
目录
-
概述
-
代码:
概述
在 Go 中,字符串是 UTF-8 编码的。GO 的 strings 包提供了一个 Index 方法,可以用于获取特定字符串中子字符串第一次出现的索引。如果子字符串不在给定字符串中,则返回 -1。
以下是函数的签名
func Index(s, substr string) int
让我们看一下工作代码
代码:
package main
import (
"fmt"
"strings"
)
func main() {
//Output will be 1 as "bc" is present in "abcdef" at index 1
res := strings.Index("abcdef", "bc")
fmt.Println(res)
//Output will be 2 as "cd" is present in "abcdefabcdef" at index 2
res = strings.Index("abcdefabcdef", "cd")
fmt.Println(res)
//Output will be -1 as "ba" is not present in "abcdef"
res = strings.Index("abcdef", "ba")
fmt.Println(res)
}
输出:
1
2
-1
```*
<!--yml
分类:未分类
date: 2024-10-13 06:13:10
-->
# 在 Go(Golang)中查找子字符串最后一次出现的索引
> 来源:[`golangbyexample.com/index-last-occurence-substring-go/`](https://golangbyexample.com/index-last-occurence-substring-go/)
目录
+ 概述
+ 代码:
# **概述**
在 GO 中,字符串是 UTF-8 编码的。GO 的 **strings** 包提供了一个 **LastIndex** 方法,可以用来获取特定字符串中子字符串最后一次出现的索引。如果给定字符串中不存在该子字符串,它将返回 -1。
以下是该函数的签名
```go
func LastIndex(s, substr string) int
让我们来看一下工作代码
代码:
package main
import (
"fmt"
"strings"
)
func main() {
//Output will be 1 as "bc" is present in "abcdef" at index 1
res := strings.LastIndex("abcdef", "bc")
fmt.Println(res)
//Output will be 8 as "cd" is present in "abcdefabcdef" at index 8
res = strings.LastIndex("abcdefabcdef", "cd")
fmt.Println(res)
//Output will be -1 as "ba" is not present in "abcdef"
res = strings.LastIndex("abcdef", "ba")
fmt.Println(res)
}
输出:
1
8
-1
```*
<!--yml
类别:未分类
日期:2024-10-13 06:42:54
-->
# 在 Go 中找到一个数字的下一个排列
> 来源:[`golangbyexample.com/next-permuation-number-go/`](https://golangbyexample.com/next-permuation-number-go/)
目录
+ 概述
+ 程序
## **概述**
目标是根据字典序排序找到给定数字的下一个排列。如果下一个排列不可行,则返回相同的数字
例如
```go
Input: [1,2,3]
Output: [1,3,2]
Input: [2,1]
Output: [2,1]
Input: [1, 3, 5, 4, 1]
Output: [1, 4, 1, 3, 5]
Input: [1, 3, 2]
Output: [2, 1, 3]
下面将是策略
-
从左侧开始,找到第一个小于其右侧数字的数字。假设该数字在索引“first”处找到
-
然后找到数组右侧在索引first之后大于该数字的最小值。然后用索引first的数字替换该数字
-
对索引first之后的数组右部分进行排序
程序
下面是相同的程序
package main
import (
"fmt"
"sort"
)
func main() {
nextPermutation([]int{1, 2, 3})
nextPermutation([]int{2, 1})
nextPermutation([]int{1, 3, 5, 4, 1})
nextPermutation([]int{1, 3, 2})
}
func nextPermutation(nums []int) {
numsLen := len(nums)
first := -1
second := -1
for i, j := numsLen-2, numsLen-1; i >= 0; {
if nums[i] < nums[j] {
first = i
second = j
break
} else {
i--
j--
}
}
if !(first == -1) {
smallestGreaterIndex := second
for i := second + 1; i < numsLen; i++ {
if nums[i] > nums[first] && nums[i] < nums[smallestGreaterIndex] {
smallestGreaterIndex = i
}
}
nums[first], nums[smallestGreaterIndex] = nums[smallestGreaterIndex], nums[first]
sort.Slice(nums[second:numsLen], func(i, j int) bool {
return nums[second+i] < nums[second+j]
})
}
fmt.Println(nums)
}
输出
[1 3 2]
[2 1]
[1 4 1 3 5]
[2 1 3]
注意: 查看我们的 Golang 高级教程。本系列的教程内容详尽,我们努力涵盖所有概念并提供示例。此教程适合希望获得专业知识和对 Golang 有扎实理解的人 - Golang 高级教程
如果你有兴趣了解如何在 Golang 中实现所有设计模式。如果是的话,那么这篇文章适合你 - 所有设计模式 Golang
在 Go(Golang) 中找到数组中只出现一次的数字
目录
-
概述
-
程序
概述
给定一个数组,其中每个元素出现两次,只有一个元素出现一次。目标是以常量额外空间找到该元素
示例 1
Input: [2, 1, 2, 3, 3]
Output: 1
示例 2
Input: [1, 1, 4]
Output: 4
这里的思路是使用 XOR。我们将利用 XOR 的两个特性
-
一个数字与其自身进行 XOR 结果是 0
-
0 和任何数字的 XOR 结果是该数字
所以这个思路是对数组中的所有数字进行 XOR。最终得到的数字就是答案。
程序
以下是相应的程序
package main
import "fmt"
func singleNumber(nums []int) int {
lenNums := len(nums)
res := 0
for i := 0; i < lenNums; i++ {
res = res ^ nums[i]
}
return res
}
func main() {
output := singleNumber([]int{2, 1, 2, 3, 3})
fmt.Println(output)
output = singleNumber([]int{1, 1, 4})
fmt.Println(output)
}
输出:
1
4
注意: 请查看我们的 Golang 高级教程。本系列教程内容详细,我们尽力涵盖所有概念及示例。这个教程适合那些希望获得专业知识和扎实理解 Golang 的人 - Golang 高级教程
如果你对了解如何在 Golang 中实现所有设计模式感兴趣。那么这篇文章适合你 - 所有设计模式 Golang
同时,请查看我们的系统设计教程系列 - 系统设计教程系列*
在 Go (Golang) 中找到排序和基准化数组中的基准索引。
目录
- 概述
概述
我们有一个已排序的输入数组,但在某个索引处被基准化。例如,考虑下面的数组。
[1, 3, 5, 7, 9]
它在索引 3 处被旋转和基准化。
[5, 7, 9, 1, 3]
目标是找到基准索引。因此,上述数组的答案将是 3。如果数组没有被基准化,则应返回-1。例如,对于下面的输入数组,它应该返回-1。
[0, 1, 2, 3, 4]
其他示例
Pivoted Array: [7, 9, 1, 3, 5]
Pivot Index: 2
Pivoted Array: [9, 1, 3, 5, 9]
Pivot Index: 2
Pivoted Array: [1, 3, 5, 7, 9]
Pivot Index: -1
找到基准索引的预期时间复杂度为O(logn)。因此,我们必须进行一些修改的二分搜索来找到基准索引。
这将是策略。
-
进行二分搜索。对于每个中间元素,检查mid或mid+1是否为基准。
-
如果mid的值小于输入数组的起始值,则在mid的左侧进行搜索。
-
如果 mid 的值大于输入数组起始值,则在mid的右侧进行搜索。
下面是相应的程序。
package main
import "fmt"
func main() {
pivot := findPivot([]int{0, 1, 2, 3, 4, 5})
fmt.Println(pivot)
pivot = findPivot([]int{1, 2, 3, 4, 5, 0})
fmt.Println(pivot)
pivot = findPivot([]int{2, 3, 4, 5, 0, 1})
fmt.Println(pivot)
pivot = findPivot([]int{3, 4, 5, 0, 1, 2})
fmt.Println(pivot)
pivot = findPivot([]int{4, 5, 0, 1, 2, 3})
fmt.Println(pivot)
pivot = findPivot([]int{5, 0, 1, 2, 3, 4})
fmt.Println(pivot)
}
func findPivot(nums []int) int {
return findPivotUtil(nums, 0, len(nums)-1)
}
func findPivotUtil(nums []int, start, end int) int {
if start > end {
return -1
}
mid := (start + end) / 2
if mid+1 <= end && nums[mid] > nums[mid+1] {
return mid + 1
}
if mid-1 >= start && nums[mid] < nums[mid-1] {
return mid
}
if nums[mid] < nums[start] {
return findPivotUtil(nums, start, mid-1)
}
return findPivotUtil(nums, mid+1, end)
}
func binarySearch(nums []int, start, end, target int) int {
if start > end {
return -1
}
mid := (start + end) / 2
if nums[mid] == target {
return mid
}
if target < nums[mid] {
return binarySearch(nums, start, mid-1, target)
} else {
return binarySearch(nums, mid+1, end, target)
}
}
输出
-1
5
4
3
2
1
注意: 请查看我们的 Golang 高级教程。本系列教程内容详尽,我们尽力覆盖所有概念并附有示例。这个教程适合那些希望获得专业知识和对 Golang 有深入理解的人 - Golang 高级教程
如果你有兴趣了解如何在 Golang 中实现所有设计模式。如果是的话,这篇文章适合你 - 所有设计模式 Golang
使用数组中的三个数字找到最接近目标数字的和,或者说是 Go 语言中的 3Sum 最接近问题。
目录
-
概述
-
程序
概述
目标是使用给定数组中的三个三元组找到最接近给定目标和的和。
对于
Input Array: [0,2,3,-1] Target Sum:6
Output: 5 . It can be formed using 0+2+3
程序
以下是相同的程序
package main
import (
"fmt"
"math"
"sort"
)
func main() {
output := threeSumClosest([]int{0, 2, 3, -1}, 6)
fmt.Println(output)
}
func threeSumClosest(nums []int, target int) int {
numsLength := len(nums)
closestSum := 0
nearest := 10000
sort.Slice(nums, func(i, j int) bool {
return nums[i] < nums[j]
})
for k := 0; k < numsLength-2; k++ {
i := k + 1
j := numsLength - 1
for i < j {
sum := nums[k] + nums[i] + nums[j]
absSum := int(math.Abs(float64(target - sum)))
if absSum < nearest {
nearest = absSum
closestSum = sum
}
if nums[k]+nums[i]+nums[j] > target {
j--
} else {
i++
}
}
}
return closestSum
}
输出
5
注意: 请查看我们的 Golang 高级教程。此系列的教程内容详尽,我们尽量用例子覆盖所有概念。本教程适合那些希望获得 Golang 专业知识和扎实理解的人 – Golang 高级教程
如果你有兴趣了解如何在 Golang 中实现所有设计模式。如果是的话,这篇文章就是为你准备的 – 所有设计模式 Golang
在 Go (Golang) 中查找对象的类型
来源:
golangbyexample.com/find-the-type-of-an-object-in-golang/
本文将描述在 Go 中了解对象类型的不同方法
目录
** 使用反射包
-
使用类型断言
-
使用 Switch
-
使用 printf 或 sprintf
使用反射包
反射包提供了一些有用的检查函数,可以让我们了解类型
package main
import (
"fmt"
"reflect"
)
func main() {
var test interface{}
test = "test_string"
fmt.Println(reflect.TypeOf(test))
}
输出:
string
使用类型断言
package main
import "fmt"
func main() {
var test interface{}
test = "test_string"
val, ok := test.(string)
if ok {
fmt.Printf("Test is of type string with value %s\n", val)
} else {
fmt.Printf("Unknown Type %T", test)
}
test = 2
val2, ok := test.(int)
if ok {
fmt.Printf("Test is of type int with value %d\n", val2)
} else {
fmt.Printf("Unknown Type %T", test)
}
}
输出:
Test is of type string with value test_string
Test is of type int with value 2
使用 Switch
package main
import "fmt"
func main() {
printType("test_string")
printType(2)
}
func printType(t interface{}) {
switch v := t.(type) {
case string:
fmt.Println("Type: string")
case int:
fmt.Println("Type: int")
default:
fmt.Printf("Unknown Type %T", v)
}
}
输出:
Type: string
Type: int
使用 printf 或 sprintf
package main
import (
"fmt"
)
func main() {
var test interface{}
test = "test_string"
//Using Sprintf
testType := fmt.Sprintf("%T", test)
fmt.Println(testType)
//Using printf
fmt.Printf("%T\n", test)
}
输出:
string
string
在 Go (Golang) 中查找两个数组中的数字,使它们的和等于目标数字
目录
-
概述
-
程序
概述
例如,假设我们有一个给定的数组
[2, 5, 1, 3]
目标数字是 4
那么答案将是索引
[2, 3]
因为我们有
-
索引 2 的数字 1
-
索引 3 的数字 3
并且 1+3 = 4
请注意数组是无序的
预期时间复杂度 – O(n)
我们可以使用哈希来解决这个问题。它的基础思想是
-
如果说其中一个数字是 x
-
那么另一个数字将是 target-x
因此,如果对于一个数字 x,我们检查 target-x 是否在哈希中。如果在,那么我们知道我们找到了答案
我们来看一个相同的程序。
程序
package main
import "fmt"
func main() {
output := twoTargetSums([]int{2, 5, 1, 3}, 4)
fmt.Println(output)
}
func twoTargetSums(nums []int, target int) []int {
numberMap := make(map[int]int)
output := make([]int, 2)
for i := 0; i < len(nums); i++ {
val, ok := numberMap[target-nums[i]]
if ok {
output[0] = val
output[1] = i
return output
} else {
numberMap[nums[i]] = i
}
}
return output
}
输出
[2 3]
Go(Golang)中的数字下限
目录
-
概述
-
代码:
概述
Go的math包提供了一个Floor方法,可以用来获取一个数的下限。一个数的下限是小于或等于该数的最大整数值。
下面是该函数的签名。它接受一个浮点数作为输入,并返回一个浮点数。
func Floor(x float64) float64
下限函数的一些特殊情况是
-
Floor(±0) = ±0
-
Floor(±Inf) = ±Inf
-
Floor(NaN) = NaN
代码:
package main
import (
"fmt"
"math"
)
func main() {
res := math.Floor(1.6)
fmt.Println(res)
res = math.Floor(-1.6)
fmt.Println(res)
res = math.Floor(1)
fmt.Println(res)
}
输出:
1
-2
1
Go(Golang)中的享元设计模式
注意:如果您想了解如何在 GO 中实现所有其他设计模式,请查看此完整参考 – Go 中的所有设计模式(Golang)
目录
** 定义:
-
使用时机:
-
UML 图:
-
映射:
-
实用示例:
定义:
这是一种结构设计模式。当需要创建大量相似对象时,使用此模式。这些对象称为享元对象且是不可变的。
让我们先看一个例子。享元模式将在此示例之后变得清晰。
在反恐精英游戏中,恐怖分子和反恐分子有不同类型的服装。为简化起见,我们假设恐怖分子和反恐分子各有一种服装类型。服装对象嵌入在玩家对象中,如下所示。
以下是玩家的结构,我们可以看到服装对象嵌入在玩家结构中。
type player struct {
dress dress
playerType string //Can be T or CT
lat int
long int
}
假设有 5 个恐怖分子和 5 个反恐分子,总共 10 名玩家。现在有两个关于服装的选项。
-
每个 10 个玩家对象都会创建不同的服装对象并嵌入它们。将创建总共 10 个服装对象。
-
我们创建两个服装对象。
-
单个恐怖分子服装对象:将共享给 5 个恐怖分子。
-
单个反恐分子服装对象:将共享给 5 个反恐分子。
-
如您所见,在方法 1 中,总共创建了 10 个服装对象,而在方法 2 中仅创建了 2 个服装对象。第二种方法是我们在享元设计模式中遵循的方法。我们创建的两个服装对象称为享元对象。享元模式提取公共部分并创建享元对象。这些享元对象(这里是服装)可以在多个对象(这里是玩家)之间共享。这大大减少了服装对象的数量,并且即使您创建更多玩家,仍然只需两个服装对象即可。
在享元模式中,我们将享元对象存储在映射中。每当创建共享享元对象的其他对象时,享元对象将从映射中获取。
内在和外在状态
-
内在状态 –服装是内在状态,因为它可以在多个恐怖分子和反恐分子对象之间共享。
-
外部状态 – 玩家位置和玩家武器是外部状态,因为它对于每个对象都是不同的。
使用时机:
-
当对象具有可以共享的某些内在属性时。
- 如上述示例,服装是被提取并共享的内在属性。
-
当需要创建大量对象时使用享元,这可能会导致内存问题。要确定所有共同或内在状态,并为其创建享元对象。
UML 图:
以下是与上面示例相对应的映射 UML 图
映射:
下表表示 UML 图中的参与者与代码中实际实现参与者之间的映射。
享元工厂 | dressFactory.go |
---|---|
享元接口 | dress.go |
具体享元对象 1 | terroristDress.go |
具体享元对象 1 | counterTerroristDress.go |
上下文 | player.go |
客户端 | main.go |
实际示例:
dressFactory.go
package main
import "fmt"
const (
//TerroristDressType terrorist dress type
TerroristDressType = "tDress"
//CounterTerrroristDressType terrorist dress type
CounterTerrroristDressType = "ctDress"
)
var (
dressFactorySingleInstance = &dressFactory{
dressMap: make(map[string]dress),
}
)
type dressFactory struct {
dressMap map[string]dress
}
func (d *dressFactory) getDressByType(dressType string) (dress, error) {
if d.dressMap[dressType] != nil {
return d.dressMap[dressType], nil
}
if dressType == TerroristDressType {
d.dressMap[dressType] = newTerroristDress()
return d.dressMap[dressType], nil
}
if dressType == CounterTerrroristDressType {
d.dressMap[dressType] = newCounterTerroristDress()
return d.dressMap[dressType], nil
}
return nil, fmt.Errorf("Wrong dress type passed")
}
func getDressFactorySingleInstance() *dressFactory {
return dressFactorySingleInstance
}
dress.go
package main
type dress interface {
getColor() string
}
terroristDress.go
package main
type terroristDress struct {
color string
}
func (t *terroristDress) getColor() string {
return t.color
}
func newTerroristDress() *terroristDress {
return &terroristDress{color: "red"}
}
counterTerroristDress.go
package main
type counterTerroristDress struct {
color string
}
func (c *counterTerroristDress) getColor() string {
return c.color
}
func newCounterTerroristDress() *counterTerroristDress {
return &counterTerroristDress{color: "green"}
}
player.go
package main
type player struct {
dress dress
playerType string
lat int
long int
}
func newPlayer(playerType, dressType string) *player {
dress, _ := getDressFactorySingleInstance().getDressByType(dressType)
return &player{
playerType: playerType,
dress: dress,
}
}
func (p *player) newLocation(lat, long int) {
p.lat = lat
p.long = long
}
main.go
package main
import "fmt"
func main() {
game := newGame()
//Add Terrorist
game.addTerrorist(TerroristDressType)
game.addTerrorist(TerroristDressType)
game.addTerrorist(TerroristDressType)
game.addTerrorist(TerroristDressType)
//Add CounterTerrorist
game.addCounterTerrorist(CounterTerrroristDressType)
game.addCounterTerrorist(CounterTerrroristDressType)
game.addCounterTerrorist(CounterTerrroristDressType)
dressFactoryInstance := getDressFactorySingleInstance()
for dressType, dress := range dressFactoryInstance.dressMap {
fmt.Printf("DressColorType: %s\nDressColor: %s\n", dressType, dress.getColor())
}
}
输出:
DressColorType: ctDress
DressColor: green
DressColorType: tDress
DressColor: red