MergeSort实现的结果出乎意料

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

Unexpected result from MergeSort implementation

问题

我已经用中文翻译了你提供的内容:

我已经用Go语言编写了一个归并排序算法程序(见下面的代码),但是我没有得到正确的输出。

下面的程序打印出:

[2 2 2 2 3]

但并不是排序后的数组。

package main
import "fmt"

func MergeSort(arr []int) {
  if len(arr) < 2 {
    return
  }

  mid := len(arr) / 2
  left := arr[:mid]
  right := arr[mid:]
  
  MergeSort(left)
  MergeSort(right)
  
  merge(arr, left, right)
}

func merge(a []int, l []int, r []int) {
  i := 0
  j := 0
  k := 0

  for i < len(l) && j < len(r) {
    if l[i] <= r[j] {
      a[k] = l[i]
      i++
    } else {
      a[k] = r[j]
      j++
    }
    k++
  }
  
  for i < len(l) {
    a[k] = l[i]
    i++
    k++
  }
  
  for j < len(r) {
    a[k] = r[j]
    j++
    k++
  }

}

func main() {
  t := []int{23, 67, 98, 2, 3}
  MergeSort(t)
  fmt.Println(t)
}
英文:

I have written a merge sort algorithm program in go (see code below), but I am not getting the correct output.

The program below prints

[2 2 2 2 3]

but not the sorted array.

package main
import &quot;fmt&quot;
    
func MergeSort(arr []int) {
  if len(arr) &lt; 2 {
    return
  }

  mid := len(arr) / 2
  left := arr[:mid]
  right := arr[mid:]
    
  MergeSort(left)
  MergeSort(right)
    
  merge(arr, left, right)
}

func merge(a []int, l []int, r []int) {
  i := 0
  j := 0
  k := 0

  for i &lt; len(l) &amp;&amp; j &lt; len(r) {
    if l[i] &lt;= r[j] {
      a[k] = l[i]
      i++
    } else {
      a[k] = r[j]
      j++
    }
    k++
  }
  
  for i &lt; len(l) {
    a[k] = l[i]
    i++
    k++
  }
  
  for j &lt; len(r) {
    a[k] = r[j]
    j++
    k++
  }

}

func main() {
  t := []int{23, 67, 98, 2, 3}
  MergeSort(t)
  fmt.Println(t)
}

答案1

得分: 1

请注意,我的解决方案只修复了代码中的错误,并没有解决性能问题。我建议在随机整数集上进行测试,并检查切片是否已排序。

package main

import "fmt"

func main() {
	t := []int{23, 67, 98, 2, 3}
	MergeSort(t)
	fmt.Println(t)
}

func MergeSort(arr []int) {
	if len(arr) < 2 {
		return
	}
	mid := len(arr) / 2

	// 这些行很重要,你不能像你那样就地进行分割排序,
	// 因为将切片合并到自身中会导致内存损坏。
	left := append([]int{}, arr[:mid]...)

	right := append([]int{}, arr[mid:]...)

	MergeSort(left)
	MergeSort(right)

	merge(arr, left, right)
}

func merge(a []int, l []int, r []int) {
	i := 0
	j := 0
	k := 0
	for i < len(l) && j < len(r) {
		if l[i] <= r[j] {
			a[k] = l[i]
			i++
		} else {
			a[k] = r[j]
			j++
		}
		k++
	}
	for i < len(l) {
		a[k] = l[i]
		i++
		k++
	}

	for j < len(r) {
		a[k] = r[j]
		j++
		k++
	}
}
英文:

Be aware that my solution just fixes the bug in the code, and does not solve performance problems. I also advise testing it on a random set of integers and checking if the slice is sorted.

package main

import &quot;fmt&quot;

func main() {
	t := []int{23, 67, 98, 2, 3}
	MergeSort(t)
	fmt.Println(t)
}

func MergeSort(arr []int) {
	if len(arr) &lt; 2 {
		return
	}
	mid := len(arr) / 2
   
    // these lines are important, you cannot do split sort 
    // in-place like you did because the memory would get 
    // corrupted as you join slice into itself.
	left := append([]int{}, arr[:mid]...)

	right := append([]int{}, arr[mid:]...)

	MergeSort(left)
	MergeSort(right)

	merge(arr, left, right)
}

func merge(a []int, l []int, r []int) {
	i := 0
	j := 0
	k := 0
	for i &lt; len(l) &amp;&amp; j &lt; len(r) {
		if l[i] &lt;= r[j] {
			a[k] = l[i]
			i++
		} else {
			a[k] = r[j]
			j++
		}
		k++
	}
	for i &lt; len(l) {
		a[k] = l[i]
		i++
		k++
	}

	for j &lt; len(r) {
		a[k] = r[j]
		j++
		k++
	}
}

答案2

得分: 0

一个切片只是一个窗口,指向作为切片的后备存储的数组。

问题几乎肯定是这样的:

mid := len(arr) / 2
left := arr[:mid]
right := arr[mid:]

这创建了两个新的切片,但它们只是指向同一个数组的窗口,所以当你将源切片分成左/右两半时,这两个新的切片共享同一个后备存储——即支持原始切片的数组。它不会复制后备存储。

此外,当你合并时,你试图合并回同一个原始数组。

所以你试图在同一个基本数组中进行所有操作。而这是行不通的,特别是原地合并。

在拆分和合并时,你需要克隆你的切片,使用make()copy()。这将引导你到这个实现,你可以在Go Playground上进行调试。

归并排序在对链表进行排序时表现出色,因为不需要额外的空间。这已经以每个节点指向下一个节点的形式存在:

package main

import (
  "fmt"
)

func main() {
  unsorted := []int{10, 1, 9, 2, 8, 3, 7, 4, 6, 5, 0, 5, 6, 4, 7, 3, 8, 2, 9, 1, 10}
  sorted := sort(unsorted)

  fmt.Println(sorted)
}

func sort(unsorted []int) []int {

  // 获取源切片的长度
  n := len(unsorted)

  // 终止的特殊情况:
  // 长度小于等于1的切片根据定义是已排序的
  if n <= 1 {
    return clone(unsorted)
  }

  // 计算到中点的偏移量
  m := n / 2

  // 将源切片分成两半,
  // 然后递归地对它们进行排序,
  // 并将它们合并以产生已排序的切片
  sorted := merge(
    sort(clone(unsorted[:m])),
    sort(clone(unsorted[m:])),
  )
  return sorted
}

func merge(a []int, b []int) []int {
  aMax := len(a)
  bMax := len(b)

  // 构造一个新的切片来容纳两个切片的合并
  c := make([]int, aMax+bMax)

  // 初始化我们的索引
  i := 0 // 当前元素在'a'切片中的偏移量
  j := 0 // 当前元素在'b'切片中的偏移量
  k := 0 // 当前元素在'c'切片中的偏移量

  // 当我们既有'a'元素又有'b'元素时...
  for i < aMax && j < bMax {

    if a[i] <= b[j] {
      c[k] = a[i]
      i++
    } else {
      c[k] = b[j]
      j++
    }

    k++
  }

  // 如果我们还有剩余的'a'元素,取出它们
  for ; i < aMax; k++ {
    c[k] = a[i]
    i++
  }

  // 如果我们还有剩余的'b'元素,取出它们
  for ; j < bMax; k++ {
    c[k] = b[j]
    j++
  }

  // 返回两个切片的合并
  return c
}

func clone(arr []int) []int {
  arr1 := make([]int, len(arr))
  copy(arr1, arr)
  return arr1
}
英文:

A slice is just a window onto an array that is the backing store for the slice.

The problem is almost certainly this:

mid := len(arr) / 2
left := arr[:mid]
right := arr[mid:]

That creates 2 new slices, but they are just windows onto the same array, so when you divide the source slice into left/right halves, the two new slices share the same backing store — the array that backs the original slice. It does not copy the backing store.

Further, when you merge, you're trying to merge back into the same, original array.

So you are trying to do everything in place within the same base array. And that doesn't work, in particular, in-place merging.

You need to clone your slices as you split and merge, using make() and copy(). That leads you to this implementation, which you can fiddle with here in the Go Playground.

Where merge sort shines is sorting linked lists, as no extra space is required. That's already present in the form of each node's pointer to the next node:

package main

import (
  &quot;fmt&quot;
)

func main() {
  unsorted := []int{10, 1, 9, 2, 8, 3, 7, 4, 6, 5, 0, 5, 6, 4, 7, 3, 8, 2, 9, 1, 10}
  sorted := sort(unsorted)

  fmt.Println(sorted)
}

func sort(unsorted []int) []int {

  // get the length of the source slice
  n := len(unsorted)

  // The terminating special case:
  // slices of length &lt;= 1 is sorted by definition
  if n &lt;= 1 {
    return clone(unsorted)
  }

  // compute the offset to the midpoint
  m := n / 2

  // divide the source slice into two halves,
  // then recursively sort them,
  // and merge them to produce the sorted slice
  sorted := merge(
    sort(clone(unsorted[:m])),
    sort(clone(unsorted[m:])),
  )
  return sorted
}

func merge(a []int, b []int) []int {
  aMax := len(a)
  bMax := len(b)

  // construct a new slice to contain the merger of the two slices
  c := make([]int, aMax+bMax)

  // initialize our indices
  i := 0 // offset to the current element in the &#39;a&#39; slice
  j := 0 // offset to the current element in the &#39;b&#39; slice
  k := 0 // offset to the current element in the &#39;c&#39; slice

  // while we have both an &#39;a&#39; element and a &#39;b&#39; element...
  for i &lt; aMax &amp;&amp; j &lt; bMax {

    if a[i] &lt;= b[j] {
      c[k] = a[i]
      i++
    } else {
      c[k] = b[j]
      j++
    }

    k++
  }

  // if we have any &#39;a&#39; elements remaining, take them
  for ; i &lt; aMax; k++ {
    c[k] = a[i]
    i++
  }

  // if we have any &#39;b&#39; elements remaining, take them.
  for ; j &lt; bMax; k++ {
    c[k] = b[j]
    j++
  }

  // return the merger of the two slices
  return c
}

func clone(arr []int) []int {
  arr1 := make([]int, len(arr))
  copy(arr1, arr)
  return arr1
}

huangapple
  • 本文由 发表于 2022年3月23日 02:04:42
  • 转载请务必保留本文链接:https://go.coder-hub.com/71576931.html
匿名

发表评论

匿名网友

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

确定