C语言:排序(一)

目录

[1. 前言](#1. 前言)

[2. 冒泡排序](#2. 冒泡排序)

[2.1 动态演示](#2.1 动态演示)

[2.2 代码实现](#2.2 代码实现)

[3. 插入排序](#3. 插入排序)

[3.1 直接插入排序](#3.1 直接插入排序)

[3.1.1 动态演示](#3.1.1 动态演示)

[3.1.2 代码实现](#3.1.2 代码实现)

[3.2 希尔排序](#3.2 希尔排序)

[3.2.1 代码实现](#3.2.1 代码实现)

[4. 选择排序](#4. 选择排序)

[4.1 动态演示](#4.1 动态演示)

[4.2 代码实现](#4.2 代码实现)

[5. 堆排序](#5. 堆排序)

[5.1 代码实现](#5.1 代码实现)


1. 前言

本篇主要讲解基础的排序算法,教学意义较大,实践意义较小。

在排序代码中会频繁使用自定义函数Swap,因此在前言中进行声明,后文不再过多赘述。

cpp 复制代码
void Swap(int* x, int* y)
{
    int tmp = *x;
    *x = *y;
    *y = tmp;
}

2. 冒泡排序

  • 时间复杂度 O( N )
  • **思想:**重复遍历数组,依次比较相邻的两个元素,如果左边比右边大,就交换它们的位置。每一轮遍历,都会把当前未排序部分中最大的那个元素"冒泡"到最右边。

2.1 动态演示

冒泡排序

2.2 代码实现

cpp 复制代码
//冒泡排序
void BubbleSort(int* a, int n)
{
    for(int i=0; i<n-1; i++)
    {
        int flag = 0;
        for(int j=0; j<n-1-i; j++)
        {
            if(a[j] > a[j+1])
            {
                flag = 1;
                Swap(&a[j], &a[j+1]);
            }
        }
        if(flag == 0)
        {
            return;
        }
    }
}

使用flag可以保证在后续元素有序的情况下,直接终止循环,省略多余判断。

3. 插入排序

3.1 直接插入排序

  • 时间复杂度 O( N^2 ),最好情况 O( N )
  • **思想:**将数组分为"已排序"和"未排序"两部分,每次从未排序部分取出第一个元素,插入到已排序部分的正确位置。

3.1.1 动态演示

插入排序

3.1.2 代码实现

cpp 复制代码
//插入排序
void InsertSort(int* a, int n)
{
    for(int i=0; i<n-1; i++) //小于n-1是因为后面涉及end+1,如果小于n会越界
    {
        int end = i;
        int obj = a[end + 1];

        while(end >= 0)
        {
            if(a[end] > obj)
            {
                a[end + 1] = a[end]; //挪动比Swap更快
                end--;
            }
            else
            {
                break;
            }
        }

        a[end + 1] = obj;
    }
}

3.2 希尔排序

  • 时间复杂度 **O( N^1.3 )**左右
  • **思想:**希尔排序是插入排序的改进版,通过"分组跳跃式插入"让数组先变得"大致有序",最后再用一次插入排序完成精细调整。

3.2.1 代码实现

cpp 复制代码
//希尔排序(预排序+插入排序,插入排序已被预排序包含)
//法一:逐组进(三层循环)
void ShellSort1(int* a, int n)
{
    int gap = n;
    while(gap > 1) //gap为1时为插入排序(最后一次循环先判断再变为1,因此不用加=)
    {
        gap = gap / 3 + 1;
        for(int j=0; j<gap; j++) //一共有gap数量的组
        {
            for(int i=j; i<n-gap; i+=gap) //改为n-gap,否则会越界;i要从j开始!(每组的第一个数)
            {
                int end = i;
                int obj = a[end + gap];
                while(end >= 0)
                {
                    if(a[end] > a[end+gap])
                    {
                        Swap(&a[end], &a[end+gap]);
                        end-=gap;
                    }
                    else
                    {
                        break;
                    }
                }
                a[end + gap] = obj;
            }
        }
    }
}
cpp 复制代码
//法二:多组并行
void ShellSort2(int* a, int n)
{
    int gap = n;
    while(gap > 1)
    {
        gap = gap / 3 + 1;
        for(int i=0; i<n-gap; i++)
        {
            int end = i;
            int obj = a[end + gap];
            while(end >= 0)
            {
                if(a[end] > obj)
                {
                    a[end + gap] = a[end];
                    end -= gap;
                }
                else
                {
                    break;
                }
            }
            a[end + gap] = obj;
        }
    }
}

4. 选择排序

  • 时间复杂度 O( N^2 )
  • **思想:**每次从待排序部分选出最小(或最大)的元素,放到已排序部分的末尾。

4.1 动态演示

选择排序

// 这是最直接的选择排序,但在代码中采用了首尾双指针的方法进行选择排序:

同时找出区间内的最大和最小元素,放在区间首尾

4.2 代码实现

cpp 复制代码
//选择排序(双指针)
void SelectSort(int* a, int n)
{
    int begin = 0;
    int end = n - 1;

    while(begin < end)
    {
        int mini = begin, maxi = begin;
        for(int i=begin+1; i<=end; i++)
        {
            if(a[i] > a[maxi])
            {
                maxi = i;
            }
            if(a[i] < a[mini])
            {
                mini = i;
            }
        }
        Swap(&a[maxi], &a[end]);

        //避免刻舟求剑TvT
        if(mini == end) mini = maxi;

        Swap(&a[mini], &a[begin]);

        begin++;
        end--;
    }
}

5. 堆排序

  • 时间复杂度 O( NlogN )
  • 思想: 向上调整算法 + 模拟堆删除操作。顺序建大堆,倒序建小堆

详细介绍可以阅读博主之前写的这篇博客:

堆(C语言)-CSDN博客

5.1 代码实现

cpp 复制代码
//堆排序(向下调整建大堆->每次交换首尾元素并end--,向下调整)
void HeapSort(int* a, int n)
{
    //向下调整
    for(int i=(n-1-1)/2; i>=0; i--)
    {
        int parent = i;
        int child = parent * 2 + 1;

        while(child < n)
        {
            //假设法(右孩子存在且右孩子更大)
            if(child+1<n && a[child+1] > a[child])
            {
                child += 1;
            }

            if(a[parent] < a[child])
            {
                Swap(&a[parent], &a[child]);
                parent = child;
                child = parent * 2 + 1;
            }
            else
            {
                break;
            }
        }
    }

    //设置end,交换首尾元素
    int end = n-1;
    while(end)
    {
        Swap(&a[end], &a[0]);

        //向下调整
        int parent = 0;
        int child = parent * 2 + 1;

        while(child < end)
        {
            if(child+1<end && a[child+1] > a[child])
            {
                child += 1;
            }

            if(a[parent] < a[child])
            {
                Swap(&a[parent], &a[child]);
                parent = child;
                child = parent * 2 + 1;
            }
            else
            {
                break;
            }
        }

        end--;
    }
}

//感谢阅读~(●'◡'●)

相关推荐
12.=0.5 小时前
【stm32_5】Systick嘀嗒定时器、解析时钟源、分析时钟树、应用Systick设计延时
c语言·stm32·单片机·嵌入式硬件
j_xxx404_5 小时前
力扣题型--链表(两数相加|两两交换链表中的节点|重排链表)
数据结构·c++·算法·leetcode·蓝桥杯·排序算法
_日拱一卒6 小时前
LeetCode:240搜索二维矩阵Ⅱ
数据结构·线性代数·leetcode·矩阵
计算机安禾7 小时前
【数据结构与算法】第44篇:堆(Heap)的实现
c语言·开发语言·数据结构·c++·算法·排序算法·图论
汀、人工智能7 小时前
[特殊字符] 第91课:课程表
数据结构·算法·数据库架构·图论·bfs·课程表
jolimark7 小时前
C语言标准与编译器,新手该看哪些?
c语言·开发工具·环境搭建·编译器·新手指南
测绘第一深情8 小时前
MapQR:自动驾驶在线矢量化高精地图构建的端到端 SOTA 方法
数据结构·人工智能·python·神经网络·算法·机器学习·自动驾驶
想带你从多云到转晴8 小时前
04、数据结构与算法---双向链表
java·数据结构·算法·链表
网域小星球8 小时前
C 语言从 0 入门(二十)|指针进阶:指针数组、数组指针与函数指针
c语言·开发语言·函数指针·数组指针·指针进阶