循环链表
单项链表尾节点的指针域指向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