目标
前面完成了动态数组的实现,现在学习几种排序算法。
实现:
①冒泡排序②快速排序③归并排序
具体要求:
①同时支持升序排序和降序排序。
②同时支持多种数据类型。
提示:
看到这两点要求,应该马上想到使用回调函数。
前篇示例已定义过比较回调函数的原型:typedef int (*DataCompareFunc)(void* ctx, void* data);
故整数升序比较函数:
int int_cmp(void* a, void*b)
{
return (int)a - (int)b;
}
故整数降序比较函数:
int int_cmp_invert(void* a, void*b)
{
return (int)b - (int)a;
}
算法实现
冒泡排序
冒泡排序是简单直观的排序算法,性能上与其他排序算法比较,似乎没有存在意义。但还是有实际价值的,原因如下:
①实现简单,而简单的程序通常更可靠。
②数据量不大,性能差别并不大。
Ret bubble_sort(void** array, size_t nr, DataCompareFunc cmp)
{
size_t i = 0;
size_t max = 0;
size_t right = 0;
return_val_if_fail(array != NULL && cmp != NULL, RET_INVALID_PARAMS);
if(nr < 2)
{
return RET_OK;
}
for(right = nr - 1; right > 0; right--)
{
for(i = 1, max = 0; i < right; i++)
{
if(cmp(array[i], array[max]) > 0)
{
max = i;
}
}
if(cmp(array[max], array[right]) > 0)
{
void* data = array[right];
array[right] = array[max];
array[max] = data;
}
}
return RET_OK;
}
快速排序
快速排序性能优异且无需额外空间(故数据量大且存放内存首选快速排序)。
实现原理:
①先将排序分为两个区。(所有小于某个元素的值在第一区,其他元素在第二区)
②分别对两个区进行快速排序,直到所分的区只剩下一个元素为止
快速排序可看做自顶向下的方法。
void quick_sort_impl(void** array, size_t left, size_t right, DataCompareFunc cmp)
{
size_t save_left = left;
size_t save_right = right;
void* x = array[left];
while(left < right)
{
while(cmp(array[right], x) >= 0 && left < right) right--;
if(left != right)
{
array[left] = array[right];
left++;
}
while(cmp(array[left], x) <= 0 && left < right) left++;
if(left != right)
{
array[right] = array[left];
right--;
}
}
array[left] = x;
if(save_left < left)
{
quick_sort_impl(array, save_left, left-1, cmp);
}
if(save_right > left)
{
quick_sort_impl(array, left+1, save_right, cmp);
}
return;
}
Ret quick_sort(void** array, size_t nr, DataCompareFunc cmp)
{
Ret ret = RET_OK;
return_val_if_fail(array != NULL && cmp != NULL, RET_INVALID_PARAMS);
if(nr > 1)
{
quick_sort_impl(array, 0, nr - 1, cmp);
}
return ret;
}
归并排序
归并排序需要额外空间,且这部分空间和被排序数组所占空间一样大。
大部分示例代码里,都会在每次递归调用中分配空间,这样会使性能降低,故这边选择实现分配一块空间,排序过程重复使用,更简单,性能更高。
实现原理:
①先让左右两部分进行排序,然后把它们在合并起来。
②排序左右两部分时,同样使用归并排序。
归并排序可看做自底向上的方法。
因为会把排序数组分为N分,故又可称为N路排序。
以下程序实现为归并算法中的特例:两路归并。看似与快速排序相比无优势可言,但是归并排序更重要的能力在于处理大量数据的排序,它不要求被排序的数据全部在内存中,所以在数据大于内存的容纳能力时,归并排序就能大展身手了。归并排序最常用的地方是数据库管理系统(DBMS),因为数据库中存储的数据通常无法全部加载到内存中来的。有兴趣的读者可以阅读相关资料。
排序算法的测试
虽然排序算法的实现各有不同,但它们的目的都一样:让数据处于有序状态。所以在写自动测试时,没有必要为每一种算法都写一个测试程序。通过将排序算法作为回调函数传入,我们可以共用一个测试程序。
static void** create_int_array(int n)
{
int i = 0;
int* array = (int*)malloc(sizeof(int) * n);
for(i = 0; i < n; i++)
{
array[i] = rand();
}
return (void**)array;
}
static void sort_test_one_asc(SortFunc sort, int n)
{
int i = 0;
void** array = create_int_array(n);
sort(array, n, int_cmp);
for(i = 1; i < n; i++)
{
assert(array[i] >= array[i-1]);
}
free(array);
return;
}
static void sort_test_one_dec(SortFunc sort, int n)
{
int i = 0;
void** array = create_int_array(n);
sort((void**)array, n, int_cmp_invert);
for(i = 1; i < n; i++)
{
assert(array[i] <= array[i-1]);
}
free(array);
return;
}
static void sort_test(SortFunc sort)
{
int i = 0;
for(i = 0; i < 1000; i++)
{
sort_test_one_dec(sort, i);
sort_test_one_asc(sort, i);
}
return ;
}
将排序算法集成到动态数组中
把排序算法放到动态数组里面并不合适,原因在于:
(1)绑定动态数组与特定算法不如让用户根据需要自行选择:
(2)在动态数组中实现排序算法不利于算法的重用。
所以我们给动态数组增加一个排序函数,但排序算法通过回调函数传入。Ret darray_sort (DArray* thiz, SortFunc sort,DataCompareFunc cmp) ;
书中程序
sort.c
/*
* File: sort.c
* Author: Li XianJing <xianjimli@hotmail.com>
* Brief: implementation of sort functions.
*
* Copyright (c) Li XianJing
*
* Licensed under the Academic Free License version 2.1
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
/*
* History:
* ================================================================
* 2009-02-07 Li XianJing <xianjimli@hotmail.com> created.
*
*/
#include "sort.h"
Ret bubble_sort(void** array, size_t nr, DataCompareFunc cmp)
{
size_t i = 0;
size_t max = 0;
size_t right = 0;
return_val_if_fail(array != NULL && cmp != NULL, RET_INVALID_PARAMS);
if(nr < 2)
{
return RET_OK;
}
for(right = nr - 1; right > 0; right--)
{
for(i = 1, max = 0; i < right; i++)
{
if(cmp(array[i], array[max]) > 0)
{
max = i;
}
}
if(cmp(array[max], array[right]) > 0)
{
void* data = array[right];
array[right] = array[max];
array[max] = data;
}
}
return RET_OK;
}
void quick_sort_impl(void** array, size_t left, size_t right, DataCompareFunc cmp)
{
size_t save_left = left;
size_t save_right = right;
void* x = array[left];
while(left < right)
{
while(cmp(array[right], x) >= 0 && left < right) right--;
if(left != right)
{
array[left] = array[right];
left++;
}
while(cmp(array[left], x) <= 0 && left < right) left++;
if(left != right)
{
array[right] = array[left];
right--;
}
}
array[left] = x;
if(save_left < left)
{
quick_sort_impl(array, save_left, left-1, cmp);
}
if(save_right > left)
{
quick_sort_impl(array, left+1, save_right, cmp);
}
return;
}
Ret quick_sort(void** array, size_t nr, DataCompareFunc cmp)
{
Ret ret = RET_OK;
return_val_if_fail(array != NULL && cmp != NULL, RET_INVALID_PARAMS);
if(nr > 1)
{
quick_sort_impl(array, 0, nr - 1, cmp);
}
return ret;
}
static Ret merge_sort_impl(void** storage, void** array, size_t low, size_t mid, size_t high, DataCompareFunc cmp)
{
size_t i = low;
size_t j = low;
size_t k = mid;
if((low + 1) < mid)
{
size_t x = low + ((mid - low) >> 1);
merge_sort_impl(storage, array, low, x, mid, cmp);
}
if((mid + 1) < high)
{
size_t x = mid + ((high - mid) >> 1);
merge_sort_impl(storage, array, mid, x, high, cmp);
}
while(j < mid && k < high)
{
if(cmp(array[j], array[k]) <= 0)
{
storage[i++] = array[j++];
}
else
{
storage[i++] = array[k++];
}
}
while(j < mid)
{
storage[i++] = array[j++];
}
while(k < high)
{
storage[i++] = array[k++];
}
for(i = low; i < high; i++)
{
array[i] = storage[i];
}
return RET_OK;
}
Ret merge_sort(void** array, size_t nr, DataCompareFunc cmp)
{
void** storage = NULL;
Ret ret = RET_OK;
return_val_if_fail(array != NULL && cmp != NULL, RET_INVALID_PARAMS);
if(nr > 1)
{
storage = (void**)malloc(sizeof(void*) * nr);
if(storage != NULL)
{
ret = merge_sort_impl(storage, array, 0, nr>>1, nr, cmp);
free(storage);
}
}
return ret;
}
#ifdef SORT_TEST
#include <assert.h>
int int_cmp(void* a, void* b)
{
return (int)a - (int)b;
}
int int_cmp_invert(void* a, void* b)
{
return (int)b - (int)a;
}
static void** create_int_array(int n)
{
int i = 0;
int* array = (int*)malloc(sizeof(int) * n);
for(i = 0; i < n; i++)
{
array[i] = rand();
}
return (void**)array;
}
static void sort_test_one_asc(SortFunc sort, int n)
{
int i = 0;
void** array = create_int_array(n);
sort(array, n, int_cmp);
for(i = 1; i < n; i++)
{
assert(array[i] >= array[i-1]);
}
free(array);
return;
}
static void sort_test_one_dec(SortFunc sort, int n)
{
int i = 0;
void** array = create_int_array(n);
sort((void**)array, n, int_cmp_invert);
for(i = 1; i < n; i++)
{
assert(array[i] <= array[i-1]);
}
free(array);
return;
}
static void sort_test(SortFunc sort)
{
int i = 0;
for(i = 0; i < 1000; i++)
{
sort_test_one_dec(sort, i);
sort_test_one_asc(sort, i);
}
return ;
}
int main(int argc, char* argv[])
{
srand(100);
sort_test(quick_sort);
sort_test(merge_sort);
sort_test(bubble_sort);
return 0;
}
#endif/*SORT_TEST*/
sort.h
/*
* File: sort.h
* Author: Li XianJing <xianjimli@hotmail.com>
* Brief: prototype of sort functions.
*
* Copyright (c) Li XianJing
*
* Licensed under the Academic Free License version 2.1
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
/*
* History:
* ================================================================
* 2009-02-07 Li XianJing <xianjimli@hotmail.com> created.
*
*/
#ifndef SORT_H
#define SORT_H
#include "typedef.h"
Ret bubble_sort(void** array, size_t nr, DataCompareFunc cmp);
Ret quick_sort(void** array, size_t nr, DataCompareFunc cmp);
Ret merge_sort(void** array, size_t nr, DataCompareFunc cmp);
#endif/*SORT_H*/
typedef.h
/*
* File: typedef.h
* Author: Li XianJing <xianjimli@hotmail.com>
* Brief: common types definition.
*
* Copyright (c) Li XianJing
*
* Licensed under the Academic Free License version 2.1
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
/*
* History:
* ================================================================
* 2008-12-10 Li XianJing <xianjimli@hotmail.com> created.
*
*/
#include <stdio.h>
#include <assert.h>
#include <stdlib.h>
#ifndef TYPEDEF_H
#define TYPEDEF_H
typedef enum _Ret
{
RET_OK,
RET_OOM,
RET_STOP,
RET_INVALID_PARAMS,
RET_FAIL
}Ret;
typedef void (*DataDestroyFunc)(void* ctx, void* data);
typedef int (*DataCompareFunc)(void* ctx, void* data);
typedef Ret (*DataVisitFunc)(void* ctx, void* data);
#ifdef __cplusplus
#define DECLS_BEGIN extern "C" {
#define DECLS_END }
#else
#define DECLS_BEGIN
#define DECLS_END
#endif/*__cplusplus*/
#define return_if_fail(p) if(!(p)) \
{printf("%s:%d Warning: "#p" failed.\n", \
__func__, __LINE__); return;}
#define return_val_if_fail(p, ret) if(!(p)) \
{printf("%s:%d Warning: "#p" failed.\n",\
__func__, __LINE__); return (ret);}
#define SAFE_FREE(p) if(p != NULL) {free(p); p = NULL;}
typedef Ret (*SortFunc)(void** array, size_t nr, DataCompareFunc cmp);
#endif/*TYPEDEF_H*/
Makefile
all:
gcc -g sort.c -DSORT_TEST -o sort_test
clean:
rm -f *test *.exe *.so