經典算法-排序(golang)

package main

import "fmt"

func main() {
    arr := []int{8, 9, 5, 7, 1, 2, 5, 7, 6, 3, 5, 4, 8, 1, 8, 5, 3, 5, 8, 4}
    //result := mergeSort(arr)
    result := buckerSort(arr)
    fmt.Println(result, arr)
}

//要求在O(nlogn)的時間複雜度下排序鏈表,且時間複雜度在O(1)
//
//涉及到O(logn)的算法有
//
//二分法
//快速排序
//歸併排序
//堆排序
//二分法一般應用在已排序的序列中,且經常使用語查找算法,而不用做排序算法

// 冒泡排序:O(n^2)
// 外層循環 0到n-1      //控制比較輪數   n 表示元素的個數
// 內層循環 0到n-i-1     //控制每一輪比較次數
// 兩兩比較作交換
func bubbleSort(data []int) {
    if len(data) < 2 {
        return
    }
    for i := 0; i < len(data)-1; i++ {
        for j := 0; j < len(data)-i-1; j++ {
            if data[j] > data[j+1] {
                data[j], data[j+1] = data[j+1], data[j]
            }
        }
    }
}

// 快速排序:O(n*logn)
// i, j := left, right,將左邊第一個數挖出來做爲基準。
// j--由後向前找比它小的數,找到後挖出此數填前一個坑a[i]中。
// i++由前向後找比它大的數,找到後也挖出此數填到前一個坑a[j]中。
// 再重複執行2,3二步,直到i==j,將基準數填入a[i]中。
func quickSort(data []int, left, right int) {
    fmt.Println("every000: ", left, right)
    if left >= right {
        return
    }
    i, j := left, right
    x := data[left]
    for i < j {
        for i < j && data[j] > x {
            j--
        }
        data[i] = data[j]
        for i < j && data[i] <= x {
            i++
        }
        data[j] = data[i]
    }
    fmt.Println("every: ", data)
    data[i] = x
    quickSort(data, left, i-1)
    quickSort(data, i+1, right)
}

// 歸併排序:O(n*log(n))
// 創建在歸併操做上的一種有效的排序算法。該算法是採用分治法(Divide and Conquer)的一個很是典型的應用
// 先分治 -> 再合併
func mergeSort(arr []int) []int {
    if len(arr) < 2 {
        return arr
    }
    i := len(arr) / 2
    left := mergeSort(arr[0:i])
    right := mergeSort(arr[i:])
    result := merge(left, right)
    return result
}

func merge(left, right []int) []int {
    result := make([]int, 0)
    m, n := 0, 0 // left和right的index位置
    l, r := len(left), len(right)
    for m < l && n < r {
        if left[m] > right[n] {
            result = append(result, right[n])
            n++
            //continue
        } else {
            result = append(result, left[m])
            m++

        }
    }
    result = append(result, right[n:]...) // 這裏居然沒有報數組越界的異常?
    result = append(result, left[m:]...)
    return result
}

// 堆排序:O(n*log(n)) https://blog.csdn.net/code_beeps/article/details/91488526
// s[0]不用,實際元素從角標1開始
// 父節點元素大於子節點元素
// 左子節點角標爲2*k
// 右子節點角標爲2*k+1
// 父節點角標爲k/2
func HeapSort(s []int) {
    N := len(s) - 1 //s[0]不用,實際元素數量和最後一個元素的角標都爲N
    //構造堆
    //若是給兩個已構造好的堆添加一個共同父節點,
    //將新添加的節點做一次下沉將構造一個新堆,
    //因爲葉子節點均可看做一個構造好的堆,因此
    //能夠從最後一個非葉子節點開始下沉,直至
    //根節點,最後一個非葉子節點是最後一個葉子
    //節點的父節點,角標爲N/2
    for k := N / 2; k >= 1; k-- {
        sink(s, k, N)
    }
    //下沉排序
    for N > 1 {
        s[1], s[N] = s[N], s[1] //將大的放在數組後面,升序排序
        N--
        sink(s, 1, N)
    }
}

//下沉(由上至下的堆有序化)
func sink(s []int, k, N int) {
    for {
        i := 2 * k
        if i > N { //保證該節點是非葉子節點
            break
        }
        if i < N && s[i+1] > s[i] { //選擇較大的子節點
            i++
        }
        if s[k] >= s[i] { //沒下沉到底就構造好堆了
            break
        }
        s[k], s[i] = s[i], s[k]
        k = i
    }
}

// 桶排序:O(n)
// 藉助一個一維數組就能夠解決這個問題
// 最大數值做爲這個數組的長度,數組的下標key等於這元素的,value就+1
func buckerSort(s []int) []int {
    arr := make([]int, 10)
    for _, v := range s {
        arr[v] += 1
    }
    sortList := make([]int, 0, len(s))
    for k, v := range arr {
        if v > 0 {
            for i := 0; i < v; i++ {
                sortList = append(sortList, k)
            }
        }
    }
    return sortList
}
相關文章
相關標籤/搜索