C语言双向循环链表的实现及约瑟夫环问题

看了这篇博主写的关于动态链表的文章,感触颇深,今天我就以c语言来实现双向循环列表并解决约瑟夫环问题。

(4条消息) 数据结构:动态链表(C语言描述)_知道什么是码怪吗?的博客-CSDN博客

一.约瑟夫环问题

已知 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 出列。

二:解题思路:

1.解决这个问题,我们首先要创建一个双向循环链表,将值1,2,3,4,5分别赋值给5个结点的数据域。

struct rand{
    int number;
    struct rand *next;     // 指向下一个节点的指针
    struct rand *up;       // 指向上一个节点的指针
};

int length(rand *head);    // 获得链表实际长度的函数 
typedef struct rand rand; 
int main(){
    rand *head = (rand*)malloc(sizeof(rand));
    head -> number = 1;    // 首先赋值头指针,头指针数据域为 1 
    rand *p = (rand*)malloc(sizeof(rand)); 
    for(int i = 2; i <= 5; ++i){
        p -> number = i;
            if(i == 2){       
            // 头插法数据域为 2 的结点需要与数据域 1 的头结点连接,双向循环,建立循环关系 
            p -> next = head;
            head -> up = p;
            head -> next = p; 
        } else {
            p -> next = head -> next;
            p -> next -> up = p;
            p -> up = head;
            head -> next = p;
        }
        p = (rand*)malloc(sizeof(rand));
    }
}

我们来检测以下循环链表是否创建成功

    printf("让我们检测以下双向循环链表是否成功创建\n");
    rand *test = head;
    int k = 5;
    printf("顺时针方向:\n");
    while( k-- ){
        printf("%d \n",test -> number);
        test = test -> up;
        // 顺时针方向以当前指针指向上一个结点的方式进行循环 
    }  
    k = 5;
    test = head;
    printf("\n逆时针方向:\n");
    while( k-- ){
        printf("%d \n",test -> number);
        test = test -> next;
        // 顺时针方向以当前指针指向下一个结点的方式进行循环 
    } 

好的,双向循环链表创建成功!!!

2.开始进行约瑟夫环问题啦,其实思路很简单,话不多少,直接上代码。

    rand *st = (rand*)malloc(sizeof(rand));
    int start = 3 - 1;    
    // 编号为 3 开始 ,因为头结点是 1 ,所以走到 3 的结点只需要循环两次。 
    st = head;
    while(start --)
    st = st -> up;        // 获得编号为 3 的结点 
        while(length(st) > 1){
            // 最后整个链表只剩下一个结点,该结点即最后出列的结点 
            st = st -> up;       
            // 顺时针方向获得数到 2 的结点 ,当前结点数 1 ,所以他的上一个(顺时针方向)结点就是数 2 的结点。 
            rand *st2 = (rand*)malloc(sizeof(rand));
            st2 = st -> up;    // 获得该数到 2 的节点的顺时针方向的下一个结点 ,便于后面重新赋值 st 
            st -> up ->next = st -> next;   // 将该数到 2 的结点的临近结点更改关系 
            st ->next -> up = st -> up;	
            free(st);          // 删除该数到 2 的结点 
            st = st2;	       // st 重新赋值,指向数到 2 的结点的下一个结点 
        } 
    printf("最后出列的是 %d\n",st -> number);

附上一段获取链表长度的代码。 

int length(rand *head){
	/* 形参就是链表的任意结点都可,不用一定为头结点
	   思路就是以一个变量记录形参数据域的值,遍历链表,
	   直到新节点的数据域与形参的数据域相同时,退出
	   遍历,返回长度。  
	 */
    rand *p = (rand*)malloc(sizeof(rand));
    p = head;
    int pm = p -> number;   // 记录形参数据域 
    int len = 0;            // 记录长度的值 
    p = p -> next;
    while(1){
        len ++;
        if(pm == p -> number)
            break;
        p = p -> next;
    }
    return len;
} 

参考资料https://blog.csdn.net/weixin_41746479/article/details/118468058

  • 4
    点赞
  • 18
    收藏
    觉得还不错? 一键收藏
  • 5
    评论
评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值