Go实现常用的排序

背景

学习Go语言的基础内容后,准备写点内容练练手,用Go实现平时常用的排序算法;也方便之后复习。

package main

import (
	"fmt"
	"math/rand"
)

//测试Go里的排序

//Go版本
//https://blog.csdn.net/weixin_41922289/article/details/103350301

//java版
//https://blog.csdn.net/weixin_41922289/article/details/100574583#_7

//排序算法的稳定性
//假定在待排序的记录序列中,存在多个具有相同的关键字的记录,若经过排序,这些记录的相对次序保持不变,
//即在原序列中,r[i]=r[j],且r[i]在r[j]之前,而在排序后的序列中,r[i]仍在r[j]之前,则称这种排序算法是稳定的;否则称为不稳定的。



//稳定排序,时间复杂度 O(n^2) 空间复杂度 O(1)
//思路:找到切片中的最大值放在最后,再找第二大放在倒数第二位 ...
func bubbleSort(data []int) {
	if len(data) <= 1 {
		return
	}
	for i := 0; i < len(data); i++ {
		for j := 0; j < len(data)-i-1; j++ {
			if data[j] > data[j+1] {
				data[j], data[j+1] = data[j+1], data[j]
			}
		}
	}
}

//选择排序
//不稳定排序: 数据项移动最少的排序算法,每次遍历只移动2个元素的位置   时间复杂度:O(n^2)    空间复杂度: O(1)
//思路:和冒泡排序相反,选择排序是先找到最小的放在切片的最前面 ...
func selectSort(data []int) {
	for i := 0; i < len(data); i++ {
		minIndex := i
		for j := i + 1; j < len(data); j++ {
			if data[minIndex] > data[j] {
				minIndex = j
			}
		}
		data[i], data[minIndex] = data[minIndex], data[i]
	}
}

//插入排序
//稳定排序 事件复杂度:O(n^2) 空间复杂度:O(1)
//思路:外侧循环从index = 1开始,保证左侧的元素是有序的,依次向后取元素,对比左侧有序数组,插入合适位置
func insertSort(data []int) {
	//假设左侧的元素都是有序的,外侧循环负责遍历获取右侧的排序元素
	//内侧的循环负责对比当前拿取的元素应该插入的位置
	var j int
	for i := 1; i < len(data); i++ {
		temp := data[i]
		for j = i; j > 0 && data[j-1] > temp; j-- {
			data[j] = data[j-1]
		}
		//不满足则跳出内测循环,此位置就是插入的位置
		data[j] = temp
	}
}

//希尔排序
//不稳定排序  事件复杂度 O(n)  空间复杂度:O(1)
//思路:基于插入排序,只不过引入了步长的概念,尽量把大的元素放在后面,小的元素放在前边
//  相比较插入排序减少元素移动的次数;还是插入排序的思想
func shellSort(data []int) {
	//外侧循环控制步长
	var j int
	for h := len(data) / 2; h > 0; h /= 2 {
		//内测就是一个插入排序
		for i := h; i < len(data); i++ {
			temp := data[i]
			for j = i; j >= h && temp < data[j-h]; j -= h {
				data[j] = data[j-h]
			}
			data[j] = temp
		}
	}
}

//快速排序
//不稳定排序  时间复杂度:O(nlogn) 空间复杂度:O(1)
//思路:指定一个基准值:base,左右双指针遍历,<=base 放在左侧,>base 放在右侧,指针最后停留的位置
// 就是分割点,重复操作分割点左右两个集合
func quikSort1(data []int) {
	if len(data) < 1 {
		return
	}
	l, r := 0, len(data)-1
	//基准值
	base := data[0]
	for l < r {
		for l < r && data[r] >= base {
			r--
		}
		if l < r {
			data[l] = data[r]
		}
		for l < r && data[l] < base {
			l++
		}
		if l < r {
			data[r] = data[l]
		}
	}
	data[l] = base
	quikSort1(data[:l])
	quikSort1(data[l+1:])
}

//堆排序 : https://www.jb51.net/article/245744.htm
//大顶堆(大堆):每个节点的值必须大于它的两个子节点   小顶堆(小堆):每个节点的值必须小于它的两个子节点
//完全二叉树:设二叉树的深度为h,除第 h 层外,其它各层 (1~h-1) 的结点数都达到最大个数,第 h 层所有的结点都连续集中在最左边,这就是完全二叉树。
//                        A(1)
//				 B(2)					 C(3)
//        D(4)          E(5)     F(6)            G(7)
//   H(8)    I(9)  J(10)

// 堆中最后一个节点的父节点下标: length / 2 -1
// 任何一个下标为n的节点的左右子节点下标为: 左子节点:ln = n * 2 + 1   右子节点: rn = n * 2 + 2
func heapSort(data []int) {
	//堆排序,只能确认确认根节点是最大值或者最小值
	//调换根节点和最后一个元素的位置,再依次构建i-1个元素的堆... 依次类推
	i := len(data)
	for i > 1 {
		//构建堆
		buildHeap(data, i)
		//已大堆为例
		//swap(&data[0], &data[i-1])
		data[0], data[i-1] = data[i-1], data[0]
		i--
	}
}

func buildHeap(data []int, len int) {
	//找到最后一个节点的父节点
	parent := len/2 - 1
	for parent >= 0 {
		heapify(data, parent, len)
		parent--
	}
	fmt.Println(data[:len])
}

func heapify(data []int, parent, len int) {
	//判断两个子节点是否比父节点大,如果是则替换
	max := parent
	lson := parent*2 + 1
	rson := parent*2 + 2
	if lson < len && data[lson] > data[max] {
		max = lson
	}
	if rson < len && data[rson] > data[max] {
		max = rson
	}
	if parent != max {
		swap(&data[max], &data[parent])
		heapify(data, max, len)
	}
}

//交换位置
func swap(a, b *int) {
	*a, *b = *b, *a
}

//归并排序
// 稳定排序  时间复杂度:O(nlogn) 空间复杂度:O(n)
// 思路: 分治法,拆分为左右两部分并分别排好序,再结合成一个新的切片
func mergeSort(data []int) (result []int) {
	if len(data) <= 1 {
		return data
	}
	mid := len(data) / 2
	left := mergeSort(data[:mid])
	right := mergeSort(data[mid:])
	return merge(left, right)
}

func merge(left, right []int) (result []int) {
	l, r := 0, 0
	for l < len(left) && r < len(right) {
		if left[l] > right[r] {
			result = append(result, right[r])
			r++
		} else {
			result = append(result, left[l])
			l++
		}
	}
	result = append(result, left[l:]...)
	result = append(result, right[r:]...)
	return
}

func main() {
	intSlice := make([]int, 0, 30)
	for i := 0; i < 30; i++ {
		intSlice = append(intSlice, rand.Intn(100))
	}
	for _, e := range intSlice {
		fmt.Printf("%d \t", e)
	}
	fmt.Printf("\n")
	fmt.Println("--------------------------分割线----------------------------")
	//冒泡
	//bubbleSort(intSlice)
	//选择排序
	//selectSort(intSlice)
	//插入排序
	//insertSort(intSlice)
	//希尔排序
	//shellSort(intSlice)
	//快速排序
	//quikSort1(intSlice)
	//堆排序
	//heapSort(intSlice)
	//归并排序
	mergeSort := mergeSort(intSlice)
	for _, s := range mergeSort {
		fmt.Printf("%d \t", s)
	}
	/*for _, s := range intSlice {
		fmt.Printf("%d \t", s)
	}*/
}

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值