Master公式
归并排序
递归版本
将一个数组不断划分,然后从下至上合并成一个有序数组
func MergeSort(array []int, left, right int) {//right传参为len-1
if left == right {
return
}
mid := left + ((right - left) >> 1)
MergeSort(array, left, mid)
MergeSort(array, mid+1, right)
Merge(array, left, mid, right)
}
func Merge(array []int, left, mid, right int) {
helper := make([]int, right-left+1)
i, p1, p2 := 0, left, mid+1
for p1 <= mid && p2 <= right {
if array[p1] < array[p2] {
helper[i] = array[p1]
p1++
} else {
helper[i] = array[p2]
p2++
}
i++
}
for p1 <= mid {
helper[i] = array[p1]
i++
p1++
}
for p2 <= right {
helper[i] = array[p2]
i++
p2++
}
for i := 0; i < len(helper); i++ {
array[left+i] = helper[i]
}
}
非递归版本
func mergesort(arr []int) {
n := len(arr)
for step := 1; step < n; step <<= 1 {
l := 0
for l < n {
m := l + step - 1
if m+1 >= n {
break
}
r := int(math.Min(float64(m+step), float64(n-1)))
merge(arr, l, m, r)
l = r + 1
}
}
}
func merge(arr []int, l, m, r int) {
n := r - l + 1
help := make([]int, n)
i, p1, p2 := 0, l, m+1
for p1 <= m && p2 <= r {
if arr[p1] <= arr[p2] {
help[i] = arr[p1]
i++
p1++
} else {
help[i] = arr[p2]
i++
p2++
}
}
for p1 <= m {
help[i] = arr[p1]
i++
p1++
}
for p2 <= r {
help[i] = arr[p2]
i++
p2++
}
for i := 0; i < n; i++ {
arr[l] = help[i]
l++
}
}
题一:小和问题
在一个数组中,每一个数左边比当前数小的数累加起来,叫做这个数组的小和。求一个数组 的小和。
逆向思维:即当前数右边有多少个比当前数大的数,则在小和中该数出现几次。
递归解
func smallSum(array []int) (sum int) {
if array == nil || len(array) < 2 {
return 0
}
return MergeSort(array, 0, len(array)-1)
}
func MergeSort(array []int, left, right int) int { //right传参为len-1
if left == right {
return 0
}
mid := left + ((right - left) >> 1)
return MergeSort(array, left, mid) + MergeSort(array, mid+1, right) + Merge(array, left, mid, right)
}
func Merge(array []int, left, mid, right int) int {
sum := 0
helper := make([]int, right-left+1)
i, p1, p2 := 0, left, mid+1
for p1 <= mid && p2 <= right {
if array[p1] < array[p2] {
sum += array[p1] * (right - p2 + 1)
helper[i] = array[p1]
p1++
} else {
helper[i] = array[p2]
p2++
}
i++
}
for p1 <= mid {
helper[i] = array[p1]
i++
p1++
}
for p2 <= right {
helper[i] = array[p2]
i++
p2++
}
for i := 0; i < len(helper); i++ {
array[left+i] = helper[i]
}
return sum
}
循环解(用于测试)
func forsmallSum(array []int) (sum int) {
sum = 0
for i := 0; i < len(array); i++ {
for j := i + 1; j < len(array); j++ {
if array[i] < array[j] {
sum += array[i]
}
}
}
return sum
}
题二:逆序对问题
在一个数组中,左边的数如果比右边的数大,则折两个数构成一个逆序对,请打印所有逆序对。
递归解
func InversePair(array []int, left, right int) int { //right传参为len-1
if left == right {
return 0
}
mid := left + ((right - left) >> 1)
return InversePair(array, left, mid) + InversePair(array, mid+1, right) + Merge(array, left, mid, right)
}
func Merge(array []int, left, mid, right int) int {
var sum = 0
helper := make([]int, right-left+1)
i, p1, p2 := 0, left, mid+1
for p1 <= mid && p2 <= right {
if array[p1] > array[p2] {
for j := p1; j <= mid; j++ {
fmt.Println(array[j], array[p2])
sum += 1
}
helper[i] = array[p2]
p2++
} else {
helper[i] = array[p1]
p1++
}
i++
}
for p1 <= mid {
helper[i] = array[p1]
i++
p1++
}
for p2 <= right {
helper[i] = array[p2]
i++
p2++
}
for i := 0; i < len(helper); i++ {
array[left+i] = helper[i]
}
return sum
}
循环解(用于测试)
func ForInversePair(array []int) (sum int) {
sum = 0
for i := 0; i < len(array); i++ {
for j := i + 1; j < len(array); j++ {
if array[i] > array[j] {
fmt.Println(array[i], array[j])
sum += 1
}
}
}
return sum
}
快速排序
题一:小荷兰国旗问题
给定一个数组arr,和一个数num,请把小于等于num的数放在数 组的左边,大于num的数放在数组的右边。要求额外空间复杂度O(1),时间复杂度O(N)
func NetherlandsFlag(array []int, left, right, num int) {
p := left //该位进行遍历
left = left - 1 //该位标志已排序的小于num的边界
right = right + 1 //该变量标志已经排序好的大于num的边界
for p < right {
if array[p] <= num {
p++
left++
} else {
right--
array[p], array[right] = array[right], array[p]
}
}
}
把小于num和等于num的放到一起没有进一步细化。
题二:荷兰国旗问题
给定一个数组arr,和一个数num,请把小于num的数放在数组的 左边,等于num的数放在数组的中间,大于num的数放在数组的 右边。要求额外空间复杂度O(1),时间复杂度O(N)
func NetherlandsFlag(array []int, left, right, num int) { //分为两块的
p := left //该位进行遍历
left = left - 1 //该位标志已排序的小于num的边界
right = right + 1 //该变量标志已经排序好的大于num的边界
for p < right {
if array[p] < num {
left++
array[p], array[left] = array[left], array[p]
p++
} else if array[p] > num {
right--
array[p], array[right] = array[right], array[p]
} else {
p++
}
}
fmt.Println("边界", left, right)
}
快速排序
func QuickSort(array []int, left, right int) {
if left >= right {
return
}
num := array[rand.New(rand.NewSource(time.Now().UnixNano())).Intn(right)]
p := left //该位进行遍历
Leftboundary := left - 1 //该位标志已排序的小于num的边界
rightboundary := right + 1 //该变量标志已经排序好的大于num的边界
for p < rightboundary {
if array[p] < num {
Leftboundary++
array[p], array[Leftboundary] = array[Leftboundary], array[p]
p++
} else if array[p] > num {
rightboundary--
array[p], array[rightboundary] = array[rightboundary], array[p]
} else {
p++
}
}
QuickSort(array, left, Leftboundary)
QuickSort(array, rightboundary, right)
}
空间复杂度O(log N)