冒泡排序

从左到右依次比较两个相邻元素,如果左边元素大于右边元素,就将两者交换;如果左边元素小于等于右边元素,不进行任何操作。

Version1

package main

import "fmt"

func main() {
	slice := []int{7, 5, 8, 4, 3, 2, 6, 9, 1}
	//执行次数
	for i := 0; i < len(slice)-1; i++ {
		//比较次数
		for j := 0; j < len(slice)-1-i; j++ {
			//大于表示升序,小于表示降序
			if slice[j] > slice[j+1] {
				slice[j], slice[j+1] = slice[j+1], slice[j]
			}
		}
	}
	fmt.Println(slice)
}

Version 2:

增加标志位表示切片已经排好序。当flag为true时,直接跳出循环, 也就是当数组接近升序排列时,可以减少循环次数。

package main

import "fmt"

func main() {
	slice := []int{7, 5, 8, 4, 3, 2, 6, 9, 1}
	for i := 0; i < len(slice)-1; i++ {
		var flag = true
		for j := 0; j < len(slice)-1-i; j++ {
			if slice[j] > slice[j+1] {
				slice[j], slice[j+1] = slice[j+1], slice[j]
				flag = false
			}
		}
		if flag {
			break
		}
	}
	fmt.Println(slice)
}


选择排序

假设数组中第 1 个元素最小,然后让剩余的 N - 1 个元素依次和数组第 1 个元素进行比较,如果比第 1 个元素小,那么就进行交换。然后第2个就是第2小的数,以此类推

Version1:

package main

import "fmt"

func main() {
	slice := []int{7, 5, 8, 4, 3, 2, 6, 9, 1}
	for i := 0; i < len(slice); i++ {
		//用后面的数依次和第一位进行对比,因为j从1开始,进入循环判断时,j已经等于2,所以判断条件不减1
		for j := i + 1; j < len(slice); j++ {
			if slice[j] < slice[i] {
				slice[i], slice[j] = slice[j], slice[i]
				fmt.Println(slice)
			}
		}
	}
	fmt.Println(slice)
}

Version2:

用变量来维护最小数的索引,在遍历一遍结束后直接交换。

package main

import "fmt"

func main() {
	slice := []int{7, 5, 8, 4, 3, 2, 6, 9, 1}
	for i := 0; i < len(slice)-1; i++ {
		var index = i
		for j := i + 1; j < len(slice); j++ {
			if slice[j] < slice[index] {
				index = j
			}
		}
		slice[index], slice[i] = slice[i], slice[index]
	}
	fmt.Println(slice)
}

插入排序

在已排序序列中从后向前扫描,找到相应位置并插入。类似于扑克中理牌。

package main

import "fmt"

func main() {
	slice := []int{7, 5, 8, 4, 3, 2, 6, 9, 1}
	for i := 1; i < len(slice); i++ {
		for j := i; j > 0; j-- {
			if slice[j] < slice[j-1] {
				slice[j], slice[j-1] = slice[j-1], slice[j]
			}
		}
	}
	fmt.Println(slice)
}


希尔排序

设定步长,由粗颗粒到细颗粒,当gap为1时,相当于插入排序

package main

import "fmt"

func main() {
	slice := []int{7, 5, 8, 4, 3, 2, 6, 9, 1}

	gap := len(slice) / 2

	for gap >= 1 {
		for i := gap; i < len(slice); i++ {
			for j := i; j-gap >= 0; j -= gap {

				if slice[j] < slice[j-gap] {
					slice[j], slice[j-gap] = slice[j-gap], slice[j]
				}
				fmt.Println(slice)
			}
		}
		gap /= 2
	}
}

归并排序

总结来说就是将数组依次拆分,排序后再将数组合并,这里使用递归的办法。

package main

import "fmt"

// 合并并排序两个数组
func merge(s1 []int, s2 []int) []int {
	// 获取两个需要合并数组的长度
	s1_length, s2_length := len(s1), len(s2)

	// 创建临时数组, 长度为需要合并的两个数组长度之和
	var tmp_sice = make([]int, s1_length+s2_length)

	// 在循环外部创建指针
	var i, j int

	// 比较两个数组,分别从0开始,将比较好后较小的值放入临时数组,当一个数组循环完毕时,推出循环
	for i, j = 0, 0; i < s1_length && j < s2_length; {
		if s1[i] <= s2[j] {
			tmp_sice[i+j] = s1[i]
			i++
		} else {
			tmp_sice[i+j] = s2[j]
			j++
		}
	}

	// 将剩余的数组元素追加到临时数组中
	for ; i < s1_length; i++ {
		tmp_sice[i+j] = s1[i]
	}
	for ; j < s2_length; j++ {
		tmp_sice[i+j] = s2[j]
	}

	fmt.Println(tmp_sice)
	// 直接将合并并排序好的临时数组返回
	return tmp_sice
}

func merge_sort(slice []int) []int {
	slice_length := len(slice)

	// 当数组长度大于1时,进行排序,否则长度为1肯定为有序,为递归的退出条件
	if slice_length > 1 {
		var middle = slice_length / 2
		// 递归,先将数组进行拆分, 再进行排序合并
		return merge(merge_sort(slice[:middle]), merge_sort(slice[middle:]))
	} else {
		return slice
	}
}

func main() {
	slice := []int{7, 5, 8, 4, 3, 2, 6, 9, 1}
	slice = merge_sort(slice)
	fmt.Println(slice)
}

堆排序

参考https://www.cnblogs.com/chengxiao/p/6129630.html

数据结构

堆是具有以下性质的完全二叉树:每个结点的值都大于或等于其左右孩子结点的值,称为大顶堆;或者每个结点的值都小于或等于其左右孩子结点的值,称为小顶堆。

在这里插入图片描述
在数组中就如下图:
在这里插入图片描述
就能推导出以下规律:
大顶堆:arr[i] >= arr[2i+1] && arr[i] >= arr[2i+2]
小顶堆:arr[i] <= arr[2i+1] && arr[i] <= arr[2i+2]

a.将无需序列构建成一个堆,根据升序降序需求选择大顶堆或小顶堆;

b.将堆顶元素与末尾元素交换,将最大元素"沉"到数组末端;

c.重新调整结构,使其满足堆定义,然后继续交换堆顶元素与当前末尾元素,反复执行调整+交换步骤,直到整个序列有序。

// study project main.go
package main

import "fmt"

func heap_sort(slice []int) []int {
	var slice_length = len(slice)
	Max_heap(slice, slice_length)
	// 初始值为数组长度减去1表示最后一位不进行调整
	for i := slice_length - 1; i >= 0; i-- {
		// 第一次进入循环时,将最大数放到数组最后面
		swap(slice, 0, i)
		slice_length -= 1
		heapify(slice, 0, slice_length)

	}
	return slice
}

// 从最后一个父子节点开始调正,循环过后堆顶得到的是最大的一个数
func Max_heap(slice []int, slice_length int) {
	for i := slice_length / 2; i >= 0; i-- {
		heapify(slice, i, slice_length)
	}
}

// 调整堆
func heapify(slice []int, i, slice_length int) {
	// 左子节点
	left := 2*i + 1

	// 右子节点
	right := 2*i + 2

	// i节点
	largest := i

	// 当左或者右子节点索引大于数组长度,不进行调整
	// 当左或者右子节点大于其父节点时交换位置 大顶堆
	if left < slice_length && slice[left] > slice[largest] {
		largest = left
	}
	if right < slice_length && slice[right] > slice[largest] {
		largest = right
	}
	if largest != i {
		// 将父和子节点中最大的数向堆顶调整
		swap(slice, i, largest)
		// 调整子根, 此时largest指向的是交换后子节点的索引
		heapify(slice, largest, slice_length)
		fmt.Println(slice)

	}
}

func swap(slice []int, i, j int) {
	slice[i], slice[j] = slice[j], slice[i]
}

func main() {
	slice := []int{4, 3, 6, 7, 5, 1, 9, 0, 2, 8}
	slice = heap_sort(slice)
	fmt.Println(slice)
}



快速排序

  1. 从数列中挑出一个元素,称为 “基准”(pivot)
  2. 重新排序数列,所有元素比基准值小的摆放在基准前面,所有元素比基准值大的摆在基准的后面
  3. 递归地(recursive)把小于基准值元素的子数列和大于基准值元素的子数列排序
package main

import "fmt"

func quick_sort(slice []int, Left int, Right int) {
	// 等价于当子数组长度为1
	if Left >= Right {
		return
	}

	left := Left
	right := Right

	// 取最左边为比较值
	var pivot = slice[left]

	// 退出条件为左索引等于右索引
	for left < right {
		// 当右索引的值大于比较值时,索引向左移动
		for left < right && slice[right] >= pivot {
			right -= 1
		}
		// 顺序结构,此时表示右索引的值比比较值小,所以将小的数放到左边
		if left < right {
			slice[left] = slice[right]
		}
		// 当左索引的值小于比较值时,索引向右移动
		for left < right && slice[left] <= pivot {
			left += 1
		}
		// 此时表示,左索引的值大于比较值,将大的数放到右边
		if left < right {
			slice[right] = slice[left]
		}
		// 左右索引重合时,将比较值放到此处
		if left >= right {
			slice[left] = pivot
		}
	}

	// 将数组再分割成左右两组
	// 左边部分递归
	quick_sort(slice, Left, right-1)
	// 右边部分递归
	quick_sort(slice, right+1, Right)
}

func main() {
	slice := []int{4, 3, 6, 7, 5, 1, 9, 0, 2, 8}
	Left_index := 0
	Right_index := len(slice)
	quick_sort(slice, Left_index, Right_index-1)
	fmt.Println(slice)
}