排序算法-冒泡排序

算法排序-冒泡排序

简介

冒泡排序(Bubble Sort)是最简单直观的排序算法之一。它重复地遍历要排序的数列,一次比较两个元素,如果它们的顺序错误就把它们交换过来。遍历数列的工作是重复地进行直到没有再需要交换,也就是说该数列已经排序完成。

这个算法的名字由来是因为越小的元素会经由交换慢慢"浮"到数列的顶端,就如同碳酸饮料中二氧化碳的气泡最终会上浮到顶端一样。

算法原理

冒泡排序的基本思想是:

  1. 比较相邻的元素。如果第一个比第二个大,就交换它们两个
  2. 对每一对相邻元素作同样的工作,从开始第一对到结尾的最后一对
  3. 在这一点,最后的元素应该会是最大的数
  4. 针对所有的元素重复以上的步骤,除了最后一个
  5. 重复步骤1~4,直到排序完成

Go语言实现

基础版本

go 复制代码
package main

import "fmt"

// BubbleSort 冒泡排序基础实现
func BubbleSort(arr []int) {
    n := len(arr)
    
    // 外层循环控制排序轮数
    for i := 0; i < n-1; i++ {
        // 内层循环进行相邻元素比较和交换
        for j := 0; j < n-1-i; j++ {
            if arr[j] > arr[j+1] {
                // 交换元素
                arr[j], arr[j+1] = arr[j+1], arr[j]
            }
        }
    }
}

func main() {
    arr := []int{64, 34, 25, 12, 22, 11, 90}
    fmt.Println("排序前:", arr)
    
    BubbleSort(arr)
    fmt.Println("排序后:", arr)
}

优化版本

go 复制代码
package main

import "fmt"

// BubbleSortOptimized 优化的冒泡排序
func BubbleSortOptimized(arr []int) {
    n := len(arr)
    
    for i := 0; i < n-1; i++ {
        swapped := false // 标记本轮是否发生交换
        
        for j := 0; j < n-1-i; j++ {
            if arr[j] > arr[j+1] {
                arr[j], arr[j+1] = arr[j+1], arr[j]
                swapped = true
            }
        }
        
        // 如果本轮没有发生交换,说明数组已经有序
        if !swapped {
            break
        }
    }
}

// BubbleSortWithSteps 带步骤显示的冒泡排序
func BubbleSortWithSteps(arr []int) {
    n := len(arr)
    fmt.Printf("初始数组: %v\n", arr)
    
    for i := 0; i < n-1; i++ {
        swapped := false
        fmt.Printf("\n第 %d 轮排序:\n", i+1)
        
        for j := 0; j < n-1-i; j++ {
            if arr[j] > arr[j+1] {
                fmt.Printf("  交换 %d 和 %d\n", arr[j], arr[j+1])
                arr[j], arr[j+1] = arr[j+1], arr[j]
                swapped = true
            }
        }
        
        fmt.Printf("  本轮结果: %v\n", arr)
        
        if !swapped {
            fmt.Println("  数组已有序,提前结束")
            break
        }
    }
}

func main() {
    // 测试基础版本
    arr1 := []int{64, 34, 25, 12, 22, 11, 90}
    fmt.Println("=== 基础冒泡排序 ===")
    fmt.Println("排序前:", arr1)
    BubbleSort(arr1)
    fmt.Println("排序后:", arr1)
    
    // 测试优化版本
    arr2 := []int{5, 2, 8, 1, 9}
    fmt.Println("\n=== 优化冒泡排序 ===")
    fmt.Println("排序前:", arr2)
    BubbleSortOptimized(arr2)
    fmt.Println("排序后:", arr2)
    
    // 测试带步骤显示的版本
    arr3 := []int{5, 2, 8, 1}
    fmt.Println("\n=== 带步骤显示的冒泡排序 ===")
    BubbleSortWithSteps(arr3)
}

复杂度分析

时间复杂度

  • 最坏情况: O(n²) - 数组完全逆序时
  • 最好情况: O(n) - 数组已经有序时(优化版本)
  • 平均情况: O(n²)

空间复杂度

  • 空间复杂度: O(1) - 只需要常数级别的额外空间

算法特点

优点

  1. 实现简单: 代码逻辑清晰,容易理解和实现
  2. 原地排序: 只需要常数级别的额外空间
  3. 稳定排序: 相等元素的相对位置不会改变
  4. 自适应性: 对于已经部分有序的数组,优化版本可以提前结束

缺点

  1. 效率低下: 时间复杂度为O(n²),不适合大数据集
  2. 比较次数多: 即使数组已经有序,基础版本仍会进行所有比较

实际应用场景

冒泡排序虽然效率不高,但在以下场景中仍有其价值:

  1. 教学演示: 作为排序算法的入门教学
  2. 小数据集: 当数据量很小时(通常小于50个元素)
  3. 几乎有序的数据: 优化版本对于几乎有序的数据表现较好
  4. 嵌入式系统: 在内存极其有限的环境中,简单性比效率更重要

性能测试

go 复制代码
package main

import (
    "fmt"
    "math/rand"
    "time"
)

// 生成随机数组
func generateRandomArray(size int) []int {
    rand.Seed(time.Now().UnixNano())
    arr := make([]int, size)
    for i := 0; i < size; i++ {
        arr[i] = rand.Intn(1000)
    }
    return arr
}

// 性能测试
func benchmarkBubbleSort(size int) {
    arr := generateRandomArray(size)
    
    start := time.Now()
    BubbleSortOptimized(arr)
    duration := time.Since(start)
    
    fmt.Printf("数组大小: %d, 排序时间: %v\n", size, duration)
}

func main() {
    fmt.Println("=== 冒泡排序性能测试 ===")
    sizes := []int{100, 500, 1000, 2000}
    
    for _, size := range sizes {
        benchmarkBubbleSort(size)
    }
}

总结

冒泡排序是一个经典的排序算法,虽然效率不高,但其简单性使其成为学习排序算法的绝佳起点。通过理解冒泡排序的原理和实现,可以为学习更复杂的排序算法打下坚实的基础。

在实际开发中,除非是处理非常小的数据集或者在资源极其有限的环境中,否则建议使用更高效的排序算法如快速排序、归并排序或者直接使用语言内置的排序函数。

关键要点:

  • 冒泡排序通过重复比较相邻元素并交换来实现排序
  • 时间复杂度为O(n²),空间复杂度为O(1)
  • 是稳定的排序算法
  • 适合教学和小数据集排序
  • 优化版本可以在数组已有序时提前结束
相关推荐
智者知已应修善业38 分钟前
【51单片机8位数码管同时倒计时从9999】2024-1-25
c++·经验分享·笔记·算法·51单片机
洛水水41 分钟前
【力扣100题】86.柱状图中最大的矩形
算法·leetcode·职场和发展
渡之1 小时前
GRiM-Net 深度解析 | 无人机 GNSS 拒止场景下两阶段跨视角视觉定位框架
深度学习·算法·动态规划·无人机
测试仪器廖生135902563851 小时前
罗德与施瓦茨 FSP13频谱分析仪FSP30
网络·人工智能·算法
happymaker06261 小时前
LeetCodeHot100——560.和为K的子数组
算法
dtq04241 小时前
C语言刷题数组5,6(求平均值,求最大值)
c语言·数据结构·算法
郭梧悠2 小时前
Hash算法入门Hash冲突解决方案
算法·哈希算法
洛水水2 小时前
【力扣100题】81.寻找两个正序数组的中位数
数据结构·算法·leetcode
happymaker06263 小时前
LeetCodeHot100——155.最小栈
算法
洛水水3 小时前
【力扣100题】85.每日温度
算法·leetcode·职场和发展