从冒泡到模拟q sort函数——初见排序算法的探索和思考

从冒泡到模拟q sort函数——初见排序算法的探索和思考

国庆中秋喜相连,万家团圆乐同庆。

各位小伙伴们大家好,我是此方,在此,先祝大家双节快乐!

我们都知道排序有很多种:例如冒泡排序,插入排序,快速排序,等等很多种。

而冒泡排序,是各种计算机语言中最经典的一种排序算法。

今天我将从冒泡排序开始,到实现qsort函数的模拟。逐层深入,探索排序问题。

并给出鄙人的一些拙见。

上正文:

一,冒泡排序:最经典的排序算法

假如有一个十元素整型数组,他是完全倒着排序的:就像这样

now,我们要按照从小到大的顺序将这十个数字重新排列。

如果我们想要用冒泡排序:那么他的逻辑应该是这样的:

首先让最左边的数字和他右边的数字比较:9>8,将9和8互换位置:

让9继续和他右边的数字比较,再互换位

以此类推:9不断的比较——>移动——>再比较:最后;会到达最右边,这样,我们就让最大的数字9放在了最低位置

然后是8,接下来是7,6,5......................直到最后,数列变成0,1,2,3,4,5,6,7,8,9

结束;

我们将一个数字通过不断和他右边的数字”比较,移动。直到这个数字到达他基于升序排序应该到达的位置“这一整个过程称为”一趟“。

将每一个数字的一趟中的每一次移动称为”一次“

那么,冒泡排序函数应该是这样的:

int*arr表示接收arr(数组首元素的地址),sz是数组元素个数,(width(串了,应该删除))

变量 i 表示趟数:一共有sz-1趟:因为有sz个数字,而将前9个数字全部排好后,最后一个数字默认到达了他应该在的位置。

变量 j 表示次数:每一趟有j-1-i次:因为某一个数字(首先假设他是最左边的那个),在考虑最坏的情况下,他可能要移动到数列的最后一个,即他后面有几个数字就移动几次。紧接着第二趟(此时i+1),同样在考虑最坏的情况下,他后面有几个数字就要移动几次但是要去掉目前最后一个数字。以此类推;第一趟额外-0,第二趟额外-1:这与i的变化完全一致:所以使用j-i-1来说明每趟需要的次数。

呈现一下源代码:

#define _CRT_SECURE_NO_WARNINGS 1 #include<stdio.h> #include<string.h> #include<stdlib.h> void bouble_qsort( int* arr,size_t sz,int width)//默认升序 { int i = 0; for (i = 0; i < sz - 1; i++) { int j = 0; for (j = 0; j < sz - 1 - i; j++) { if (arr[j] > arr[j + 1]) { int change = 0; change = *(arr + j); *(arr + j) = *(arr + 1 + j); *(arr + 1 + j) = change; } } } } void print(int* arr, size_t sz) { int i; for (i = 0; i < sz; i++) { printf("%d ", *(arr + i)); } } int main() { int arr[10] = { 2,5,8,7,4,1,3,6,9,0}; int sz = sizeof(arr) / sizeof(arr[0]); bouble_qsort(arr,sz,arr[0]); print(arr,sz); return 0; }

二,qsort函数

qsort函数的功能是为一切类型进行排序;他的使用需要头文件<string.h>,

他有四个参数:

base:待排序数组的首元素地址。

num:待排序元素个数。

size:待排序元素的大小单位字节。

cmp:比较函数指针。

同时对比较函数的返回值做了规定:

比较函数必须返回三种值:

  • 负数:表示 a < b(a应该在b前面)
  • :表示 a == b
  • 正数:表示 a > b(a应该在b后面)

学会了这些之后,我们不妨写下如下代码测试一下qsort的强大功能:

#define _CRT_SECURE_NO_WARNINGS 1 #include<stdio.h> #include<stdlib.h> int cmp_int(const void* p1,const void* p2) { return *((int*)p1) - *((int*)p2); } test1() { int arr[] = { 1,4,7,2,5,8,6,3,9,0 }; int sz = sizeof(arr) / sizeof(arr[0]); qsort(arr,sz,sizeof(arr[0]),cmp_int); int i = 0; for (i = 0; i < sz; i++) { printf("%d ", *(arr + i)); } } int main() { test1();//排序整型 // test2();//排序结构体————以整数成员 // test3();//排序结构体————以字符成员 return 0; } 

实际上,为了测试qsort可以排序任意类型数据的万能性:我们还有以下代码:

struct stu { char name; int age; }; int cmp_stu_by_name(const void*p1,const void*p2) { return strcmp(((struct stu*)p1)->name, ((struct stu*)p2)->name); } void test2() { struct stu s1[3] = { {"zhangsan",18},{"lisi",19} ,{"wangwu",22}}; int sz = sizeof(s1) / sizeof(s1[0]); qsort(s1, sz, sizeof(s1[0]), cmp_stu_by_name); } int main() { //test1();//排序整型 test2();//排序结构体————以整数成员 //test3();//排序结构体————以字符成员 return 0; }

好了,既然qsort的功能如此强大。那么我们可不可以模拟一个qsort函数呢?

事实上是可以的:

三,冒泡排序模拟qsort函数

qsort函数,其本质上是使用了快速排序算法;

这里我们可以用冒泡排序算法来完成他:

不难发现:qsort的前三个参数用来用指针的方式排序每个元素。

最后一个函数指针参数,传入比较函数的指针,在qsort函数内用来调用比较函数。(回调函数)

此外,对比冒泡排序的局限性(只能排序整数。)模拟qsort函数必须具备可以排序任意类型给能力:所以交换函数也得改成泛式编程。

直接上源码

#define _CRT_SECURE_NO_WARNINGS 1 #include<stdio.h> #include<string.h> #include<stdlib.h> void swap(void* buf1, void* buf2, size_t width) { int i = 0; for (i = 0; i < width; i++) { char tmp = *((char* )buf1+i); *((char*)buf1 + i) = *((char*)buf2 + i); *((char*)buf2 + i) = tmp; } } void bouble_qsort( void* base,size_t sz,size_t width,int (*cmp)(const void*,const void*))//默认升序 { int i = 0; for (i = 0; i < sz - 1; i++) { int j = 0; for (j = 0; j < sz - 1 - i; j++) { if (cmp((char*)base+j*width,(char*)base+(j+1)*width)>0) { swap((char*)base + j * width, (char*)base + (j + 1) * width,width); } } } } int cmp_int(const void*p1, const void*p2) { return (*(int*)p1 - *(int*)p2); } void print(int* arr, size_t sz) { int i; for (i = 0; i < sz; i++) { printf("%d ", *(arr + i)); } } int main() { int arr[10] = { 2,5,8,7,4,1,3,6,9,0}; int sz = sizeof(arr) / sizeof(arr[0]); bouble_qsort(arr,sz,sizeof(arr[0]),cmp_int); print(arr,sz); return 0; }

好的,本期到此结束,感谢你的观看。

Read more

RUST异步并发安全与内存管理的最佳实践

RUST异步并发安全与内存管理的最佳实践

RUST异步并发安全与内存管理的最佳实践 一、引言 异步并发编程在提高系统性能和响应时间的同时,也带来了并发安全和内存管理的挑战。Rust语言以其独特的所有权、借用和生命周期系统,为解决这些问题提供了强大的工具。本章将深入探讨异步并发安全与内存管理的核心概念、常见问题及解决方案,并通过实战项目优化演示这些方法的应用。 二、异步并发安全的基础概念 2.1 所有权、借用与生命周期 Rust的所有权系统是其并发安全的基础。每个值都有唯一的所有者,当所有者离开作用域时,值会被自动释放。借用分为可变借用和不可变借用,同一时间只能有一个可变借用或多个不可变借用,从而避免数据竞争。生命周期则确保引用在所有者有效的时间内使用。 fnmain(){letmut s =String::from("hello");// s是所有者let r1 =&s;// 不可变借用let r2 =&s;// 不可变借用(允许)// let r3 = &mut s; // 可变借用(禁止,

By Ne0inhk
Python入门:Python3爬虫BeautifulSoup全面学习教程

Python入门:Python3爬虫BeautifulSoup全面学习教程

Python入门:Python3爬虫BeautifulSoup全面学习教程 Python入门:Python3爬虫BeautifulSoup全面学习教程,该教程围绕 Python 爬虫核心工具 BeautifulSoup4(BS4)展开,先介绍爬虫 “发送 HTTP 请求、解析内容、提取数据、存储数据” 的核心流程,点明 BS4 在解析 HTML/XML 中的优势 ——API 简单、支持多解析器、功能全面。接着讲解环境搭建,需通过 pip 安装 beautifulsoup4 与 lxml 解析器,再以实例演示基础用法:用 requests 获取网页 HTML,创建 BS 对象,提取网页标题;深入介绍标签查找(find ()/find_all ())、属性筛选(

By Ne0inhk
Spring Boot AOP(二) 代理机制解析

Spring Boot AOP(二) 代理机制解析

博主社群介绍: ① 群内初中生、高中生、本科生、研究生、博士生遍布,可互相学习,交流困惑。 ② 热榜top10的常客也在群里,也有数不清的万粉大佬,可以交流写作技巧,上榜经验,涨粉秘籍。 ③ 群内也有职场精英,大厂大佬,跨国企业主管,可交流技术、面试、找工作的经验。 进群免费赠送写作秘籍一份,助你由写作小白晋升为创作大佬,进群赠送ZEEKLOG评论防封脚本,送真活跃粉丝,助你提升文章热度。 群公告里还有全网大赛约稿汇总/博客提效工具集/ZEEKLOG自动化运营脚本 有兴趣的加文末联系方式,备注自己的ZEEKLOG昵称,拉你进群,互相学习共同进步。 文章目录 * Spring Boot AOP(二) 代理机制解析 * 1. 代理机制概述 * 2. JDK 动态代理源码解析 * 核心类和方法 * 流程示意 * 特点 * 3. CGLIB 代理源码解析 * 核心类 * 调用流程

By Ne0inhk
【MySQL】三大范式

【MySQL】三大范式

下面我们来聊聊表的设计,如何设计一张比较合理,冗余性低且IO次数比较少,效率高的表。 我们需要先认识一下范式 什么是范式? 范式是⼀组规则。在设计关系数据库时,遵从不同的规范要求,设计出合理的关系型数据库,这些不同的规范要求被称为不同的范式。 范式有哪些? 关系数据库有六种范式:第⼀范式(1NF)、第⼆范式(2NF)、第三范式(3NF)、巴斯-科德范式(BCNF)、第四范式(4NF)和第五范式(5NF,⼜称完美范式),越高的范式数据库冗余越小。然而,普遍认为范式越高虽然对数据关系有更好的约束性,但也可能导致数据库IO更繁忙,因此在实际应用中,数据库设计通常只需满足第三范式即可,如果在想提高效率,再去增加某个字段的冗余性 为啥越高的范式数据库冗余越小,IO效率越忙呢?继续看 第一范式 第一范式即:数据库表的每⼀列都是不可分割的原子数据项,而不能是集合,数组,对象等非原子数据 在关系型数据库的设计中,满足第⼀范式是对关系模式的基本要求。

By Ne0inhk