链表排序
uthash版本 :2.0.2
作者:jafon.tian
转载请注明出处:https://blog.youkuaiyun.com/JT_Notes
排序API宏
uthash提供了宏用于hash表中元素的排序
| API名称 | 参数与说明 |
|---|---|
| HASH_SORT | (head,cmpfcn) head:hash表头;cmpfcn:元素比较函数 |
排序算法
uthash采用了分治算法,算法复杂度为O(n log(n))。算法的基本原理是递归,每一次递归都将输入链表当成是规模小一些的有序链表的连接组合。排序过程会遍历log(n)次整个链表,每次遍历都会将相邻的两个有序链表组成一个有序链表,最后就产生了一个完整的有序链表。
HASH_SORT的实现的妙处在于并未使用递归调用,而是反其道行之,从只包含一个元素的有序链表开始,生成包含两个元素的有序链表,然后生成包含四个元素的有序链表,最后得到整个有序链表。
下面是排序源码,为了能直观看出每次排序的变化,进行了一点调整,再输入参数中增加了pfcn打印函数,每次排序结束都会将链表头作为参数传给pfcn。
#define HASH_SORT_P(head, cmpfcn, pfcn) HASH_SRT_P(hh, head, cmpfcn, pfcn)
#define HASH_SRT_P(hh, head, cmpfcn, pfcn) \
do \
{ \
unsigned _hs_i; \
unsigned _hs_looping, _hs_nmerges, _hs_insize, _hs_psize, _hs_qsize; \
struct UT_hash_handle *_hs_p, *_hs_q, *_hs_e, *_hs_list, *_hs_tail; \
if (head != NULL) \
{ \
_hs_insize = 1; \
_hs_looping = 1; \
_hs_list = &((head)->hh); \
while (_hs_looping != 0U) \
{ \
_hs_p = _hs_list; \
_hs_list = NULL; \
_hs_tail = NULL; \
_hs_nmerges = 0; \
while (_hs_p != NULL) \
{ \
_hs_nmerges++; \
_hs_q = _hs_p; \
_hs_psize = 0; \
for (_hs_i = 0; _hs_i < _hs_insize; ++_hs_i) \
{ \
_hs_psize++; \
_hs_q = ((_hs_q->next != NULL) ? HH_FROM_ELMT((head)->hh.tbl, _hs_q->next) : NULL); \
if (_hs_q == NULL) \
{ \
break; \
} \
} \
_hs_qsize = _hs_insize; \
while ((_hs_psize != 0U) || ((_hs_qsize != 0U) && (_hs_q != NULL))) \
{ \
if (_hs_psize == 0U) \
{ \
_hs_e = _hs_q; \
_hs_q = ((_hs_q->next != NULL) ? HH_FROM_ELMT((head)->hh.tbl, _hs_q->next) : NULL); \
_hs_qsize--; \
} \
else if ((_hs_qsize == 0U) || (_hs_q == NULL)) \
{ \
_hs_e = _hs_p; \
if (_hs_p != NULL) \
{ \
_hs_p = ((_hs_p->next != NULL) ? HH_FROM_ELMT((head)->hh.tbl, _hs_p->next) : NULL); \
} \
_hs_psize--; \
} \
else if ((cmpfcn( \
DECLTYPE(head)(ELMT_FROM_HH((head)->hh.tbl, _hs_p)), \
DECLTYPE(head)(ELMT_FROM_HH((head)->hh.tbl, _hs_q)))) <= 0) \
{ \
_hs_e = _hs_p; \
if (_hs_p != NULL) \
{ \
_hs_p = ((_hs_p->next != NULL) ? HH_FROM_ELMT((head)->hh.tbl, _hs_p->next) : NULL); \
} \
_hs_psize--; \
} \
else \
{ \
_hs_e = _hs_q; \
_hs_q = ((_hs_q->next != NULL) ? HH_FROM_ELMT((head)->hh.tbl, _hs_q->next) : NULL); \
_hs_qsize--; \
} \
if (_hs_tail != NULL) \
{ \
_hs_tail->next = ((_hs_e != NULL) ? ELMT_FROM_HH((head)->hh.tbl, _hs_e) : NULL); \
} \
else \
{ \
_hs_list = _hs_e; \
} \
if (_hs_e != NULL) \
{ \
_hs_e->prev = ((_hs_tail != NULL) ? ELMT_FROM_HH((head)->hh.tbl, _hs_tail) : NULL); \
} \
_hs_tail = _hs_e; \
} \
_hs_p = _hs_q; \
} \
if (_hs_tail != NULL) \
{ \
_hs_tail->next = NULL; \
} \
if (_hs_nmerges <= 1U) \
{ \
_hs_looping = 0; \
(head)->hh.tbl->tail = _hs_tail; \
DECLTYPE_ASSIGN(head, ELMT_FROM_HH((head)->hh.tbl, _hs_list)); \
} \
_hs_insize *= 2U; \
pfcn(DECLTYPE(head)(ELMT_FROM_HH((head)->hh.tbl, _hs_list))); \
} \
HASH_FSCK(hh, head, "HASH_SRT_P"); \
} \
} while (0)
使用示例
示例使用tests/test11.c,不过为了演示方便,进行了一些调整。源码如下
#include "uthash.h" //增加了HASH_SORT_P
#include <stdlib.h> /* malloc */
#include <errno.h> /* perror */
#include <stdio.h> /* printf */
#define BUFLEN 20
#if 0
/* Print a message if the hash's no-expand flag is set. */
#undef uthash_noexpand_fyi
#undef uthash_expand_fyi
#define uthash_noexpand_fyi(tbl) printf("noexpand set\n");
#define uthash_expand_fyi(tbl) printf("hash expanded\n");
#endif
typedef struct name_rec {
char boy_name[BUFLEN];
UT_hash_handle hh;
} name_rec;
static int namecmp(void *_a, void *_b)
{
name_rec *a = (name_rec*)_a;
name_rec *b = (name_rec*)_b;
return strcmp(a->boy_name,b->boy_name);
}
static void printel(void *namelist)
{
char boy_name[BUFLEN];
int n_inx;
name_rec *name = (name_rec*)namelist;
for(; name!=NULL; (name = (name_rec*)(name->hh.next)) ? printf(" <=> "):printf("\n\n")) {
sprintf(boy_name, "%s", name->boy_name);
n_inx = strlen(boy_name);
if(n_inx > 0)
boy_name[n_inx-1] = '\0';
printf("%s", boy_name);
}
}
int main(int argc,char *argv[])
{
name_rec *name, *names=NULL;
char linebuf[BUFLEN];
FILE *file;
file = fopen( "test11.dat", "r" );
if (file == NULL) {
perror("can't open: ");
exit(-1);
}
while (fgets(linebuf,BUFLEN,file) != NULL) {
name = (name_rec*)malloc(sizeof(name_rec));
if (name == NULL) {
exit(-1);
}
strcpy(name->boy_name, linebuf);
HASH_ADD_STR(names,boy_name,name);
}
fclose(file);
printel(names);
HASH_SORT_P(names,namecmp,printel);
return 0;
}
编译test11.c,并运行
gcc -I../src -o test11 test11.c
./test11
下面对输出进行说明
- 原始序列,一共是51个无序英文名
JOHN <=> WILLIAM <=> WALTER <=> DOUGLAS <=> GERALD <=> FREDERICK <=> WARREN <=> SHANE <=> LESTER <=> RON <=> HARVEY <=> ADRIAN <=> CODY <=> NELSON <=> CLIFTON <=> WILLARD <=> DOUG <=> ORLANDO <=> REX <=> OMAR <=> DAMON <=> LOWELL <=> IRVING <=> CARROLL <=> LAURENCE <=> ROLANDO <=> CARY <=> XAVIER <=> ISAIAH <=> GUS <=> JARVIS <=> WINFRED <=> RAYMUNDO <=> LINCOLN <=> CORNELL <=> NIGEL <=> NORMAND <=> FRITZ <=> DONN <=> TRINIDAD <=> ODIS <=> DANNIE <=> DARIO <=> KENTON <=> CHONG <=> NEVILLE <=> TONEY <=> WARNER <=> WES <=> COLTON <=> ARNOLDO
- 第1次排序之后,原始序列变成了以两个元素为一组的有序序列,比如第3和第4个英文名组成的两元素链表由之前的WALTER <=> DOUGLAS变成了DOUGLAS <=> WALTER。
JOHN <=> WILLIAM <=> DOUGLAS <=> WALTER <=> FREDERICK <=> GERALD <=> SHANE <=> WARREN <=> LESTER <=> RON <=> ADRIAN <=> HARVEY <=> CODY <=> NELSON <=> CLIFTON <=> WILLARD <=> DOUG <=> ORLANDO <=> OMAR <=> REX <=> DAMON <=> LOWELL <=> CARROLL <=> IRVING <=> LAURENCE <=> ROLANDO <=> CARY <=> XAVIER <=> GUS <=> ISAIAH <=> JARVIS <=> WINFRED <=> LINCOLN <=> RAYMUNDO <=> CORNELL <=> NIGEL <=> FRITZ <=> NORMAND <=> DONN <=> TRINIDAD <=> DANNIE <=> ODIS <=> DARIO <=> KENTON <=> CHONG <=> NEVILLE <=> TONEY <=> WARNER <=> COLTON <=> WES <=> ARNOLDO
- 第2次排序之后,序列变成了以四个元素为一组的有序序列。
DOUGLAS <=> JOHN <=> WALTER <=> WILLIAM <=> FREDERICK <=> GERALD <=> SHANE <=> WARREN <=> ADRIAN <=> HARVEY <=> LESTER <=> RON <=> CLIFTON <=> CODY <=> NELSON <=> WILLARD <=> DOUG <=> OMAR <=> ORLANDO <=> REX <=> CARROLL <=> DAMON <=> IRVING <=> LOWELL <=> CARY <=> LAURENCE <=> ROLANDO <=> XAVIER <=> GUS <=> ISAIAH <=> JARVIS <=> WINFRED <=> CORNELL <=> LINCOLN <=> NIGEL <=> RAYMUNDO <=> DONN <=> FRITZ <=> NORMAND <=> TRINIDAD <=> DANNIE <=> DARIO <=> KENTON <=> ODIS <=> CHONG <=> NEVILLE <=> TONEY <=> WARNER <=> ARNOLDO <=> COLTON <=> WES
- 第3次排序之后,序列变成了以八个元素为一组的有序序列。
DOUGLAS <=> FREDERICK <=> GERALD <=> JOHN <=> SHANE <=> WALTER <=> WARREN <=> WILLIAM <=> ADRIAN <=> CLIFTON <=> CODY <=> HARVEY <=> LESTER <=> NELSON <=> RON <=> WILLARD <=> CARROLL <=> DAMON <=> DOUG <=> IRVING <=> LOWELL <=> OMAR <=> ORLANDO <=> REX <=> CARY <=> GUS <=> ISAIAH <=> JARVIS <=> LAURENCE <=> ROLANDO <=> WINFRED <=> XAVIER <=> CORNELL <=> DONN <=> FRITZ <=> LINCOLN <=> NIGEL <=> NORMAND <=> RAYMUNDO <=> TRINIDAD <=> CHONG <=> DANNIE <=> DARIO <=> KENTON <=> NEVILLE <=> ODIS <=> TONEY <=> WARNER <=> ARNOLDO <=> COLTON <=> WES
- 第4次排序之后,序列变成了以十六个元素为一组的有序序列。
ADRIAN <=> CLIFTON <=> CODY <=> DOUGLAS <=> FREDERICK <=> GERALD <=> HARVEY <=> JOHN <=> LESTER <=> NELSON <=> RON <=> SHANE <=> WALTER <=> WARREN <=> WILLARD <=> WILLIAM <=> CARROLL <=> CARY <=> DAMON <=> DOUG <=> GUS <=> IRVING <=> ISAIAH <=> JARVIS <=> LAURENCE <=> LOWELL <=> OMAR <=> ORLANDO <=> REX <=> ROLANDO <=> WINFRED <=> XAVIER <=> CHONG <=> CORNELL <=> DANNIE <=> DARIO <=> DONN <=> FRITZ <=> KENTON <=> LINCOLN <=> NEVILLE <=> NIGEL <=> NORMAND <=> ODIS <=> RAYMUNDO <=> TONEY <=> TRINIDAD <=> WARNER <=> ARNOLDO <=> COLTON <=> WES
- 第5次排序之后,序列变成了以三十二个元素为一组的有序序列。
ADRIAN <=> CARROLL <=> CARY <=> CLIFTON <=> CODY <=> DAMON <=> DOUG <=> DOUGLAS <=> FREDERICK <=> GERALD <=> GUS <=> HARVEY <=> IRVING <=> ISAIAH <=> JARVIS <=> JOHN <=> LAURENCE <=> LESTER <=> LOWELL <=> NELSON <=> OMAR <=> ORLANDO <=> REX <=> ROLANDO <=> RON <=> SHANE <=> WALTER <=> WARREN <=> WILLARD <=> WILLIAM <=> WINFRED <=> XAVIER <=> ARNOLDO <=> CHONG <=> COLTON <=> CORNELL <=> DANNIE <=> DARIO <=> DONN <=> FRITZ <=> KENTON <=> LINCOLN <=> NEVILLE <=> NIGEL <=> NORMAND <=> ODIS <=> RAYMUNDO <=> TONEY <=> TRINIDAD <=> WARNER <=> WES
- 第6次排序之后,序列变成了完整的有序序列。
ADRIAN <=> ARNOLDO <=> CARROLL <=> CARY <=> CHONG <=> CLIFTON <=> CODY <=> COLTON <=> CORNELL <=> DAMON <=> DANNIE <=> DARIO <=> DONN <=> DOUG <=> DOUGLAS <=> FREDERICK <=> FRITZ <=> GERALD <=> GUS <=> HARVEY <=> IRVING <=> ISAIAH <=> JARVIS <=> JOHN <=> KENTON <=> LAURENCE <=> LESTER <=> LINCOLN <=> LOWELL <=> NELSON <=> NEVILLE <=> NIGEL <=> NORMAND <=> ODIS <=> OMAR <=> ORLANDO <=> RAYMUNDO <=> REX <=> ROLANDO <=> RON <=> SHANE <=> TONEY <=> TRINIDAD <=> WALTER <=> WARNER <=> WARREN <=> WES <=> WILLARD <=> WILLIAM <=> WINFRED <=> XAVIER
本文详细解析了uthash库中的链表排序算法,该算法采用分治策略,复杂度为O(n log(n))。通过多次迭代,将无序链表转化为有序链表。文章通过示例代码展示了排序过程,从单个元素有序链表逐步合并成完整有序链表。
1万+

被折叠的 条评论
为什么被折叠?



