Estructura de datos y algoritmo: establecimiento de una lista circular enlazada (Joseph Ring) e implementación en lenguaje C

Ya sea una lista enlazada estática o una lista enlazada dinámica, a veces, al resolver problemas específicos, necesitamos ajustar ligeramente su estructura. Por ejemplo, los dos extremos de la lista enlazada se pueden conectar para convertirla en una lista enlazada circular, generalmente llamada lista enlazada circular.

Como su nombre lo indica, simplemente apunte el puntero del último nodo de la lista al nodo principal y la lista enlazada puede formar un anillo, como se muestra en la Figura 1.

Cabe señalar que, aunque la lista enlazada circular es circular, sigue siendo una lista enlazada en esencia, por lo que en la lista enlazada circular, el puntero principal y el nodo principal todavía se pueden encontrar. En comparación con las listas enlazadas ordinarias, la única diferencia entre las listas enlazadas circulares y las listas enlazadas ordinarias es que las listas enlazadas circulares están conectadas de extremo a extremo y todo lo demás es exactamente igual.

La lista enlazada circular implementa el anillo Joseph

El problema del anillo de Joseph es un problema clásico de lista circular enlazada. El significado de la pregunta es: n personas conocidas (representadas por los números 1, 2, 3, ..., n) se sientan alrededor de una mesa redonda, comenzando por la persona numerada k Cuente en el sentido de las agujas del reloj, la persona que cuenta hasta m sale; su siguiente persona comienza de nuevo desde 1, y comienza a contar en el sentido de las agujas del reloj, y la persona que cuenta hasta m sale de nuevo; repita a su vez hasta que quede uno en la mesa redonda de personas.

Como se muestra en la figura a continuación, suponiendo que hay 5 personas alrededor del círculo en este momento, se requiere contar en el sentido de las agujas del reloj desde la persona número 3, y la persona que cuenta hasta 2 queda fuera:

La lista enlazada circular implementa el anillo Joseph

El orden de cotización es el siguiente:

  • La persona numerada 3 comienza a contar 1, luego 4 cuenta 2, entonces 4 sale primero;
  • Después de sacar 4 de la cola, cuente 1 desde 5 y 1 cuenta 2, por lo que 1 se saca de la cola;
  • Después de sacar 1 de la cola, cuente 1 a partir de 2 y cuente 2 a partir de 3, por lo que 3 se saca de la cola;
  • Después de sacar 3 de la cola, cuente 1 a partir de 5 y cuente 2 a partir de 2, por lo que 2 se saca de la cola;
  • Al final solo quedamos 5 de nosotros, así que 5 gana.

Hay muchas variantes del problema del anillo de Joseph, como girar de sentido horario a antihorario, etc. Aunque los detalles del problema tienen muchas variables, la idea central de resolver el problema es la misma, es decir, usar un enlace circular. lista.

A través del análisis anterior, podemos intentar escribir código en lenguaje C, el código completo es el siguiente:

#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\n",p->number);
        free(p);
        p=tail->next;//继续使用p指针指向出列编号的下一个编号,游戏继续
    }
    printf("出列人的编号为:%d\n",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;
}

Resultado de salida:

输入圆桌上的人数n:5
从第k人开始报数(k>1且k<5):3
数到m的人出列:2
出列人的编号为:4
出列人的编号为:1
出列人的编号为:3
出列人的编号为:2
出列人的编号为:5

La persona que quede en último lugar es la ganadora. Por supuesto, también puede mejorar el programa para que cuando se encuentre a la última persona, muestre información sobre la victoria de la persona.

Resumir

La única diferencia entre una lista enlazada circular y una lista enlazada dinámica es su conexión de extremo a extremo, que también está destinada a atravesar la lista enlazada cuando se utiliza la lista enlazada circular.

En el proceso de recorrido, es especialmente importante tener en cuenta que aunque la lista enlazada circular está conectada de extremo a extremo, eso no significa que la lista enlazada no tenga el primer nodo y el último nodo. Por lo tanto, no cambie la orientación del puntero de la cabeza a voluntad.

2023 nueva versión de estructura de datos y algoritmo Java video tutorial (Parte 1), estructura de datos y algoritmo que los programadores senior de Java deben aprender
2023 nueva versión de estructura de datos y algoritmo Java video tutorial (parte 2), estructura de datos y algoritmo que Java programador senior debe aprender

Supongo que te gusta

Origin blog.csdn.net/Itmastergo/article/details/131844830
Recomendado
Clasificación