go常用排序算法

53 篇文章 4 订阅
15 篇文章 0 订阅

go常用排序算法

之前写过一篇用java实现的常用排序算法的博客,可以参考,这次改用go语言实现,一来用来复习下排序算法,二来也可以学习下go语言,java常用排序算法总结

一,冒泡排序

在这里插å
¥å›¾ç‰‡æè¿°

/**
冒泡排序
 */
func bubbleSort(data []int){
	for i := 0;i < len(data) - 1;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]
			}
		}
	}
}
  • 在乱序的条件下,时间复杂度是O(N^2),有序条件下时间复杂度是O(N)
  • 稳定排序
  • 空间复杂度:O(1)

二,选择排序

img

首先找到数组中的最小元素,然后将这个最小元素和数组的第一个元素交换位置,如果第一个元素就是最小元素,就和自己交换位置;再次,在剩下的元素中找到最小元素和数组中的第二个元素交换位置,如此往复,直到将整个数组排序,一句话总结就是,不断在剩余元素中找最小元素

/**
选择排序
 */
func selectSort(data []int){
	for i := 0;i < len(data) - 1;i++{
		//每次循环找出 i + 1到数组最后一个元素的这个区间的最小值,然后最小值和当前元素data[i]比较
		minIndex := i
		for j := i+1;j < len(data);j++{
			if data[j] < data[minIndex]{
				minIndex = j
			}
		}
		if i != minIndex{
			data[i],data[minIndex] = data[minIndex],data[i]
		}
	}
}
  • 不稳定排序
  • 时间复杂度:O(n^2)
  • 空间复杂度:O(1)
  • 选择排序是数据移动最少的,每次交换两会改变两个数组元素的值,因此选择排序用了N次交换,交换次数和数组大小是线性关系

三,插入排序

在这里插å
¥å›¾ç‰‡æè¿°

与选择排序一样,当前索引左边的所有元素都是有序的,但是他们的最终位置还不确定,为了给更小的元素腾出空间,他们可能会被移动,但是当索引到达数组的末端,数组排序就完成了

/**
插入排序
 */
func insertSort(data []int){
	var j int
	for i := 1;i < len(data);i++{
		temp := data[i]
		for j = i;j > 0 && temp < data[j - 1];j--{
			data[j] = data[j - 1]
		}
		data[j] = temp
	}
}
  • 稳定排序
  • 时间复杂度分析:O(N^2),如果序列在排序前已经是有序序列,则为O(N)
  • 空间复杂度分析:O(1)
  • 数据量较少时效率高。插入排序适合数据量少的情况
  • 算法的实际运行效率优于选择排序和冒泡排序。
  • 插入排序对于部分有序的数组很有效,部分有序的数组类如数组中每个元素距离他的最终位置不远,一个有序的大数组接一个小数组,数组中只有几个元素的位置不正确

四,希尔排序

在这里插å¥å›¾ç‰‡æè¿°

希尔排序是一种基于插入排序的快速排序算法,对于大规模的数据,插入排序很慢,因为随着数据规模的增大,移动规模也可能随着增大,因为插入排序只会交换相邻的元素,元素只能一点点的从数组一端移动到指定位置,但是希尔排序是使用一种步长的思想,根据指定步长分为h个子数组,每个子数组进行插入排序,形成了h个有序子数组

/**
希尔排序
 */

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
		}
	}
}
  • 不稳定排序

  • 希尔排序的时间复杂度较直接插入排序低,它的时间是所取“增量”(步长gap)序列的函数。

  • 最好时间复杂度: O(n) – 有序情况下

  • 平均时间复杂度: O(1.2^n ~ 1.5^n) – Hibbard

  • 最坏时间复杂度: O(n^2) — 希尔增量

  • 空间复杂度:O(1)

五,快速排序

img

快速排序是将一个数组分成两个子数组,将两个子数组分别独立排序,当两个子数组有序时数组就有序,需要找到一个基准值来划分数组成两个子数组,分别是比基准值大和比基准值小,递归的进行排序

/**
快速排序
 */
func quickSort(data []int){
	if len(data) < 1{
		return
	}
	l,r := 0,len(data) - 1
	//基准值
	base := data[0]
	for i := 0;i <= r;{
		//比基准值大的放右边
		if data[i] > base{
			data[r],data[i] = data[i],data[r]
			r--
		}else{
			//比基准值小或等于的放左边
			data[l],data[i] = data[i],data[l]
			l++
			i++
		}
	}
	quickSort(data[:l])
	quickSort(data[l + 1:])
}
  • 时间复杂度:O(nlogn)
  • 空间复杂度:O(1)
  • 不稳定排序
  • 算法思想:分治

快速排序优化:

  • 对于小数组,快速排序比插入排序要慢,所以当对小数组进行排序时,可以切换到插入排序

六,堆排序

在这里插å¥å›¾ç‰‡æè¿°

堆排序是基于二叉堆(优先队列)实现的,通过建立最大堆或者最小堆,删除堆顶的最值元素后将堆顶元素放入指定集合,重新建堆,最后集合便是有序序列

/**
堆排序
*/
func heapSort(data []int){
	m := len(data)
	s := m / 2
	for i := s;i >= 0;i--{
		heap(data,i,m - 1)
	}
	for i := m - 1;i > 0;i--{
		data[i], data[0] = data[0], data[i]
		heap(data, 0, i-1)
	}
}

//以i为根节点建堆
func heap(data []int,i int,end int){
	//左子节点
	l := 2 * i + 1
	if l > end{
		return
	}
	n := l
	//右子节点
	r := 2 * i + 2
	//当前最大元素是右子节点
	if r <= end && data[r] > data[n]{
		n = r
	}
	if data[i] > data[n]{
		return
	}
	//比根节点大则交换
	data[i],data[n] = data[n],data[i]
	//往下建堆
	heap(data,n,end)
}
  • 不稳定排序
  • 时间复杂度:O(nlogn)

七,归并排序

在这里插å¥å›¾ç‰‡æè¿°

归并排序将数组分成两个子数组分别排序,并将有序的子数组归并以将整个数组排序

/**
归并排序
 */
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 []int,right []int)(result []int){

	l,r := 0,0
	//排序,左右两边比较,放入result
	for l < len(left) && r < len(right){
		if left[l] > right[r]{
			result = append(result,left[l])
			l++
		}else{
			result = append(result,right[r])
			r++
		}
	}
	result = append(result,left[l:]...)
	result = append(result,right[r:]...)
	return
}
  • 算法思想:分治
  • 时间复杂度:O(nlogn)
  • 空间复杂度:O(n)
  • 稳定排序

八,计数排序

在这里插å¥å›¾ç‰‡æè¿°

/**
计数排序
 */
func countSort(data []int){

	min := getMin(data)
	max := getMax(data)
    //建立辅助数组
	c := make([]int, max - min + 1)
	for i := range data{
        //以跟最小值的差为下标计数
		j := data[i] - min
		c[j]++
	}
	k := 0
    //恢复数组
	for i := range c{
		for ; c[i] != 0 ;{
			data[k] = i + min
			k++
			c[i]--
		}
	}
}

func getMax(data []int)(m int){
	max := 0
	for i := 0;i < len(data);i++{
		if max < data[i]{
			max = data[i]
		}
	}
	return max
}

func getMin(data []int)(m int){
	min := 0
	for i := 0;i < len(data);i++{
		if min > data[i]{
			min = data[i]
		}
	}
	return min
}
  • 稳定排序
  • 其空间复杂度和时间复杂度均为O(n+k)线性时间复杂度,其中k是整数的范围(取决于辅助数组大小)
  • 非比较排序
  • 计数排序其实是桶数取 max - min + 1最大时的桶排序
  • 2
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: package main import ( "fmt" ) func quickSort(arr []int) []int { if len(arr) <= 1 { return arr } pivot := arr[0] left := []int{} right := []int{} for _, v := range arr[1:] { if v < pivot { left = append(left, v) } else { right = append(right, v) } } left = quickSort(left) right = quickSort(right) left = append(left, pivot) return append(left, right...) } func main() { arr := []int{3, 4, 1, 5, 9, 6, 2, 8, 7} fmt.Println(quickSort(arr)) } ### 回答2: package main import ( "fmt" ) func quickSort(arr []int) []int { if len(arr) <= 1 { return arr } pivot := arr[0] left := make([]int, 0) right := make([]int, 0) for _, num := range arr[1:] { if num <= pivot { left = append(left, num) } else { right = append(right, num) } } left = quickSort(left) right = quickSort(right) return append(append(left, pivot), right...) } func main() { arr := []int{7, 9, 3, 5, 1, 8, 6, 2, 4} sortedArr := quickSort(arr) fmt.Println(sortedArr) } 快速排序是一种常用排序算法,利用“分治”的思想。首先选择一个元素作为“枢纽”(pivot),然后将整个数组划分为两个子数组:所有小于pivot的元素放在左边的子数组,所有大于pivot的元素放在右边的子数组。然后分别对左边子数组和右边子数组进行递归调用快速排序。最终将两个子数组的结果合并起来,即可得到排序后的结果。 在上述代码中,首先定义了一个quickSort函数,接收一个整型切片作为参数,并返回排序后的整型切片。若切片长度小于等于1,则直接返回。选择第一个元素作为pivot,初始化左子数组和右子数组。遍历除第一个元素之外的所有元素,将小于等于pivot的元素放入左子数组,大于pivot的元素放入右子数组。然后递归调用quickSort函数对左右子数组进行排序,并将结果合并起来,得到最终的排序结果。 在main函数中,定义一个待排序的切片arr,使用quickSort函数对其进行排序,并打印排序后的结果。 ### 回答3: 快速排序是一种常用排序算法,它的基本思想是通过递归的方式将原始序列划分为较小和较大的两个子序列,并不断重复这个过程,直到所有子序列都有序为止。 下面是使用Go语言实现快速排序的代码: ```go package main import "fmt" 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 } func main() { arr := []int{5, 1, 6, 2, 4, 3} fmt.Println("Before sorting:", arr) quickSort(arr, 0, len(arr)-1) fmt.Println("After sorting:", arr) } ``` 以上代码定义了两个函数,`quickSort` 和 `partition`。`quickSort` 函数是快速排序的主函数,它负责递归调用 `partition` 函数,实现将序列划分为较小和较大的两个子序列,并继续对子序列进行快速排序操作。`partition` 函数是划分函数,它选择一个基准元素(通常是最后一个元素),并将小于基准元素的放在左边,大于基准元素的放在右边,最后返回基准元素的位置。 在 `main` 函数中,定义了一个待排序的整数数组 `arr`,并将其传入 `quickSort` 函数进行排序。运行程序后,将输出排序前后的结果。 以上就是使用Go语言实现快速排序的代码。快速排序具有较高的效率和灵活性,是常用排序算法之一。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值