Go: 使用外部切片进行排序的 sort.Slice

huangapple go评论70阅读模式
英文:

Go: sort.Slice using foreign slice to sort

问题

我正在使用LeetCode 973上编写代码。

这是其中一种解答。但是这种解法涉及到冗余的距离计算,所以我想创建一个切片来存储距离。我试图使用这个外部数据来对切片points进行排序,代码如下:

func kClosest(points [][]int, k int) [][]int {
    dist := make([]int, len(points))
    for i := 0; i < len(points); i++ {
        dist[i] = hypot(points[i])
    }

    sort.Slice(points, func(i, j int) bool {
        if dist[i] > dist[j] {
            dist[i], dist[j] = dist[j], dist[i]
            return false
        } else {
            return true
        }
    })
    return points[:k]
}

func hypot(point []int) int {
    ans := 0
    for _, n := range point {
        ans += n * n
    }
    return ans
}

但是在这种情况下,只有切片dist被排序了,而切片points并没有改变。我试图查看源代码,但它们似乎被封装起来了。

如果有人能解释给我听,或者告诉我在哪里可以找到答案,我将不胜感激。

英文:

I'm coding on <a href=https://leetcode.cn/problems/k-closest-points-to-origin/submissions> leetcode 973.</a>

func kClosest(points [][]int, k int) [][]int {

    sort.Slice(points, func(i, j int) bool {
           if hypot(points[i]) &gt; hypot(points[j]){
               return false
           }else{
               return true
           }
        })
    return points[:k]
}

func hypot(point []int) int {
    ans := 0
    for _, n := range point{
        ans+=n*n
    }
    return ans
}

this is one of the answer. But this resolution involves redundant computation of distance, so i want to create a slice to restore the distance. and i'm trying to sort slice points using this foreign data like

func kClosest(points [][]int, k int) [][]int {
    dist := make([]int, len(points))
    for i:=0; i&lt; len(points); i++{
        dist[i] = hypot(points[i])
    }

    sort.Slice(points, func(i, j int) bool {
           if dist[i] &gt; dist[j]{
               dist[i], dist[j] = dist[j], dist[i]
               return false
           }else{
               return true
           }
        })
    return points[:k]
}

func hypot(point []int) int {
    ans := 0
    for _, n := range point{
        ans+=n*n
    }
    return ans
}

But in this case only slice dist is sorted while the slice points doesn't change.
I'm trying to refer to the source code but they seem to be wrapped.

Thanks if anyone could explain it to me or tell me where i can find the answer.

答案1

得分: 1

less()函数是你传递给sort.Slice()的函数,它的目的和责任是告诉可排序切片中两个元素的小于关系。它不应对切片进行更改,并且应该是幂等的。但是你的less函数不是幂等的:如果你用相同的ij调用它两次,在这两次调用之间即使切片没有被修改,它可能返回不同的结果。

初始解决方案

你的初始改进解决方案如下:

func kClosest(points [][]int, k int) [][]int {
    sort.Slice(points, func(i, j int) bool {
        return hypot(points[i]) < hypot(points[j])
    })
    return points[:k]
}

使用indices切片

一种基于另一个切片对切片进行排序的方法是创建一个包含索引的切片,对这个索引切片进行排序(基于参考切片),一旦我们有了最终的索引顺序,就可以组装结果。

下面是一个示例:

func kClosest2(points [][]int, k int) [][]int {
    dist := make([]int, len(points))
    indices := make([]int, len(points))
    for i := range dist {
        indices[i] = i
        dist[i] = hypot(points[i])
    }

    sort.Slice(indices, func(i, j int) bool {
        return dist[indices[i]] <= dist[indices[j]]
    })

    result := make([][]int, k)
    for i := range result {
        result[i] = points[indices[i]]
    }
    return result
}

创建可排序的对

另一种方法是从点和它们的距离创建对,并对这些对进行排序。

下面是一个示例:

func kClosest3(points [][]int, k int) [][]int {
    type pair struct {
        dist  int
        point []int
    }

    pairs := make([]pair, len(points))
    for i := range pairs {
        pairs[i].dist = hypot(points[i])
        pairs[i].point = points[i]
    }

    sort.Slice(pairs, func(i, j int) bool {
        return pairs[i].dist <= pairs[j].dist
    })

    result := make([][]int, k)
    for i := range result {
        result[i] = pairs[i].point
    }
    return result
}

实现sort.Interface并使用一个swap()方法在两个切片中进行交换

标题已经说得很清楚了。我们自己实现sort.Interface,其中的less()方法将基于距离切片进行报告,但swap()函数将在两个切片中执行交换:

type mySorter struct {
    dist   []int
    points [][]int
}

func (m mySorter) Len() int { return len(m.dist) }
func (m mySorter) Swap(i, j int) {
    m.dist[i], m.dist[j] = m.dist[j], m.dist[i]
    m.points[i], m.points[j] = m.points[j], m.points[i]
}
func (m mySorter) Less(i, j int) bool { return m.dist[i] < m.dist[j] }

func kClosest4(points [][]int, k int) [][]int {
    dist := make([]int, len(points))
    for i := range dist {
        dist[i] = hypot(points[i])
    }

    ms := mySorter{dist, points}

    sort.Sort(ms)
    return points[:k]
}

测试解决方案

下面是对上述所有解决方案进行测试的代码:

solutions := []func(points [][]int, k int) [][]int{
    kClosest, kClosest2, kClosest3, kClosest4,
}
for _, solution := range solutions {
    points := [][]int{
        {3, 5},
        {0, 5},
        {0, 0},
        {1, 2},
        {6, 0},
    }
    fmt.Println(solution(points, len(points)))
}

输出结果为:

[[0 0] [1 2] [0 5] [3 5] [6 0]]
[[0 0] [1 2] [0 5] [3 5] [6 0]]
[[0 0] [1 2] [0 5] [3 5] [6 0]]
[[0 0] [1 2] [0 5] [3 5] [6 0]]
英文:

The purpose and responsibility of less() function you pass to sort.Slice() is to tell the is-less relation of 2 elements of the sortable slice. It should not perform changes on the slice, and it should be idempotent. Your less function is not idempotent: if you call it twice with the same i and j, it may return different result even if the slice is not modified between the calls.

Genesis: your original solution

Your original, improved solution is this:

func kClosest(points [][]int, k int) [][]int {
	sort.Slice(points, func(i, j int) bool {
		return hypot(points[i]) &lt; hypot(points[j])
	})
	return points[:k]
}

Using an indices slice

One way of sorting a slice based on another is to create a slice holding the indices, sort this indices slice (based on the reference slice), and once we have the final index order, assemble the result.

Here's how it could look like:

func kClosest2(points [][]int, k int) [][]int {
	dist := make([]int, len(points))
	indices := make([]int, len(points))
	for i := range dist {
		indices[i] = i
		dist[i] = hypot(points[i])
	}

	sort.Slice(indices, func(i, j int) bool {
		return dist[indices[i]] &lt;= dist[indices[j]]
	})

	result := make([][]int, k)
	for i := range result {
		result[i] = points[indices[i]]
	}
	return result
}

Creating sortable pairs

Another approach is to create pairs from the points and their distances, and sort the pairs.

Here's how it could be done:

func kClosest3(points [][]int, k int) [][]int {
	type pair struct {
		dist  int
		point []int
	}

	pairs := make([]pair, len(points))
	for i := range pairs {
		pairs[i].dist = hypot(points[i])
		pairs[i].point = points[i]
	}

	sort.Slice(pairs, func(i, j int) bool {
		return pairs[i].dist &lt;= pairs[j].dist
	})

	result := make([][]int, k)
	for i := range result {
		result[i] = pairs[i].point
	}
	return result
}

Implementing sort.Interface and using a swap() method that swaps in both slices

Title says it all. We implement sort.Interface ourselves whose less() method will report based on the distance slice, but the swap() function will perform swapping on both slices:

type mySorter struct {
	dist   []int
	points [][]int
}

func (m mySorter) Len() int { return len(m.dist) }
func (m mySorter) Swap(i, j int) {
	m.dist[i], m.dist[j] = m.dist[j], m.dist[i]
	m.points[i], m.points[j] = m.points[j], m.points[i]
}
func (m mySorter) Less(i, j int) bool { return m.dist[i] &lt; m.dist[j] }

func kClosest4(points [][]int, k int) [][]int {
	dist := make([]int, len(points))
	for i := range dist {
		dist[i] = hypot(points[i])
	}

	ms := mySorter{dist, points}

	sort.Sort(ms)
	return points[:k]
}

Testing the solutions

Here's a test code for all the above solutions:

solutions := []func(points [][]int, k int) [][]int{
	kClosest, kClosest2, kClosest3, kClosest4,
}
for _, solution := range solutions {
	points := [][]int{
		{3, 5},
		{0, 5},
		{0, 0},
		{1, 2},
		{6, 0},
	}
	fmt.Println(solution(points, len(points)))
}

Which will output (try it on the Go Playground)

[[0 0] [1 2] [0 5] [3 5] [6 0]]
[[0 0] [1 2] [0 5] [3 5] [6 0]]
[[0 0] [1 2] [0 5] [3 5] [6 0]]
[[0 0] [1 2] [0 5] [3 5] [6 0]]

huangapple
  • 本文由 发表于 2022年5月31日 17:09:29
  • 转载请务必保留本文链接:https://go.coder-hub.com/72445066.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定