3.1.5【约瑟夫问题:双向循环链表】

82 阅读2分钟

思想和单向循环一样,只需要将双向链表尾的next和头的prior双向链接即可。

#include <stdio.h>
#include <stdlib.h>

typedef int datatype;
typedef struct node_t
{
    datatype data;
    struct node_t *prior;
    struct node_t *next;
} node_t, *node_p;

typedef struct doublelinklist
{
    node_p head;
    node_p tail;
} double_link_t, *double_link_p;

// 无头 双向 循环链表 解决 约瑟夫问题
int main(int argc, const char *argv[])
{
    int i;
    int all_num = 6;   //猴子总数
    int start_num = 1; //从1号猴子开始数
    int kill_num = 5;  //数到几杀死猴子

    node_p h = NULL;
    node_p pdel = NULL; //用来指向被杀死猴子的节点

    //1.创建一个操作 无头双向的循环链表 的结构体
    double_link_p p = (double_link_p)malloc(sizeof(double_link_t));
    if (NULL == p)
    {
        perror("malloc failed");
        return -1;
    }

    // 申请第一个结点,用 头指针和尾指针 指向其
    p->head = p->tail = (node_p)malloc(sizeof(node_t));
    if (NULL == p->tail)
    {
        perror("p->tail malloc failed");
        return -1;
    }

    // 给第一个结点 初始化
    p->head->data = 1; //初值为1
    p->head->prior = NULL;
    p->head->next = NULL;

    //将创建n个新的节点,链接到链表的尾
    for (i = 2; i <= all_num; i++)
    {
        node_p pnew = (node_p)malloc(sizeof(node_t));
        if (NULL == pnew)
        {
            perror("pnew malloc failed");
            return -1;
        }

        // 初始化赋值
        pnew->data = i;
        pnew->prior = NULL;
        pnew->next = NULL;

        //(1)将新的节点链接到链表的尾
        p->tail->next = pnew;
        pnew->prior = p->tail;

        //(2)尾指针向后移动,指向当前链表的尾
        p->tail = pnew;
    }
    // 1 2 3 4 5 6 创建完成

    //(3)形成双向循环链表
    p->tail->next = p->head;
    p->head->prior = p->tail;

#if 1
    //调试程序,看看有几个猴子
    node_p ptemp_debug = p->head;
    printf("now has: ");
    while (ptemp_debug != p->tail)
    {
        printf("%d  ", ptemp_debug->data);
        ptemp_debug = ptemp_debug->next;
    }
    putchar(10);
#endif

    //2.循环进行杀死猴子
    h = p->head;

    //(1)先将h移动到start_num处,也就是开始数数的猴子号码处
    // -1 因为 从1开始编号
    for (i = 0; i < start_num - 1; i++)
        h = h->next;

    while (h->next != h) //当h->next == h 就剩一个节点了,循环结束
    {
        //(2)将h移动到即将杀死猴子号码的位置
        // -1 因为 从1开始编号
        // 双向结点,可以直接移动到目标位置操作
        for (i = 0; i < kill_num - 1; i++)
            h = h->next;

        //(3)进行杀死猴子,经过上面的循环后,此时的h指向即将杀死的猴子
        h->prior->next = h->next;
        h->next->prior = h->prior;

        printf("kill is -------%d\n", h->data);

        //pdel指向被杀死猴子的位置
        pdel = h;
        h = h->next; //需要移动,从杀死猴子后的下一个位置开始数
        free(pdel);
    }
    printf("猴王是%d\n", h->data);
    return 0;
}