算法2---归并排序与快速排序(golang)

  • 取中值
    mid:=(left+right)/2 //有可能越界
    mid:=left+(right-left)/2 //改进1
    mid := left + (right-left)>>1 //改进2 右移一位比除以2快

  • 用递归来找数组最大值

    解题思路:

    二分法取左边的最大值与右边的最大值比较
    左边的最大值如何得到呢
    将左边的最大值二分法取左右两边的最大值比较
    以此递归下去

    1 0 3 5 9 12
    1 0 3
    5 9 12
    1 0
    3
    5 9
    12
    1
    0
    5
    9
    func Getmax(arr []int, left, right int) int {
    	if left == right {
    		return arr[left]
    	}
    	mid := left + (right-left)>>1
    	leftMax := Getmax(arr, left, mid)
    	rightMax := Getmax(arr, mid+1, right)
    	return max(leftMax, rightMax)
    }
    
    func max(a, b int) int {
    	if a < b {
    		a = b
    	}
    	return a
    }
    
  • 归并排序
    将数组 一直二分到单个数,而单个数必然是有序的可直接返回。
    难点在于merge方法,该方法将左边的有序集合 与 右边的有序集合进行归并。

    如0 1 9 和3 5 12 ,先用双指针分别指向最左侧的0 和 3 ,谁小就塞进help数组,同时该指针往右移动一位,该指针越界,则将另一侧的数据填进help。

    问题1:为什么左边和右边是有序的集合?
    答:因为从单个数开始保证是有序的,而递归merge成两个数的时候也进行了排序 0 3

    问题2:为什么最左侧的0小于3就可以塞进help数据,它不用跟右侧其他数进行比较?
    答:因为右侧也是有序的,3>0,而3右边的数都是>3,就必定>0 (这里也是比选择排序\冒泡排序\插入排序(O(n^2))等优势的地方,减少了很多重复的比较,充分利用子资源,符合master公式,时间复杂度做到了O(n*logn))

    
    // 归并排序
    func process(arr []int, left, right int) {
    	if left == right {
    		return
    	}
    	mid := left + (right-left)>>1
    	process(arr, left, mid)
    	process(arr, mid+1, right)
    	merge(arr, left, mid, right)
    }
    
    func merge(arr []int, left, mid, right int) {
    	hLenght := right - left + 1
    	help := make([]int, hLenght)
    	i := 0
    	p1 := left
    	p2 := mid + 1
    	for p1 <= mid && p2 <= right {
    		if arr[p1] <= arr[p2] {
    			help[i] = arr[p1]
    			i++
    			p1++
    		} else {
    			help[i] = arr[p2]
    			i++
    			p2++
    		}
    	}
    	for p1 <= mid {
    		help[i] = arr[p1]
    		i++
    		p1++
    	}
    	for p2 <= right {
    		help[i] = arr[p2]
    		i++
    		p2++
    	}
    	for i = 0; i < hLenght; i++ {
    		arr[left+i] = help[i]
    	}
    }
    
    func main() {
    	arr := []int{1, 0, 9, 5, 12, 3}
    	process(arr, 0, 5)
    	fmt.Println(arr)
    	}
    
  • 排序优劣
    选择排序,第一次需要遍历n个数,才能找到arr[0]
    第二次需要遍历n-1个数,才能找到arr[1]
    第三次需要遍历n-2个数,才能找到arr[2]
    并且第一次,第二次,第三次之间的运算都是独立的,浪费了很多比较行为,而归并排序的比较信息是可以往下传递的,所以能做到O(n*logn)

  • 题目:求{1, 3, 4, 2, 5}的小和

在一个数组中,每一个数左边比当前数小的数累加起来,叫做这个数组的小和

举例4,左侧比它小的数有1 3 ,4的小和就是4
2,左侧比他小的数有1,2的小和就是1

解题思路:
求所有的小和,也可以转换为右边比1大的数的个数1+右边比3大的个数3+右边比4大的个数4+右边比2大的个数2,所以这里我们改版上面的归并算法


// 归并排序
func process(arr []int, left, right int) int {
	if left == right {
		return 0
	}
	mid := left + (right-left)>>1
	return process(arr, left, mid) +
		process(arr, mid+1, right) +
		merge(arr, left, mid, right)
}

func merge(arr []int, left, mid, right int) int {
	hLenght := right - left + 1
	help := make([]int, hLenght)
	i := 0
	p1 := left
	p2 := mid + 1

	smallSum := 0
	for p1 <= mid && p2 <= right {
		fmt.Println(p1, arr[p1], p2, arr[p2])
		if arr[p1] < arr[p2] {
			//只有左侧比右侧小的情况才产生小和
			smallSum += (right - p2 + 1) * arr[p1] //当1 < 3 时,产生3 5 9(right-p2+1)个1的小和
			help[i] = arr[p1]
			i++
			p1++
		} else {
			help[i] = arr[p2]
			i++
			p2++
		}
	}
	for p1 <= mid {
		help[i] = arr[p1]
		i++
		p1++
	}
	for p2 <= right {
		help[i] = arr[p2]
		i++
		p2++
	}
	for i = 0; i < hLenght; i++ {
		arr[left+i] = help[i]
	}
	return smallSum
}
func main() {
	arr := []int{1, 3, 4, 2, 5}
	fmt.Println(process(arr, 0, len(arr)-1))
}
  • 快速排序
    思路:
    问题:给定一个乱序数组1,3,4,2,5,3,要求比最右边的3小的数放在左边,等于3的数放在中间,大于3的数放在右边。

    答:
    1.指定左边界下标p1为-1,右边界下标p2为5
    2.接着从i=0遍历数组,若arr[i]小于最右侧数,将p1右移,交换p1和i位置的数,i右移。若arr[i]等于最右侧数,i右移。若arr[i]大于最右侧数,将p2左移,交换p2和i位置的数,i不移动。
    3.第1次得到1,|3,4,2,5,|3
    第2次得到1,|3,5,2,|4,3
    第3次得到1,|3,2,|5,4,3
    第4次得到1,2,3,||5,4,3
    p1=p2 退出循环
    将p2与最右侧3交换得到 1,2,3,3,4,5

快排3.0就是将原数组分为三份,左边是小于等于a的,中间是等于a的,右边是大于a的,虽然左边和右边依然乱序,但是a的位置是顺序的!!!,我们就定位到了a的位置。然后我们将左边和右边的数组分别分为三份,又确定了左a和右a的位置,一直切割下去直到只有一个数(单数就是有序)

我们如何选取a呢,如果选择最右侧的,存在最极端的情况是1,2,3,4,5,只切割成T(4/5)+T(1/5),接近O(n^2),而取到3,能很好的均分,所以我们a选择其中的随机数,就能做到O(n*logn)

代码实现:


func quickSort(arr []int) {
	if arr == nil || len(arr) < 2 {
		return
	}
	qS(arr, 0, len(arr)-1)
}

func qS(arr []int, l, r int) {

	if l < r {
		swap(arr, l+rand.Intn(r-l+1), r) //将最右边的数与随机数交换
		p := partition(arr, l, r)
		qS(arr, l, p[0]-1)
		qS(arr, p[1]+1, r)
	}
}

// 该方法就是取最右边的数为锚点
// 小于最右边的数放在左侧 等于放中间 大于放右边
// 返回中间数的最左侧 和最右侧下标
func partition(arr []int, l, r int) []int {
	p1 := l - 1  //左侧小于目标数下标
	p2 := r      //右侧大于目标数结界 这里没有加一是因为把最右边的数当做比较值
	for l < p2 { //当遍历到大于结界就停止 不需要遍历到最右侧
		if arr[l] < arr[r] {
			p1++
			swap(arr, p1, l)
			l++
		} else if arr[l] == arr[r] {
			l++
		} else {
			p2--
			swap(arr, l, p2)
		}
	}
	swap(arr, p2, r)         //将最右侧的3 放到右侧结界处
	return []int{p1 + 1, p2} //返回中间数据的最左边和最右边
}

func main() {
	arr := []int{1, 3, 4, 2, 5, 3}
	quickSort(arr)
	fmt.Println(arr)
}

### 回答1: 很抱歉,由于篇幅限制,我不能在这里提供七个排序算法的完整 Golang 代码。但是,我可以简述每种算法的实现思路,并给出部分代码: 1. 冒泡排序:比较相邻的两个数,如果第一个数比第二个数大,就交换它们两个。对每一对相邻元素作同样的工作,从开始第一对到结尾的最后一对,这样在最后的元素应该是最大的数。针对所有的元素重复以上的步骤,除了最后一个。持续每次对越来越少的元素重复上面的步骤,直到没有任何一对数字需要比较。 ``` func BubbleSort(arr []int) { n := len(arr) for i := 0; i < n-1; i++ { for j := 0; j < n-i-1; j++ { if arr[j] > arr[j+1] { arr[j], arr[j+1] = arr[j+1], arr[j] } } } } ``` 2. 选择排序:首先在未排序的数列中找到最小元素,然后将其存放到数列的起始位置。接着,再从剩余未排序的元素中继续寻找最小元素,然后放到已排序序列的末尾。以此类推,直到所有元素均排序完毕。 ``` func SelectSort(arr []int) { n := len(arr) ### 回答2: 以下是七大排序算法Golang代码: 1. 冒泡排序(Bubble Sort): ``` func BubbleSort(arr []int) { length := len(arr) for i := 0; i < length-1; i++ { for j := 0; j < length-1-i; j++ { if arr[j] > arr[j+1] { arr[j], arr[j+1] = arr[j+1], arr[j] } } } } ``` 2. 选择排序(Selection Sort): ``` func SelectionSort(arr []int) { length := len(arr) for i := 0; i < length-1; i++ { minIndex := i for j := i+1; j < length; j++ { if arr[j] < arr[minIndex] { minIndex = j } } arr[i], arr[minIndex] = arr[minIndex], arr[i] } } ``` 3. 插入排序(Insertion Sort): ``` func InsertionSort(arr []int) { length := len(arr) for i := 1; i < length; i++ { key := arr[i] j := i - 1 for j >= 0 && arr[j] > key { arr[j+1] = arr[j] j-- } arr[j+1] = key } } ``` 4. 希尔排序(Shell Sort): ``` func ShellSort(arr []int) { length := len(arr) gap := 1 for gap < length/3 { gap = gap*3 + 1 } for gap > 0 { for i := gap; i < length; i++ { temp := arr[i] j := i - gap for j >= 0 && arr[j] > temp { arr[j+gap] = arr[j] j -= gap } arr[j+gap] = temp } gap = gap / 3 } } ``` 5. 归并排序(Merge Sort): ``` func MergeSort(arr []int) []int { if len(arr) <= 1 { return arr } mid := len(arr) / 2 left := MergeSort(arr[:mid]) right := MergeSort(arr[mid:]) return merge(left, right) } func merge(left, right []int) []int { var result []int for len(left) > 0 && len(right) > 0 { if left[0] <= right[0] { result = append(result, left[0]) left = left[1:] } else { result = append(result, right[0]) right = right[1:] } } result = append(result, left...) result = append(result, right...) return result } ``` 6. 快速排序(Quick Sort): ``` func QuickSort(arr []int) { quickSortHelper(arr, 0, len(arr)-1) } func quickSortHelper(arr []int, low, high int) { if low < high { pivot := partition(arr, low, high) quickSortHelper(arr, low, pivot-1) quickSortHelper(arr, pivot+1, high) } } func partition(arr []int, low, high int) int { pivot := arr[high] i := low - 1 for j := low; j < high; j++ { if arr[j] < pivot { i++ arr[i], arr[j] = arr[j], arr[i] } } arr[i+1], arr[high] = arr[high], arr[i+1] return i + 1 } ``` 7. 堆排序(Heap Sort): ``` func HeapSort(arr []int) { size := len(arr) for i := size/2 - 1; i >= 0; i-- { heapify(arr, size, i) } for i := size - 1; i > 0; i-- { arr[i], arr[0] = arr[0], arr[i] heapify(arr, i, 0) } } func heapify(arr []int, size int, root int) { largest := root left := 2 * root + 1 right := 2 * root + 2 if left < size && arr[left] > arr[largest] { largest = left } if right < size && arr[right] > arr[largest] { largest = right } if largest != root { arr[root], arr[largest] = arr[largest], arr[root] heapify(arr, size, largest) } } ``` 希望这些代码能帮助你了解每个排序算法的实现。 ### 回答3: 下面是七种常见的排序算法Golang代码实现: 1. 冒泡排序: ```go func bubbleSort(arr []int) { n := len(arr) for i := 0; i < n-1; i++ { for j := 0; j < n-i-1; j++ { if arr[j] > arr[j+1] { arr[j], arr[j+1] = arr[j+1], arr[j] } } } } ``` 2. 选择排序: ```go func selectionSort(arr []int) { n := len(arr) for i := 0; i < n-1; i++ { minIndex := i for j := i + 1; j < n; j++ { if arr[j] < arr[minIndex] { minIndex = j } } arr[i], arr[minIndex] = arr[minIndex], arr[i] } } ``` 3. 插入排序: ```go func insertionSort(arr []int) { n := len(arr) for i := 1; i < n; i++ { key := arr[i] j := i - 1 for j >= 0 && arr[j] > key { arr[j+1] = arr[j] j-- } arr[j+1] = key } } ``` 4. 归并排序: ```go func mergeSort(arr []int) []int { if len(arr) <= 1 { return arr } mid := len(arr) / 2 left := mergeSort(arr[:mid]) right := mergeSort(arr[mid:]) return merge(left, right) } func merge(left, right []int) []int { result := make([]int, 0) i, j := 0, 0 for i < len(left) && j < len(right) { if left[i] < right[j] { result = append(result, left[i]) i++ } else { result = append(result, right[j]) j++ } } result = append(result, left[i:]...) result = append(result, right[j:]...) return result } ``` 5. 快速排序: ```go func quickSort(arr []int, low, high int) { if low < high { pivot := partition(arr, low, high) quickSort(arr, low, pivot-1) quickSort(arr, pivot+1, high) } } func partition(arr []int, low, high int) int { pivot := arr[high] i := low - 1 for j := low; j < high; j++ { if arr[j] < pivot { i++ arr[i], arr[j] = arr[j], arr[i] } } arr[i+1], arr[high] = arr[high], arr[i+1] return i + 1 } ``` 6. 堆排序: ```go func heapSort(arr []int) { n := len(arr) for i := n/2 - 1; i >= 0; i-- { heapify(arr, n, i) } for i := n - 1; i > 0; i-- { arr[0], arr[i] = arr[i], arr[0] heapify(arr, i, 0) } } func heapify(arr []int, n, i int) { largest := i left := 2*i + 1 right := 2*i + 2 if left < n && arr[left] > arr[largest] { largest = left } if right < n && arr[right] > arr[largest] { largest = right } if largest != i { arr[i], arr[largest] = arr[largest], arr[i] heapify(arr, n, largest) } } ``` 7. 计数排序: ```go func countingSort(arr []int) []int { max := 0 for _, num := range arr { if num > max { max = num } } count := make([]int, max+1) for _, num := range arr { count[num]++ } sortedArr := make([]int, 0, len(arr)) for i, c := range count { for c > 0 { sortedArr = append(sortedArr, i) c-- } } return sortedArr } ```
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值