系统程序员成长计划 ——学习篇7:排序算法

目标

  前面完成了动态数组的实现,现在学习几种排序算法。
  实现:
    ①冒泡排序②快速排序③归并排序
  具体要求:
    ①同时支持升序排序和降序排序。
    ②同时支持多种数据类型。
  提示:
    看到这两点要求,应该马上想到使用回调函数。
    前篇示例已定义过比较回调函数的原型: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

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值