C语言实现交换排序算法(冒泡,快速排序)的示例代码

目录
  • 前言
  • 一、冒泡排序
    • 1.基本思想
    • 2.优化
    • 3.扩展
  • 二、快速排序
    • 1.基本思想
    • 2.优化
    • 3.代码

前言

查找和排序是数据结构与算法中不可或缺的一环,是前辈们在算法道路上留下的重要且方便的一些技巧,学习这些经典的查找和排序也能让我们更好和更快的解决问题。在这个专栏中我们会学习六大查找和十大排序的算法与思想,而本篇将详细讲解其中的交换排序——冒泡排序和快速排序;

注意:本文中所有排序按照升序排序,降序只需要把逻辑反过来即可!

一、冒泡排序

1.基本思想

对于很多同学来说冒泡排序是再熟悉不过了,冒泡的思想在于:不断的比较两个元素并交换,大的在右边,小的在左边;

有一个数组{5, 1, 4, 2, 8, 4}

第一轮

  • arr[0] = 5和 arr[1] = 1比较 5 > 1,交换;
  • arr[2] = 4和 arr[1] = 5比较 5 > 4,交换;
  • arr[2] = 5和 arr[3] = 2比较 5 > 2,交换;
  • arr[3] = 5和 arr[4] = 8比较 5 < 8,不交换;
  • arr[4] = 8和 arr[5] = 4比较 8 > 4,交换;

第二轮

从arr[1]开始重复上述的步骤;

... ...直到循环 N - 1 次,排序结束;

#include <stdio.h>
#include<stdlib.h>

//冒泡排序
void bubbleSort(int a[], int n)
{
    //一共要扫描n-1趟
    for(int i = 0; i < n - 1; i++)
    {
        //用来比较 交换
        for(int j = 0; j < n - i - 1; j++)
        {
            if(a[j] > a[j + 1])
            {
                int temp = a[j + 1];
                a[j + 1] = a[j];
                a[j] = temp;
            }
        }
    }
}

int main()
{
    int arr[] = {5, 1, 4, 2, 8, 4};
    int length = sizeof(arr) / sizeof(arr[0]);
    bubbleSort(arr, length);
    for(int i = 0; i < length; i++)
    {
        printf("%d", arr[i]);
    }
    system("pause");
    return 0;
}

那么问题来了,问题一:这里我们发现对于这个数组而言在第二轮排序就已经排好了整体甚至稳定的有序,剩下的循环就相当于浪费了,那么有没有一种方法能够判断数组已经有序?

还有这样一个数组{4, 2, 1, 5, 6, 8}

问题二:我们发现{5, 6, 8}的部分是已经有序了的,对于已经有序的部分还要继续比较,那么能不能确定出有序的部分和无序的部分的边界呢?

2.优化

针对问题一,我们可以添加一个标志,用来标识数组是否有序:当某一轮排序没有发生交换,就可以认为数组已经有序了;

针对问题二,我们可以记录冒泡排序的过程中最后一次发生交换的地方index,如在下图中index==1,每一次后面的排序只要从第一个到index就可以了;

值得注意的是,不管怎样去优化,最坏情况的时间复杂度都是O(n^2),即数组逆序的情况;

3.扩展

虽然用栈实现冒泡排序可能在实际中没有应用场景(也没必要用),但是可能会有面试的时候要求用栈或者其他的结构去实现冒泡排序来考查对算法和思想熟练度,所以这里也提供用栈实现冒泡排序的思路;

void bubbleSort(int a[], int n)
{
    //定义两个栈S1和S2
    stack<int>stk1, stk2;
    //将数组中的所有元素入栈S1
    for (int i = 0; i < n; i++)
    {
        stk1.push(a[i]);
    }
    //循环N次 每一次找出最大的元素(就像真冒泡一样 最大的元素浮在最上面)
    for (int i = 0; i < n; i++)
    {
        //如果S1不为空
        while (!stk1.empty())
        {
            //如果S2为空就把栈S1顶的元素入栈S2
            if (stk2.empty())
            {
                stk2.push(stk1.top());
                stk1.pop();
            }
            else
            {
                int temp = 0;//用于接收需要交换的元素
                if (stk1.top() < stk2.top())
                {
                    //如果S1的栈顶小于S2的栈顶 把S1的栈顶压在S2的栈顶下面
                    temp = stk2.top();
                    stk2.pop();
                    stk2.push(stk1.top());
                    stk1.pop();
                    stk2.push(temp);
                }
                else
                {
                    stk2.push(stk1.top());
                    stk1.pop();
                }
            }
        }
        //把最大的元素从后往前更新回原数组中
        a[n - i - 1] = stk2.top();
        stk2.pop();
        //把剩下的元素倒栈 重复
        for (int j = stk2.size(); j > 0; j--)
        {
            stk1.push(stk2.top());
            stk2.pop();
        }
    }
}

二、快速排序

1.基本思想

选取一个基准(可以认为是要放到排序以后正确的位置的元素,可以是第一个元素、最后一个 中间的、随机的都可以);

把数组中的元素做一个划分,每一趟划分将作为基准的值x放到排序后数组正确的位置,并将所有比x小的放到左边,比x大的放到右边;

有一个数组{1, 8, 3, 9, 4, 5, 4, 7}

假定选择元素arr[7] = 7为基准,就是要把7放在正确的位置,那么只有两种情况:

要么7本身就是正确的位置,要么就在前面;

第一步:初始化指针 i = -1,j = 0,把 j 指向的元素和7比较 ,当1 < 7,将 i++, 交换 i 和 j 指向的元素,j++;

第二步:把 j 指向的元素和7比较 ,当8 > 7,将 j++;

第三步:把 j 指向的元素和7比较 ,当3 < 7,将 i++, 交换 i 和 j 指向的元素,j++;

第四步:把 j 指向的元素和7比较 ,当4 < 7,将 i++, 交换 i 和 j 指向的元素,j++;

第五步:把 j 指向的元素和7比较 ,当5 < 7,将 i++, 交换 i 和 j 指向的元素,j++;

第五步:把 j 指向的元素和7比较 ,当4 < 7,将 i++, 交换 i 和 j 指向的元素,j++;

第六步:当j到7遍历结束,让i++的位置和7交换,第一趟排序结束;

如此一来,基准7就找到了它在数组中的正确位置,并且把数组划分成了两边【0,5)和(5,7】这时再选一个基准再进行上述步骤,如下图所示:

是不是觉得很眼熟?没错这就是一棵二叉树:

2.优化

既然是二叉树,那么排出一棵斜树自然就是最坏的情况;要缓解这个问题,可以以中间的值为基准来减少这种情况的发生;

即复杂度与数组长度和基准的选择有关,尾基准是O(n^2)因为n趟每一趟划分需要O(n),而平衡树是O(nlogn),通过数学方法能够得到更优的基准选择,但无论选什么为基准都应该能满足:基准左边小、右边大;

我们之前说过,快速排序其实是一个不稳定排序(不稳定的排序就意味着交换的次数多,如果需要按多条件排序就会乱),而我们又说过任何一个不稳定的排序算法都有办法使其变得稳定,用到的是以空间换时间的思想;

也就是我们可以用一个变量对原来的顺序做标记;

3.代码

既然是通过不断划分数组来减少比较的次数,这一听就知道用到了分治的思想,也就是可以用递归来实现代码:

#include <stdio.h>
#include<stdlib.h>

//快排
void quickSort(int a[], int low, int high)
{
   if(low < high)
   {
        int i = low;//这里以i下标的值为基准
        int j = high;
        int k = a[low];//k是用来记录基准的值
        while(i < j)
        {
            //从右往左找第一个比k要小的值
            while(i < j && a[j] >= k)
            {
                j--;
            }
            if(i < j)
            {
                a[i++] = a[j];
            }
            //从左往右找第一个比k要大的值
            while(i < j && a[i] < k)
            {
                i++;
            }
            if(i < j)
            {
                a[j--] = a[i];
            }
        }
        a[i] = k;
        //递归
        quickSort(a, low, i - 1);
        quickSort(a, i + 1, high);
   }
}

int main()
{
    int arr[] = {1, 8, 3, 9, 4, 5, 4, 7};
    int length = sizeof(arr) / sizeof(arr[0]);
    quickSort(arr, 0, length - 1);
    for(int i = 0; i < length; i++)
    {
        printf("%d ", arr[i]);
    }
    system("pause");
    return 0;
}

运行结果

到此这篇关于C语言实现交换排序算法(冒泡,快速排序)的示例代码的文章就介绍到这了,更多相关C语言交换排序算法内容请搜索我们以前的文章或继续浏览下面的相关文章希望大家以后多多支持我们!

(0)

相关推荐

  • C语言实现选择排序、冒泡排序和快速排序的代码示例

    选择和冒泡 #include<stdio.h> void maopao(int a[],int len){ int i,j,temp; for(i = 0;i < len - 1 ; i ++){//从第一个到倒数第二个 for (j = 0 ; j < len - 1 - i ; j ++)//排在后的是已经排序的 { if (a[j] > a[j + 1])//大的数换到后面去 { temp = a[j]; a[j] = a[j + 1]; a [j + 1] = tem

  • C语言排序方法(冒泡,选择,插入,归并,快速)

    目录 1.冒泡排序 2.选择排序 3.插入排序 4.归并排序 5.快速排序 总结 1.冒泡排序 它重复地走访过要排序的元素列,依次比较两个相邻的元素,如果顺序错误就把他们交换过来.走访元素的工作是重复地进行直到没有相邻元素需要交换,也就是说该元素列已经排序完成. 算法步骤 比较相邻的元素.如果第一个比第二个大,就交换他们两个. 对每一对相邻元素作同样的工作,从开始第一对到结尾的最后一对.这步做完后,最后的元素会是最大的数. 针对所有的元素重复以上的步骤,除了最后一个. 持续每次对越来越少的元素重

  • C语言的冒泡排序和快速排序算法使用实例

    冒泡排序法 题目描述: 用一维数组存储学号和成绩,然后,按成绩排序输出. 输入: 输入第一行包括一个整数N(1<=N<=100),代表学生的个数.     接下来的N行每行包括两个整数p和q,分别代表每个学生的学号和成绩. 输出: 按照学生的成绩从小到大进行排序,并将排序后的学生信息打印出来.     如果学生的成绩相同,则按照学号的大小进行从小到大排序. 样例输入: 3     1 90     2 87     3 92 样例输出: 2 87     1 90     3 92 代码: #

  • 用c语言实现冒泡排序,选择排序,快速排序

    代码如下所示: 复制代码 代码如下: /* * 冒泡排序 */void BubbleSort(int arr[], int n){ int temp; for (int i = 0; i < n - 1; i++) {  for (int j = i + 1; j < n; j++)  {   if (arr[i] > arr[j])   {    temp = arr[i];    arr[i] = arr[j];    arr[j] = temp;   }  } }}/* * 选择排

  • C语言实现交换排序算法(冒泡,快速排序)的示例代码

    目录 前言 一.冒泡排序 1.基本思想 2.优化 3.扩展 二.快速排序 1.基本思想 2.优化 3.代码 前言 查找和排序是数据结构与算法中不可或缺的一环,是前辈们在算法道路上留下的重要且方便的一些技巧,学习这些经典的查找和排序也能让我们更好和更快的解决问题.在这个专栏中我们会学习六大查找和十大排序的算法与思想,而本篇将详细讲解其中的交换排序——冒泡排序和快速排序: 注意:本文中所有排序按照升序排序,降序只需要把逻辑反过来即可! 一.冒泡排序 1.基本思想 对于很多同学来说冒泡排序是再熟悉不过

  • C语言编程内存分配通讯录静态实现示例代码教程

    实现一个通讯录: 通讯录可以用来存储1000个人的信息,每个人的信息包括:姓名.性别.年龄.电话.住址 提供方法: 1. 添加联系人信息 2. 删除指定联系人信息 3. 查找指定联系人信息 4. 修改指定联系人信息 5. 显示所有联系人信息 6. 清空所有联系人 7. 以名字排序所有联系人 首先我们采用顺序表的方式来实现一个通讯录,顺序表就是一种静态的模式.但是呢,静态的方式存在着一些明显的弊端,比如说:(1)信息少了存在空间浪费现象,信息多了存在空间不足的现象:(2)无法对信息进行保存,没有实

  • C语言实现飞机游戏(进阶版)的示例代码

    目录 前言 一.代码重构 二.新式子弹代替激光 三.优化敌方战机 四.增加积分模块 五.更好的清屏功能 前言 没有学习函数,以上功能都在main()中实现是有点痛苦的.在学了函数之后会模块化重构相应的游戏,大家经历过上面的痛苦才能真正理解函数的好处.如果只是被动地学习语法知识,做些简单的算法题,是很难体会到函数封装的重要性的. 我们前面制作的用c语言实现一个最简单的飞机游戏但存在如下缺陷: 可能会遇到子弹运动时无法输入 键盘控制比较卡 不按键时敌人不会自动移动等问题,且敌人只出现一次 屏幕图标闪

  • C语言实现堆的简单操作的示例代码

    目录 一.堆的概念 二.堆的实现 三.堆的代码实现 一.堆的概念 (1)定义 如果有一个关键码的集合K = {k0,k1, k2,…,kn-1},把它的所有元素按完全二叉树的顺序存储方式存储在一个一维数组中,并满足:Ki <= K2i+1 且 Ki<= K2i+2 (Ki >= K2i+1 且 Ki >= K2i+2) i = 0,1,2…,则称为小堆(或大堆).将根节点最大的堆叫做最大堆或大根堆,根节点最小的堆叫做最小堆或小根堆. (2)性质 1.堆中某个节点的值总是不大于或不小

  • 用R语言实现霍夫曼编码的示例代码

    可读性极低,而且其实也没必要用R语言写,图个乐罢了 p=c(0.4,0.2,0.2,0.1,0.1)###输入形如c(0.4,0.2,0.2,0.1,0.1)的概率向量,即每个待编码消息的发生概率 p1=p###将概率向量另存,最后计算编码效率要用 mazijuzhen=matrix(,nrow=length(p),ncol=length(p)-1)###码字矩阵:第i行对应向量p的第i个分量所对应的那个待编码消息的编码后的码字 group=matrix(c(1:length(p),rep(NA

  • Go语言操作数据库及其常规操作的示例代码

    Go操作MySQL 安装: go get -u github.com/go-sql-driver/mysql GO语言的操作数据库的驱动原生支持连接池, 并且是并发安全的 标准库没有具体的实现 只是列出了一些需要的第三方库实现的具体内容 //第一次连接MySQL成功 package main import ( "database/sql" _ "github.com/go-sql-driver/mysql" // _想当于init()初始化 "log&qu

  • 基于Go语言实现的简易api网关的示例代码

    浏览器的请求去请求目标地址,然后获得结果它再发送给浏览器.对于Go语言来说,实现转发只需要简单的一行代码即可实现,如下所示: httputil.NewSingleHostReverseProxy(address) 基于此功能,进行简单包装,实现从远端admin管理中心获取需要转发的路由信息或者可以从本地配置文件中获取,实现动态转发.后续可以根据业务情况,可以实现如下功能: 开发接口,实现动态添加代理规则,进行转发 过滤不合法的接口 接口限流 统一日志记录 - 代码如下: package main

  • C语言实现线性动态(单向)链表的示例代码

    目录 什么是链表 为什么不用结构体数组 链表的操作 创建表 删除元素 插入元素 代码及运行结果 什么是链表 链表是数据结构里面的一种,线性链表是链表的一种,线性链表的延伸有双向链表和环形链表.在编程语言中优化数据结构可以在处理大数据时大大降低程序的空间复杂性和时间复杂性.这里我只用一个简单的例子——线性单向链表为例,说明C语言是如何实现该结构的. 链表的元素是由结构体来实现struct table *p.结构体中有一个成员是结构体指针struct table *next,而这个结构体指针的类型和

  • Go语言实现切片增删改查的示例代码

    目录 引言 一.切片的基础语法 1. 语法 2. 示例 3. 切片的长度和容量 二.切片的初始化 1. 直接初始化 2. 使用数组初始化 3. 使用数组的部分元素初始化(切片表达式) 4. 空(nil)切片 三.切片的遍历 1. for 循环遍历 2. for range遍历 四.切片元素的添加和删除copy 1. 添加元素 2. 删除元素 3. 修改切片元素 4. 查找切片元素 5. 拷贝切片 引言 Golang 的数组是固定长度,可以容纳相同数据类型的元素的集合. 但是当长度固定了,在使用的

  • C语言实现猜数字小游戏的示例代码

    目录 一.猜数字小游戏的要求 二.猜数字小游戏实现的过程 2.1项目创建 2.2头文件内容 2.3源文件内容 三.猜数字小游戏调试结果如下 四.基于猜数字小游戏的总结 五.完整代码 一.猜数字小游戏的要求 猜数字小游戏是我们小时候喜欢我们一个经典小游戏,在本文中,猜数字小游戏主要的功能如下所示 1.登入猜数字小游戏系统,显示小时欢迎界面. 2.用户猜的数字有系统随机在1-20之间生成. 3.用户可以有5次机会猜这个随机生成的数字. 4.若用户猜大了,则系统会显示猜大了,并提示还有多少猜数字的机会

随机推荐