八大排序——冒泡排序/归并排序

八大排序------冒泡排序/归并排序

一、冒泡排序

[1.1 冒泡排序](#1.1 冒泡排序)

[1.2 冒泡排序优化](#1.2 冒泡排序优化)

二、归并排序

[1.1 归并排序(递归)](#1.1 归并排序(递归))

[1.2 递归排序(非递归)](#1.2 递归排序(非递归))


一、冒泡排序

1.1 冒泡排序

比较相邻的元素。如果第一个比第二个大,就交换它们两个。

复制代码
void Bubble_Sort(int arr[], int len) // 定义冒泡排序函数,参数为数组和数组长度
{
    // 外层循环控制排序的总趟数,从0开始,到len-1结束
    for (int i = 0; i < len - 1; i++)
    {
        // 内层循环负责每一趟排序中的实际比较和交换操作
        // 从0开始,到len-i-1结束,因为每趟排序结束后,最大的元素会"冒泡"到最后,不需要再参与比较
        for (int j = 0; j + 1 < len - i; j++)
        {
            // 比较相邻的两个元素
            if (arr[j] > arr[j + 1])
            {
                // 如果左边的元素大于右边的元素,说明它们的顺序错误,需要交换
                int tmp = arr[j]; // 使用临时变量tmp来保存arr[j]的值
                arr[j] = arr[j + 1]; // 将arr[j+1]的值赋给arr[j]
                arr[j + 1] = tmp; // 将tmp的值赋给arr[j+1],完成交换
            }
        }
    }
}
  • 外层循环 for (int i = 0; i < len - 1; i++):控制排序的总趟数。对于一个长度为 len 的数组,只需要进行 len - 1 趟排序就可以将整个数组排好序。每趟排序都会将当前未排序部分的最大元素 "冒泡" 到末尾。
  • 内层循环 for (int j = 0; j + 1 < len - i; j++):在每一趟排序中,负责对相邻元素进行比较和交换。随着外层循环的进行,每趟排序后最大的元素已经在正确的位置(数组末尾),所以下一趟排序时,不需要再对这些已经排好序的元素进行比较,因此内层循环的结束条件是 j + 1 < len - i
  • j + 1 表示当前要比较的两个相邻元素中右边元素的索引。因为是比较相邻元素,所以左边元素索引为 j,右边元素索引为 j + 1

1.2 冒泡排序优化

数据要是已经默认有序了,则后续的趟数就不排序了

问题:怎么得出已有数据是否有序?

解决方法:跑一趟,跑完了都没有一次数据交换发生,也就是说都是左边值小于右边值

复制代码
void Bubble_Sort(int arr[], int len)
{
	bool tag = true;
	for (int i = 0; i < len - 1; i++)//控制趟数
	{
		//每一趟开始时把tag重新置位真
		tag = true;
		for (int j = 0; j + 1 < len - i; j++)//j指向比较一对数据中左边的值,右边用j+1
		{
			if (arr[j] > arr[j + 1])
			{
				int tmp = arr[j];
				arr[j ] = arr[j+1];
				arr[j + 1] = tmp;
				tag = false;
			}
		}
		if (tag)//tag还是真,那么就默认递增有序,直接退出
		{
			return;
		}
	}
}

时间复杂度位n^2 空间复杂度1

稳定值:稳定 俩个俩个比

二、归并排序

将长度为n的待排序序列,看作n个长度为1的有序组,然后进行合并,合并成n/2个长度为2的有序组,然后接着合并,直到所有的数据都合并到同一个组为止

1.1 归并排序(递归)

复制代码
#include <stdio.h> // 引入标准输入输出头文件,用于使用 printf 和 scanf 函数
#include <stdlib.h> // 引入标准库头文件,用于使用内存分配函数 malloc 和 exit 函数
#include <string.h> // 引入字符串操作头文件,用于使用 memset 函数

// 函数声明,用于在主函数之后定义,显示排序后的数组
void Show(int arr[], int len);

// 合并两个有序子数组到临时数组 brr 中,然后再复制回原数组 arr
void Merge(int arr[], int len, int left, int mid, int right, int* brr) {
    int i = left; // 初始化指针 i 指向左边子数组的起始位置
    int j = mid + 1; // 初始化指针 j 指向右边子数组的起始位置
    int k = left; // 初始化指针 k 指向临时数组 brr 的起始位置
    while (i <= mid && j <= right) { // 当两个子数组中都有元素时
        if (arr[i] <= arr[j]) { // 如果左边的元素小于等于右边的元素
            brr[k] = arr[i]; // 将左边的元素放入临时数组 brr
            i++; // 左边子数组的指针后移
            k++; // 临时数组的指针后移
        } else { // 如果左边的元素大于右边的元素
            brr[k] = arr[j]; // 将右边的元素放入临时数组 brr
            j++; // 右边子数组的指针后移
            k++; // 临时数组的指针后移
        }
    }
    while (j <= right) { // 复制右边子数组中剩余的元素
        brr[k++] = arr[j++];
    }
    while (i <= mid) { // 复制左边子数组中剩余的元素
        brr[k++] = arr[i++];
    }
    for (int i = left; i <= right; i++) { // 将合并后的有序数组复制回原数组 arr
        arr[i] = brr[i];
    }
}

// 递归地将数组分成两半,并对每一半进行排序,然后合并
void Divide(int arr[], int len, int left, int right, int* brr) {
    if (left < right) { // 如果左边界小于右边界,则继续递归
        int mid = (left + right) / 2; // 计算中间索引
        Divide(arr, len, left, mid, brr); // 对左半部分递归调用 Divide 函数
        Divide(arr, len, mid + 1, right, brr); // 对右半部分递归调用 Divide 函数
        Merge(arr, len, left, mid, right, brr); // 合并两个有序子数组
    }
}

// 归并排序的入口函数
void Merge_Sort(int arr[], int len) {
    int* brr = (int*)malloc(len * sizeof(int)); // 动态分配一个临时数组 brr,用于合并时存放数据
    if (brr == NULL) { // 如果内存分配失败
        fprintf(stderr, "Memory allocation failed\n"); // 打印错误信息到标准错误输出
        exit(EXIT_FAILURE); // 退出程序
    }
    Divide(arr, len, 0, len - 1, brr); // 从数组的起始索引到结束索引进行递归排序
    free(brr); // 释放临时数组 brr 的内存
}

// 显示数组内容的函数
void Show(int arr[], int len) {
    for (int i = 0; i < len; i++) { // 遍历数组
        printf("%d ", arr[i]); // 打印数组的每个元素
    }
    printf("\n"); // 打印换行符
}

int main() {
    int arr[] = {122, 222, 11, 357, 333, 12, 111, 789, 654, 356}; // 初始化一个待排序的数组
    int len = sizeof(arr) / sizeof(arr[0]); // 计算数组的长度
    Merge_Sort(arr, len); // 调用归并排序函数对数组进行排序
    Show(arr, len); // 显示排序后的数组
    return 0; // 程序正常结束,返回 0
}

1.2 递归排序(非递归)

复制代码
#include <stdio.h>
#include <stdlib.h>

// 函数声明,用于显示排序后的数组
void Show(int arr[], int len);

// 合并两个有序子数组到临时数组brr中,然后再复制回原数组arr
void Merge(int arr[], int len, int left, int mid, int right, int* brr)
{
    int i = left;
    int j = mid + 1;
    int k = left;
    while (i <= mid && j <= right)
    {
        if (arr[i] <= arr[j])
        {
            brr[k] = arr[i];
            i++;
            k++;
        }
        else
        {
            brr[k] = arr[j];
            j++;
            k++;
        }
    }
    while (j <= right)
    {
        brr[k++] = arr[j++];
    }
    while (i <= mid)
    {
        brr[k++] = arr[i++];
    }
    for (int i = left; i <= right; i++)
    {
        arr[i] = brr[i];
    }
}

// 非递归的分治函数
//gap是当前子数组的长度
void Merge_No_Recursion(int arr[], int len, int gap, int* brr)
{
    int left1 = 0;
    int right1 = left1 + gap - 1;
    int left2 = right1 + 1;
    int right2 = left2 + gap - 1 < len ? left2 + gap - 1 : len - 1;
    int k = 0;

    while (left2 < len)
    {
        int i = left1;
        int j = left2;

        while (i <= right1 && j <= right2)
        {
            if (arr[i] <= arr[j])
            {
                brr[k] = arr[i];
                i++;
                k++;
            }
            else
            {
                brr[k] = arr[j];
                j++;
                k++;
            }
        }
        while (j <= right2)
        {
            brr[k++] = arr[j++];
        }
        while (i <= right1)
        {
            brr[k++] = arr[i++];
        }

        left1 = right2 + 1;
        right1 = left1 + gap - 1;
        left2 = right1 + 1;
        right2 = left2 + gap - 1 < len ? left2 + gap - 1 : len - 1;
    }

    while (left1 < len)
    {
        brr[k++] = arr[left1++];
    }

    for (int i = 0; i < len; i++)
    {
        arr[i] = brr[i];
    }
}

// 归并排序的非递归版本
void Merge_Sort_No_Recursion(int arr[], int len)
{
    int* brr = (int*)malloc(len * sizeof(int));
    if (brr == NULL)
        exit(EXIT_FAILURE);

    for (int gap = 1; gap < len; gap *= 2 )
    {
        Merge_No_Recursion(arr, len, gap, brr);
    }
    free(brr);
}

// 显示数组的函数
void Show(int arr[], int len) {
    for (int i = 0; i < len; i++) {
        printf("%d ", arr[i]);
    }
    printf("\n");
}

int main() {
    int arr[] = { 120000, 230000, -100, 195, 71, 89, 42, 8, 902, 894, 194, 80, 194, 128, 90, 18, 490, 18, 409, 180, 95, 12, 489, 404 };
    int len = sizeof(arr) / sizeof(arr[0]);
    Merge_Sort_No_Recursion(arr, len);
    Show(arr, len);
    return 0;
}
相关推荐
wuqingshun31415938 分钟前
蓝桥杯 11. 打印大X
数据结构·算法·职场和发展·蓝桥杯·深度优先
Blossom.1181 小时前
量子网络:构建未来通信的超高速“高速公路”
网络·opencv·算法·安全·机器学习·密码学·量子计算
A林玖1 小时前
【机器学习】朴素贝叶斯
人工智能·算法·机器学习
六边形战士DONK1 小时前
神经网络基础[损失函数,bp算法,梯度下降算法 ]
人工智能·神经网络·算法
wuqingshun3141592 小时前
蓝桥杯 2. 确定字符串是否是另一个的排列
数据结构·c++·算法·职场和发展·蓝桥杯
小刘|2 小时前
JVM 自动内存管理
java·jvm·算法
小羊不会c++吗(黑客小羊)2 小时前
c++头文件知识
算法
拓端研究室TRL3 小时前
PyMC+AI提示词贝叶斯项目反应IRT理论Rasch分析篮球比赛官方数据:球员能力与位置层级结构研究
大数据·人工智能·python·算法·机器学习
长沙火山3 小时前
9.ArkUI List的介绍和使用
数据结构·windows·list
CoovallyAIHub4 小时前
Vision Transformers与卷积神经网络详细训练对比(附代码)
深度学习·算法·计算机视觉