Go实现并发排序

来源:互联网 发布:淘宝网开通账户 编辑:程序博客网 时间:2024/05/29 04:51

前言

最近在学习Go语言,于是用go实现了两种排序的并发实现,下面我将一一进行介绍。

快排实现

下面先贴出快排代码:

func QuickSort(num []int, low, high int) {    if low >= high {        return    }    i, j := low, high    key := num[low]    for i < j {        for j > i && num[j] >= key {            j--        }        num[i] = num[j]        for i < j && num[i] < key {            i++        }        num[j] = num[i]    }    num[j] = key    QuickSort(num, low, i-1)    QuickSort(num, i+1, high)}

接着我们先贴出并发代码,再进行一些讨论

import "sync"func MutiQuickSort(num []int){    //如果缺少该句,由于下面第一次调用QuickSort,没有加一操作,执行完后直接lock.Done(),将导致数量减为-1而报错    lock.Add(1)    QuickSort(num, 0, len(num)-1)    lock.Wait()}var lock sync.WaitGroupfunc QuickSort(num []int, low, high int) {    defer lock.Done()    if low >= high {        return    }    i, j := low, high    key := num[low]    for i < j {        for j > i && num[j] >= key {            j--        }        num[i] = num[j]        for i < j && num[i] < key {            i++        }        num[j] = num[i]    }    num[j] = key    lock.Add(2)    go QuickSort(num, low, i-1)    go QuickSort(num, i+1, high)}

并发快排代码的思考

(1)为什么使用WaitGroup来实现?    因为快速排序对数组不断进行分割,最终使其完全有序。这里在考虑并发实现时,只需对其每次分割后的两部分    调用go启动协程进行实现,而这种并发情况下,主线程只需等待所有的排序携程都执行完成,即可实现并发排序。    [可参考下面的归并排序进行对比](2)关于WaitGroup的使用?    //定义sync.WaitGroup变量(全局变量)    var Lock sync.WaitGroup    //主线程等待(等待数量减为0,主线程结束)    Lock.Wait()    //子协程开启时(数量加2)    Lock.Add(2)    //子协程完成时(数量减1)    Lock.Done()

归并实现

我们仍然先来贴归并代码

func MergeSort(a []int, left, right int) {    if left < right {        mid := left + (right-left)/2        MergeSort(a, left, mid)        MergeSort(a, mid+1, right)        Merge(a, left, mid, right)    }}func Merge(a []int, left, mid, right int) {    arr := make([]int, 0)    i, j := left, mid+1    for i <= mid && j <= right {        if a[i] <= a[j] {            arr = append(arr,a[i])            i++        } else {            arr = append(arr,a[j])            j++        }    }    arr = append(arr, a[i:mid+1]...)    arr = append(arr, a[j:right+1]...)    for i, v := range arr {        a[left+i] = v    }}

接着是并发归并的代码

func MutiMergeSort(a []int) {    ch := make(chan int, 1)    defer close(ch)    MergeSort(a, 0, len(a)-1, ch)}func MergeSort(a []int, left, right int, c chan int) {    if left < right {        ch := make(chan int, 2)        defer close(ch)        mid := left + (right-left)/2        go MergeSort(a, left, mid, ch)        go MergeSort(a, mid+1, right, ch)        <-ch        <-ch        Merge(a, left, mid, right)    }    c <- 1}func Merge(a []int, left, mid, right int) {    arr := make([]int, 0)    i, j := left, mid+1    for i <= mid && j <= right {        if a[i] <= a[j] {            arr = append(arr, a[i])            i++        } else {            arr = append(arr, a[j])            j++        }    }    arr = append(arr, a[i:mid+1]...)    arr = append(arr, a[j:right+1]...)    for i, v := range arr {        a[left+i] = v    }}

思考

(1)这里为什么使用channel来实现?    首先,我们还是来分析归并排序。归并排序在将数组进行分割成单个元素之后,还要不断合并已经排好有序的数组段。    所以在合并时,就需要等待 执行这两个数组段排序的协程都完成,方可继续。否则,会出现错误。(2)channel的使用?    //创建缓冲区大小为1024的int类型的channel    c := make(chan int, 1024)    //写入数据    c <- 1    //读出数据    ch := <- c

测试代码

Go本身提供了一套轻量级的测试框架。符合规则的测试代码会在运行测试时被自动识别并执行。
命名规则:以 “_test”结尾的go文件会被看作测试程序

import (    "testing"    "fmt"    "math/rand")func TestMergeSort(t *testing.T) {    list := make([]int, 0)    for i := 0; i < 1000000; i++ {        v := rand.Int() % 100000        list = append(list, v)    }    MutiMergeSort(list)    for _, v := range list {        fmt.Print(v, " ")    }}
原创粉丝点击