数据结构线性表的逻辑结构(四)单链表的基本操作的实现

一、 实验目的

1. 掌握线性表的逻辑结构;

2. 链表的基本操作的实现;

3. 掌握利用C++/C编程语言实现数据结构的编程方法;

4. 通过上机时间加强利用数据结构解决实际应用问题的能力;

二、 实验要求

1. 实验前做好充分准备,包括复习线性表所学内容,事先预习好本次实验内容;

2. 实验时记录实验结果,按要求完成各题;

三、 实验题目与要求

一、基础题:链表的实现

1. 编写链表基本操作函数:

l InitList(LinkList *L)初始化链表;(P49)

l InsertList(LinkList  L, int item, int rc )向链表的指定位置插入元素;(P54)

l SeqInsertList(LinkList  L, int item)向有序链表的插入元素,插入后链表仍有序;

扫描二维码关注公众号,回复: 2147392 查看本文章

l DeleteList(LinkList  L, int item)删除指定元素值的链表记录;(P55)

l FindList(LinkList  L, int item)查找链表中的元素;(P52)

l OutputList(LinkList  L)输出链表中元素;

l FreeList(LinkList L)释放链表

2. 调用上述函数实现下列操作(验证)

l 初始化链表;

l 调用插入函数建立一个普通链表(InsertList1)和一个有序链表(InsertList2)

l 在链表中寻找指定的元素;

l 在链表中删除指定值的元素;

l 遍历并输出链表

注:每完成一个步骤,必须及时地输出链表元素,以便于观察操作结果。

LinkList.h

#include <stdio.h>
#include <stdlib.h>

typedef int ElemType;

/**
单链表存储结构定义
**/
typedef struct  Node
{
	ElemType data;
	struct Node *next;
}Node,*LinkList;

/*******初始功能
*初始时链表只有一个头结点
********/
void InitList(LinkList *L);

/*********插入功能******
* @参数 LinkList L: 链表头指针
* @参数 ElemType item:插入的数据值
* @参数 ElemType item:插入的位置
* @无返回值参数
***********************/
void InsertList(LinkList L,ElemType item,int rc);

/*********插入有序链表功能******
* @参数 LinkList L: 链表头指针
* @参数 ElemType item:插入的数据值
* 插入后链表仍有序
* @无返回值参数
***********************/
void SeqInsertList(LinkList L,ElemType item);

/*********删除功能******
* @参数 LinkList L:链表头指针
* @参数 ElemType item:删除结点的数据值
* @无返回值参数
***********************/
void DelList(LinkList L,ElemType item);

/*********查找功能******
* @参数 LinkList L:链表头指针
* @参数 ElemType item:查找的数据值
* @返回值:成功找到返回1否则返回0
***********************/
int FindList( LinkList L,ElemType item);

/*********输出功能******
* @参数 LinkList L:链表头指针
* @无返回值
***********************/
void OutputList(LinkList L);

/*********释放链表空间******
* @参数 LinkList L:链表头指针
* @无返回值
***********************/
void FreeList(LinkList L);
LinkList.cpp
#include "LinkList.h"
void InitList(LinkList *L)
{
	*L = (LinkList)malloc(sizeof(Node));
	(*L)->next=NULL;
}
void InsertList(LinkList L,ElemType item,int rc)
{  
	Node *pre,*s;
	int k;
	pre=L;  
	k=0;                     /*从"头"开始,查找第rc-1个结点*/
	while(pre!=NULL&&k<rc-1)  /*表未遍历完且未查到第rc-1个时,继续找下一个结点*/ 
	{ 
		pre=pre->next;
		k=k+1; 
	}									
	if(!pre)    /*如当前位置pre为空表已找完还未数到第rc-1个,说明插入位置不合理*/ 
	{ 
		printf("插入位置不合理!");
		return;
	}
	s=(Node*)malloc(sizeof(Node));    /*申请一个新的结点S */
	s->data=item;    /*值e置入s的数据域*/
	s->next=pre->next;    	/*修改指针,完成插入操作*/
	pre->next=s;
}
void SeqInsertList(LinkList L,ElemType item)
{  
	Node *pre, *r;
	pre = L;
	while (pre->next != NULL && pre->next->data <item)	
	{
		pre = pre->next;
	}
	r = (Node*)malloc(sizeof(Node));
	r->data = item;   
	r->next = pre->next;
	pre->next = r;
}
void DelList(LinkList L,ElemType item)
{  
	Node *pre,*r;
	pre=L;
	while(pre->next!=NULL && pre->next->data!=item)	/*寻找元素值为item结点的前驱*/
	{ 
		pre=pre->next;
	}								
	if(!(pre->next))     /* 没有找到合法的前驱位置,说明链表中不存在item值*/
	{
		printf("删除结点不合理!");
		return;
	}
	r=pre->next;
	pre->next=r->next;    /*修改指针,删除结点r*/
	free(r);    /*释放被删除的结点所占的内存空间*/
	printf("成功删除结点!\n");
}
int FindList( LinkList L,ElemType item)
{ 
	Node *p;
	p=L;   /*从表中第一个结点开始 */
	while (p!=NULL)
	{
		if (p->data!=item)   /*若没找到,继续找下一结点*/
			p=p->next;
		else  
			break;      /*找到结点值=item时退出循环 */
	}
	if (!p) return 0;
	else return 1;
} 
void OutputList(LinkList L)
{
	Node *p;
	p = L;
	p = p->next;
	while (p != NULL)
	{
		printf("%d  ",p->data);
		p = p->next;
	}
	printf("\n");
}

void FreeList(LinkList L)
{
	Node *p,*r;
	p = L;
	while (p->next != NULL)
	{
		r = p->next;
		p->next = r->next;
		free(r);
	}
   /*释放所有结点,自行完成*/
}

main.cpp

#include "LinkList.h"
void main()
{
	LinkList L;
	InitList(&L);    /*初始化,填空1*/;
	ElemType item;  /*某个线性表元素值*/
	int rc;         /*某个线性表元素的序号*/
	int choice;    /*输入选择*/
	while(1)
	{
		printf( "\n请选择操作  1:指定位置追加  2: 升序追加  3: 查找结点\n" );
		printf( "     4: 删除结点      5: 输出结点  6: 清空链表  0:退出\n" );
		fflush( stdin );	/* 清空标准输入缓冲区 */
		scanf( "%d", &choice );
		switch( choice ) {
			case 0:		/* 退出 */
				return;
			case 1:		
				printf( "请输入新增结点键值和位置:" );
				scanf( "%d%d", &item, &rc );
				InsertList(L, item, rc);/*填空2,指定位置追加结点*/
				break;
			case 2:		
				printf( "请输入新增结点键值:" );
				scanf( "%d", &item );
				SeqInsertList(L,item);/*填空3,按升序追加结点*/
				break;
			case 3:		
				printf( "请输入要查找结点的键值:" );
				scanf( "%d", &item );
				rc=FindList(L, item);/*填空4,查找结点*/
				if( rc > 0 )
					printf( "  成功找到\n");
				else
                    printf( "  没找到\n" );
				break;
			case 4:		
				printf( "请输入要删除结点的键值:" );
				scanf( "%d", &item );
				DelList(L, item);/*填空4,删除结点*/
				break;
			case 5:		
				printf( "\n链表内容为:\n" );
				OutputList(L);/*填空5,输出结点*/
				break;
			case 6:		
				FreeList(L);/*填空6,清空链表*/
                break;
		}
	}
}

二、提高题:链表的应用

1. 利用单链表实现多项式的乘法

【功能说明】利用单链表表示一元多项式,并完成两多项式的乘法运算。按指数的升序输入第一个一元多项式polya各项的指数和系数,且以输入0 0结束,构造第一个多项式单链表;按指数的升序输入第二个一元多项式polyb各项的指数和系数,构造第二个多项式单链表,  输出两一元多项式乘积的一元多项式polyc,并进行算法时间复杂度的分析。

1: 

【测试用例】

输入

0 2 1 3 4 -3 0 0

2 4 6 6 0 0

0 2 1 3 2 -3 0 0

1 1 2 4 0 0

输出

2 8 3 12 7 18 10 -18

1 2 2 11 3 9 4 -12

【设计要求】在给出的代码素材polymul.cpp文件中补充完整以下函数,实现多项式相乘的计算。

polymul.h

#include <stdio.h>
#include <stdlib.h>
#include <iostream>
using namespace std;

#define OK   1
#define ERROR  0
#define TRUE 1
#define FALSE 0

/*多项式结点结构定义*/
typedef struct Polynode 
{
	int coef; //系数 
	int exp;  //指数 
	Polynode *next;
}Polynode, *Polylist;

/********输出功能
* @参数 Polylist poly: 多项式链表头指针
* @无返回值
*************/
void OutputPolylist(Polylist poly);

/********创建多项式链表
* @参数 Polylist poly: 多项式链表头指针
* @输入多项式各项的系数和指数,按指数升序构造链表,输入0 0结束 
* @无返回值
****************/
void PolyCreate(Polylist poly);//按升序的方式输入多项式各项的系数和指数,输入0 0结束 

/********插入功能
* @参数 Polylist poly: 多项式链表头指针
* @参数 int coef:系数
* @参数 int exp:指数
* @插入新的一项,按指数升序插入 
* @无返回值
****************/
void PolyInsert(Polylist poly, int coef, int exp);//多项式中增加一项 

/********多项式相加功能1
* @参数 Polylist polya: 多项式链表a的头指针
* @参数 Polylist polyb: 多项式链表b的头指针
* @多项式和由polya表示,多项式ployb释放 
* @无返回值
****************/
void PolyAdd(Polylist polya, Polylist polyb); /*两个多项式相加,然后将和多项式存放在多项式polya中,并将多项式ployb删除*/

/********多项式相加功能2
* @参数 Polylist polya: 多项式链表a的头指针
* @参数 Polylist polyb: 多项式链表b的头指针
* @参数 Polylist polyc: 多项式链表c的头指针
* @多项式和由polyc表示
* @无返回值
****************/
void PolyAdd(const Polylist polya, const Polylist polyb,Polylist polyc);

/********多项式相乘功能
* @参数 Polylist polya: 多项式链表a的头指针
* @参数 Polylist polyb: 多项式链表b的头指针
* @参数 Polylist polyc: 多项式链表c的头指针
* @多项式乘积由polyc表示
* @无返回值
****************/
void  PolyMul(Polylist polya,Polylist polyb,Polylist polyc);

polymul.cpp

#include "polymul.h"

void PolyInsert(Polylist poly, int coef, int exp)
{
	Polynode *p = (Polynode *)poly;
	bool flag = 0;
	while (p->next != NULL) {
		if (p->next->exp < exp) {
			p = p->next;
		}
		else if (p->next->exp == exp) {
			p->next->coef += coef;
			flag = 1;
			break;
		}
		else if (p->exp < exp  && p->next->exp > exp) {
			Polynode *s = new Polynode;
			s->exp = exp, s->coef = coef;
			s->next = p->next;
			p->next = s;
			flag = 1;
			break;
		}
	}
	if (!flag) {
		Polynode *s = (Polynode *)new Polynode;
		s->exp = exp, s->coef = coef;
		s->next = p->next;
		p->next = s;
	}
}

void OutputPolylist(Polylist poly) //输出多项式 
{
	Polylist p = poly->next;
	if (p!=NULL)
	{
		if (p->coef<0) cout<<"-";
	    cout<<p->coef<<"x^"<<p->exp<<" ";
	    p=p->next;
	}
	while (p != NULL)
	{
		if (p->coef > 0) cout << "+ ";
		if (p->coef) cout << p->coef << "x^" << p->exp << " ";
		p = p->next;
	}
	cout << endl;
	return;
}

void PolyCreate(Polylist poly)
{
	int c, e;
	cin >> e >> c;                   /*键入多项式的指数和系数*/
	while (c != 0)	        /*若c=0,则代表多项式的输入结束*/
	{
		PolyInsert(poly, c, e);
		cin >> e >> c;
	}
}

void  PolyAdd(Polylist polya, Polylist polyb)
{
	Polynode *p = polyb->next;
	while (p != NULL)
	{
		PolyInsert(polya, p->coef, p->exp);//把polyb的第一项插入到polya中 
		polyb->next = p->next;// polyb多项式删除第1项 		
		free(p);
		p = polyb->next; //p指向polyb的新的第一个节点 
	}
	free(polyb);//释放polyb的头结点 
}

void  PolyAdd(const Polylist polya, const Polylist polyb, Polylist polyc)
{
	Polynode *p = polya->next;
	while (p != NULL)
	{
		PolyInsert(polyc, p->coef, p->exp);
		p = p->next;
	}
	p = polyb->next;
	while (p != NULL)
	{
		PolyInsert(polyc, p->coef, p->exp);
		p = p->next;
	}
}

void  PolyMul(Polylist polya, Polylist polyb, Polylist polyc)
{
	Polynode *p = polya->next;
	while (p != NULL) {
		Polynode *pre = new Polynode;
		pre = polyb->next;
		while (pre != NULL) {
			PolyInsert(polyc, p->coef * pre->coef, p->exp + pre->exp);
			pre = pre->next;
		}
		delete pre;
		p = p->next;
	}
}

Main.cpp

#include "polymul.h"
int main()
{
	Polylist polya, polyb, polyc;
	Polynode *p;
	int coef, exp;
	cout << "请输入多项式A中各项指数和系数:(以0,0结束!)\n";
	polya = (Polynode *)malloc(sizeof(Polynode));
	polya->next = NULL;
	PolyCreate(polya);
	OutputPolylist(polya);
	cout << "请输入多项式B中各项指数和系数:(以0,0结束!)\n";
	polyb = (Polynode *)malloc(sizeof(Polynode));
	polyb->next = NULL;
	PolyCreate(polyb);
	OutputPolylist(polyb);
	polyc = (Polynode *)malloc(sizeof(Polynode));
	polyc->next = NULL;
	//PolyAdd(polya, polyb, polyc);
	PolyMul(polya, polyb, polyc);
	OutputPolylist(polyc);
	return 0;
}

2. 利用循环单链表求解约瑟夫环问题

【问题描述】约瑟夫环问题:即n个人围成一个圆圈,然后从第一个人开始,按:1,2,3,……,m报数,数到m的人出圈,并有出圈者的下一个人重新开始报数,数到m又要出圈,如此类推,直到所有人都出圈,打印出圈的次序,其中nm为输入数据

【算法思想】

l 约瑟夫环满足线性表的逻辑结构,相邻两项具有前驱和后继的关系,故可以使用表结构表示。

通过尾插法创建不带头的单向循环链表(1,2,3……n);创建完毕后cur指针指向表尾;  

 

计算出列人数number,初始值为0;

l 若出列人数不足n,则重复通过移动current指针到报数为m的前驱,删除current的后继并输出被删除结点的编号,number++;

#include <stdio.h>
#include <stdlib.h>
#include <malloc.h>
#include <time.h>

 /*结点类型定义
 * @data:某结点的序号
 * @next:指针域
 * LinkList为结构指针类型
 */
typedef struct Node {
	int data;
	struct Node *next;
}Node, *link;

/*用循环链表求解约瑟夫环
* @参数link joseling:循环链表头指针
* @参数int n:总人数
* @参数int m:报数
* 无返回值
*/
void josephus(link joseling, int n, int m);

/*创建值为1~n的n个结点的不带头结点的循环单链表
* @参数int n:总人数
* 返回循环单链表的头指针
*/
link creatlink(int n);

/*打印循环链表
* @参数link head:循环单链表的头指针
*无返回值
*/
void printlink(link head);

/*移动,使p指针向前走i步
* @参数Node *p:指针p
* @参数int i:移动的次数
* 返回值指向结点的指针
*/
Node * move(Node *p, int i);

/*程序入口*/
int main()
{
	int n, m, count, number;//count用于报数计数,number用于出列人数计数 
	link joselink, current, s;
	printf("输入总人数n和报数规律m:\n");
	scanf("%d %d", &n, &m);
	joselink = creatlink(n);
	printlink(joselink);

    //不带头结点的循环单练表存储的约瑟夫环问题求解 
	josephus(joselink, n, m);
	return 0;
}


link creatlink(int n)
{
	Node *head = (Node *)malloc(sizeof(*head));
	Node *cur;

	head->data = 1;
	cur = head;

	for (int i = 2; i <= n; i++) {
		cur->next = (Node *)malloc(sizeof(*cur->next));
		cur = cur->next;
		cur->data = i;
	}
	cur->next = head;

	return head;
}


void josephus(link joselink, int n, int m)
{
	Node *cur = joselink;
	Node *del = NULL;

	if (m == 1) {
		while (cur->next != joselink) {
			del = cur;
			cur = cur->next;
			printf("%d 被删除\n", del->data);
			free(del);
		}
		printf("%d 被删除\n", cur->data);
		free(cur);
		return;
	}

	while (cur != del) {
		cur = move(cur, m - 1);
		del = cur->next;
		printf("%d 被删除\n", del->data);
		cur->next = del->next;
		cur = del->next;
		free(del);
	}
}

Node *move(Node *p, int i)
{
	if (i <= 0)
		return p;

	while (--i)
		p = p->next;

	return p;
}

void printlink(link head)
{
	Node * current = head;
	do {
		printf("%d  ", current->data);
		current = current->next;
	} while (current != head);
	printf("\n");
	return;
}

【测试结果】

 

【测试用例】采用下面的两组测试用例试试

输入

9 3

6 2

输出

3,6,9,4,8,5,2,7,1

2 4 6 3 1 5 

 

猜你喜欢

转载自blog.csdn.net/weixin_42442713/article/details/80959697