「题解」环形链表的约瑟夫问题

文章目录

🍉题目

🍉解析

题目的意思就是从环形链表的第一个节点开始数,数到第 m 的时候释放对应的节点,然后从下个节点又从1开始数,然后继续释放节点。所以我们要做的就是通过循环不断删除指定位置的节点。比如有5个节点,在删除第二个节点之前,得先让第一个节点的 next 指向第三个。

不过题目现在还没有链表,我们得先来创建一个。

(下面这个图演示有五个节点,m = 2的情况,绿色箭头表示删除某些节点之后剩余节点 next 指针的指向)

🍌创建环形链表

环形链表和单链表的区别在于,环形它最后一个节点的 next 指向的是第一个节点而非NULL,

既然首尾节点有这种联系,那一开始就得建立这两个指针pheadptail,等下创建链表的时候ptail就在循环中不断往后走,当它走到最后一个节点时,就让该节点的next指向第一个节点。

既然要产生节点,那么先来写个创建节点的函数(前面的文章有讲),代码如下:

复制代码
ListNode* BuyListNode(int i) {
    ListNode* node = (ListNode*)malloc(sizeof(ListNode));
    node->val = i;
    node->next = NULL;
    return node;
}

接下来要创建链表,这个也不难:

复制代码
ListNode* CreatListNode(int n) {
    ListNode* phead = BuyListNode(1);
    ListNode* ptail = phead;
    for (int i = 2;i <= n ; i++) {   //注意循环从 i == 2开始
        ListNode* node = BuyListNode(i);
        ptail->next = node;
        ptail = ptail->next; 
    }
    ptail->next = phead;
    return ptail;
}

注意:
①题目有说节点个数可以为1,检查下节点数为1有没有问题,此时首尾节点是同一个,而且不会进入循环,没问题。
②这里返回的是ptail,因为返回它的话我们很容易就能找到phead,而如果返回phead,那要找ptail的话还要遍历链表。

🍌释放指定节点

因为涉及到多次释放节点,不难想到应该要用while循环,那循环终止条件是啥呢?暂时看不出来,先把循环里面的东西写一下再说。

如上图,调用创建链表函数后,我们用prev接收返回的节点地址(其实就是尾节点),然后定义一个phead(prev->next)作为头指针,再来一个计数器,只要计数器的值为m,就先让prev保存phead的下一个节点,然后删除phead所指节点,然后让phead走向下一节点;若 count 不为m,则让pheadprev都前进一步。下面再画个图方便你理解这个过程。


再来说下循环结束条件,按照我刚才上面画的图,按那个走势走下去,最后只剩下节点3,此时pheadphead的next重合了,就可以出循环了,然后返回节点的值。

所以代码如下:

复制代码
int ysf(int n, int m ) {
    ListNode* prev = CreatListNode(n);
    ListNode* phead = prev->next;
    int count = 1;  
    while (phead->next != phead) {
        
        if(count == m) {
            prev->next = phead->next;
            free(phead);
            phead = prev->next;
            count = 1;   //注意count一定要置为1!
        } else {
            prev = phead;
            phead = phead->next;
            count++;
        }
    }
    return phead->val;
}

整道题的代码如下:

复制代码
typedef struct ListNode ListNode;

ListNode* BuyListNode(int i) {
    ListNode* node = (ListNode*)malloc(sizeof(ListNode));
    node->val = i;
    node->next = NULL;
    return node;
}

ListNode* CreatListNode(int n) {
    ListNode* phead = BuyListNode(1);
    ListNode* ptail = phead;
    for (int i = 2;i <= n ; i++) {
        ListNode* node = BuyListNode(i);
        ptail->next = node;
        ptail = ptail->next; 
    }
    ptail->next = phead;
    return ptail;
}

int ysf(int n, int m ) {
    ListNode* prev = CreatListNode(n);
    ListNode* phead = prev->next;
    int count = 1;
    while (phead->next != phead) {
        
        if(count == m) {
            prev->next = phead->next;
            free(phead);
            phead = prev->next;
            count = 1;
        } else {
            prev = phead;
            phead = phead->next;
            count++;
        }
    }
    return phead->val;
}

🍌其他思路

在上面演示的例子中,你也可以定义pheadpcur,其中pcur指向第二个节点,不过此时count的初始值要置为2。

🍉写在最后

如果你觉得本文写得还不错的话,那就点个小小的赞和关注喔,你们的支持是我更新的不懈动力!

相关推荐
MegaDataFlowers2 分钟前
101.对称二叉树
算法
小成202303202655 分钟前
C++~01面向对象基础
开发语言·c++
会编程的土豆13 分钟前
Go 方法接收者超清晰笔记(类型名 vs 变量名)
开发语言·笔记·golang
Jasmine_llq21 分钟前
《B3939 [GESP样题 四级] 绝对素数》
数据结构·算法·素数判断算法·数字拆分与反转算法·区间遍历枚举·双条件判断逻辑
郝学胜-神的一滴36 分钟前
干货版《算法导论》07:递归视角下的选择排序与归并排序
java·数据结构·c++·python·程序人生·算法·排序算法
YY&DS41 分钟前
Qt 嵌入 CEF 在 Linux 下必须设置 `QT_XCB_GL_INTEGRATION=xcb_egl才能加载网页
linux·开发语言·qt
csdn_aspnet44 分钟前
javascript 算法 LeetCode 编号 70 - 爬楼梯
开发语言·javascript·算法·leetcode·ecmascript
shehuiyuelaiyuehao1 小时前
多线程入门
java·python·算法
Navigator_Z1 小时前
LeetCode //C - 1073. Adding Two Negabinary Numbers
c语言·算法·leetcode
han_hanker1 小时前
BeanUtils.copyProperties 和序列化的问题
java·开发语言·spring boot