排序算法分析
冒泡排序
定义
1、两个元素对比,然后将大得向右移动,每次循环都将最大得元素移动到最右侧
2、如果元素个数小于2个,则表示原有数据已经有序
使用两层循环实现
// bubblingSort
// 冒泡排序
func bubblingSort(data []int) {
length := len(data)
if length < 2 {
return
}
// 外层循环,告知排序得次数
for i := 0; i < length-1; i++ {
// 内层循环,用户循环比较,每一步骤都将最大得移动到最右侧
for j := 0; j < length-i-1; j++ {
log.Println(j)
// 是否会溢出?
if data[j] > data[j+1] {
data[j], data[j+1] = data[j+1], data[j]
}
}
}
}
选择排序
定义
1、找到数组中最大的元素,与数组最后一位元素交换
2、如果元素个数小于2个,则表示原有数据已经有序
使用两层循环实现
// selectSort
// 选择排序
func selectSort(data []int) {
length := len(data)
if length < 2 {
return
}
for i := 0; i < length-1; i++ {
// 假定最大值索引位置为第一个
maxpos := 0
// 内部循环找到真实得最大值索引位置
for j := 0; j < length-i; j++ {
if data[j] > data[maxpos] {
maxpos = j
}
}
data[length-i-1], data[maxpos] = data[maxpos], data[length-i-1]
}
}
插入排序
定义
1、将一个元素插入到已有序的数组中,在初始时未知是否存在有序的数据,因此将元素第一个元素看成是有序的;
2、与有序的数组进行比较,比它大则直接放入,比它小则移动数组元素的位置,找到个合适的位置插入;
3、当只有一个数时,则不需要插入了,因此需要n-1趟排序,比如10个数,需要9趟排序
代码
// insertSort
// 插入排序
func insertSort(data []int) {
length := len(data)
if length < 2 {
return
}
// 假设第一个元素为有序数组
for i := 1; i < length; i++ {
// 选取一个值 向有序集合中插入
temp := data[i]
// 将选取的值插入到有序集合的指定位置
j := i - 1
// 挑选的值与倒序与有序集合对比,如果选择的值小于则移动有序集合索引
for ; j >= 0 && data[j] > temp; j-- {
data[j+1] = data[j]
}
data[j+1] = temp
}
}
希尔排序
定义
希尔排序实质上就是插入排序的增强版,希尔排序将数组分隔成n组来进行插入排序,**直至该数组宏观上有序,**最后再进行插入排序时就不用移动那么多次位置了~
代码
// shellSort
// 希尔排序
// 希尔增量一般是gap = gap / 2,只是比普通版插入排序多了这么一个for循环罢了
func shellSort(data []int) {
length := len(data)
if length < 2 {
return
}
var temp int
var j int
// 将一个数组分成多个片段
for d := length / 2; d > 0; d = d / 2 {
// 对每一组片段进行插入排序
for x := 0; x < d; x++ {
// 下面为一个插入排序
for i := x + d; i < length; i = i + d {
temp = data[i]
j = i - d
for ; j >= 0 && data[j] > temp; j = j - d {
data[j+d] = data[j]
}
data[j+d] = temp
}
}
}
}
归并排序
定义
将两个已排好序的数组合并成一个有序的数组
将元素分隔开来,看成是有序的数组,进行比较合并
断拆分和合并,直到只有一个元素
代码
/**
* @desc 归并排序
* 思路: 选择中间索引将数组分割为两个,然后组合两个数组按大小顺序组合
* 时间复杂度 O(nlog2n)
* 空间复杂度 O(n) + O(log2n)
* 稳定性:稳定
*/
func MergeSort(item []int) []int {
mergeSort(item, 0, len(item)-1)
return item
}
// @param item 排序数组
// @param 开始索引位置
// @param 结束索引位置
func mergeSort(item []int, left, right int) {
if left < right {
center := (left + right) / 2
mergeSort(item, left, center)
mergeSort(item, center+1, right)
merge(item, left, center+1, right)
}
}
// @desc 合并两个数组
func merge(item []int, left, center, right int) {
// 左侧数组大小
leftData := make([]int, center-left)
// 右侧数组大小
rightData := make([]int, right-center+1)
// 向两个数组中填充数据
for i := left; i < center; i++ {
leftData[i-left] = item[i]
}
for i := center; i <= right; i++ {
rightData[i-center] = item[i]
}
// 用于遍历两个数组
i, j := 0, 0
// 数组中的第一个元素
index := left
// 循环对比合并两个数组
for i < len(leftData) && j < len(rightData) {
if leftData[i] < rightData[j] {
item[index] = leftData[i]
i++
} else {
item[index] = rightData[j]
j++
}
// 增加后索引增加1
index++
}
// 将数据中剩余的元素继续插入
for i < len(leftData) {
item[index] = leftData[i]
i++
index++
}
for j < len(rightData) {
item[index] = rightData[j]
j++
index++
}
}
func mergeSortV2(item []int, left, center, right int) {
temp := make([]int, right-left+1)
i := 0
// 左边开始
start := left
// 右边开始
end := center + 1
for start <= center && end <= right {
if item[start] < item[end] {
temp[i] = item[start]
start++
} else {
temp[i] = item[end]
end++
}
i++
}
for start <= center {
temp[i] = item[start]
i++
start++
}
for end <= end {
temp[i] = item[end]
i++
end++
}
// 将结果返回给原素组
for i := 0; i < len(temp); i++ {
item[left+i] = temp[i]
}
}
快速排序
定义
1、在数组中找一个元素(节点),比它小的放在节点的左边,比它大的放在节点右边。一趟下来,比节点小的在左边,比节点大的在右边;
2、不断执行这个操作....
使用递归实现
优点:逻辑简单,一看就懂
缺点:浪费空间,因为每次递归都需要申请两个切片保存左右节点
// quickSort
// 快速排序,使用递归方式
func quickSort(data []int) []int {
if len(data) == 0 {
return data
}
// 取第一个元素作为比较节点
// temp := data[0]
left := []int{}
right := []int{}
// 此处循环从1开始,第一个节点已经拿出来用于比较了
for i := 1; i < len(data); i++ {
if data[i] > data[0] {
right = append(right, data[i])
} else {
left = append(left, data[i])
}
}
left = quickSort(left)
right = quickSort(right)
return append(append(left, data[0]), right...)
}
递归优化版本
使用两个节点交换,减少内存的申请
// quickSort
// 快速排序,使用递归方式
func quickSort(data []int, left, right int) {
if left >= right {
return
}
start := left
end := right
// 选择第一个节点
value := data[left]
// 根据选取的节点将数组 分成两部分
for left < right {
// 如果右侧节点大于选取节点则向左移动指针
for right > left && data[right] >= value {
right--
}
data[left] = data[right]
// 如果左边节点小于选取节点则向右移动指针
for left < right && data[left] <= value {
left++
}
data[right] = data[left]
}
// 将选取得值赋值左指针
data[left] = value
quickSort(data, start, left-1)
quickSort(data, left+1, end)
}
基数排序
定义
基数排序(桶排序):将数字切割成个、十、百、千位放入到不同的桶子里,放一次就按桶子顺序回收一次,直至最大位数的数字放完~那么该数组就有序了。
代码
// radixSort
// 基数排序
// 时间复杂度:O(wn) w:元素的最大长度,N元素的个数
// 思路:将所有待比较数值统一为同样的数位长度,数位较短的数前面补充零;
// 然后从最低位开始,依次进行一次排序
func radixSort(item []int) {
if len(item) == 0 {
return
}
// 首先得到数组中最大的数的位数
// 假设第一个元素为最大
max := item[0]
for i := 1; i < len(item); i++ {
if item[i] > max {
max = item[i]
}
}
// 得到最大数的几位数
maxlength := len(strconv.Itoa(max))
// 定义一个二维数组,表示10个桶,每个桶就是一个一位数组
// 1、二维数组包含10个一维数组
// 2、为了防止在放入数的时候,数据溢出,则每个一维数组(桶),大小定为arr.length
// 3、明确,基数排序是使用空间换时间的经典算法
//count := len(item)
bucket := make(map[int][]int, 0)
// 记录每个桶实际存放多少数据
bucketElementCounts := make([]int, 10)
// 根据最大位数进行循环
for i, n := 0, 1; i < maxlength; i++ {
// 针对每个元素对应位进行排序处理(第一位、第二位、第三位)
for j := 0; j < len(item); j++ {
// 取出每个元素的对应位的值
digitOfElement := item[j] / n % 10
// 放入到对应桶中
bucket[digitOfElement] = append(bucket[digitOfElement], item[j])
// 更新对应桶的元素数量
bucketElementCounts[digitOfElement]++
}
// 按照桶的顺序(一维数组的下标一次取出数据,放入原来数组)【数组按顺序输出】
index := 0
for key, number := range bucketElementCounts {
fmt.Println(key, number)
// 将每个桶的元素循环输出
for k := 0; k < number; k++ {
item[index] = bucket[key][k]
index++
}
delete(bucket, key)
bucketElementCounts[key] = 0
}
// 用于处理位数 x/n%10 取出每一个元素的指定位
n = n * 10
}
}
计数排序
定义
代码
func countSort(item []int) []int {
// 获取数据最大的值和最小的值用于范围
max := item[0]
min := item[0]
for _, value := range item {
if value > max {
max = value
}
if value < min {
min = value
}
}
// 需要桶的数量
lenth := max - min
// 根据数列最大值确定统计数组的长度,声明一个桶用于存放数据
bucket := make([]int, lenth+1)
// 遍历数列,统计每个索引的个数,填充到桶中
for i := 0; i < len(item); i++ {
index := item[i] - min
bucket[index]++
}
// 遍历桶,输出结果
//index := 0
rs := make([]int, 0)
for i := 0; i < len(bucket); i++ {
// 如果存在数据
if bucket[i] > 0 {
for j := 0; j < bucket[j]; j++ {
rs = append(rs, i+min)
}
}
}
return rs
}
总结
以上主要是使用golang实现了常见的排序算法