算法 7年前

Go 语言实现算法之冒泡排序

作者头像 刘宇帅
3382 0

冒泡排序是最基本的排序算法,它的复杂度为O(n²)。
它的实现原理非常简单,就是从头遍历数组,如果第 i 个数比第 i + 1 个数大,那么就交换他们,这样遍历第一次就会让最大的数放到数组末尾,第二遍会把第二大的数放到数组倒数第二的位置,依次类推……
冒泡排序过程图形表示如下:

冒泡排序

那么我们可以实现最基本的冒泡排序如下:

func BubbleSort1(list []int) {
    n := len(list)
    for i := 0; i < n; i++ {
        for j := 0; j < n-1; j++ {
            if list[j] > list[j+1] {
                list[j], list[j+1] = list[j+1], list[j]
            }
        }
    }
}

BubbleSort1 的实现是最基本实现,每次遍历都是遍历所有数据,其实根据上面我们讲解冒泡的原理知道每冒泡依次我们最少会把一个大的数据更新到数组尾部,也就是说我们可以每次遍历数量少一个。改进算法如下:

func BubbleSort2(list []int) {
    n := len(list)
    for i := 0; i < n; i++ {
        for j := 0; j < n-1-i; j++ {
            if list[j] > list[j+1] {
                list[j], list[j+1] = list[j+1], list[j]
            }
        }
    }
}

BubbleSort2 做了一个小的改进,其实我们还可以进一步改进,从上面的冒泡原理我们知道,每次遍历的时候,只要该次遍历有顺序需要调整的那么这个数组就还没有完全有序,反过来如果我们该次遍历没有元素的位置需要调整,那么就表明数组内元素已经完全有序了,那么我们可以设置一个标志量来进一步改进算法。改进算法如下:

func BubbleSort3(list []int) {
    n := len(list)
    flag := true

    for flag {
        flag = false
        for j := 0; j < n-1; j++ {
            if list[j] > list[j+1] {
                list[j], list[j+1] = list[j+1], list[j]
                flag = true
            }
        }
        n = n - 1
    }
}

到这里冒泡排序基本完了,可优化的点也没了。
源码地址

单元测试

写了三个单元测试用来测试三个算法的正确性,源码地址


var list = []map[string][]int{
    {
        "origin": {10, 9, 8, 7, 6, 5, 4, 3, 2, 1},
        "result": {1, 2, 3, 4, 5, 6, 7, 8, 9, 10},
    },
    {
        "origin": {22, 33, 22, 1, 2, 11, 22, 123, 110},
        "result": {1, 2, 11, 22, 22, 22, 33, 110, 123},
    },
    {
        "origin": {22, 23, 10, 9, 54, 17, 100, 2},
        "result": {2, 9, 10, 17, 22, 23, 54, 100},
    },
    {
        "origin": {22, 23, 22, 23, 22, 23, 22, 23},
        "result": {22, 22, 22, 22, 23, 23, 23, 23},
    },
    {
        "origin": {1, 1111, 298989, 8422, 2222, 44422},
        "result": {1, 1111, 2222, 8422, 44422, 298989},
    },
    {
        "origin": {22, 10, 1, 99, 22, 10, 11, 10, 2, 100},
        "result": {1, 2, 10, 10, 10, 11, 22, 22, 99, 100},
    },
    {
        "origin": {23, 22},
        "result": {22, 23},
    },
    {
        "origin": {1},
        "result": {1},
    },
    {
        "origin": {},
        "result": {},
    },
}

func listEqual(list1, list2 []int) bool {
    for index, _ := range list1 {
        if list1[index] != list2[index] {
            return false
        }
    }
    return true
}

func TestBubbleSort1(t *testing.T) {
    for _, item := range list {
        origin := make([]int, len(item["origin"]))
        copy(origin, item["origin"])
        BubbleSort1(origin)
        if !listEqual(origin, item["result"]) {
            t.Error("bubble sort", item["origin"], "should be", item["result"],
                "but get", origin)
        }
    }
}
func TestBubbleSort2(t *testing.T) {
    for _, item := range list {
        origin := make([]int, len(item["origin"]))
        copy(origin, item["origin"])
        BubbleSort2(origin)
        if !listEqual(origin, item["result"]) {
            t.Error("bubble sort", item["origin"], "should be", item["result"],
                "but get", origin)
        }
    }
}

func TestBubbleSort3(t *testing.T) {
    for _, item := range list {
        origin := make([]int, len(item["origin"]))
        copy(origin, item["origin"])
        BubbleSort3(origin)
        if !listEqual(origin, item["result"]) {
            t.Error("bubble sort", item["origin"], "should be", item["result"],
                "but get", origin)
        }
    }
}

运行测试

> $ go test -run=BubbleSort* -v
=== RUN   TestBubbleSort1
--- PASS: TestBubbleSort1 (0.00s)
=== RUN   TestBubbleSort2
--- PASS: TestBubbleSort2 (0.00s)
=== RUN   TestBubbleSort3
--- PASS: TestBubbleSort3 (0.00s)
PASS
ok      github.com/yushuailiu/go-algorithm/sort 0.006s

基准测试

基准测试源码

func BenchmarkBubbleSort1(b *testing.B) {
    for i := 0; i < b.N; i++ {
        for _, item := range list {
            origin := make([]int, len(item["origin"]))
            copy(origin, item["origin"])
            BubbleSort1(origin)
            if !listEqual(origin, item["result"]) {
                b.Error("bubble sort", item["origin"], "should be", item["result"],
                    "but get", origin)
            }
        }
    }
}
func BenchmarkBubbleSort2(b *testing.B) {
    for i := 0; i < b.N; i++ {
        for _, item := range list {
            origin := make([]int, len(item["origin"]))
            copy(origin, item["origin"])
            BubbleSort2(origin)
            if !listEqual(origin, item["result"]) {
                b.Error("bubble sort", item["origin"], "should be", item["result"],
                    "but get", origin)
            }
        }
    }
}
func BenchmarkBubbleSort3(b *testing.B) {
    for i := 0; i < b.N; i++ {
        for _, item := range list {
            origin := make([]int, len(item["origin"]))
            copy(origin, item["origin"])
            BubbleSort3(origin)
            if !listEqual(origin, item["result"]) {
                b.Error("bubble sort", item["origin"], "should be", item["result"],
                    "but get", origin)
            }
        }
    }
}

运行基准测试

> $ go test -bench=BubbleSort* -run=^1 -v
goos: darwin
goarch: amd64
pkg: github.com/yushuailiu/go-algorithm/sort
BenchmarkBubbleSort1-4       1000000          1012 ns/op
BenchmarkBubbleSort2-4       2000000           993 ns/op
BenchmarkBubbleSort3-4       2000000           883 ns/op
PASS
ok      github.com/yushuailiu/go-algorithm/sort 6.676s

可以看到 BubbleSort2 比 BubbleSort1 稍有提升,而 BubbleSort3 比 BubbleSort2 提升还是挺大的。

作者头像

刘宇帅

非著名程序员,全栈开发工程师,长期专注系统开发与架构设计。

提示

功能待开通!


暂无评论~

相关文章

【每日一题】我为什么喜欢面试链表反向打印

题目 输入一个单链表链表的头结点,从尾到头打印每个节点的值。 题目很简单但是实现方法却可以有很多种,也能考察人的不同方面的知识。这里实现使用 go 语言。 实现 节点定义 首先说明这里不能使用各语言内置的链表,比如 java 的 LinkedList。这里要求的单链表只能有两个属性不能有其他的属性和方法。 go 语言定义如下 type ListNode struct { Val int Next *ListNode } 堆栈 or 数组 看到这个题目最先想到的就是用堆栈或数组来做,方法就是遍历所有节点放入到数组或堆栈然后再遍历打印。数组就比较简单了,这里写下堆栈的实现。 首先

【每日一题】合并有序链表

题目 合并两个有序链表,合并之后仍是有序的。 分析 这个题目很简单,类似于合并排序的合并过程。 实现 节点定义 type ListNode struct { Val int Next *ListNode } 单元测试 var mergeOrderListList = [][][]int{ { {1, 2, 3, 4}, {7,8,9}, {1, 2, 3, 4, 7, 8, 9}, },{ {1, 2, 3, 4}, {3,8}, {1, 2, 3, 3, 4, 8

嵌套循环性能

下面有两个函数,猜一下哪一个执行的快 func nestFor1() { a := 0 for i := 0; i &lt; 10000000; i++ { for j := 0; j &lt; 2; j++ { a = a + i a = a + j } } } func nestFor2() { a := 0 for i := 0; i &lt; 2; i++ { for j := 0; j &lt; 10000000; j++ {