qsort的自主实现

2023-11-01

目录

qsort()函数的功能:

首先回忆一下冒泡排序是如何实现的

需要改动的地方:

compare():

swap():

qosrt()函数实现

快速排序实现qsort()已经成功


今天我要分享的是qsort的自主实现。

冒泡版qsort()(标准是用的快速排序,好吧,我还没想到怎么准确实现)

qsort()函数的功能:

qsort()函数能将一个数组的元素进行排序。

作用:
 

Remarks

The qsort function implements a quick-sort algorithm to sort an array of num elements, each of width bytes. The argument base is a pointer to the base of the array to be sorted.qsort overwrites this array with the sorted elements. The argument compare is a pointer to a user-supplied routine that compares two array elements and returns a value specifying their relationship. qsort calls the compare routine one or more times during the sort, passing pointers to two array elements on each call:

翻译:

评论

qsort函数实现了一种快速排序算法,可以对num元素数组进行排序,每个元素的宽度为字节。参数base是指向要排序的数组的基的指针。qsort用已排序的元素覆盖此数组。参数compare是指向用户提供的例程的指针,该例程比较两个数组元素,并返回一个指定其关系的值。qsort在排序过程中调用比较例程一次或多次,每次调用时将指针传递到两个数组元素:

简单来说就是排序,但是你要自己写一个比较大小的函数。

注意:这个函数的函数指针的类型为 int (*)(const void *, const void * );

如果不知道函数指针就别管这个了(以后我会出一篇详解指针文章)。

先看一下qsort函数的类型(带参数名字)

可复制:

void qsort( void *base, size_t num, size_t width, int (__cdecl *compare )(const void *elem1, const void *elem2 ) );

由于qsort只是让待排序数组排序,所以不必设置返回值,返回类型为空。base是传入的首元素地址,void*说明它可以接收任意类型的地址;num是待排数组的元素个数,size_t是unsigned int类型的重定义类型,其实就是unsigned int(上一篇文章提到了),元素个数不可能是负数,所以用的是无符号类型;width是数组每一个元素的所占空间的大小;int (__cdecl *compare )(const void *elem1, const void *elem2 ) 是一个函数指针(不知道函数指针也没事,看案例就可明白)。

先看一下标准qsort函数的应用:

#include<stdio.h>
#include<stdlib.h>
int compare1(const void* e1, const void* e2)
{
	return (int)(*(int*)e1 - *(int*)e2);
}

int main(void)
{
	int arr[5] = { 2,4,3,1,0 };

	int i = 0;
	int j = 0;
	for (i = 0; i < 5; i++)
	{
		printf("%d ", arr[i]);
	}
	int len = sizeof(arr) / sizeof(arr[0]);
	printf("\n======================\n");
	qsort(arr, len, sizeof(int), compare1);

	for (i = 0; i < 5; i++)
	{
		printf("%d ", arr[i]);
	}
	return 0;
}

运行结果:

 自主实现(利用冒泡排序):

首先回忆一下冒泡排序是如何实现的

	for (i = 0; i < len - 1; i++)
	{
		for (j = 0; j < len - 1 - i; j++)
		{
			if (arr[j] > arr[j + 1])
			{
				int t = arr[j];
				arr[j] = arr[j + 1];
				arr[j + 1] = t;
			}
		}
	}

我们就以这个为基础来实现,

需要改动的地方:

一:if的判断条件。

二:if里面需要设立一个函数实现数组元素的交换。

这里用两个函数,if的判断条件用compare()函数的返回值来加以修饰来作为判断条件,而swap()函数作为交换两个元素的函数。

于是我们可以改写为:

以每个元素为int类型的数组为例:compare()函数是是自己设置的,目的是比较两个元素的大小。我们先实现这个函数的实现。

compare():

因为我们需要对待排数组排序,所以我们应该知道待排数组的每个元素的类型是什么,以int类型为例:

 (int*)e1是把e1前置类型转换为int*类型,而*(int*)e1是对强制类型转换后的e1解引用,就是取出元素的值,那么*(int*)e1 - *(int*)e2就是两个元素相减,有人可能会问了,这个结果不就是int类型吗,为什么还要对最终结果强制类型转换为int?因为我想以后编写其他类型的元素的数组方便。一会换其他类型的实例就很容易明白(升序是e1在前,降序是e1在后,主要是返回的结果与0比较)。

判断条件:

compare(前一个元素, 后一个元素) > 0

因为我们是用冒泡排序的思想,传入的是相邻前后的元素,这个函数的返回值如果大于0那么就交换。

swap():

swap()需要交换的元素类型是未知的,所以用void*类型来接收,但是如果写成void*我们怎么对元素进行交换呢?我们学过的最小类型是char一个字节,数据的是以字节为单位在计算机里存储的,那么我们有一个思路,我们可以先把void*强制类型转换为char*类型,一个字节一个字节的进行交换。但是我们还要知道每个元素占用多少字节,即交换几个字节,要做到恰好两个元素每个字节都交换,所以我们要传入一个元素所占用字节数,我们可以用传入qsort()的形参width作为swap()的一个实参,代表一个元素的所占字节数。

那么swap()函数就可以这样实现:

void swap(char* c1, char* c2, int len)
{
	int i = 0;
	for (i = 0; i < len; i++)
	{
		char t = *(c1 + i);
		*(c1 + i) = *(c2 + i);
		*(c2 + i) = t;
	}
}

char* c1, char* c2表示我们直接把传进来元素的地址以char*类型进行操作。

现在我们的代码大概可以写出

    for (i = 0; i < num - 1; i++)
    {
        for (j = 0; j < num - 1 - i; j++)
        {
            if (前一个元素, 后一个元素) > 0)
            {
                swap(前一个元素, 后一个元素, width);
            }
        }
    }

现在我们有了一种思想,char*类型可以解决我们遇到的问题。

所以copare()和swap()传入数组元素的地址怎么传入合理呢?

答案是先转换为char*类型在利用j和width寻找代操作元素。

用compare()举例:

compare((char*)base + j * width, (char*)base + (j + 1) * width)

(char*)base + j * width 的含义是先以char*找到base地址,然后移动j*width个char类型距离,这里就相当于base[j],但是不能用base[j]直接访问,因为传入base是未知的类型,不可直接访问base的某个元素

那么(char*)base + (j + 1) * width就相当于base[j + 1]。

所以我们可以写出swap()函数的传参

swap((char*)base + j * width, (char*)base + (j + 1) * width, width)

qosrt()函数实现

void swap(char* c1, char* c2, int len)
{
	int i = 0;
	for (i = 0; i < len; i++)
	{
		char t = *(c1 + i);
		*(c1 + i) = *(c2 + i);
		*(c2 + i) = t;
	}
}

void my_qsort(void* base, size_t num, size_t width,
	int(__cdecl* compare)(const void* e1, const void* e2))
{
	assert(base);
	int i = 0;
	int j = 0;
	for (i = 0; i < num - 1; i++)
	{
		for (j = 0; j < num - 1 - i; j++)
		{
			if (compare((char*)base + j * width, (char*)base + (j + 1) * width) > 0)
			{
				swap((char*)base + j * width, (char*)base + (j + 1) * width, width);
			}
		}
	}
}

看一下成果:

示例一(int):

#include<stdio.h>
#include<assert.h>
int compare1(const void* e1, const void* e2)
{
	return (int)(*(int*)e1 - *(int*)e2);
}

void swap(char* c1, char* c2, int len)
{
	int i = 0;
	for (i = 0; i < len; i++)
	{
		char t = *(c1 + i);
		*(c1 + i) = *(c2 + i);
		*(c2 + i) = t;
	}
}
void my_qsort(void* base, size_t num, size_t width,
	int(__cdecl* compare)(const void* e1, const void* e2))
{
	assert(base);//防止传入空指针,保证代码的健壮性
	int i = 0;
	int j = 0;
	for (i = 0; i < num - 1; i++)
	{
		for (j = 0; j < num - 1 - i; j++)
		{
			if (compare((char*)base + j * width, (char*)base + (j + 1) * width) > 0)
			{
				swap((char*)base + j * width, (char*)base + (j + 1) * width, width);
			}
		}
	}
}


int main(void)
{
	int arr[5] = { 2,4,3,1,0 };

	int i = 0;
	int j = 0;
	for (i = 0; i < 5; i++)
	{
		printf("%d ", arr[i]);
	}

	printf("\n======================\n");
	int len1 = sizeof(arr) / sizeof(arr[0]);

	my_qsort(arr, len1, sizeof(int), compare1);

	for (i = 0; i < 5; i++)
	{
		printf("%d ", arr[i]);
	}
	return 0;
}

运行结果:

 示例二(char):

#include<stdio.h>
#include<assert.h>
#include<string.h>
int compare2(const void* e1, const void* e2)
{
	return (int)(*(char*)e1 - *(char*)e2);
}

void swap(char* c1, char* c2, int len)
{
	int i = 0;
	for (i = 0; i < len; i++)
	{
		char t = *(c1 + i);
		*(c1 + i) = *(c2 + i);
		*(c2 + i) = t;
	}
}

void my_qsort(void* base, size_t num, size_t width,
	int(__cdecl* compare)(const void* e1, const void* e2))
{
	assert(base);//防止传入空指针,保证代码的健壮性
	int i = 0;
	int j = 0;
	for (i = 0; i < num - 1; i++)
	{
		for (j = 0; j < num - 1 - i; j++)
		{
			if (compare((char*)base + j * width, (char*)base + (j + 1) * width) > 0)
			{
				swap((char*)base + j * width, (char*)base + (j + 1) * width, width);
			}
		}
	}
}

int main(void)
{
	char ch[5] = "adxb";

	int i = 0;
	int j = 0;
	printf("%s", ch);

	printf("\n======================\n");
	int len2 = strlen(ch);

	my_qsort(ch, len2, sizeof(char), compare2);

	printf("%s", ch);

	return 0;
}

运行结果:

示例三(double):

#include<stdio.h>
#include<assert.h>
int compare3(const void* e1, const void* e2)
{
	return (int)(*(double*)e1 - *(double*)e2);
}

void swap(char* c1, char* c2, int len)
{
	int i = 0;
	for (i = 0; i < len; i++)
	{
		char t = *(c1 + i);
		*(c1 + i) = *(c2 + i);
		*(c2 + i) = t;
	}
}

void my_qsort(void* base, size_t num, size_t width,
	int(__cdecl* compare)(const void* e1, const void* e2))
{
	assert(base);//防止传入空指针,保证代码的健壮性
	int i = 0;
	int j = 0;
	for (i = 0; i < num - 1; i++)
	{
		for (j = 0; j < num - 1 - i; j++)
		{
			if (compare((char*)base + j * width, (char*)base + (j + 1) * width) > 0)
			{
				swap((char*)base + j * width, (char*)base + (j + 1) * width, width);
			}
		}
	}
}

int main(void)
{
	double dd[5] = { 0.2, 0.3, 5.0, 0.1, -10.2 };

	int i = 0;
	int j = 0;

	for (i = 0; i < 5; i++)
	{
		printf("%lf ", dd[i]);
	}
	printf("\n======================\n");
	int len3 = sizeof(dd) / sizeof(dd[0]);

	my_qsort(dd, len3, sizeof(double), compare3);

	for (i = 0; i < 5; i++)
	{
		printf("%lf ", dd[i]);
	}

	return 0;
}

运行结果:

实例四(结构体):

#include<stdio.h>
#include<assert.h>

typedef struct Student
{
	char name[10];
	double scores;
} Student;

int compare4(const void* e1, const void* e2)
{
	return (int)(((Student*)e1)->scores - ((Student*)e2)->scores);
}

void swap(char* c1, char* c2, int len)
{
	int i = 0;
	for (i = 0; i < len; i++)
	{
		char t = *(c1 + i);
		*(c1 + i) = *(c2 + i);
		*(c2 + i) = t;
	}
}

void my_qsort(void* base, size_t num, size_t width,
	int(__cdecl* compare)(const void* e1, const void* e2))
{
	assert(base);//防止传入空指针,保证代码的健壮性
	int i = 0;
	int j = 0;
	for (i = 0; i < num - 1; i++)
	{
		for (j = 0; j < num - 1 - i; j++)
		{
			if (compare((char*)base + j * width, (char*)base + (j + 1) * width) > 0)
			{
				swap((char*)base + j * width, (char*)base + (j + 1) * width, width);
			}
		}
	}
}

int main(void)
{
	Student s[5] = { {"张三", 10}, {"李四", 0}, {"王五", 100}, {"乌龟", 0.5}, {"兔子", -20} };

	int i = 0;
	int j = 0;

	for (i = 0; i < 5; i++)
	{
		printf("%-6s%lf\n", s[i].name, s[i].scores);
	}
	printf("\n======================\n");
	int len4 = sizeof(s) / sizeof(s[0]);

	my_qsort(s, len4, sizeof(s[0]), compare4);

		for (i = 0; i < 5; i++)
		{
			printf("%-6s%lf\n", s[i].name, s[i].scores);
		}

	return 0;
}

运行结果:

这篇文章本来还想这再用快速排序来实现,但是没实现~~~,我会努力让它实现的!

要多记录一下写的代码了,伙伴们的支持是我最大的动力。

快速排序实现qsort()已经成功

qosrt()快速排序自主实现_逆风路上伴有谁的博客-CSDN博客

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

qsort的自主实现 的相关文章

  • mysql增量抽取方法_DataX增量抽取数据

    datax增量项目地址 datax作业配置文件 datax json job setting speed channel 16 content reader name mysqlreader parameter splitPk id use
  • 【数据结构】带头结点的单链表

    数据结构 带头结点的单链表 链表 逻辑连续 物理上不一定连续 带头结点的单链表 结构体 定义结构体 typedef int ELEM TYPE typedef struct Node ELEM TYPE mdata struct Node
  • 九、C++编译过程详解

    九 C 编译过程详解 1 什么是编译器 2 多文件编译与链接 3 为什么需要构建系统 Makefile 4 CMake CMakeLists txt 5 CMake中的静态库和动态库 1 什么是编译器 编译器是一个根据源代码生成机器码的程序
  • Windows获取密码及hash

    前言 在拿到一台 Windows 的管理员权限以后 可以通过多种方法获取 Windows 系统的明文密码或者 hash 值 这将有利于我们在内网中扩大渗透范围 0x01 Mimikatz Mimikat是一个法国人写的轻量级调试器 Mimi

随机推荐

  • 视频压缩之冗余

    视频压缩之冗余 对于数字视频信号 数据量很大 不管是存储还是传输的需要 做压缩处理是必须的 下面我们会做进一步阐述 以记录数字视频的YUV分量格式为例 YUV分别代表亮度与两个色差信号 例如对于现有的PAL制电视系统 其亮度信号采样频率为1
  • 整流器+逆变器。 前级采用PWM整流器,采用双闭环前馈解耦控制

    整流器 逆变器 前级采用PWM整流器 采用双闭环前馈解耦控制 实现并网单位功率因数 稳定直流电压 后级采用两电平逆变器 通过双闭环前馈解耦控制 稳定输出电压 整个仿真环境完全离散化 运行时间更快 主电路与控制部分以不同的步长运行 更加贴合实
  • CentOS7.4 离线升级openssh8.4

    CentOS7 4 离线升级openssh8 4 前言 工作中需要离线升级openssh 网上一些资料说要先安装telnet 这里省略 大家可以先安装telnet 预防更新ssh失败 下载openssl安装包 去https www open
  • 数据分析——时间序列分析模型(AR,MA,ARMA,ARIMA)

    1 概述 时间序列是某个时间段或者某些时间点对应的不同数值的数值对 这些数值对只有两个具体数据 时间要素 数值要素 时间要素可以是某一个时间段或者某一个时刻 例如一个杂货铺一周 七天 的销售额为时间段的时间要素 而一天二十四小时每个整点所对
  • Spring之Bean生命周期源码解析-Bean销毁

    这篇文章是我在系统学习Spring源码之后 基于自己对Spring源码的理解 来详细分析Spring之Bean的销毁过程 目录 前言 一 注册有销毁逻辑的Bean 1 判断当前Bean是否需要销毁 1 1 判断当前Bean是否有销毁方法 1
  • 华为OD机试 - 统计射击比赛成绩(Java)

    题目描述 给定一个射击比赛成绩单 包含多个选手若干次射击的成绩分数 请对每个选手按其最高3个分数之和进行降序排名 输出降序排名后的选手ID序列 条件如下 一个选手可以有多个射击成绩的分数 且次序不固定 如果一个选手成绩少于3个 则认为选手的
  • 03-NodeMCU引脚和接线、点亮外部LED

    Author teacherXue 一 ESP8266 引脚参考 ESP8266 12 E 芯片带有 17 个 GPIO 引脚 并不是所有的ESP8266开发板都开放了所有的GPIO 并且由于电力设计原因 以及有些GPIO有非常特殊的功能
  • malloc函数详解

    一 原型 extern void malloc unsigned int num bytes 头文件 include
  • Pixi的基本使用(5)--寻宝猎人

    寻宝猎人 游戏需求 使用键盘上的箭头键帮助探险家找到宝藏并将其带到出口 怪物在地牢壁之间上下移动 如果探险家触碰到怪物则变成半透明 并且右上角的生命值会减少 如果所有生命值都用光了 会显示 You Lost 如果探险家带着宝藏到达出口 会显
  • QT TCP简单的通信示例

    TCP服务端 第一步 创建监听套接字的QTcpSever QTcpServer m tsTcpServer 第二部步 listen 监听是否有新的连接进来 int iMyport 如果有新的客户端连接的话 会触发信号newConnectio
  • Lyapunov

    一 正定函数 令是向量x的标量函数 S是x空间包含原点的封闭有限区域 如果对于S中所有的x 都有 1 存在且连续 2 3 当时 则V x 是正定的 半正定 的 二 二次型 1 定义 在李雅普诺夫第二方法上的稳定性分析中 二次型函数起着重要作
  • 课时 16 深入理解 etcd:基于原理解析(曾凡松)

    本文将主要分享以下三方面的内容 第一部分 会为大家介绍 etcd 项目发展的整个历程 从诞生至今 etcd 经历的那些重要的时刻 第二部分 会为大家介绍 etcd 的技术架构以及其内部的实现机制 通过对技术架构和内部机制的学习 帮助我们正确
  • 如何在JCreator中导入.jar文件

    本文摘自http www lvchao org 上篇介绍了如何在Eclipse中导入 jar包 这篇将介绍在JCreator中导入 jar包 与上篇一样 先建立一个Project 请注意 建立工程的目的是方便管理和使用 建议那些不喜欢这样做
  • 电脑宏基电脑,【宏基电脑】报价_介绍_图片_评论_咨询-宏基电脑产品大全 -真快乐商城...

    宏基电脑怎么重装系统 标签 时间 2013 10 18 10 27 10 很多朋友有这样的疑问 宏基电脑怎么重装系统 的问题 所以国美小编总结了有关宏基电脑装系统的相关知识 现在和大家一起分享 1 使用宏基电脑自带的系统光盘安装 先要在BI
  • LeetCode 2108. 找出数组中的第一个回文字符串

    给你一个字符串数组 words 找出并返回数组中的 第一个回文字符串 如果不存在满足要求的字符串 返回一个 空字符串 回文字符串 的定义为 如果一个字符串正着读和反着读一样 那么该字符串就是一个 回文字符串 示例 1 输入 words ab
  • multiprocessing.pool详解

    由于python有全局锁限制 如果想利用多核 就需要使用多进程模块 但该模块有很多坑 本篇文章记录一下其用法以及踩过的坑 一 map apply apply async对比 先贴一个对比图 引自multiprocessin pool Mul
  • CmakeList--glog

    file GLOB GLOG LIBRARIES usr lib x86 64 linux gnu libglog so set GLOG INCLUDE DIRS usr include message glog include dirs
  • 安装vb6 正在更新系统 无响应

    新装的win10系统 安装vb6时 最后一直卡在 正在更新系统 程序无响应 没办法 kill掉后 貌似不影响软件使用 但是安装vs6sp6B无法成功安装 解决办法是 不安装 数据访问 组件 参考 http bbs pcbeta com vi
  • 多路采集存储c语言程序,stm32多路巡回数据采集系统设计 含源程序

    此次设计是利用stm32开发板设计的 数据采集系统是模拟域与数字域之间必不可少的纽带 它的存在具有着非常重要的作用 本文介绍的重点是数据采集系统 而该系统硬件部分的重心在于单片机芯片 数据采集与通信控制采用了模块化的设计 数据采集与通信控制
  • qsort的自主实现

    目录 qsort 函数的功能 首先回忆一下冒泡排序是如何实现的 需要改动的地方 compare swap qosrt 函数实现 快速排序实现qsort 已经成功 今天我要分享的是qsort的自主实现 冒泡版qsort 标准是用的快速排序 好