【数据结构】线性表应用举例--链表实现约瑟夫环和一元多项式求和

应用举例

约瑟夫环

问题背景

据说著名犹太历史学家 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中实现存在问题,还需改进......

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值