浅析uthash系列之五:链表排序(list sort)

本文详细解析了uthash库中的链表排序算法,该算法采用分治策略,复杂度为O(n log(n))。通过多次迭代,将无序链表转化为有序链表。文章通过示例代码展示了排序过程,从单个元素有序链表逐步合并成完整有序链表。

链表排序

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

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值