zoukankan      html  css  js  c++  java
  • 数据结构7: 循环链表(约瑟夫环)的建立及C语言实现

    链表的使用,还可以把链表的两头连接,形成了一个环状链表,称为循环链表。


    和它名字的表意一样,只需要将表中最后一个结点的指针指向头结点,就形成了一个环。

    图1 循环链表
    循环链表和动态链表相比,唯一的不同就是循环链表首尾相连,其他都完全一样。

    实际应用:约瑟夫环问题

    约瑟夫环问题,是一个经典的循环链表问题,题意是:已知 n 个人(以编号1,2,3,…,n分别表示)围坐在一张圆桌周围,从编号为 k 的人开始顺时针报数,数到 m 的那个人出列;他的下一个人又从 1 还是顺时针开始报数,数到 m 的那个人又出列;依次重复下去,要求找到最后出列的那个人。

    例如有 5 个人,要求从编号为 3 的人开始,数到 2 的那个人出列:

    出列顺序依次为:

    编号为 3 的人开始数 1,然后 4 数 2,所以 4 先出列;
    4 出列后,从 5 开始数 1,1 数 2,所以 1 出列;
    1 出列后,从 2 开始数 1,3 数 2,所以 3 出列;
    3 出列后,从 5 开始数 1,2 数 2,所以 2 出列;
    最后只剩下 5 自己,所以 5 出列。

    完整代码

    #include <stdio.h>
    #include <stdlib.h>
    typedef
    struct node
    {   
    int number;   struct node *next; }person;
    person
    *initLink(int n)
    {
      person
    * head = (person*)malloc(sizeof(person));   head->number = 1;   head->next = NULL;   person *cyclic = head;   for (int i=2; i<=n; i++)
      {     person
    *body = (person*)malloc(sizeof(person));     body->number = i;     body->next = NULL;     cyclic->next = body;     cyclic = cyclic->next;   }   cyclic->next = head;  //首尾相连
      return head; }
    void findAndKillK(person *head, int k, int m)
    {   person
    *tail = head;   //找到链表第一个结点的上一个结点,为删除操作做准备   while (tail->next != head)
      {     tail
    = tail->next;   }   person *p = head;   //找到编号为k的人   while (p->number!=k)
      {     tail
    =p;     p=p->next;   }   //从编号为k的人开始,只有符合p->next==p时,说明链表中除了p结点,所有编号都出列了,   while (p->next!=p)
      {     
    //找到从p报数1开始,报m的人,并且还要知道数m-1de人的位置tail,方便做删除操作。     for (int i=1; i<m; i++)
        {       tail
    = p;       p = p->next;     }
        tail
    ->next = p->next;  //从链表上将p结点摘下来     printf("出列人的编号为:%d ", p->number);     free(p);     p = tail->next;//继续使用p指针指向出列编号的下一个编号,游戏继续   }   printf("出列人的编号为:%d ", p->number);   free(p);
    }
    int main()
    {   printf(
    "输入圆桌上的人数n:");   int n;   scanf("%d", &n);   person *head = initLink(n);   printf("从第k人开始报数(k>1且k<%d):", n);   int k;   scanf("%d", &k);   printf("数到m的人出列:");   int m;   scanf("%d", &m);   findAndKillK(head, k, m);
      
    return 0; }
    输出结果:
    输入圆桌上的人数n:
    5 从第k人开始报数(k>1且k<5):3 数到m的人出列:2 出列人的编号为:4 出列人的编号为:1 出列人的编号为:3 出列人的编号为:2 出列人的编号为:5

    总结

    循环链表和动态链表唯一不同在于它的首尾连接,这也注定了在使用循环链表时,附带的最多的操作就是遍历链表。

    在遍历的过程中,尤其要注意循环链表虽然首尾相连,但并不表示该链表没有第一个节点和最后一个结点。所以,不要随意改变头指针的指向。

  • 相关阅读:
    WPF DelegateCommand 出现Specified cast is not valid
    WPF DelegateCommand 出现Specified cast is not valid
    WPF DelegateCommand 出现Specified cast is not valid
    win10 sdk 是否向下兼容
    win10 sdk 是否向下兼容
    win10 sdk 是否向下兼容
    PHP extract() 函数
    PHP end() 函数
    PHP each() 函数
    PHP current() 函数
  • 原文地址:https://www.cnblogs.com/ciyeer/p/9028040.html
Copyright © 2011-2022 走看看