Go实现并发排序

前言

最近在学习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.WaitGroup

func 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, " ")
    }
}
  • 1
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值