Go 实现10种排序算法 ??
比较类排序: ————————————————————————————————————————
1、冒泡排序
从头开始两两互比然后进行交换。将最大值/最小值 冒到最后一位。依次循环
func bubbleSort(nums []int){
for i:=0;i<len(nums)-1;i++{
for j:=0;j<len(nums)-1-i;j++{
if nums[j]>nums[j+1]{
nums[j],nums[j+1]=nums[j+1],nums[j]
}
}
}
}
————————————————————————————————————————
2、 选择排序
在未排序序列中找到最小(大)元素,存放到排序序列的起始位置; 再从剩余未排序元素中继续寻找最小(大)元素,然后放到已排序序列的末尾; (一次在元素中选择符合需求的元素,然后交换。每个元素只移动一次)
func selectSorted(nums []int) {
for i := 0; i < len(nums)-1; i++ {
min := i
for j := i + 1; j < len(nums); j++ {
if nums[min] > nums[j] {
min = j
}
}
nums[i], nums[min] = nums[min], nums[i]
}
}
————————————————————————————————————————
3、插入排序
可以假设前面的已经有序,随机在后面抽取一个元素,插入到前面,并保持有序; 已排好序的依次后移!!! (扑克牌:每拿起一张,插入到合适的位置。之前的已经有序)
func insertSorted(nums []int) {
for i := 1; i < len(nums); i++ {
preIndex := i - 1
nowNum := nums[i]
for nums[preIndex] > nowNum {
nums[preIndex+1] = nums[preIndex]
preIndex--
}
nums[preIndex+1] = nowNum
}
}
————————————————————————————————————————
4、希尔排序
本质是 分组+插入排序 也称递减增量排序算法,是插入排序的一种更高效的改进版本。但希尔排序是 非稳定 排序算法。 实践中:数据的交换次数远远小于插入排序的交换次数
func shellSorted(nums []int) {
lens := len(nums)
tag := 1
for tag < lens/3 {
tag = 3*tag + 1
}
for tag > 0 {
for i := tag; i < lens; i++ {
j := i - tag
temp := nums[i]
for j >= 0 && nums[j] > temp {
nums[j+tag] = nums[j]
j -= tag
}
nums[j+tag] = temp
}
tag /= 3
}
}
5、归并排序
乱序数组,单个元素为一组,两两对比排序;然后已排序数据2个为一组,两两对比排序,以此类推 总结:先分后合(合的时候排好序)
func mergeSorted(nums []int) []int {
length := len(nums)
if length < 2 {
return nums
}
left := nums[:length/2]
right := nums[length/2:]
return merge(mergeSorted(left), mergeSorted(right))
}
func merge(left []int, right []int) []int {
var res []int
for len(left) > 0 && len(right) > 0 {
if left[0] <= right[0] {
res = append(res, left[0])
left = left[1:]
} else {
res = append(res, right[0])
right = right[1:]
}
}
if len(left) > 0 {
res = append(res, left...)
}
if len(right) > 0 {
res = append(res, right...)
}
return res
}
6、快速排序
在平均状况下,排序 n 个项目要 Ο(nlogn) 次比较 随机取一个树(一般是第一个数),一次比较,比他小的放左边,大的放右边,依次类推 个人感觉:和归并反着来! 每一次分开,左右都对应已比较完成! 1、从数列中挑出一个元素,称为 “基准”(pivot); 2、重新排序数列,所有元素比基准值小的摆放在基准前面,所有元素比基准值大的摆在基准的后面(相同的数可以到任一边)。在这个分区退出之后,该基准就处于数列的中间位置。这个称为分区(partition)操作; 3、递归地(recursive)把小于基准值元素的子数列和大于基准值元素的子数列排序; 优化: 1、机选三个数,取中间的数为 基准 2、当数组比较小的时候采用插入算法,更快
func quickSorted(nums []int) []int {
return quick(nums, 0, len(nums)-1)
}
func quick(arr []int, left, right int) []int {
if left < right {
partitionVal := partition(arr, left, right)
quick(arr, left, partitionVal-1)
quick(arr, partitionVal+1, right)
}
return arr
}
func partition(arr []int, left, right int) int {
pivot := left
index := left + 1
for i := index; i <= right; i++ {
if arr[i] < arr[pivot] {
swap(arr, i, index)
index++
}
}
swap(arr, pivot, index-1)
return index - 1
}
func swap(arr []int, left, right int) {
arr[left], arr[right] = arr[right], arr[left]
}
7、堆排序
堆的特点: 完全二叉树、(大顶堆) 所有父节点大于子节点 1、构建一个堆(所有值小于父节点) 2、把堆首和队尾互换 3、堆的大小减一,并重构堆,目的是把最大值放入堆头 4、重复2/3
func heapSorted(arr []int) []int {
arrlen := len(arr)
buildHeap(arr, arrlen)
fmt.Println(arr)
for arrlen > 0 {
swap(arr, 0, arrlen-1)
arrlen -= 1
heapify(arr, 0, arrlen)
}
return arr
}
func buildHeap(arr []int, arrlen int) {
for i := arrlen / 2; i >= 0; i-- {
heapify(arr, i, arrlen)
}
}
func heapify(arr []int, i, arrlen int) {
left := 2*i + 1
right := 2*i + 2
parent := i
if left < arrlen && arr[left] > arr[parent] {
parent = left
}
if right < arrlen && arr[right] > arr[parent] {
parent = right
}
if parent != i {
swap(arr, i, parent)
heapify(arr, parent, arrlen)
}
}
func swap(arr []int, i, j int) {
arr[i], arr[j] = arr[j], arr[i]
}
基数排序 vs 计数排序 vs 桶排序 基数排序有两种方法: 这三种排序算法都利用了桶的概念,但对桶的使用方法上有明显差异: 基数排序:根据键值的每位数字来分配桶; 计数排序:每个桶只存储单一键值; 桶排序:每个桶存储一定范围的数值;
8 、计数排序
1、根据待排序集合中最大元素和最小元素的差值范围,申请额外空间; 2、遍历待排序集合,将每一个元素出现的次数记录到元素值对应的额外空间内; 3、对额外空间内数据进行计算,得出每一个元素的正确位置; 4、将待排序集合每一个元素移动到计算得出的正确位置上。 即: 将相同的数,统计出现次数,然后从小到大,把该数取出对应的次数
func counrSorted(arr []int) []int {
min, max := 0, 0
for i := 0; i < len(arr); i++ {
if arr[i] > max {
max = arr[i]
}
if arr[i] < min {
min = arr[i]
}
}
blen := max - min + 1
bucket := make([]int, blen)
fmt.Println(blen)
for i := 0; i < len(arr); i++ {
bucket[arr[i]-min] += 1
}
index := 0
for i := 0; i < len(bucket); i++ {
for bucket[i] > 0 {
arr[index] = i - min
index++
bucket[i]--
}
}
return arr
}
9、桶排序
桶排序是计数排序的升级版。它利用了函数的映射关系,高效与否的关键就在于这个映射函数的确定。 桶排序 (Bucket sort)的工作的原理:
假设输入数据服从均匀分布,将数据分到有限数量的桶里,每个桶再分别排序(有可能再使用别的排序算法或是以递归方式继续使用桶排序进行排)
计数排序时对单个数据,桶排序是对一组数据。分配再排序
func bucketSorted(arr []int){
vari;
varminValue = arr[0];
varmaxValue = arr[0];
for(i = 1; i < arr.length; i++) {
if(arr[i] < minValue) {
minValue = arr[i];
}elseif(arr[i] > maxValue) {
maxValue = arr[i];
}
}
varDEFAULT_BUCKET_SIZE = 5;
bucketSize = bucketSize || DEFAULT_BUCKET_SIZE;
varbucketCount = Math.floor((maxValue - minValue) / bucketSize) + 1;
varbuckets =newArray(bucketCount);
for(i = 0; i < buckets.length; i++) {
buckets[i] = [];
}
for(i = 0; i < arr.length; i++) {
buckets[Math.floor((arr[i] - minValue) / bucketSize)].push(arr[i]);
}
arr.length = 0;
for(i = 0; i < buckets.length; i++) {
insertionSort(buckets[i]);
for(varj = 0; j < buckets[i].length; j++) {
arr.push(buckets[i][j]);
}
}
returnarr;
10、基数排序
基数排序是一种非比较型整数排序算法,其原理是将整数按位数切割成不同的数字,然后按每个位数分别比较。由于整数也可以表达字符串(比如名字或日期)和特定格式的浮点数,所以基数排序也不是只能使用于整数。 对数字或者字符串进行拆分,单个对比的排序。以此类推
func radixSorted(arr []int) []int {
max := -1
length := len(arr)
for i := 0; i < length; i++ {
if max < arr[i] {
max = arr[i]
}
}
maxlen := 0
for max > 0 {
maxlen++
max /= 10
}
bucket := [10][20]int{{0}}
count := [10]int{0}
divisor := 1
for i := 1; i <= maxlen; i++ {
for j := 0; j < length; j++ {
tmp := arr[j]
index := (tmp / divisor) % 10
bucket[index][count[index]] = tmp
count[index]++
}
k := 0
for m := 0; m < len(bucket); m++ {
if count[m] == 0 {
continue
}
for n := 0; n < count[n]; n++ {
arr[k] = bucket[m][n]
k++
}
bucket[m] = [20]int{0}
}
divisor *= 10
}
return arr
}```
|