应用举例
约瑟夫环
问题背景
据说著名犹太历史学家 Josephus有过以下的故事:在罗马人占领乔塔帕特后,39 个犹太人与Josephus及他的朋友躲到一个洞中,39个犹太人决定宁愿死也不要被敌人抓到,于是决定了一个自杀方式:41个人排成一个圆圈,由第1个人开始报数,每报数到第3人该人就必须自杀,然后再由下一个重新报数,直到所有人都自杀身亡为止。然而Josephus 和他的朋友并不想遵从。首先从一个人开始,越过k-2个人(因为第一个人已经被越过),并杀掉第k个人。接着,再越过k-1个人,并杀掉第k个人。这个过程沿着圆圈一直进行,直到最终只剩下一个人留下,这个人就可以继续活着。问题是一开始要站在什么地方才能避免自杀?Josephus要他的朋友先假装遵从,他将朋友与自己安排在第16个与第31个位置,于是逃过了这场死亡游戏。
问题描述
- 现有N个人围成一桌坐下,编号从1到N,从编号为1的人开始报数。
- 报数也从1开始,报到M的人离席,从离席者的下一位在座成员开始,继续从1开始报数。
- 复现这个过程(各成员的离席次序),或者求最后一个在座的成员编号。
N = 5, M = 3时出圈次序:3,1,5,2,4
约瑟夫环问题本身具有循环性质,考虑采用循环单链表
基本思想:设置一个计数器count和工作指针p,当计数器累加到m删除节点p。
为统一对链表的任意节点进行计数和删除操作,循环单链表不带头节点
为了便于删除操作,设置两个工作指针pre和p,指针pre指向节点p的前驱节点
为了使计数器从1开始计数,采用尾指针指示的循环单链表,将指针pre初始化为指向终端节点,将指针p初始化为指向开始节点
代码实现
#include <iostream>
using namespace std;
struct Node
{
int data;
struct Node* next;
};
class JosephRing
{
public:
JosephRing();
JosephRing(int n);//构造函数 初始化n个节点的循环单链表
~JosephRing();//析构函数 出环就已有释放空间的操作 这里析构和单链表析构基本相同
void Joseph(int m);//密码为m 打印出环的顺序
private:
Node* rear;
};
JosephRing::JosephRing()
{
rear = nullptr;
}
JosephRing::JosephRing(int n)
{
Node* s = nullptr;
rear = new Node;
//建立长度为1的循环单链表
rear->data = 1;
rear->next = rear;
//依次插入数据域为2 3 4 ... n的节点
for (int i = 2; i <= n; i++)
{
s = new Node;
s->data = i;
//将节点s插入尾节点rear的后面
s->next = rear->next;
rear->next = s;
rear = s;
}
}
void JosephRing::Joseph(int m)
{
Node* pre = rear, * p = rear->next;
int count = 1;
cout << "出环顺序是:";
while (p->next != p)//直到循环链表中只剩一个节点
{
if (count < m)//计数器未累加到密码值
{
// pre = pre->next;
//将工作指针pre和p同时后移
pre = p;
p = p->next;
count++;
}
else//计数器已经累加到密码值
{
cout << p->data << "\t";//输出出环编号
pre->next = p->next;//将节点p摘链
delete p;
p = pre->next;//工作指针p后移 但pre不动
count = 1;
}
}
cout << p->data;
delete p;//释放最后一个节点
}
JosephRing::~JosephRing()
{
if (rear != nullptr)
{
Node* p = rear->next;
while (rear->next != rear)
{
rear->next = p->next;
delete p;
p = rear->next;
}
delete rear;
}
}
int main()
{
int n, m;
cout << "请输入约瑟夫环的长度:";
cin >> n;
cout << "请输入密码:";
cin >> m;
JosephRing R(n);
R.Joseph(m);
return 0;
}
86行未经处理的异常:读取访问权限冲突。
p 是 0xFFFFFFFFFFFFFFF7。
一元多项式求和
若相加的某两项的指数不相等,则两项应分别加在结果中,将引起线性表的插入;若某两项指数相等,则系数相加,若相加结果为0,将引起线性表删除。由于线性表合并过程中需要频繁的执行插入和删除操作,因此考虑采用单链表存储。
代码实现
#include <iostream>
using namespace std;
struct Node
{
int coef, exp;//coef表示系数 exp表示指数
Node* next;
};
class Polynomial
{
public:
Polynomial();//构造函数
Polynomial(const Polynomial& B);//拷贝构造函数
~Polynomial();
Polynomial& operator+(Polynomial& B);//重载运算符,多项式相加
void Print();//打印一元多项式单链表
private:
Node* first;//一元多项式单链表的头指针
};
Polynomial::Polynomial()
{
first = new Node;
Node* s = nullptr, * r = nullptr;//两个工作指针 s指向新开辟的堆区数据 r指向头节点用于遍历
r = first;
int tcoef, texp;
cout << "请输入系数和指数:";
cin >> tcoef >> texp;
while (tcoef != 0)//系数为0就停止
{
s = new Node;
s->coef = tcoef;
s->exp = texp;
//尾插法
r->next = s;
r = s;
cout << "请输入系数和指数:";
cin >> tcoef >> texp;
}
r->next = nullptr;
}
Polynomial::Polynomial(const Polynomial& B)
{
first = B.first;
}
Polynomial& Polynomial::operator+(Polynomial& B)
{
//工作指针初始化
Node* pre = first, * p = pre->next;
Node* qre = B.first, * q = qre->next;//qre一直指向B表头不改变
Node* qtemp = nullptr;
while (p != nullptr && q != nullptr)
{
//将q链表移到p链表中
if (p->exp < q->exp)
{
//两根指针后移
pre = p;
p = p->next;
}
else if (p->exp > q->exp)
{
//用qtemp暂存q下一个节点位置
qtemp = q->next;
pre->next = q;
q->next = p;
pre = q;
q = qtemp;
qre->next = q;
}
else//考虑相等两个指数相等的情况
{
p->coef = p->coef + q->coef;
if (p->coef == 0)
{
pre->next = p->next;
delete p;
p = pre->next;
}
else
{
pre = p;
p = p->next;
}
//第三种情况都要删除节点q
qre->next = q->next;
delete q;
q = qre->next;
}
}
if (q != nullptr)
{
pre->next = q;
}
return *this;//返回本身
}
void Polynomial::Print()
{
Node* p = first->next;
while (p != nullptr)
{
if (p = first->next)
{
cout << p->coef << "x^" << p->exp;
p = p->next;
}
if (p->coef > 0)
{
cout << "+" << p->coef << "x^" << p->exp;
p = p->next;
}
else
{
cout << p->coef << "x^" << p->exp;
p = p->next;
}
}
cout << endl;
}
Polynomial::~Polynomial()
{
Node* q = nullptr;
while (first != nullptr)
{
q = first;
first = first->next;
delete q;
}
}
int main()
{
Polynomial A;
Polynomial B;
A.Print();
B.Print();
A = A + B;//重载加法运算符
cout << "结果是:";
A.Print();
return 0;
}
代码在vs中实现存在问题,还需改进......