2.认识O(NlogN)的排序

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)

  • 16
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值