内部排序算法(Golang实现)

package main

import (
    "fmt"
)

func main() {
    //保存需要排序的Slice
    arr := []int{9, 3, 3, 4, 7, 2, 4, 7, 2, 1, 4, 7, 2, 11, 12, 11, 18, 19, 12,
	 3, 4, 7, 2, 1, 0, 11, 12, 11, 13, 4, 7, 2, 1, 0, 11, 12, 11, 18,0,1,0,1}

    //实际用于排序的Slice
    list := make([]int, len(arr))

    copy(list, arr)
    QuickSort(list)
    fmt.Println("快速排序:\t", list)

    copy(list, arr)
    QuickSortX(list)
    fmt.Println("快速排序X:\t", list)

    copy(list, arr)
    list = MergeSort(list)
    fmt.Println("二路归并排序:\t", list)
	
    copy(list, arr)
    BubbleSort(list)
    fmt.Println("冒泡排序:\t", list)

    copy(list, arr)
    BubbleSortX(list)
    fmt.Println("冒泡排序X:\t", list)

    copy(list, arr) //将arr的数据覆盖到list,重置list
    InsertSort(list)
    fmt.Println("直接插入排序:\t", list)

    copy(list, arr)
    ShellSort(list)
    fmt.Println("希尔排序:\t", list)

    copy(list, arr)
    SelectSort(list)
    fmt.Println("简单选择排序:\t", list)

    copy(list, arr)
    HeapSort(list)
    fmt.Println("堆排序:     \t", list)

}


//region 快速排序
/*
步骤:
1.从数列中挑出一个元素作为基准数
2.分区过程,将比基准数大的放到右边,小于或等于它的数都放到左边。(每次归位一个基准数到它最终应该在的位置)
3.再对左右区间递归执行第二步,直至各区间只有一个数
PS:
快速排序里面比较精妙,要注意基准数的选择和哨兵指针移动的先后顺序
*/
func QuickSort(list []int) {
    if len(list) <= 1 {
        return
    }
    var low int = 0
    var high int = len(list) - 1
    //以list[0]为基准数
    for low < high {
        if list[high] >= list[0] {
            high--
            continue
        }
        if list[low] <= list[0] {
            low++
            continue
        }

        list[low], list[high] = list[high], list[low]
    }
    //low == high
    if list[low] < list[0] {
        list[low], list[0] = list[0], list[low]
    }
    QuickSort(list[:low])
    QuickSort(list[low+1:])
}

func QuickSortX(list []int) {
    if len(list) <= 1 {
        return
    }
    key, i := list[0], 1
    low, high := 0, len(list)-1
    for low < high {
        if list[i] > key {
            list[i], list[high] = list[high], list[i]
            high--
        } else {
            list[i], list[low] = list[low], list[i]
            low++
            i++
        }
    }
    QuickSortX(list[:low])
    QuickSortX(list[low+1:])
}
//endregion

//region 二路归并排序
/*
步骤:
1.将待排序序列R[0...n-1]看成是n个长度为1的有序序列,将相邻的有序表成对归并,得到n/2个长度为2的有序表;
2.将这些有序序列再次归并,得到n/4个长度为4的有序序列;
3.如此反复进行下去,最后得到一个长度为n的有序序列。

归并排序其实要做两件事:
(1)“分解”——将序列每次折半划分。
(2)“合并”——将划分后的序列段两两合并后排序。
*/
func MergeSort(list []int) []int {   //着重理解该函数
	if len(list) <= 1 {
        return list
    } 
    mid := len(list)/2
    left := MergeSort(list[:mid])
    right := MergeSort(list[mid:])
    return merge(left,right)
}

func merge(left, right []int) (result []int) {
    i,j := 0,0
    for i < len(left) && j < len(right){
        if left[i] < right[j]{
            result = append(result,left[i])
			i++
        }else{
            result = append(result,right[j])
			j++
        }
    }
    result = append(result, left[i:]...)
    result = append(result, right[j:]...)
    return
}
//endregion

//region 冒泡排序
/*
步骤:
1.比较相邻的元素。如果第一个比第二个大,就交换他们两个
2.对每一对相邻元素作同样的工作,从开始第一对到结尾的最后一对。在这一点,最后的元素应该会是最大的数
3.针对所有的元素重复以上的步骤,除了最后一个
4.持续每次对越来越少的元素重复上面的步骤,直到没有任何一对数字需要比较
*/
func BubbleSort(list []int) {
    for i := 0; i < len(list)-1; i++ {
        for j := 0; j < len(list)-i-1; j++ {
            if list[j] > list[j+1] {
                list[j], list[j+1] = list[j+1], list[j]
            }
        }
    }
}

//优化:如果没有交换发生,代表已经有序,即可结束
func BubbleSortX(list []int) {
    var exchange bool = false
    for i := 0; i < len(list)-1; i++ {
        for j := 0; j < len(list)-i-1; j++ {
            if list[j] > list[j+1] {
                list[j], list[j+1] = list[j+1], list[j]
                exchange = true
            }
        }
        if !exchange {
            break
        }
        exchange = false
    }

}
//endregion

//region 插入排序
/*
步骤:
1.从第一个元素开始,该元素可以认为已经被排序
2.取出下一个元素,在已经排序的元素序列中从后向前扫描
3.如果被扫描的元素(已排序)大于新元素,将该元素后移一位
4.重复步骤3,直到找到已排序的元素小于或者等于新元素的位置
5.将新元素插入到该位置后
6.重复步骤2~5
*/
func InsertSort(list []int) {
    var temp, i, j int
    for i = 1; i < len(list); i++ {
        temp = list[i]
        for j = i - 1; j >= 0 && temp < list[j]; j-- {
            list[j+1] = list[j]
        }
        list[j+1] = temp
    }
}

//region 希尔排序
/*
基本思想:
把记录按步长 gap 分组,对每组记录采用直接插入排序方法进行排序。
随着步长逐渐减小,所分成的组包含的记录越来越多,当步长的值减小到 1 时,整个数据合成为一组,构成一组有序记录,则完成排序。
*/
func ShellSort(list []int) {
    for gap := (len(list) + 1) / 2; gap >= 1; gap = gap / 2 {
        for i := 0; i+gap < len(list); i++ {
            InsertSort(list[i : i+gap+1])
        }
    }
}
//endregion

//region 简单选择排序
/*
步骤:
1.在未排序序列中找到最小(大)元素,存放到排序序列的起始位置。
2.再从剩余未排序元素中继续寻找最小(大)元素,然后放到已排序序列的末尾。
3.以此类推,直到所有元素均排序完毕。
*/
func SelectSort(list []int) {
    var index int
    for i := 0; i < len(list)-1; i++ {
        index = i
        for j := i + 1; j < len(list); j++ {
            if list[index] > list[j] {
                index = j
            }
        }
        list[index], list[i] = list[i], list[index]
    }
}
//endregion


//region 堆排序
/*
步骤:
1.构造最大堆(Build_Max_Heap):
	若数组下标范围为0~n,考虑到单独一个元素是大根堆,则从下标n/2开始的元素均为大根堆。
于是只要从n/2-1开始,向前依次构造大根堆,这样就能保证,构造到某个节点时,它的左右子树都已经是大根堆。

2.堆排序(HeapSort):
	由于堆是用数组模拟的。得到一个大根堆后,数组内部并不是有序的。因此需要将堆化数组有序化。
思想是移除根节点,并做最大堆调整的递归运算。第一次将heap[0]与heap[n-1]交换,再对heap[0...n-2]做最大堆调整。
第二次将heap[0]与heap[n-2]交换,再对heap[0...n-3]做最大堆调整。重复该操作直至heap[0]和heap[1]交换。
由于每次都是将最大的数并入到后面的有序区间,故操作完后整个数组就是有序的了。

3.最大堆调整(Max_Heapify):
	该方法是提供给上述两个过程调用的。目的是将堆的末端子节点作调整,使得子节点永远小于父节点 。
*/
func heapAdjust(list []int, parent int, length int) {
	temp := list[parent]  // temp保存当前父节点
	child := 2*parent + 1 // 先获得左孩子

	for child < length {
		// 如果有右孩子结点,并且右孩子结点的值大于左孩子结点,则选取右孩子结点
		if child+1 < length && list[child] < list[child+1] {
			child++
		}

		// 如果父结点的值已经大于孩子结点的值,则直接结束
		if temp >= list[child] {
			break
		}

		// 把孩子结点的值赋给父结点
		list[parent] = list[child]

		// 选取孩子结点的左孩子结点,继续向下筛选
		parent = child
		child = 2*child + 1
	}

	list[parent] = temp
}

func HeapSort(list []int) {
	// 循环建立初始堆
	for i := len(list) / 2; i >= 0; i-- {
		heapAdjust(list, i, len(list)-1)
	}

	// 进行n-1次循环,完成排序
	for i := len(list) - 1; i > 0; i-- {
		// 最后一个元素和第一元素进行交换
		list[0], list[i] = list[i], list[0]

		// 筛选 R[0] 结点,得到i-1个结点的堆
		heapAdjust(list, 0, i)
	}
}
//endregion
  


Previous(前一篇)     Next(后一篇)
stach.tan /
Published in categories 算法  tagged with golang