c語言實現單鏈表資料結構及其基本操作
阿新 • • 發佈:2019-02-13
帶頭結點單鏈表。分三個檔案,分別為標頭檔案,測試檔案,原始碼檔案。
給出了單鏈表的部分操作,每個操作都有對應的註釋說明該函式功能。
test.c 檔案中,對輸入資料是否合法進行了檢測,但是並未實現輸入資料不合法時應該採取的措施。
測試檔案 通過一個選單來測試單鏈表功能。
list.h:
/* 單鏈表型別的標頭檔案 */ #ifndef LIST_H_ #define LIST_H_ struct Node; //先宣告,就可以在下面使用,而不必因定義在後面而產生衝突 /* 一般型別定義 */ typedef int ElementType; //抽象資料型別 typedef struct Node * PtrToNode; typedef PtrToNode List; typedef PtrToNode Position; /* 特定於程式的宣告 */ struct Node { ElementType element; Position next; }; /* 函式原型 */ List MakeEmpty ( List l ); int IsEmpty ( List L ); int IsLast ( Position p, List l ); Position Find ( ElementType x, List l ); void Delete ( ElementType x, List l ); Position FindPrevious ( ElementType x, List l ); void Insert ( ElementType x, List l, Position p ); void DeleteList ( List l ); Position Header ( List l ); Position First ( List l ); Position Last ( List l ); Position Advance ( Position p ); ElementType Retrieve ( Position p ); void PushFront ( ElementType x, List l ); void PopFront ( List l ); void PushBack ( ElementType x, List l ); void PopBack ( List l ); #endif
test.c:
list.c:/* 單鏈表 */ #include <stdio.h> #include <windows.h> #include "list.h" struct Node; void Menu ( void ); //選單 void PrintList ( List l ); //列印單鏈表 int main ( void ) { List l; int choice; ElementType x; l = NULL; choice = 0; x = 0; Menu ( ); scanf ( "%d", &choice ); while ( 0 != choice ) { switch ( choice ) { case 1: if ( NULL == ( l = MakeEmpty ( l ) ) ) //如若不接受返回值,則外面 l 未改變。因為 MakeEmpty 中形參 不影響 實參值,開闢的空間反而找不到了!重點!! { printf ( "初始化失敗!\n" ); } else { printf ( "初始化成功!\n" ); } break; case 2: if ( 1 == IsEmpty ( l ) ) { printf ( "單鏈表為空!\n" ); } else { printf ( "單鏈表不為空!\n" ); } break; case 3: { Position p; p = Last ( l ); if ( NULL == p ) { printf ( "0.連結串列中沒有結點,當前位置是連結串列末尾!\n" ); break; } if ( 1 == IsLast ( p , l ) ) { printf ( "1.當前位置是連結串列末尾!\n" ); } if ( 0 == IsLast ( l, l ) ) { printf ( "2.當前位置不是連結串列末尾!\n" ); } break; } case 4: printf ( "請輸入需要查詢的元素: " ); if ( 1 == scanf ( "%d", &x ) ) { Position tmp; tmp = Find ( x ,l ); if ( NULL == tmp ) { printf ( "元素%d不存在!\n", x ); } else { printf ( "找到了元素%d!\n", ( Retrieve ( tmp ) ) ); } } else { printf ( "輸入資料不合法,查詢失敗!\n" ); } break; case 5: printf ( "請輸入需要刪除的元素: " ); if ( 1 == scanf ( "%d", &x ) ) { Delete ( x, l ); printf ( "刪除成功!\n" ); } else { printf ( "輸入資料不合法,刪除失敗!\n" ); } break; case 6: printf ( "請輸入需要插入的元素: " );//此處實現為頭插 if ( 1 == scanf ( "%d", &x ) ) { Insert ( x, l, l ); printf ( "插入成功!\n" ); } else { printf ( "輸入資料不合法,插入失敗!\n" ); } break; case 7: DeleteList ( l ); printf ( "刪除單鏈表成功!\n" ); l = NULL; break; case 8: printf ( "請輸入需要插入的元素: " ); if ( 1 == scanf ( "%d", &x ) ) { PushFront ( x, l ); printf ( "插入成功!\n" ); } else { printf ( "輸入資料不合法,插入失敗!\n" ); } break; case 9: PopFront ( l ); printf ( "刪除成功!\n" ); break; case 10: printf ( "請輸入需要插入的元素: " ); if ( 1 == scanf ( "%d", &x ) ) { PushBack ( x, l ); printf ( "插入成功!\n" ); } else { printf ( "輸入資料不合法,插入失敗!\n" ); } break; case 11: PopBack ( l ); printf ( "刪除成功!\n" ); break; case 12: PrintList ( l ); break; default: printf ( "放棄吧!\n" ); break; } Menu ( ); scanf ( "%d", &choice ); } system ( "pause" ); return 0; } void Menu ( void ) //選單 { printf ( "***************************************************************\n" ); printf ( " 1.初始化 2.是否為空\n" ); printf ( " 3.當前位置是否是連結串列末尾 4.查詢元素\n" ); printf ( " 5.刪除元素 6.插入元素\n" ); printf ( " 7.刪除連結串列 8.頭插資料\n" ); printf ( " 9.頭刪資料 10.尾插資料\n" ); printf ( " 11.尾刪資料 12.列印\n" ); printf ( "***************************************************************\n" ); } void PrintList ( List l ) //列印單鏈表 { Position p; p = l -> next; while ( NULL != p ) { printf ( "%d -> ", ( p -> element ) ); p = p -> next; } printf ( "\n" ); }
/* list.c -- 支援單鏈表操作的函式 */ #include <stdio.h> #include <stdlib.h> #include "list.h" /* 介面函式 */ /* 把單鏈表初始化為空單鏈表 */ //必須先初始化再使用連結串列(未初始化連結串列下列函式可能出錯.) List MakeEmpty ( List l ) //傳進來的 l 是一個指向單鏈表這種資料結構的指標,但不一定已經開闢了這種結構的空間使指標去指向 { if ( NULL != l ) //結合 收藏夾 -> c語言, 理解為什麼將指標free( )後需置NULL { DeleteList ( l ); } l = ( List )malloc ( sizeof ( struct Node ) );//malloc 完一定要檢查是否 開闢空間成功! if ( NULL == l ) { printf ( "開闢空間失敗!" ); return NULL; } l -> element = 0; l -> next = NULL; return l; } /* 檢查單鏈表是否為空 */ int IsEmpty ( List l ) { return ( NULL == l -> next ); } /* 檢查當前位置是否是連結串列的末尾 */ //將只有頭結點情況視為連結串列末尾 int IsLast ( Position p, List l ) { return ( NULL == p -> next ); } /* 在單鏈表中查詢元素x */ Position Find ( ElementType x, List l ) { Position p; p = l -> next; //宣告和定義分開. while ( ( NULL != p ) && ( x != p -> element ) ) { p = p -> next; } return p; } /* 在單鏈表中刪除元素x */ //元素不存在,函式不做任何處理 void Delete ( ElementType x, List l ) //使用到了 FindPrevious( )函式 { Position p; Position tmpCell; p = FindPrevious ( x, l ); if ( !( IsLast ( p, l ) ) ) { tmpCell = p -> next; p -> next = p -> next -> next; //注意!是賦值給p的next域,而不是p! free ( tmpCell ); //free( )掉指標後,最好將指標置NULL. tmpCell = NULL; } } /* 在單鏈表中找出元素x的前驅位置 */ //輔助Find函式實現,不提供測試程式碼(若Find函式執行成功,則此函式正確) Position FindPrevious ( ElementType x, List l ) { Position p; p = l; //在外面申請頭節點,所以l不可能為空 while ( ( NULL != p -> next ) && ( x != p -> next -> element ) ) { p = p -> next; } return p; //返回的不是next域!也永遠不可能是! } /* 在單鏈表中插入元素x */ /* 插入到當前位置的後面 */ void Insert ( ElementType x, List l, Position p ) { Position tmpCell; //只是建立了一個指標,還需要malloc出結構體的大小.5. tmpCell = ( List )malloc ( sizeof ( struct Node ) );//struct Node 只是一個型別, sizeof( sturct Node )才是它的大小. 才能使用malloc. if ( NULL == tmpCell ) //因為系統資源問題,所以malloc申請空間可能會失敗,養成malloc後檢查申請是否成功的習慣6. { printf ( "申請空間失敗" ); } else { tmpCell -> element = x; tmpCell -> next = p -> next; p -> next = tmpCell; } } /* 刪除單鏈表 */ void DeleteList ( List l ) //刪除單鏈表後再使用單鏈表必須先初始化. { Position p; Position tmp; p = l -> next; //l 的next 域存放地址給p free l 後,這個p所儲存地址依然在和 l 無關。 lnext域只是之前用來儲存它 free ( l ); //此處通過指標l釋放l指向的頭結點,而不是釋放指標l,指標l依然存在. l = NULL; //記得,你在此處改變l的值,外面l值依舊沒改變。 所以,這種錯誤不要再犯了! while ( NULL != p ) { tmp = p -> next; free ( p ); //此處釋放的是p,不是p的next域(不要釋放下一個節點) p = tmp; } } /* 返回連結串列頭結點 */ //不提供測試用例 Position Header ( List l ) { return l; } /* 返回連結串列第一個結點 */ //不提供測試用例 Position First ( List l ) { return ( l -> next ); } /* 返回連結串列的最後一個結點 */ //不提供測試用例 Position Last ( List l ) { Position p = l; if ( NULL == l -> next ) //將頭節點後那個結點視為第一個結點,所以只有頭結點時,視為沒有結點. { return NULL; } else { while ( NULL != p -> next ) { p = p -> next; } } return p; } /* 返回位置p的下一個位置 */ //不提供測試用例 Position Advance ( Position p ) { return ( p -> next ); } /* 返回位置p處的元素 */ //不提供測試用例 ElementType Retrieve ( Position p ) { return ( p -> element ); } /////* 在單鏈表頭部插入元素x */ //版本1 //void PushFront ( ElementType x, List l ) //使用到了 Header( )函式 與 Insert( )函式 //{ // Insert ( x, l, Header ( l ) ); //} /* 在單鏈表頭部插入元素x */ //版本2 void PushFront ( ElementType x, List l ) { Position tmp; //不需要再定義一個多餘的變數儲存 l -> next. tmp = ( List )malloc ( sizeof ( struct Node ) ); if ( NULL == tmp ) { printf ( "申請空間失敗,插入不成功!" );//抽象資料型別中別加多餘東西( 換行符 ). } else { tmp -> element = x; tmp -> next = l -> next; l -> next = tmp; } } /* 刪除單鏈表第一個結點 */ void PopFront ( List l ) { if ( NULL == l -> next ) { ; } else { Position tmp; tmp = l -> next; //注意此處釋放 l的next域中值才是釋放了第一個結點,而不是釋放第一個結點next域(第二個結點). l -> next = tmp -> next; free ( tmp ); //釋放的next域只是下一個結點,不是這個結點,也不是釋放了next. tmp = NULL; } } /* 在單鏈表尾部插入元素x */ void PushBack ( ElementType x, List l ) //使用 Header( )函式 { Position p; Position tmp; p = Header ( l ); while ( NULL != p -> next ) { p = p -> next; } tmp = ( List )malloc ( sizeof ( struct Node ) ); if ( NULL == tmp ) { printf ( "申請空間失敗,插入失敗" ); } else { tmp -> element = x; tmp -> next = NULL; p -> next = tmp; } } /* 刪除單鏈表最後一個結點 */ void PopBack ( List l ) { if ( NULL == l -> next ) { ; } else { Position p; p = l; while ( NULL != p -> next -> next ) { p = p -> next; } free ( p -> next ); p -> next = NULL; } }