对链表进行排序的函数
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&, LNodeStruct&)
函数并使用它。请记住,它需要更改i->prev->next
、j->next->prev
等以及i
和j
中的指针本身。
可以通过更改链接以不同的顺序重新排列节点。
@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&, LNodeStruct&)
的评论和 OP 关于如何做的评论。我可以调整答案的顶部
... 当然,由于 OP 的列表是双链接的,因此还需要保存和恢复 prev
指针。这是完全可行的,但是当人们完成它时,他们可能会发现交换链接会更容易和更清晰。链接交换 (TM):不再只是用于归并排序!
@JohnBollinger 从您的 TM 来看,我假设您的年龄足以记住 Anita Bryant 的佛罗里达橙汁广告 :-) 我错过了 OP 的代码是双重链接的事实 [我是咖啡前,叹]。上面的单链接代码实际上是从我的双链接代码创建的,我只是在外部函数中注释掉了prev
链接修复。我没有对动态维护prev
链接而不是 O(n) 修复进行基准测试,所以老实说,我不知道额外的代码复杂性是否值得与额外/单独传递中可能的额外缓存未命中.以上是关于对链表进行排序的函数的主要内容,如果未能解决你的问题,请参考以下文章