这里存放的是笔者在学习算法和数据结构时相关的学习笔记,记录了笔者通过网络和书籍资料中学习到的知识点和技巧,在供自己学习和反思的同时为有需要的人提供一定的思路和帮助。
从排序开始
基本的排序算法包括冒泡排序、插入排序、选择排序和快速排序的算法原理从基础的数据结构教程中即可学习,不在本文的讨论范围之内。这里主要介绍的是在算法设计程序中可以直接利用的排序工具。C/C++ 为使用者提供了标准的快速排序算法以供使用,在实际的算法设计中,使用者可以通过简单的函数调用实现排序功能。具体而言,C 实现了 qsort 函数提供排序功能,而 C++ 则提供 sort 供用户使用。
sort
sort 为 C++ 标准模版库实现的提供排序功能的函数模版。在使用时,需要包含头文件 <algorithm> ,并声明 C++ 的标准命名空间 std.
#include <algorithm> using namespace std; //使用 sort 需要包含对应的头文件,并使用命名空间 std
在实际使用 sort 时,主要通过以下两种参数形式来调用 sort 提供的排序功能。sort 实现的快速排序算法的时间复杂度为O(nlogn).
sort( start , end ) //对 start 至 end 之间的数据排序,其中,处理的数据包括 start 而不包括 end sort( start , end , cmp) //使用自定义的排序规则 cmp 对 start 和 end 之间的数据排序
其中 start 为待排序的数据的第一个元素的起始地址,end 为待排序的数据的最后一个元素的地址的下一个地址( 注意不是尾元素的地址 )。当使用第一种形式时,默认对待排序的元素进行升序排列。
例: int a[5] 为一个包含有 5 个 int 数据的数组,调用 sort( a , a + 5 ) 对数组 a 进行排序,则数组 a 中的元素将按照升序排列。这里注意,排序数据的第一个元素的地址为 a ,最后一个元素的地址为 a + 4 ,调用 sort 时 end 参数应该为最后一个元素地址的下一位也就是 a + 5.
sort 的第一种调用方式可以对 c++ 支持的基础数据类型进行排序。使用者也可以通过第二种形式来调用 sort ,通过 cmp 来指定排序的规则,从而对自定义的数据结构体进行排序。cmp 函数使用两个待排序的数据作为参数,并返回一个 bool 类型的值,当 返回值为 true 时,则将作为参数的第二个数据排在第一个数据前面。cmp 的大致结构如下所示。
bool cmp( type a , type b ) { 判断规则 //当 cmp 函数返回 true 时,则将数据 b 排在数据 a 的前面 }
例:对于一个自定义的学生的数据结构类型,其为一个包含有准考证、姓名和分数信息,要求对包含有学生数据的数组进行排序,使得学生按分数从高到低,分数一致时按准考证号从低到高排序。学生结构的定义如下所示。
typedef struct{ int sno; int score; char name[20]; }Student;
则根据上述排序规则和学生数据结构的信息,可以编写一个 cmp 函数定义 sort 的比较规则。这里注意,C++ 的比较运算符会根据比较结果返回逻辑值 True 或 False.
bool cmp( Student a , Student b ) { if( a.score != b.score ) return a.score < b.score ; // b 的分数比 a 高时,返回值为 true ,此时将 b 排在 a 前面 elsereturn a.sno > b.sno ; // b 的学号比 a 低时,返回值为 true,此时将 b 排在 a 前面 }
对一个包含有 100 个学生信息的数组 Student a[ 100 ] 进行排序,则只需通过 sort( a , a + 100 , cmp ) 调用即可。
注: sort 的第一种调用形式实际是使用 < 运算符对待排序数据进行比较和排序( 小者在前 ),故而若使用者为自定义的数据结构定义了 < 运算符,则也可以直接使用 sort 的第一种形式进行排序。
qsort
qsort 为 C 语言标准库提供的快速排序算法接口,在使用时需要包括 C 标准库 stdio.h 或 C++ 库 cstdlib 。相比于 C++ 提供的 sort ,qsort 由于涉及更多的指针操作在使用时相对而言更加复杂。qsort 函数的函数原型如下所示。
void qsort (void* base, size_t num, size_t size, //base 为比较元素的起始地址,num 为待比较元素的个数,size 为单个待比较元素占用的字节长度 int (*compar)(const void*,const void*)); //compar 为函数指针,用于规定元素比较规则
在使用 qsort 函数时,待比较数据的起始位置 base 、待比较数据的数量 num 和单个数据所占用的字节数 size 共同指定了待比较数据所在区域。而函数指针 compar 则指定了如何对上述区域中的元素进行比较。
这里主要介绍下比较函数 compar 的写法。如 qsort 的函数原型所示,函数 compar 使用两个 void * 指针作为参数,故而在函数内部进行元素比较时,需要对上述指针进行类型转换以符合原始比较数据的类型格式,函数的返回值为 int 类型,函数返回值小于 0 时,将第一个参数放置在第二个参数前面,返回值大于 0 时,将第一个参数放置在第二个参数后面。下面以 sort 中的排序要求实现使用 qsort 时所需的具体的排序函数。注意 C 语言的比较运算符返回值为 0 和 1 ,和上述比较函数要求返回的正值和负值略有不同,可以使用 exp1? exp2 : exp3 形式的方式简化程序。
int compareStudent( const void *a , const void *b ) { if( ( Student * ) a -> socre != ( Student * ) b -> score ) return ( ( Student * ) a -> socre > ( Student * ) b -> score ) ? 1 : 0 ; else return ( ( Student * ) a -> sno < ( Student * ) b -> sno ) ? 1 : 0 ; }
在定义好比较函数后,以 sort 中的情况为例,调用的方式 qsort 函数的方式如下所示。
qsort( a , 100 , sizeof( Student ) , compareStudent ); //使用 compar 函数对一百个学生的数据进行排序
另注:虽然 C 和 C++ 为用户提供了方便快捷的快速排序接口,但在实际的算法学习过程中,理解算法的原理和享受算法所带来的方便一样重要。故而除了学习上述接口的使用外,也应该了解和掌握基础的排序算法的原理和具体实现,让自己不局限于做一个仅会使用别人提供的工具的人,自勉。
参考:
算法笔记 p235-242
王道论坛计算机考研机试指南 - 2.1 排序