对链表进行排序的函数

Posted

技术标签:

【中文标题】对链表进行排序的函数【英文标题】:Function that sorts a linked List 【发布时间】:2022-01-18 20:35:02 【问题描述】:

所以我编写了一个代码,它从文本文件中获取一个字符串,对其进行处理并将其保存到一个双向链表中,到目前为止一切都很好,但是,现在我想按字母出现的频率对列表进行排序在列表中。 该列表非常简单(目前:D):

"ABBBCC"

我希望输出是:

B C A

作为

B出现了3次, C出现了2次并且 A只出现过一次

但是我得到这个作为输出

C A B

这是我的结构,

typedef struct LNodeStruct LNode;
struct LNodeStruct

 unsigned char value;
 int freq;
 LNode* next;
 LNode* prev;
;

值 = 字母本身 freq = 字母出现的频率

这是一个函数,它接受名为“List”的链表,运行到第一个节点并将第一个节点与node->next进行比较,如果第一个节点较小,则交换它:

LNode* insertSorted(LNode* List)


while (List != NULL && List->prev != NULL)
    
        List = List->prev;
    

for(LNode* i = List; i->next != NULL; i = i->next)
    
        for (LNode* j = i->next; j != NULL; j = j->next)
            
                if(i->freq < j->freq)
                
                    int temp = i->value;
                    i->value = j->value;
                    j->value = temp;
                

            

    
return (List);

任何人都可以帮助我解决我的错误。我怀疑函数中的两个 for 循环之一。

【问题讨论】:

您只是在交换value。你也需要换掉freq 这很快,非常感谢,成功了,有没有办法将两个节点作为一个整体交换?如创建一个临时节点,将 i 保存在该节点中等等? 当然 - 只需编写一个 swap(LNodeStruct&amp;, LNodeStruct&amp;) 函数并使用它。请记住,它需要更改i-&gt;prev-&gt;nextj-&gt;next-&gt;prev 等以及ij 中的指针本身。 可以通过更改链接以不同的顺序重新排列节点。 @Ahmed Sabti 这是什么 while (List != NULL && List->prev != NULL) List = List->prev; ?! 【参考方案1】:

这很快,非常感谢,成功了,有没有办法将两个节点作为一个整体交换?就像创建一个临时节点一样,将 i 保存在该节点中等等? – 艾哈迈德·萨布蒂

要交换整个结构内容,请将交换代码替换为:

LNode temp = *i;
*j = *i;
*j = temp;

编辑: 但是......正如约翰在下面指出的那样,上述内容还不够。我们必须保留链接。我们可以只是逐个元素地复制链接除了,但最简单的方法是复制整个结构,但保存/恢复链接:

// preserve the link pointers
LNode *temp_i = i->next;
LNode *temp_j = j->next;

// swap all contents
LNode temp = *i;
*j = *i;
*j = temp;

// restore the link pointers
i->next = temp_i;
j->next = temp_j;

由于您的排序性质,您正在交换整个结构内容。这很好,但如果你的结构中包含(例如)int array[1000000];,它就不会很好地扩展。

但是,链表非常适合归并排序。这样,您可以交换链接。

这里有一些用于链接列表的归并排序代码。它是我拥有的更大库的一部分,因此无法直接编译。

它对列表和节点使用单独的结构。

因为它来自我的个人收藏,所以我有自己的约定。 slh_p 是一个指向列表结构的指针。 slk_p 是一个指向元素结构的指针。

它可能会给你一些想法:

// ovrlib/sls -- singly linked list sort

#include <ovrlib/slk.h>

#include <stdio.h>

// _slhsort -- sort list
void
_slhsort(slh_p slh,slkcmp_p cmp)


    // NOTE: this is a separate routine so we can add the code below for a
    // doubly linked list
    _slhsortgo(slh,cmp);

#if 0
    slk_p prev;
    slk_p cur;

    // do final fixup of back chain pointers
    // FIXME/CAE -- doing a single pass here is _probably_ faster than doing
    // it in the merge step [only to toss it away]
    prev = NULL;
    for (cur = slh->slh_head;  cur != NULL;  cur = cur->slk_next) 
        cur->slk_prev = prev;
        prev = cur;
    
#endif


// _slhsortgo -- sort list
void
_slhsortgo(slh_p slhi,slkcmp_p cmp)

    slh_t slhlhs;
    slh_t slhrhs;

    do 
        // trivial case (i.e. empty list or list with _single_ element)
        if (slhi->slh_curcnt < 2)
            break;

        // split the list
        _slhsortspl(slhi,&slhlhs,&slhrhs);

        // sort left half
        _slhsortgo(&slhlhs,cmp);

        // sort right half
        _slhsortgo(&slhrhs,cmp);

        // merge them
        _slhsortmrg(slhi,&slhlhs,&slhrhs,cmp);
     while (0);


// _slhsortspl -- split up list
void
_slhsortspl(slh_p slhi,slh_p slhlhs,slh_p slhrhs)

    slhoff_t lhscnt;
    slk_p cur;
    slk_p prev;
    slk_p next;

    // NOTE: we leave slhi's slh_curcnt intact as it is invariant across the
    // merge

    // get the split point
    lhscnt = slhi->slh_curcnt;
    lhscnt /= 2;

    // set the counts
    slhlhs->slh_curcnt = lhscnt;
    slhrhs->slh_curcnt = slhi->slh_curcnt - lhscnt;

    // NOTES:
    // (1) because we're doing half the list and the list has at least two, we
    //     _know_ "cur" will _never_ be null
    // (2) the loop _must_ execute at least _once_
    // (3) thus, we know that "prev" will be non-null at the end
    // (4) and, we know that "next" will be _valid_ at the end
    prev = NULL;
    for (cur = slhi->slh_head;  lhscnt > 0;  --lhscnt, cur = next) 
        next = cur->slk_next;
        prev = cur;
    

    // finish the left list:
    //   (1) set the head
    //   (2) set the tail
    //   (3) break the chain pointer to the right half
    slhlhs->slh_head = slhi->slh_head;
    slhlhs->slh_tail = prev;
    prev->slk_next = NULL;

    // finish the right list:
    //   (1) set the head
    //   (2) set the tail
    slhrhs->slh_head = next;
    slhrhs->slh_tail = slhi->slh_tail;


// _slhsortmrg -- merge the two halves
void
_slhsortmrg(slh_p slho,slh_p slhlhs,slh_p slhrhs,slkcmp_p cmp)

    slk_p cur;
    slk_p lhs;
    slk_p rhs;
    slk_p prev;
    slk_p tail;
    int stopflg;

    lhs = slhlhs->slh_head;
    rhs = slhrhs->slh_head;

    prev = NULL;
    stopflg = 0;

    // NOTE: we are guaranteed to execute this once, so we always set the head
    // and we will always have a non-null "prev" value
    while (1) 
        // stop when [at least] one of the lists runs dry
        if (stopflg)
            break;

        // left list element is lower
        if (cmp(lhs,rhs) <= 0) 
            cur = lhs;
            lhs = lhs->slk_next;
            stopflg = (lhs == NULL);
        

        // right list element is lower
        else 
            cur = rhs;
            rhs = rhs->slk_next;
            stopflg = (rhs == NULL);
        

        // set the output list head
        if (prev == NULL)
            slho->slh_head = cur;

        // append to the output list
        else
            prev->slk_next = cur;

        // remember the previous/last element we added to the output list
        prev = cur;
    

    // select the remainder (i.e. the non-empty list)
    // there will be only one, at most, and we can have none
    do 
        cur = NULL;

        // left list has all remaining higher values
        if (lhs != NULL) 
            cur = lhs;
            tail = slhlhs->slh_tail;
            break;
        

        // right list has all remaining higher values
        if (rhs != NULL) 
            cur = rhs;
            tail = slhrhs->slh_tail;
            break;
        

        // both lists have been exhausted [equally]:
        //   (1) nothing more to append
        //   (2) we already know the tail
        tail = prev;
     while (0);

    // append the remainder to the output list
    prev->slk_next = cur;

    // set the tail of the output list
    // the tail's next pointer is already null
    slho->slh_tail = tail;

这里是相关的.h 文件:

// ovrlib/slk.h -- singly linked list control

#ifndef _ovrlib_slk_h_
#define _ovrlib_slk_h_

#include <ovrinc/ovrtypes.h>
#include <ovrinc/ovrinl.h>
#include <ovrinc/ovrstruct.h>
#include <ovrinc/ovrptr.h>

CDEFBEGIN

typedef long slhoff_t;                  // dynamic array offset

#define _SLKDEF(_typ) \
    u32 slk_stat;                   /* status */ \
    TYP_P(_typ) slk_next            /* link to next element */

#define SLKDEF(_typ) \
    _MSTRUCT(_typ)

#define SLH_P(_typa) \
    TYP_P(_typa)
#define SLK_P(_typ) \
    TYP_P(_typ)

#define SLHDEFX(_typa,_typ) \
    MFORWARD(_typ); \
    MFORWARD(_typa); \
    _MSTRUCT(_typa)  \
        u32 slh_stat;                   /* status */ \
        slhoff_t slh_itmsiz;            /* item size */ \
        slhoff_t slh_grow;              /* number of elements to grow */ \
        TYP_P(_typa) slh_pool;          /* free pool chain pointer */ \
        const char *slh_tag;            /* list name */ \
        /**/ \
        TYP_P(_typ) slh_head;           /* head pointer */ \
        TYP_P(_typ) slh_tail;           /* tail pointer */ \
        /**/ \
        slhoff_t slh_curcnt;            /* current active count */ \
        /**/ \
        void (*slh_yldproc)(TYP_P(_typa));  /* yield subroutine */ \
        void *slh_yldptr;               /* yield control */ \
        int slh_yldchunk;               /* yield chunk control */ \
    

#define SLHYLDPROC(_slh,_proc) \
    typeof(_slh->slh_yldproc) _proc = _slh->slh_yldproc

#define SLHDEF(_typ) \
    SLHDEFX(_typ,_typ)

// generic definition
SLHDEFX(slh,slk);
SLKDEF(slk) 
    _SLKDEF(slk);
    void *slk_xtra;                     // link to data
;

// initialize for yield loop
#define YLDCNTINIT(_slh,_yldcnt) \
    do  \
        if (_slh->slh_stat & SLHDONE)  \
            _yldcnt = 0; \
            break; \
         \
        _yldcnt = _slh->slh_yldchunk; \
        if (_yldcnt != 0) \
            break; \
        _yldcnt = -1; \
     while (0)

// bump down yield count
#define YLDCNTINC(_yldcnt) \
    if (_yldcnt > 0) \
        --_yldcnt

#define SLKALLOC        (1u << 31)      // allocated item
#define SLHPOOL         (1u << 30)      // list is free pool
#define SLHINITED       (1u << 29)      // list is inited
#define SLHDONE         (1u << 28)      // list is done
#define SLHDIRTY        (1u << 27)      // list is "dirty"

#define SLH(_vp)        CAST(slh_p,_vp)
#define SLK(_vp)        CAST(slk_p,_vp)

#define slhsetup(_slh,_siz,_grow)   VP(_slhsetup(SLH(_slh),_siz,_grow))
#define slhattach(_pool,_slh)       _slhattach(SLH(_pool),SLH(_slh))
#define slhgrow(_slh)               _slhgrow(SLH(_slh))

#define slknew(_slh)                VP(_slknew(SLH(_slh)))
#define slkrls(_slh,_slk)           _slkrls(SLH(_slh),SLK(_slk))

#define slkpush(_slh,_pnew)         VP(_slkpush(SLH(_slh),SLK(_pnew)))
#define slkshift(_slh,_slk)         VP(_slkshift(SLH(_slh),SLK(_slk)))

#define slhrls(_slh)                _slhrls(SLH(_slh))
#define slhkill(_slh)               _slhkill(SLH(_slh))

#define slhsort(_slh,_cmp)          _slhsort(SLH(_slh),_cmp)
typedef int (*slkcmp_p)(const void *,const void *);

// simple forward scan
#define SLHFORALL(_slh,_slk) \
    CASTEQ(_slk,_slh->slh_head);  _slk != NULL;  CASTEQ(_slk,_slk->slk_next)

// shift forward scan
#define SLHFORALL_SHIFT(_slh,_slk) \
    _slk = slkshift(_slh,NULL);  _slk != NULL;  _slk = slkshift(_slh,NULL)

// shift forward scan
#define SLHFORALL_SHIFT_RELEASE(_slh,_slk) \
    _slk = slkshift(_slh,NULL);  _slk != NULL;  _slk = slkshift(_slh,_slk)

// continuation forward scan
#define _SLHFORALL(_slh,_slk) \
    ;  _slk != NULL;  CASTEQ(_slk,_slk->slk_next)

#include <ovrlib/slk.proto>

CDEFEND

#endif

【讨论】:

我不认为你的整体结构内容交换对链接有正确的影响。假设我们从 PRED X Y SUCC (按对象身份)开始,并且我们使用您的交换来交换 X 和 Y。我认为这会产生 PRED --> X 的损坏结果- -> SUCC 和 PRED @JohnBollinger 哎呀。我只是在重复 Ian 关于做 freq 的评论,Useless 关于 swap(LNodeStruct&amp;, LNodeStruct&amp;) 的评论和 OP 关于如何做的评论。我可以调整答案的顶部 ... 当然,由于 OP 的列表是双链接的,因此还需要保存和恢复 prev 指针。这是完全可行的,但是当人们完成它时,他们可能会发现交换链接会更容易和更清晰。链接交换 (TM):不再只是用于归并排序! @JohnBollinger 从您的 TM 来看,我假设您的年龄足以记住 Anita Bryant 的佛罗里达橙汁广告 :-) 我错过了 OP 的代码是双重链接的事实 [我是咖啡前,叹]。上面的单链接代码实际上是从我的双链接代码创建的,我只是在外部函数中注释掉了prev 链接修复。我没有对动态维护prev 链接而不是 O(n) 修复进行基准测试,所以老实说,我不知道额外的代码复杂性是否值得与额外/单独传递中可能的额外缓存未命中.

以上是关于对链表进行排序的函数的主要内容,如果未能解决你的问题,请参考以下文章

如何干净地使用 QuickSort 对链表进行排序 - Python

在 C 中对链表进行排序

在C中对链表进行排序[关闭]

c语言对链表的数据排序的问题,分不是问题!

147. [链表]对链表进行插入排序

对链表进行插入排序