資料結構線性表的邏輯結構(四)單鏈表的基本操作的實現
一、 實驗目的
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又要出圈,如此類推,直到所有人都出圈,打印出圈的次序,其中n和m為輸入資料。
【演算法思想】
l 約瑟夫環滿足線性表的邏輯結構,相鄰兩項具有前驅和後繼的關係,故可以使用表結構表示。
l 通過尾插法建立不帶頭的單向迴圈連結串列(1,2,3……n);建立完畢後cur指標指向表尾;
l 計算出列人數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 |