golang 各种排序大比拼

本文介绍了一种基于Golang的排序算法实现方案,包括冒泡排序、选择排序、插入排序、希尔排序、快速排序和堆排序等算法。通过具体代码实现了这些算法,并进行了性能测试比较。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

1、准备工作

准备数据:
生成随机数并写入文件,之后在把数据读取出来

//新生成整数随机数,并存储在txt文件中,
func NewIntRandm(fileName string, number, maxrandm int) {
    filename := fileName
    file, err := os.Create(filename)

    if err != nil {
        return
    }
    r := rand.New(rand.NewSource(time.Now().UnixNano()))
    rans := make([]string, 0, number)
    for i := 0; i < number; i++ {
        rans = append(rans, strconv.Itoa(r.Intn(maxrandm)))
    }
    file.WriteString(strings.Join(rans, " "))
    defer file.Close()
}

//把一串数组存入文件总
func SavaRandmInt(fileName string, data []int) {
    if fileName == " " || len(data) == 0 {
        return
    }
    var file *os.File
    var openerr error
    file, openerr = os.Open(fileName)
    if openerr != nil {
        var newerr error
        file, newerr = os.Create(fileName)
        if newerr != nil {
            return
        }
    }

    rans := make([]string, 0, len(data))
    for _, v := range data {
        rans = append(rans, strconv.Itoa(v))
    }
    file.WriteString(strings.Join(rans, " "))
    defer file.Close()
}

准备计时的程序:

package util

import "time"

type Stopwatch struct {
    start time.Time
    stop  time.Time
}

func (s *Stopwatch) Start() {
    s.start = time.Now()
}

func (s *Stopwatch) Stop() {
    s.stop = time.Now()
}

//纳秒
func (s Stopwatch) RuntimeNs() int {

    return s.stop.Nanosecond() - s.start.Nanosecond()
}

//微妙
func (s Stopwatch) RuntimeUs() float64 {
    return (float64)(s.stop.Nanosecond()-s.start.Nanosecond()) / 1000.00
}

//毫秒
func (s Stopwatch) RuntimeMs() float64 {
    return (float64)(s.stop.Nanosecond()-s.start.Nanosecond()) / 1000000.00
}

//秒
func (s Stopwatch) RuntimeS() float64 {
    return (float64)(s.stop.Nanosecond()-s.start.Nanosecond()) / 10000000000.00
}

2、开始写排序

我模仿golang中的sort源码包中的写法,暴露了一个接口,把排序的实现都写在内部

package sort

// package main

type Interface interface {
    //获取数据的长度
    Len() int
    //判读索引为i和索引为j的值的大小,在实现的时候如果判断i>j 返回true,则为升序,反之为降序
    Less(i, j int) bool
    //交换索引i,j的值
    Swap(i, j int)
}

//冒泡排序
func BubbleSort(data Interface) {
    n := data.Len()
    for index := 0; index < n; index++ {
        for j := index + 1; j < n; j++ {
            if data.Less(index, j) {
                data.Swap(index, j)
            }
        }
    }
}

//此方法比上面的冒泡算法快,因为我找最小元素是指记住下标,并没有每一次都做元素交换
func SelectSort(data Interface) {
    n := data.Len()
    var min int
    for index := 0; index < n; index++ {
        min = index
        for j := index + 1; j < n; j++ {
            if data.Less(min, j) {
                min = j
            }
        }
        data.Swap(index, min)
    }
}

//插入排序
func InsertSrot(data Interface) {
    count := data.Len()
    for index := 1; index < count; index++ {
        for j := index; j > 0 && data.Less(j, j-1); j-- { //j>0 做一个边界守护,不让下标小于0
            data.Swap(j, j-1)
        }
    }
}

//希尔排序
func ShellSort(data Interface) {
    N := data.Len()
    h := 1
    for h < N/3 {
        h = 3*h + 1
    }
    for h > 0 {
        for index := h; index < N; index++ {
            for j := index; j >= h && data.Less(j, j-h); j -= h { //j>0 做一个边界守护,不让下标小于0
                data.Swap(j, j-h)
            }
        }
        h = h / 3
    }
}

//快速排序
func QuickSort(data Interface) {
    n := data.Len()
    low, row := 0, n-1
    quickSort(data, low, row)
}

func quickSort(data Interface, low, row int) {
    if low < row {
        i, j, x, last := low, row, low, 0 //0就是使用第一个作为基准值,last这个变量时为了基准最后一次交换变量时出现在那次
        for i < j {
            for i < j && data.Less(x, j) { //比x小的放在前面出现的坑中
                j--
            }
            if i < j {
                data.Swap(i, j)
                i++
                x = j
                last = 1
            }
            for i < j && data.Less(i, x) { //比x大的放在后面出现的坑中
                i++
            }
            if i < j {
                data.Swap(i, j)
                j--
                x = i
                last = -1
            }
        }
        if last == 1 {
            data.Swap(j, x)
        } else if last == -1 {
            data.Swap(i, x)
        }
        quickSort(data, low, i-1)
        quickSort(data, i+1, row)
    }
}

//通过控制Less方法来控制升序降序
func HeapSort(data Interface) {
    makeHeap(data)
    n := data.Len()
    for i := n - 1; i >= 1; i-- {
        data.Swap(0, i)
        heapFixdown(data, 0, i)
    }
}

func makeHeap(data Interface) {
    n := data.Len()
    for i := (n - 1) >> 1; i >= 0; i-- {
        heapFixdown(data, i, n)
    }
}
func heapFixdown(data Interface, r, n int) {
    root := r //跟结点
    for {
        leftChildIndex := root<<1 + 1
        if leftChildIndex >= n {
            break
        }
        if leftChildIndex+1 < n && data.Less(leftChildIndex+1, leftChildIndex) {
            leftChildIndex++
        }
        if data.Less(root, leftChildIndex) {
            return
        }
        data.Swap(leftChildIndex, root)
        root = leftChildIndex
    }
}

3、开始使用

//先实现这个排序接口
type InSort []int
func (is InSort) Len() int {
    return len(is)
}//降序
func (is InSort) Less(i, j int) bool {
    return is[i] > is[j]
}
func (is InSort) Swap(i, j int) {
    is[i], is[j] = is[j], is[i]
}

func main() {
    fileName := "randm.txt"
    // util.NewIntRandm(fileName, 1000000, 10000) //封装生成5000000个随机数字
    fileUtil := util.FileUtil{}
    insort := InSort{}
    insort = fileUtil.ReaderAllInt(fileName) //读取生成的随机数
    fmt.Println(insort.Len())
    t := new(util.Stopwatch) //封装的计时间的方法
    t.Start()
    // sort.HeapSort(insort) //开始排序,519.8732 ms
    sort.QuickSort(insort) //开始排序,7.0267 ms
    t.Stop()
    fmt.Println(t.RuntimeMs(), "ms")
    util.SavaRandmInt("result.txt", insort)
}
快排:10000数组 7.0267 ms,1000000数组 37.7612 ms
堆排序:10000数组 10.0039 ms,1000000数组  358.6429 ms

下面是我测试的一些数据:

     HeapSort(insort) //堆排序 10000个数 4.0013 ms,100000个数 54.0659 ms,很稳定,500000个数 208.1511 ms 很稳定
    sort.QuickSort(insort, 0, len(insort)-1) //快速排序   10000个数 3.0017 ms,100000个数,33.0222 ms,很稳定,500000个数 150.1096 ms  很稳定,100000个数 94.0823 ms 很稳定
    sort.SelectSort(insort) //选择排序 10000个数   130.8017 ms,100000个数 时间很长
    sort.BubbleSort(insort) //冒泡排序  10000个数 203.5344ms ,100000个数 187.7438 ms
    sort.InsertSrot(insort) // 插入排序   10000个数 858.6085 ms,100000个数,时间很长
    sort.ShellSort(insort) //希尔插入   10000个数  10.9876 ms,100000个数  46.0322 m  ,就做这个范围,很稳定,500000个数 141.8833 ms,相对稳定
    sort.Sort(insort) //golang源码的排序 10000个数 6.0062 ms  ,100000个数 19.9988 ms~89.0574 ms  不稳定,500000个数 358.2536 ms 稳定
<think>好的,用户让我介绍一下Golang的面试相关内容。首先,我需要确定用户的需求是什么。他们可能正在准备Golang的面试,想了解常见的面试问题、重点考察的知识点,以及如何有效准备。接下来,我要考虑用户可能的背景,可能是有一定Go语言基础的程序员,准备应聘中级或高级职位。需要涵盖基础语法、并发模型、内存管理、标准库、性能优化等方面。 首先,结构要清晰,分点说明。常见的面试问题部分需要涵盖语言特性、并发模型、内存管理、标准库、性能优化等。每个大点下再细分小点,比如并发模型里的Goroutine、Channel、Context、同步机制等。需要确保内容全面,但不过于冗长。 然后,需要提供准备建议,比如理解核心概念、动手实践、阅读源码、模拟面试等。这部分要实用,能够给用户实际的帮助。同时,要注意使用中文,并且所有行内公式用$...$,但考虑到Golang面试可能涉及数学的地方不多,不过如果有算法复杂度的问题,可能需要提到Big O表示法,比如$O(n)$,这时候要正确应用格式。 还要注意避免使用Markdown,保持自然的口语化表达,但结构不能乱。需要检查是否有需要单独成段的公式,如果有的话用$$...$$,不过Golang面试内容可能较少涉及复杂公式,暂时不需要。 另外,用户可能对实际面试中的高频问题感兴趣,比如GMP模型、defer执行顺序、slice和map的底层实现等。这些需要详细说明,并给出示例代码,但代码部分可能需要用文字描述,因为用户没有特别要求代码块。不过根据系统指令,没有提到代码格式的问题,所以可能需要用自然语言描述。 还要考虑错误处理和接口的实现,这些是Go的重要特性,面试中常被问到。比如error处理机制、接口的动态类型和静态类型区别,以及空接口的应用场景。 在并发模型部分,要解释清楚Goroutine和线程的区别,Channel的缓冲和非缓冲区别,以及如何避免死锁。Context的使用场景也很重要,比如超时控制、取消信号传递等。 内存管理方面,逃逸分析和GC原理是关键点。需要解释堆栈分配的区别,以及GC的三色标记法流程。 标准库部分,常见的有http、json、testing等包,可以举例说明如何使用,比如实现HTTP服务器或处理JSON数据。 性能优化需要提到pprof工具的使用,如何分析CPU和内存性能,以及避免内存泄漏的方法,比如defer的使用注意事项。 在准备建议中,强调动手写代码的重要性,比如实现并发模型或数据结构,阅读标准库源码来深入理解实现细节。模拟面试和算法题练习也是必要的,尤其是LeetCode上的Go题目。 最后,检查是否有遗漏的重要知识点,比如Go的模块管理、依赖管理工具go mod,以及错误处理的最佳实践。可能还需要提到测试和基准测试的编写,因为良好的测试习惯是开发中的重要部分。 总结下来,需要组织一个结构清晰、覆盖全面、实用性强且符合用户需求的回答,确保内容正确可靠,帮助用户有效准备Golang面试。</think>### Golang面试核心要点解析 #### 一、高频考察方向 1. **语言特性** - **并发模型**:GMP调度模型(Goroutine调度、M:N线程模型)、Channel底层结构(环形队列+互斥锁)、`select`非阻塞机制 - **内存管理**:逃逸分析原则、GC三色标记法(标记阶段需STW停顿约$1\text{ms}$)、内存对齐规则(如结构体字段排序优化) - **数据结构**:Slice扩容机制(容量<1024时翻倍,>1024时1.25倍)、Map的桶结构(拉链法+溢出桶) 2. **典型代码题** ```go // 高频考题示例:交替打印数字 func main() { ch := make(chan struct{}) go func() { for i := 1; i <= 10; i += 2 { <-ch fmt.Print(i) ch <- struct{}{} } }() go func() { for i := 2; i <= 10; i += 2 { ch <- struct{}{} <-ch fmt.Print(i) } }() time.Sleep(time.Second) } ``` #### 二、进阶考察点 1. **运行时机制** - **调度器**:P的数量由`GOMAXPROCS`控制(默认CPU核数)、work-stealing调度算法 - **内存分配**:mcache/mcentral/mheap三级分配体系,微小对象(<16B)使用固定大小span 2. **标准库实现** - **HTTP Server**:每个连接创建goroutine处理、`ResponseWriter`接口实现原理 - **Context**:树形结构传播、`WithCancel`内部使用通道通知取消 #### 三、性能优化实践 1. **诊断工具链** - `pprof`火焰图分析:通过`go tool pprof -http=:8080 profile.out`定位热点 - 竞争检测:编译时添加`-race`参数,运行时检测数据竞争 2. **优化案例** - 减少堆分配:通过`sync.Pool`复用对象(如JSON解析时的临时缓冲区) - 并发控制:使用`errgroup.Group`实现goroutine错误传播和上下文取消 #### 四、架构设计问题 1. **微服务场景** - 连接池管理:数据库连接池参数设置(`MaxOpenConns`/`MaxIdleConns`) - 熔断降级:集成Hystrix模式或自定义滑动窗口统计 2. **分布式系统** - 一致性实现:基于Raft协议的状态机复制(如Etcd实现原理) - 分布式锁:结合Redis的`SETNX`或Etcd的租约机制 #### 五、深度原理问题 1. **调度器细节** - 系统调用处理:当G进行阻塞调用时,M会与P解绑 - 抢占式调度:基于信号(SIGURG)的协作式抢占,1.14+版本实现 2. **GC演进** - 历史版本对比:Go1.3标记清除法→1.5并发标记→1.8混合写屏障 - 调优参数:`GOGC`控制GC触发阈值(默认100%堆增长) #### 六、面试准备策略 1. **知识体系构建** - 深入阅读《Go语言设计与实现》《Concurrency in Go》 - 研究标准库源码(如`sync.Map`的无锁设计) 2. **实战训练** - 手写生产者-消费者模型(带优雅退出机制) - 实现简易RPC框架(包含编解码、服务发现、负载均衡) 3. **模拟面试** - 常见陷阱题: ```go // 以下代码输出什么? func main() { var m = map[int]bool{1: true, 2: false} delete(m, 2) for v := range m { fmt.Print(v) } } ``` 答案:输出`1`(遍历map时元素顺序随机) 通过系统化的知识梳理(建议制作包含20+核心知识点的思维导图)、300+行典型代码编写、重点源码分析(如调度器源码`runtime/proc.go`),可显著提升面试通过率。建议每周进行2次模拟面试,重点关注代码实现和系统设计环节。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值