【问题标题】:Singly linked lists data structure in c: linking a listc中的单链表数据结构:链接列表
【发布时间】:2019-11-26 22:30:57
【问题描述】:

我正在尝试在链接列表中选择一个随机节点。我的想法是重新链接列表 n 次,以便每次 n 次时头部都会改变是一个不同的值。所以例如:列表包含他->需要->一些->牛奶。从牛奶开始,将其链接为另一个列表节点的头部,对于 n-times=2,头部将成为需要。如果 n-times=3,head 变成 he。作为一个函数,执行此操作的参数是一个列表和一个随机数。 我的想法写在下面:

typedef  struct Stops

 {

    char name[51];

    struct Stops *nxt;
  }Stops;

     Stops *relinking (Stops *alist_torelink, int rndtimes)

  {

    int linkcnt=0; //to count how many times it's been linked, i.e how many links//

    Stops *leknd=NULL;


    while(  alist_torelink!=NULL  && linkcnt < rndtimes  )
    {  
        leknd= (Stops*) malloc(sizeof(Stops));
        strcpy(leknd->name, alist_torelink->name);
        leknd->nxt=alist_torelink;
        linkcnt++;
        if(alist_torelink==NULL) 
        {alist_torelink=leknd;} //so if it reaches the end of the original list, it starts back at the head of the list being relinked and continues. this will mean the loop exits when linkcnt=rndtimes-1//
        alist_torelink=alist_torelink->nxt;
    } return leknd;

     }

这足以有效地获得随机头部,但是我没有看到代码存在问题。与具有 5 个元素的原始列表一样,如果创建新列表为 停止 *newlist= relinking(original, 4),函数的行为,我不明白,我只能看到创建的 newlist 是

【问题讨论】:

  • @awdbutahdagreyt 您需要从原始列表中再创建一个列表,还是只需要重新链接原始列表中的新节点而不创建更多列表?
  • 我无法修改原始节点,所以是的,我需要从原始节点创建新节点。
  • 问题出在哪里?
  • 有一种(甚至是相对有效的)方法可以在事先不知道列表长度的情况下从列表中选择一个随机节点,但是您当前对问题的陈述太模糊,无法判断它是否足以满足您的需求。
  • 从列表中选择一个随机节点正是我想要做的。上面的一切都是我想这样做的方式。

标签: c singly-linked-list


【解决方案1】:

有一个通用的算法可以从一个预先不知道长度的列表中选择一个具有均匀概率的项目,而链表结构通常是很好的候选者。

它是这样工作的

If empty(list)
   return a non-selection/throw an error
currentItem := firstItem
i := 1
interimSelection := currentItem
While (there is another item)
   i := i + 1
   currentItem := next(currentItem)
   if ( random() <= 1/i )
      interimSelection := currentItem
Return interimSelection

这里random()在[0,1)范围内返回一个随机值,所以条件说“选择概率为1/i的当前项目。

算法在 O(n) 时间内运行列表中的 n 个项目,并且需要超出列表本身占用的恒定空间。


为了让自己相信这个过程可以满足您的需求(给您 1/n 的机会获得 n 个项目列表中的每一个项目),请考虑要选择第 m 个项目必须发生什么:

  1. 到达时必须选择第 m 项 (P = 1/m)。
  2. 必须选择每个后续项目 (P = (k-1)/k)。

所以第m个节点的总概率是

P_m = (1/m) * prod_{k=m+1}^{n} (k-1)/k . 

但是(k-1)对于乘积中的第一项是m+1-1=m,所以可以用原项的分母对消第一个乘积项的分子。并且第二个产品 tem 的分子抵消了第一个产品的分母。以此类推,直到最终结果为 1/n。


在 c 中实现它需要您将我上面草图中的伪语句转换为代码。

这里有一些建议

  • 如果头部为NULL,则列表为空
  • 分配和返回项目是通过分配和返回指针来完成的
  • 测试是否存在另一个项目意味着检查当前节点的next 成员。
  • 绘制随机值的常用函数返回某个范围 [0,N) 中的整数值,而不是 [0,1) 中的实数,并且 c 中整数算术的行为是这样的,我编写选择条件的方式是不太理想;您应该将其转换为更惯用的形式。

【讨论】:

    【解决方案2】:
    K *copymake(K *alist)
    {
        K *one= (K*) malloc(sizeof(K));
    
        strcpy(one->name, alist->name);
        one->nodenext=alist;
        one=alist;
        return one;
    }
        K *relinking(K *alist, int rNdnum)
        {
        K *one=copymake(alist);
            while(rNdnum>0)
            {
                alist=alist->nodenext;
                rNdnum--;
                if(alist==NULL)
                {
                    alist=one;
                }
            }return alist;
        }
    

    重新链接:从头部开始遍历列表。这样做 n 次。 n次=rNdnum。头变了。如果列表为空,则将其设置为另一个列表,该列表只是原始列表的副本。这样,如果 rNdnum > 节点数,则遍历中的下一个节点不等于 NULL,而是链表的另一个节点。

    【讨论】: