1. 程式人生 > >資料結構線性表的邏輯結構(四)單鏈表的基本操作的實現

資料結構線性表的邏輯結構(四)單鏈表的基本操作的實現

一、 實驗目的

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)向有序連結串列的插入元素,插入後連結串列仍有序;

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