编程小记
  • 排序算法
  • 栈与队列
  • DAG与拓扑排序
由 GitBook 提供支持
在本页
  • 归并排序
  • 堆排序
  • 快速排序

这有帮助吗?

排序算法

—— by golang

排序被认为是算法研究中最基础的问题,也是被应用最为广泛的算法之一。本文结合Golang语法特性实践几种最经典的排序算法: 1)归并排序 2)堆排序 3)快速排序

注:拆解算法为一个或几个最小化步骤,然后通过递归调用组装起来,是算法中常用策略;递归过程中,很重要的一点是要有完备的收敛条件;

归并排序

归并排序是利用分治策略的代表性排序算法;(注:归并排序不是原址的,需要额外空间成本) 算法思想:将数组一分为二,左右两个数组分别递归进行归并排序,当某个数组只有一个元素时天然有序(收敛),然后合并两个小的有序数组得到一个大的有序数组;该算法时间复杂度为:O(nlgn);

package algo

// SortMerge
func SortMerge(s []int) []int {
    idx := len(s) / 2
    if idx <= 0 {
        return s
    }
    // recursion
    return merge(SortMerge(s[:idx]), SortMerge(s[idx:]))
}

// merge left & right
func merge(l, r []int) []int {
    ss := []int{}
    for i, j := 0, 0; ; {
        if i >= len(l) {
            if j >= len(r) {
                break
            } else {
                ss = append(ss, r[j])
                j++
            }
        } else {
            if j >= len(r) || l[i] < r[j] {
                ss = append(ss, l[i])
                i++
            } else {
                ss = append(ss, r[j])
                j++
            }
        }
    }
    return ss
}

堆排序

堆排序是借助二叉堆特性的排序算法;二叉堆可以理解为近似二叉树的数组,而最大堆是二叉堆两种形式之一(另一为最小堆),它保持根节点大于叶节点; 算法思想:通过一个维持最大堆特性的方法校正数组为最大堆,然后交换根节点到堆的最后(注意不是整个数组的最后),如此递归进行,直到堆空(收敛);该算法时间复杂度为:O(nlgn);

package algo

// SortHeap
func SortHeap(s []int) {
    heapsize := buildMaxHeap(s)
    for i := len(s) - 1; i > 0; i-- {
        s[0], s[i] = s[i], s[0]
        heapsize--
        maxHeapify(s, heapsize, 0)
    }
}

func parent(i int) int {
    return (i - 1) / 2
}

func left(i int) int {
    return i*2 + 1
}

func right(i int) int {
    return i*2 + 2
}

func maxHeapify(s []int, heapsize, i int) {
    li := left(i)
    ri := right(i)

    largest := i
    if li < heapsize && s[li] > s[i] {
        largest = li
    }

    if ri < heapsize && s[ri] > s[largest] {
        largest = ri
    }

    if largest != i {
        s[i], s[largest] = s[largest], s[i]
        maxHeapify(s, heapsize, largest)
    }
}

func buildMaxHeap(s []int) int {
    heapsize := len(s)
    for i := len(s) / 2; i >= 0; i-- {
        maxHeapify(s, heapsize, i)
    }
    return heapsize
}

快速排序

快速排序也是利用分治策略的排序算法;与归并排序不一样,快排算法是原址的(无需额外空间成本); 算法思想:以数组中某个元素为参照,小于该元素的交换到数组前面(大于该元素的自然就全在后面);然后对该元素前后两个数组递归进行快排,当数组中只有一个元素时天然有序(收敛);该算法时间复杂度期望值为:O(nlgn);

注:参照的选择具有一定随机性,故而快排不是稳定复杂度的排序算法;最坏复杂度为n^2;

package algo

// SortQuick
func SortQuick(s []int) {
    if len(s) > 0 {
        q := partition(s)
        SortQuick(s[:q])
        SortQuick(s[q+1:])
    }
}

func partition(s []int) int {
    i := 0
    r := len(s) - 1
    for j := i; j < r; j++ {
        if s[j] < s[r] {
            if j != i {
                s[i], s[j] = s[j], s[i]
                // fmt.Printf("%+v %d <= %d, %d \n", s, i, j, r)
            }
            i++
        }
    }
    if i != r {
        s[i], s[r] = s[r], s[i]
        // fmt.Printf("%+v %d <= %d \n", s, i, r)
    }
    return i
}
下一页栈与队列

最后更新于4年前

这有帮助吗?