<链表>约瑟夫环

循环链表
  单项链表尾节点的指针域指向NULL:
    ↓_head                             ↓_tail
    [数据1|指针] [数据2|指针] [数据3|指针] [数据4|指针]  Null
    ↑       ↓___↑      ↓___↑       ↓___↑       ↓___↑
    P_node1 P_next = P_node2

  循环链表尾节点的指针域指向头节点的地址:
    ↓----------------------------------------------- ←
    ↓_head                              ↓_tail       ↑
    [数据1|指针] [数据2|指针] [数据3|指针] [数据4|指针]   ↑
    ↑       ↓___↑      ↓___↑       ↓___↑       ↓____↑
    P_node1 P_next = P_node2

约瑟夫环:
  已知有n个人,用编号1~n表示,他们围坐在一张圆桌周围,从编号为k的人
  从1开始报数,数到m的那个人出列:他的下一个人继续从1开始报数,数到m的人继续出列
  依次重复,直到所有人全部出列
  eg:
     n = 13 k = 3 m = 3
     1 2 3 4 5 6 7 8 9 10 11 12 13
1) 5出列,5的前驱点为4,从3开始循环一次找到4
2) 8出列,8的前驱点为7,从6开始循环一次找到7
3) 11出列,11的前驱点为10,从9开始循环一次找到10
......
需要从之前出队列的下一个人开始报数,循环m-2次才能找到当前需要出队列的人的前驱点

注意:想要操作链表的中的某个节点,唯一的方式是,先找到其前驱点

如果k=1,m = 1,则没有前驱点。
#include <stdlib.h>
#include "josephus.h"
#include <iostream>
using namespace std;

int main()
{
    int n, k, m, i;
    // 指向链表头
    struct Node* p;
    struct Node* q;

    cout << "请输入人的个数:";
    cin >> n;
    // 遍历每一个人
    for(i = 1; i <= n; i++)
        create_looplist(i);
    print_looplist();
    p = p_head;

    cout << "请输入起始编号:";
    cin >> k;
    // 找到编号对应的节点
    while(--k)
        p = p->p_next;
    //cout << p->elem << endl;

    cout << "请输入数到几出列:" << endl;
    cin >> m;

    // 如果没有前驱点
    if(m == 1)
    {
        for(i = 0; i < n; i++)
        {
            cout << p->elem << "出队列 ";
            //free(p);
            p = p->p_next;
        }
        cout << endl;
    }
    else
    {
        while(n--)
        {
            // 循环m-2次才能找到当前需要出队列的人的前驱点p
            for(i = 0; i < m-2;i++)
                p = p->p_next;

            // 释放需要出队列的节点的内存,让其前驱点的p_next指向其后继点
            // 都指向前驱点
            q = p;
            // p指向要删除的节点
            p = p->p_next;
            cout << p->elem << "出队列 ";
            // 前驱点的p_next连接要删除节点的后继点,孤立当前节点
            q->p_next = p->p_next;
            //free(p);
            // 更新p
            p = p->p_next;
        }
        cout << endl;
    }

	return 0;

}

josephus.cpp



#include<iostream>
#include "josephus.h"
using namespace std;

// 头指针指向整个链表的第一个节点的地址,插入数据时,不可移动
struct Node* p_head = nullptr;
// 不断追加数据时需要尾指针,始终指向链表的末尾,插入数据时,可以移动
struct Node* p_tail = nullptr;



// 创建节点
void create_looplist(unsigned int elem)
{
    // 节点的数据占用多大的空间就开辟多大的内存空间,返回void* 类型,需要强制转换为节点的类型
    Node* p_node = new Node();
    //Node* p_node = (struct Node *)malloc(sizeof(struct Node));// 节点地址被赋值给指针P
    // 初始化数据域
    p_node->elem = elem;
    // 初始化指针域,此时并没有串联节点,指针指向空
    p_node->p_next = nullptr;

    // 若一个节点都没有放到链表中
    if(p_head == nullptr)
        p_head = p_node;// 插入数据时,不可移动
    else// 第二次调用时P_tail = 第一个节点的地址,相当于第一个节点的指针域指向第二个节点的节点地址
        p_tail->p_next = p_node;

    // 第一次调用时指向第一个节点地址,第二次调用时指向第二个节点地址
    p_tail = p_node;

    // 循环链表:尾节点的指针域指向头节点的地址
    p_tail->p_next = p_head;
  
}



// 显示链表数据域
void print_looplist(void)
{
	struct Node *p;
    // 初始化
    p = p_head;
    /*
    单向链表退出条件:p不为空的话就一直更新p的位置
    循环链表退出条件:p->p_next = p_head,导致最后一个没有打印就退出了
    for(p = p_head; p->p_next != p_head; p = p->p_next)
        printf("%d", p->elem);
    */
    do
    {
        cout << p->elem << " ";
        p = p->p_next;
    }
    // 直到再一次经过头指针:最后一个数据的p_next == p_head
    while(p != p_head);
    cout << endl;
}


int search_looplist(unsigned int elem)
{
	struct Node *p;
    p = p_head;
	// for(p = p_head; p; p = p->p_next)
	// 	if(p->elem == elem)
	// 		return 1;

    do
    {
        p = p->p_next;
        if(p->elem == elem)
          return 1;
    }
    // 直到再一次经过头指针
    while(p != p_head);
    return 0;
}

josephus.h

#ifndef LINKLIST_H__
#define LINKLIST_H__


struct Node
{
    // 数据域
    unsigned int elem;
    // 指针域
    struct Node* p_next;
};
extern struct Node* p_head;
// 不断追加数据时需要尾指针,始终指向链表的末尾,插入数据时,可以移动
extern struct Node* p_tail;


void create_looplist(unsigned int elem);
void print_looplist(void);
int search_looplist(unsigned int elem);



#endif

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值