go语言数据结构第六篇-排序算法

一.冒泡排序

1.算法描述:

(1)比较相邻的元素。如果左边大于右边,就交换他们两个。使得右边比左边大。这样一轮下来,最大的数就会在最右边了。

(2)因为最大的数已经在最右边了,因此对除了最右边的数重复(1)的步骤,这样倒数第二大的数也被选出来了。

(3)持续每次对越来越少的元素重复(2)的步骤,直到没有任何一对数字需要比较。

2.时间复杂度:

           O(n^2)

3.代码实现:

package main

import "fmt"

func main() {

	data := []int{91, 4, 84, 85, 80, 1, 81, 93, 27,12}
	fmt.Println("排序之前:",data)

	BubbleSort(data)
}

func BubbleSort (d []int) {
	for i := 0; i < len(d)-1 ; i++ {//外循环控制比较次数,10个数比较9次(0~8)
		for j := 0; j < len(d)-1-i; j++ {//内循环控制每次,要两两交换的次数,第一次两两交换9次,第二次两两交换8次。。。

			if  d[j]>d[j+1]{
				d[j],d[j+1] = d[j+1],d[j]
			}

		}
	}
	fmt.Println("冒泡排序之后:",d)
}

4.执行结果:

 

二.选择排序

1.算法描述:

(1)假设第0位就是最小值,将该值与剩余的1-n个数进行比较,从1-n中找出真正的最小值,与第0位的数进行交换,这样最小的数就会出现在第0位,这是第一轮

(2)假设第1位是最小值,将该值与剩余的2-n个数进行比较,从2-n中找出真正的最小值,与第1位的数进行交换,这样倒数第二小的数就会出现在第1位,这是第二轮

(3)重复上述步骤,一共需要n-1轮

2.时间复杂度:

           O(n^2)

3.代码实现:

package main

import "fmt"

func main() {

	data := []int{91, 4, 84, 85, 80, 1, 81, 93, 27,12}
	fmt.Println("排序之前:",data)

	ChooseSort(data)
}

func ChooseSort (d []int) {

	//外循环控制轮次,10个数需要9轮
	for i := 0; i < len(d)-1; i++ {//这里i为0~8,共9轮

		min := d[i]   //假设d[0]就是最小值
		minIndex := i //最小值的下标为0

		//遍历后面的1~【len(d)-1】与假设的最小值比较
		for j := i+1; j < len(d); j++ {
			if min > d[j] { //找到真正的最小值,将它的值赋给min,下标赋给minIndex
				min = d[j]
				minIndex = j
			}
		}

		//最小值找到后,将它与d[0]位置交换
		if minIndex != i { //假设的值本身就是最小值,则不用交换,否则就要交换
			d[i], d[minIndex] = d[minIndex], d[i]
		}


	}
	fmt.Println("选择排序之后:",d)
}

4.执行结果: 

三.插入排序

1.算法描述:

(1)比如有待排序元素[100,88,43,90],现将第一个元素100拿出来当做一个有序表,此时有序表为[100]

(2)然后拿88和100比较,因为88比100小,100向后移动一位,88在100的前面插入,变成[88,100,43,22,1,34,89],此时有序表为[88,100]

(3)再将43和100比较,43小于100,100向后移一位([88,_,100]),再将43和88比较,43比88小,88向后移动一位([_,88,100]),43再向前比较,此时因为前面已经没有数了,所以43插入88前面,此时有序表为[43,88,100]

(4)再将90和(3)中有序表中100比较,90比100小,100向后移动一位([43,88,_,100]),再将90和88比较,因为90比88大,所以90直接在此位置插入,变成[43,88,90,100]

(5)4个数一共经常3次比较,完成排序

2.时间复杂度:

   在最坏情况下: 数组完全逆序,插入第2个元素时,前1个元素后移一位;插入第3个元素时,前2个元素都要各后移一位,……,插入第n个元素,前n-1个元素都要各后移一位。因此,最坏情况下的比较次数是 1 + 2 + 3 + ... + (n-1),等差数列求和,结果为 (n^2)/2,所以最坏情况下的复杂度为 O(n^2)

     在最好情况下:数组已经是有序的,每插入一个元素,只需要和前一个元素比较,因此最好情况下,插入排序的时间复杂度为O(n)

3.代码实现:

package main

import "fmt"

func main()  {
	data := []int{91, 4, 84, 85, 80, 1, 81, 93, 27,12}
	fmt.Println("排序之前:",data)

	InsertSort(data)
}

//从小到大排列,一开始有序列表[91]
func InsertSort(d []int)  {

	for i := 1; i < len(d); i++{//10个元素需要9次

		insertVal := d[i] 	//待插入元素
		insertIndex := i-1	//待插入元素下标的前一个下标

		for insertIndex >= 0 && d[insertIndex] > insertVal {
			d[insertIndex + 1] = d[insertIndex] //[91,91]数据后移一位
			insertIndex-- //此时insertIndex指向第一个91,再减一等于-1<0了,跳出for循环
		}

		//插入
		if insertIndex + 1 != i {//-1+1=0 != 1
			d[insertIndex + 1] = insertVal
		}

		fmt.Printf("第%d次插入后%v\n",i,d)

	}
	fmt.Println("插入排序之后:",d)
}

4.执行结果: 

四.快速排序

1.算法描述:

(1)假设有待排序元素[3,85,30,51,25,9,78],数组下标为0~6

(2)选择下标为(0+6)/2=3的数作为中枢,这里的中枢值就是51,将比51小的数放左边,比51大的数放右边,完成之后变成[3,9,30,25,51,85,78],此时51左边的数都比51小,51右边的数都比51大

(3)再对[3,9,30,25]和[85,78]重复(2)步骤,变成[3,9,30,25,51,78,85]

(4)再对[30,25]重复(2)步骤,变成[25,30]。此时快速排序完成。最终排序结果[3,9,25,30,51,78,85]

(5)3和4步骤其实一直重复2的步骤,也就是递归运算

2.形象图:

3.时间复杂度:

        O(nlgn)

4.代码实现:

package main

import (
	"fmt"
)

func main() {

	arr := []int{91, 4, 84, 85, 80, 1, 81, 93, 27,12}
	fmt.Println("排序之前:",arr)

	QuickSort(0, len(data)-1, arr)
	fmt.Println("快速排序之后:",arr)

}

//1. left 表示 数组最左边的下标
//2. right 表示数组最右边的下标
//3  arr 表示要排序的数组
func QuickSort(left int, right int, arr []int) {

	//left始终指向数组最左边,不会动。right始终指向数组最右边,不会动
	//l一开始指向数组最左边,后面会逐渐右移。r一开始指向数组最右边,后面会逐渐左移
	l := left
	r := right

	middle := arr[(left + right) / 2]

	//for循环的目标是将比middle小的数放到左边;比middle大的数放到右边
	for ; l < r; {
		//从middle的左边找到大于等于middle的值
		for ; arr[l] < middle; {
			l++
		}
		//从middle的右边边找到小于等于middle的值
		for ; arr[r] > middle; {
			r--
		}
		// 1 >= r 表明本次分解任务完成, break
		if l >= r {
			break
		}
		//将middle左边大于middle的值 与 middle右边小于middle的值 交换
		arr[l],arr[r] = arr[r],arr[l]
		//优化
		if arr[l]== middle  {
			r--
		}
		if arr[r]== middle {
			l++
		}
	}
	// 如果  1== r, 再移动下,为了跳出循环,
	//同时也是为了下面递归时,找到左边集合数组中最右边的数r,和找到右边集合数组中最左边的数l
	if l == r {
		l++
		r--
	}
	// 向左递归
	if left < r {
		QuickSort(left, r, arr)
	}
	// 向右递归
	if right > l {
		QuickSort(l, right, arr)
	}


}

5.执行结果

五.使用goroutine实现快速排序

package main

import (
	"fmt"
	"sync"
)
func main() {
	data := []int{3, 6, 23, 7, 2, 4, 9, 13,66,61}

	fmt.Println(QuickSortOnGoroutine(data))
}

func QuickSortOnGoroutine (data []int) []int {

	if len(data) <= 1 {
		return data
	}

	var wg sync.WaitGroup

	c := data[0]

	var s1, s2 []int

	for k, v := range data {
		if k == 0 {
			continue
		}
		if c < v {
			s2 = append(s2, v)//s2中值总是比data[0]大
		} else {
			s1 = append(s1, v)//s1中值总是比data[0]小
		}
    }

	wg.Add(2)

	go func() {
		s1 = QuickSortOnGoroutine(s1)
		wg.Done()
	}()

	go func() {
	   s2 = QuickSortOnGoroutine(s2)
	   wg.Done()
	}()

	wg.Wait()

	data = []int{c}
	if len(s1) > 0 {
	   data = append(s1, data...)
	}
	if len(s2) > 0 {
	   data = append(data, s2...)
	}
	return data
}


执行结果:

 

六.测试对比四个排序算法速度

(1)对10万个随机数使用冒泡排序法,需要时间平均为15~16秒

(2)对10万个随机数使用选择排序法,需要时间平均为5~6秒 

(3)对10万个随机数使用插入排序法,需要时间平均为1~2秒  

(4)由于快速排序法太快,待排序元素数量太少无法看到时间,所以这里使用1000w个随机数,需要时间平均为1~2秒   

(5)使用goroutine实现的快速排序。使用1000w个随机数测试:

 

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值