如何在Go中反转数组?

 2020-01-14 

How do I reverse an array in Go?

http://play.golang.org/p/W70J4GU7nA

1
2
3
4
  s := []int{5, 2, 6, 3, 1, 4}
  sort.Reverse(sort.IntSlice(s))
  fmt.Println(s)
  // 5, 2, 6, 3, 1, 4

很难理解func Reverse(data Interface)Interface中的含义。

如何反转数组? 我不需要排序。


老实说,这个简单到足以让我像这样写出来:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
package main

import"fmt"

func main() {

    s := []int{5, 2, 6, 3, 1, 4}

    for i, j := 0, len(s)-1; i < j; i, j = i+1, j-1 {
        s[i], s[j] = s[j], s[i]
    }

    fmt.Println(s)
}

http://play.golang.org/p/vkJg_D1yUb

(其他答案很好地解释了sort.Interface及其用法;因此,我不再赘述。)


通常,要对整数数组进行排序,可以将它们包装在IntSlice中,后者定义了方法LenLessSwap。这些方法又被sort.Sort使用。 sort.Reverse的作用是采用一个定义了LenLessSwap的现有类型,但是它用新的方法替换了Less的方法,该方法始终是基础Less的反函数。 :

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
type reverse struct {
    // This embedded Interface permits Reverse to use the methods of
    // another Interface implementation.
    Interface
}

// Less returns the opposite of the embedded implementation's Less method.
func (r reverse) Less(i, j int) bool {
    return r.Interface.Less(j, i)
}

// Reverse returns the reverse order for data.
func Reverse(data Interface) Interface {
    return &reverse{data}
}

因此,当您编写sort.Reverse(sort.IntSlice(s))时,发生的事情是您得到了替换为Less方法的新的,"修改过的" IntSlice。因此,如果您在其上调用sort.Sort(其调用Less),则它将按降序进行排序。


我迟到了2年,但出于娱乐和兴趣,我想提出一个"赔率"解决方案。

假设任务确实是要反转列表,那么对于原始性能,bgp的解决方案可能是无与伦比的。它通过前后交换数组项来简单有效地完成工作,此操作在数组和切片的随机访问结构中非常有效。

在函数式编程语言中,惯用方法通常涉及递归。在Go中看起来有些奇怪,并且性能会很差。也就是说,这是一个递归数组反转函数(在一个小的测试程序中):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
package main

import (
   "fmt"
)

func main() {
    myInts := []int{ 8, 6, 7, 5, 3, 0, 9 }
    fmt.Printf("Ints %v reversed: %v
", myInts, reverseInts(myInts))
}

func reverseInts(input []int) []int {
    if len(input) == 0 {
        return input
    }
    return append(reverseInts(input[1:]), input[0])
}

输出:

1
Ints [8 6 7 5 3 0 9] reversed: [9 0 3 5 7 6 8]

同样,这只是出于娱乐目的,而不是为了生产。如果列表太大,不仅速度很慢,而且还会使堆栈溢出。我刚刚进行了测试,它将反转100万个x31的列表,但是崩溃了1000万个。


首先,如果要反转数组,请执行以下操作,

1
2
3
for i, j := 0, len(a)-1; i < j; i, j = i+1, j-1 {
    a[i], a[j] = a[j], a[i]
}

然后,在golang.org中查看Reverse的用法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
package main

import (
   "fmt"
   "sort"
)

func main() {
    s := []int{5, 2, 6, 3, 1, 4} // unsorted
    sort.Sort(sort.Reverse(sort.IntSlice(s)))
    fmt.Println(s)
}

// output
// [6 5 4 3 2 1]

并查看反向和排序的描述

1
2
func Reverse(data Interface) Interface
func Sort(data Interface)

Sort sorts data. It makes one call to data.Len to determine n, and O(n*log(n)) calls to data.Less and data.Swap. The sort is not guaranteed to be stable.

因此,如您所知,Sort不仅是一种排序算法,还可以将其视为工厂,当您使用Reverse时,它只是返回一个反向排序算法,Sort只是在进行排序。


这是更通用的切片反向功能。如果输入不是切片,它将惊慌。

1
2
3
4
5
6
7
8
//panic if s is not a slice
func ReverseSlice(s interface{}) {
    size := reflect.ValueOf(s).Len()
    swap := reflect.Swapper(s)
    for i, j := 0, size-1; i < j; i, j = i+1, j-1 {
        swap(i, j)
    }
}

如果要反转阵列,则可以按相反的顺序进行。由于该语言中没有"反向范围"原语(至少现在还没有),因此您必须执行以下操作(http://play.golang.org/p/AhvAfMjs_7):

1
2
3
4
5
6
7
8
s := []int{5, 2, 6, 3, 1, 4}
for i := len(s) - 1; i >= 0; i-- {
    fmt.Print(s[i])
    if i > 0 {
        fmt.Print(",")
    }
}
fmt.Println()

关于是否很难理解sort.Reverse(data Interface) Interface的功能,我一直以同样的想法,直到看到" http://golang.org/src/pkg/sort/sort.go"中的源代码。

它只是使排序所需的比较"相反"。


1
func Reverse(data Interface) Interface

这意味着它需要一个sort.Interface并返回另一个sort.Interface -实际上它本身并未进行任何排序。例如,如果传入sort.IntSlice(本质上是一个[]int,可以将其传递给sort.Sort以对其进行升序排序),则会得到一个新的sort.Interface,该整数将整数按降序进行排序。

顺便说一句,如果您单击文档中的函数名称,它将直接链接到Reverse的源。如您所见,它只是包装了传入的sort.Interface,因此从Reverse返回的值将获取原始sort.Interface的所有方法。唯一不同的方法是Less方法,该方法在嵌入式sort.Interface上返回与Less方法相反的方法。有关嵌入式字段的详细信息,请参见语言规范的这一部分。


这是一个简单的Go解决方案,它使用高效的方法(无需额外的内存)来反转数组:

1
2
3
4
5
6
7
i := 0
j := len(nums) - 1
for i < j {
    nums[i], nums[j] = nums[j], nums[i]
    i++
    j--
}

想法是反转数组等效于在中心交换每个元素及其镜像。

https://play.golang.org/p/kLFpom4LH0g


这是另一种方法

1
2
3
4
5
6
7
func main() {
    example := []int{1, 25, 3, 5, 4}
    sort.SliceStable(example, func(i, j int) bool {
        return true
    })
    fmt.Println(example)
}

https://play.golang.org/p/-tIzPX2Ds9z


从Golang Wiki SliceTricks:

To replace the contents of a slice with the same elements but in
reverse order:

1
2
3
4
for i := len(a)/2-1; i >= 0; i-- {
  opp := len(a)-1-i
  a[i], a[opp] = a[opp], a[i]
}

The same thing, except with two indices:

1
2
3
for left, right := 0, len(a)-1; left < right; left, right = left+1, right-1 {
  a[left], a[right] = a[right], a[left]
}

要在适当的位置反转数组,请迭代到其中点,然后将每个元素替换为其" mirror element":

1
2
3
4
5
6
7
8
9
10
func main() {
    xs := []int{1, 2, 3, 4, 5, 6, 7, 8, 9}
    itemCount := len(xs)
    for i := 0; i < itemCount/2; i++ {
        mirrorIdx := itemCount - i -1
        xs[i], xs[mirrorIdx] = xs[mirrorIdx], xs[i]
    }
    fmt.Printf("xs: %v
", xs)
}

https://play.golang.org/p/JeSApt80_k


这是我反转数组的解决方案:

1
2
3
4
5
6
7
8
9
10
11
func reverse_array(array []string) []string {
    lenx := len(array) // lenx holds the original array length
    reversed_array := make([]string, lenx) // creates a slice that refer to a new array of length lenx

    for i := 0; i < lenx; i++ {
        j := lenx - (i + 1) // j initially holds (lenx - 1) and decreases to 0 while i initially holds 0 and increase to (lenx - 1)
        reversed_array[i] = array[j]
    }

    return reversed_array
}

您可以在户外游乐场尝试该解决方案

1
2
3
4
5
6
7
8
9
package main

import"fmt"

func main() {
    array := []string{"a","b","c","d"}

    fmt.Println(reverse_array(array)) // prints [d c b a]
}

不要反转它,保持它现在不变,然后向后迭代。