算法3---堆排序和桶排序(golang)

  • 堆排序

堆结构就是用数组实现的完全二叉树结构
完全二叉树如果每颗子树的最大值在顶部就是大根堆(小根堆相反)

大根堆:

9
8
7
6
1
5
4

给定任意一个数组,将其转换成大根堆:

1
5
2
3
9
7
4

该数组为{1,5,2,3,9,7,4},将父节点比子节点小的数往下移
下标为i的左子为2i+1,如arr[2]=2 2i+1=5 它的左子arr[5]=7 右子arr[2i+2]=4
定义heapfify方法,将i位置的数与左子右子比较,父不是最大的就往下交换,再与子节点比较


func heapify(arr []int, index int, heapSize int) {
	//如果该节点有左子
	left := 2*index + 1
	for left < heapSize {
		//定义large 为 left right 父节点三者中最大值的下标
		large := left
		//如果右子存在且值大于左子
		if left+1 < heapSize && arr[left+1] > arr[left] {
			//更新下标为右节点
			large = left + 1
		}

		//
		if arr[index] > arr[large] {
			//更新下标为父节点
			large = index
			//最大是自己就不用排了
			break
		}
		//把父跟子换位置
		Swap(arr, index, large)
		index = large
		//到了新位置,继续往下比较 直到比两个儿子都大或者没有儿子
		left = 2*index + 1

	}

}

大根堆的根节点肯定是最大的数,我们将根节点与数组最右边的数交换,就得到该数组最大的数,再讲index=0的进行heapify,得到大根堆,将根节点与数组最右边-1的数交换,周而复始,完成堆排序

package main

import (
	"fmt"
	"sort"
)

func Swap(arr []int, i, j int) {
	if i == j {
		return
	}
	arr[i] = arr[i] ^ arr[j]
	arr[j] = arr[i] ^ arr[j]
	arr[i] = arr[i] ^ arr[j]
}
func heap(arr []int) {
	if len(arr) < 2 {
		return
	}
	//将数组转换成大根堆
	//这里是第一种思路,依次从左往右heapinsert
	//for index, _ := range arr {
	//	heapInsert(arr, index)
	//}

	//这里是第二种思路,从右往左heapify,比第一种更快,时间复杂度O(n)
	for i := len(arr) - 1; i >= 0; i-- {
		heapify(arr, i, len(arr))
	}

	heapSize := len(arr) //这里用headsize指定arr长度,就可以避免再开一个数组存结果
	for heapSize > 0 {
		//每次提取根节点(最大的数),然后将最后一个节点放到根节点
		Swap(arr, 0, heapSize-1)
		heapSize--

		//因为根节点是新人,将它heapify一次,就又能得到大根堆,根节点就是最大的,周而复始,直到heap为空
		heapify(arr, 0, heapSize)
	}
}

func heapInsert(arr []int, index int) {
	for arr[index] > arr[(index-1)/2] {
		Swap(arr, index, (index-1)/2)
		index = (index - 1) / 2
	}
	sort.Ints(arr)
}

func heapify(arr []int, index int, heapSize int) {
	//如果该节点有左子
	left := 2*index + 1
	for left < heapSize {
		//定义large 为 left right 父节点三者中最大值的下标
		large := left
		//如果右子存在且值大于左子
		if left+1 < heapSize && arr[left+1] > arr[left] {
			//更新下标为右节点
			large = left + 1
		}

		//
		if arr[index] > arr[large] {
			//更新下标为父节点
			large = index
			//最大是自己就不用排了
			break
		}
		//把父跟子换位置
		Swap(arr, index, large)
		index = large
		//到了新位置,继续往下比较 直到比两个儿子都大或者没有儿子
		left = 2*index + 1

	}

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

  • 桶排序

给一个数组 1 5 2 7 4 1,求排序后结果
定一个长度为10(十进制为10,三进制为3)的数组 count ,记录下标0 1 2 3 4 5 6 7 8 9的个数,比如1有两个,所以count[1]=2,count[5]=1,count[2]=1
那么能得到count[0,2,1,0,1,1,0,1,0,0] ,遍历count得到1 1 2 4 5 7

现在我们加大难度,求10 13 20 3 23 9 19 的排序结果
先排个位数,定一个长度为0-9的数组 count ,记录下标0 1 2 3 4 5 6 7 8 9的个数,比如个位数是9的有两个,所以count[9]=2,count[3]=3
那么能得到count[2,0,0,3,0,0,0,0,0,2]
所以排序后,个位数=1的在最左边两个,个位数为3在中间3个,个位数为9的在右边两个
arr最右边的19的个位数是9,对应最右边两个,排序后的下标就是2+3+2-1=6 ,所以bucket[6]=19,也就是下标=(count[i]+count[i-1]+…+count[0])-1
count=2 2 2 5 5 5 5 5 7
从右往左遍历题目arr,来到19,对应下标7-1=6,bucket[6]=19 count=2 2 2 5 5 5 5 5 6
来到9,对应下标6-1=5, bucket[5]=9 count= 2 2 2 5 5 5 5 5 5
来到23 ,对应下标5-1=4 ,bucket[4]=23 count = 2 2 2 4 5 5 5 5 5

得到bucket=[0 20 13 3 23 9 19] ,将它复制到arr中
再同理对arr十位数排序,得到 [0 3 9 13 19 20 23] ,由此完成全部排序

代码实现

package main

import (
	"fmt"
)

func radixSort(arr []int) {
	if len(arr) < 2 {
		return
	}
	rS(arr, 0, len(arr)-1, maxBits(arr))
}

// 求最大位数
func maxBits(arr []int) (bits int) {
	max := 0
	for _, a := range arr {
		if a > max {
			max = a
		}
	}
	for max != 0 {
		max /= 10
		bits++
	}
	return bits
}

func rS(arr []int, l, r, digit int) {
	bucket := make([]int, len(arr))
	for d := 1; d <= digit; d++ {
		count := make([]int, 10)
		for _, a := range arr {
			ad := getDigit(a, d)
			count[ad]++
		}

		for i, _ := range count {
			if i > 0 {
				count[i] += count[i-1]
			}
		}

		for i := len(arr) - 1; i > 0; i-- {
			ad := getDigit(arr[i], d)
			bucket[count[ad]-1] = arr[i]
			count[ad]--
		}
		for i, a := range bucket {
			arr[i] = a
		}
		fmt.Println(arr, count)
	}
}

func getDigit(num, digit int) int {
	pow := 1
	if digit > 1 {
		for i := 1; i < digit; i++ {
			pow *= 10
		}
	}
	return num / pow % 10
}

func main() {
	arr := []int{10, 13, 20, 3, 23, 9, 19}
	radixSort(arr)
	fmt.Println(arr)
}


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值