資料結構體模版---迴圈單鏈表
阿新 • • 發佈:2019-01-30
#include <stdio.h> #include <stdlib.h> #include <stdbool.h> #include <assert.h> //#define DEBUG // 除錯插樁資訊巨集 ///*//////////////////////////////////////////////////////////////////////////// /// /// 帶頭結點的單鏈表結構體 /// ///*//////////////////////////////////////////////////////////////////////////// typedef int ElemType; // 自定義資料型別 //typedef struct CirLinkListNode* PCirLinkListNode; // 連結串列結點指標域 // 連結串列結點資料域 typedef struct CirLinkListNode { ElemType m_data; // 資料域 struct CirLinkListNode *m_next; // 指標域 }CirLinkListNode; // 帶頭結點的單項鍊表 typedef struct CirLinkList { CirLinkListNode *m_head; // 連結串列頭結點 CirLinkListNode *m_tail; // 迴圈連結串列尾部 int m_length; // 單鏈表資料結點個數指標域 // this->m_head == this->m_tail->m_next; // 如果只設尾結點要理解此等價關係 }CirLinkList; ///*//////////////////////////////////////////////////////////////////////////// /// /// 建立和初始化單鏈表 /// /// 開闢一個單鏈表資料結構,並初始化頭結點,然後將建立好的單鏈表指標返回 /// CirLinkList* CreatCirLinkList(void) /// /// 初始化單鏈表 /// void InitCirLinkList(CirLinkList *list) ///*/////////////////////////////////////////////////////////////////////////// /** CirLinkList* CreatCirLinkList(void) 引數 list : 指向一個連結串列指標,此處傳入表頭地址 返回值 若成功返回建立好的單鏈表的指標 功能 開闢一個單鏈表資料結構,並初始化頭結點,然後將建立好的單鏈表指標返回 注意 使用CreateCirLinkList建立的單鏈表,需要用DestroyCirLinkList來銷燬 以免發生記憶體洩漏 */ CirLinkList* CreateCirLinkList(void) { CirLinkList *list = NULL; if((list = (CirLinkList *)malloc(sizeof(CirLinkList))) == NULL) // 開闢單鏈表的空間 { // 開闢失敗 fprintf(stderr, "not enough memory when CREATE LIST...\n"); exit(EXIT_FAILURE); } InitCirLinkList(list); // 初始化單鏈表 return list; } /** void InitCirLinkList(CirLinkList *list) 引數 list : 指向一個連結串列指標,此處傳入表頭地址 返回值 無 功能 初始化單鏈表, 執行以下操作 ①開闢頭結點的空間 ②進行必要的初始化[頭結點的初始化和單鏈表結點數目的初始化] 注意 使用InitCirLinkList初始化的單鏈表(初始化時malloc了頭結點m_head的空間) 而使用用FinitCirLinkList來進行後處理(後處理時free了頭結點的m_head空間) 以免發生記憶體洩漏 */ void InitCirLinkList(CirLinkList *list) { if((list->m_head = malloc(sizeof(CirLinkListNode))) == NULL) // 為頭結點開闢空間 { // 開闢失敗 fprintf(stderr, "not enough memory when MALLOC HEAD..."); exit(EXIT_FAILURE); } // 初始化頭結點資訊 list->m_head->m_next = list->m_head; // 初始化只有頭結點[空迴圈連結串列的特徵] list->m_head->m_data = 0; // 資料元素個數為0 list->m_length = 0; // 資料元素個數為0 list->m_tail = list->m_head; // 尾指標指向頭結點 } ///*//////////////////////////////////////////////////////////////////////////// /// /// 銷燬以及後處理單鏈表 /// /// 銷燬用CreateCirLinkList建立的單鏈表 /// void DestroyCirLinkList(CirLinkList *list) /// /// 後處理單鏈表, /// void FinitCirLinkList(CirLinkList *list) /// /// 清空單鏈表中的所有元素 /// void ClearCirLinkList(CirLinkList *list) ///*//////////////////////////////////////////////////////////////////////////// /** void DestroyCirLinkList(CirLinkList *list) 引數 list : 指向一個連結串列指標,此處傳入表頭地址 返回值 無 功能 銷燬用CreateCirLinkList建立的單鏈表,執行以下操作 ①清空單鏈表 ②釋放頭結點 ③釋放單鏈表 注意 使用CreateCirLinkList建立的單鏈表,需要用DestroyCirLinkList來銷燬 以免發生記憶體洩漏 */ CirLinkList* DestroyCirLinkList(CirLinkList *list) { ClearCirLinkList(list); // 清空連結串列 FinitCirLinkList(list); // 銷燬頭結點 if(list != NULL) // 銷燬連結串列的空間 { free(list); list = NULL; } } /** void FinitCirLinkList(CirLinkList *list) 引數 list : 指向一個連結串列指標,此處傳入表頭地址 返回值 無 功能 後處理單鏈表, 執行以下操作 ①開闢頭結點的空間 ②進行必要的初始化[頭結點的初始化和單鏈表結點數目的初始化] 注意 使用InitCirLinkList初始化的單鏈表(初始化時malloc了頭結點m_head的空間) 而使用用FinitCirLinkList來進行後處理(後處理時free了頭結點的m_head空間) 以免發生記憶體洩漏 */ void FinitCirLinkList(CirLinkList *list) { assert(list->m_head->m_next == list->m_head); // 後處理指標針對空連結串列 // assert(list->m_length == 0); if(list->m_head != NULL) // 如果此時頭結點空間未被銷燬 { free(list->m_head); list->m_head = NULL; list->m_length = -1; // 未經初始化的單鏈表元素個數記為-1 } } /** void ClearCirLinkList(CirLinkList *list) 引數 list : 指向一個連結串列指標,此處傳入表頭地址 返回值 無 功能 清空單鏈表中的所有元素 */ void ClearCirLinkList(CirLinkList *list) { while(list->m_head->m_next != list->m_head) { DeleteNode(list, 0); } } ///*//////////////////////////////////////////////////////////////////////////// /// /// 查詢函式 /// /// 查詢到連結串列list中第position個結點 /// CirLinkListNode* FindPosNode(CirLinkList *list, int position) /// /// 在連結串列list中找到currNode的前一個結點 /// CirLinkListNode *FindPrevNode(CirLinkList *list, CirLinkListNode *currNode) /// /// 判斷結點node指向的區域是不是連結串列中的結點 /// int IsNodeInList(CirLinkList *list, CirLinkListNode *node) /// /// 找到資料域為data的結點首次出現的位置並返回結點資訊 /// CirLinkListNode* FindDataNode(CirLinkList *list, ElemType data, int *position) ///*//////////////////////////////////////////////////////////////////////////// /** CirLinkListNode* FindPosNode(CirLinkList *list, int position) 引數 list : 指向一個連結串列指標,此處傳入表頭地址 positon : 帶查詢的連結串列指標的位置 返回值 若成功返回指向待查詢結點的指標 若失敗返回NULL 功能 該函式的功能是: 查詢到連結串列list中第position個結點 */ CirLinkListNode* FindPosNode(CirLinkList *list, int position) { assert(list != NULL); // 連結串列不能為空 assert(position >= -1 && position < list->m_length); // 插入的w位置只能在[-1~length] CirLinkListNode *pNode = list->m_head->m_next; // CirLinkListNode *pNode = list->m_tail->m_next; // 當迴圈連結串列只設尾指標時採用此程式碼 if(position == -1) // -1表示尋找頭指標的前驅 { return list->m_head; // 直接返回前驅 } int pos = 0; while(pNode != list->m_head && pos < position) // 遍歷單鏈表,找到第position個結點的位置 { pNode = pNode->m_next; pos++; } if(pos < position) // 如果找到連結串列尾部還沒有找到 { return NULL; } else { #ifdef DEBUG printf("Find the %d point SUCCESS...[%p]\n", position, pNode); #endif // DEBUG return pNode; } } /** CirLinkListNode *FindPrevNode(CirLinkList *list, CirLinkListNode *currNode); 引數 list : 指向一個連結串列指標,此處傳入表頭地址 currNode : 待查詢的連結串列指標的位置 返回值 若成功返回指向待查詢結點的指標 若失敗返回NULL 功能 在連結串列list中找到currNode的前一個結點 */ CirLinkListNode *FindPrevNode(CirLinkList *list, CirLinkListNode *currNode) { assert(list != NULL); assert(currNode != NULL); CirLinkListNode *pNode = list->m_head; // CirLinkListNode *pNode = list->m_tail->m_next; // 當迴圈連結串列只設尾指標時採用此程式碼 while(pNode->m_next != list->m_head && pNode->m_next != currNode) { pNode = pNode->m_next; } if(pNode->m_next == currNode) // 查詢成功 { return pNode; } else // 查詢失敗 { return NULL; } } /** int IsNodeInList(CirLinkList *list, CirLinkListNode *node) 引數 list : 指向一個連結串列指標,此處傳入表頭地址 node : 指向待查詢的結點的指標 返回值 若成功 返回結點node在連結串列中的位置 若失敗 返回-1 功能 判斷結點node指向的區域是不是連結串列中的結點 */ int IsNodeInList(CirLinkList *list, CirLinkListNode *node) { assert(list != NULL); // 連結串列不能為空 assert(Node != NULL); // 待查詢的指標不能為空 CirLinkListNode *pNode = list->m_head->m_next; // CirLinkListNode *pNode = list->m_tail->m_next; // 當迴圈連結串列只設尾指標時採用此程式碼 int pos = 0; while(pNode != list->m_head && pNode != node) // 遍歷單鏈表,找到第position個結點的位置 { pNode = pNode->m_next; pos++; } if(pNode != node) { // 查詢成功 return -1; } else { // 查詢失敗 #ifdef DEBUG printf("Find the [%p] point in the first %d pointer of the list...\n", fNode, pos); #endif // DEBUG return pos; } } /** CirLinkListNode* FindDataNode(CirLinkList *list, ElemType data, int *position 引數 list : 指向一個連結串列指標,此處傳入表頭地址 data : 待查詢的結點的資料資訊 返回值 若成功 返回結點node在連結串列中的位置 若失敗 返回-1 功能 找到資料域為data的結點首次出現的位置並返回結點資訊 */ CirLinkListNode* FindDataNode(CirLinkList *list, ElemType data, int *position) { CirLinkListNode *node = list->m_head->m_next; // CirLinkListNode *pNode = list->m_tail->m_next; // 當迴圈連結串列只設尾指標時採用此程式碼 int pos = 0; while(node != list->m_head && node->m_data != data) { node = node->m_next; pos++; } *position = pos; // 將出現的位置傳遞回去 return node; // 返回結點的資訊 } ///*//////////////////////////////////////////////////////////////////////////// /// /// 插入函式 /// /// 將資料data插入連結串列的prevNode結點的下一個位置個位置 /// CirLinkListNode *AddNode(CirLinkList *list, CirLinkListNode *prevNode, ElemType data) /// /// 將資料data插入連結串列的第position個位置 /// CirLinkListNode *InsertNode(CirLinkList *list, int position, ElemType data) ///*//////////////////////////////////////////////////////////////////////////// /** CirLinkListNode* AddNode(CirLinkList *list, CirLinkListNode *prevNode, ElemType data); 引數 list : 指向一個連結串列指標,此處傳入表頭地址 prevNode : 待插入位置的前一個結點 data : 待插入結點的資料 返回值 無 功能 該函式的功能是: 將資料data插入連結串列的prevNode結點的下一個位置個位置 */ CirLinkListNode* AddNode(CirLinkList *list, CirLinkListNode *prevNode, ElemType data) { assert(prevNode != NULL); // 插入點不能是空指標 CirLinkListNode *newNode = NULL; if((newNode = (CirLinkListNode *)malloc(sizeof(CirLinkListNode))) == NULL) // 為新結點開闢空間 { // 開闢新結點失敗 fprintf(stderr, "not enough memeory\n"); exit(EXIT_FAILURE); } else { // 開闢新結點成功 newNode->m_data = data; newNode->m_next = NULL; } // 將指標newNode連線在pNode的後面 newNode->m_next = prevNode->m_next; prevNode->m_next = newNode; list->m_length++; // 結點數目增加一個 list->m_head->m_data++; // 頭結點的資料域同樣儲存著結點總數 //} #ifdef DEBUG printf("The new node is inserted after point pointer[%p]\n", pNode); #endif // DEBUG return newNode; } /** void InsertNode(CirLinkList *list, int position, ElemType data) 引數 list : 指向一個連結串列指標,此處傳入表頭地址 positon : 待插入結點的位置 data : 待插入結點的資料 返回值 無 功能 該函式的功能是: 將資料data插入連結串列的第position個位置 */ CirLinkListNode* InsertNode(CirLinkList *list, int position, ElemType data) { assert(list != NULL); assert(position >=0 && position < list->m_length + 1); CirLinkListNode *prevNode = FindPosNode(list, position - 1); // 找到待插入位置的前一個結點 CirLinkListNode *newNode = NULL; // 下面呼叫InsertPointNode直接將結點插入到pNode結點後面 if((newNode = AddNode(list, prevNode, data)) != NULL) // 將新的結點插入到待插入前一個指標的後面 { // 插入成功 return newNode; // 返回新插入的結點 #ifdef DEBUG printf("Insert the value %d into list at position %d...\n", data, position); #endif // DEBUG } else { return NULL; // 插入失敗返回NULL } // // 以可以使用下面的程式碼 // if((newNode = (CirLinkListNode *)malloc(sizeof(CirLinkListNode))) == NULL) // 為新結點開闢空間 // { // 開闢新結點失敗 // fprintf(stderr, "not enough memeory\n"); // exit(EXIT_FAILURE); // } // else // { // 開闢新結點成功 // newNode->m_data = data; // newNode->m_next = NULL; // // // 將指標newNode連線在pNode的後面 // newNode->m_next = prevNode->m_next; // prevNode->m_next = newNode; // // list->m_length++; // 結點數目增加一個 // list->m_head->m_data++; // 頭結點的資料域同樣儲存著結點總數 // } } ///*//////////////////////////////////////////////////////////////////////////// /// /// 刪除函式 /// /// 刪除連結串列list中prevNode結點之後的指標個指標 /// void DeleteNode(CirLinkList *list, int position) /// /// 刪除連結串列list中prevNode結點之後的指標個指標 /// ElemType SubNode(CirLinkList *list, CirLinkListNode *prevNode) /// /// 刪除連結串列list中prevNode結點之後的指標個指標 /// ElemType DeleteCurrNode(CirLinkList *list, CirLinkListNode *currNode) ///*//////////////////////////////////////////////////////////////////////////// /** void DeleteNode(CirLinkList *list, int position) 引數 list : 指向一個連結串列指標,此處傳入表頭地址 positon : 待刪除結點的位置 返回值 返回待刪除結點的資料域 功能 將單鏈表的第position個結點刪除 */ ElemType DeleteNode(CirLinkList *list, int position) { assert(list != NULL); assert(position >=0 && position < list->m_length); CirLinkListNode *prevNode = FindPosNode(list, position - 1); // 找到第position - 1個結點 // 刪除pNode的後一個結點 CirLinkListNode *delNode = prevNode->m_next; ElemType delElem = delNode->m_data; prevNode->m_next = delNode->m_next; free(delNode); list->m_length--; // 結點數目減少一個 list->m_head->m_data--; // 頭結點的資料域同樣儲存著結點總數 return delElem; } /** ElemType SubNode(CirLinkList *list, CirLinkListNode *prevNode) 引數 list : 指向一個連結串列指標,此處傳入表頭地址 positon : 待刪除結點的位置 返回值 返回待刪除結點的資料域 功能 刪除連結串列list中prevNode結點之後的指標個指標 */ ElemType SubNode(CirLinkList *list, CirLinkListNode *prevNode) { assert(list != NULL); // 連結串列不能為空 assert(prevNode != NULL); // 待刪除結點的前一個位置不能為空 assert(IsNodeInList(list, prevNode) != -1); // 待刪除位置的前一個結點必須在連結串列中 // 刪除pNode的後一個結點 CirLinkListNode *delNode = prevNode->m_next; ElemType delElem = delNode->m_data; prevNode->m_next = delNode->m_next; free(delNode); list->m_length--; // 結點數目減少一個 list->m_head->m_data--; // 頭結點的資料域同樣儲存著結點總數 return delElem; } /** ElemType DeleteCurrNode(CirLinkList *list, CirLinkListNode *currNode); 引數 list : 指向一個連結串列指標,此處傳入表頭地址 positon : 待刪除結點的位置 返回值 返回待刪除結點的資料域 功能 刪除連結串列list中prevNode結點之後的指標個指標 */ ElemType DeleteCurrNode(CirLinkList *list, CirLinkListNode *currNode) { assert(list != NULL); // 連結串列不能為空 assert(currNode != NULL); // 待刪除結點的前一個位置不能為空 assert(IsNodeInList(list, currNode) != -1); // 待刪除的結點必須在連結串列中 ElemType delElem = -1; // 待刪除結點的資料域 CirLinkListNode *delNode = NULL; // 指向將要刪除的結點的指標 // if(list->m_length == 0) // { // // return -1; // } // printf("length = %d\n", list->m_length); if(currNode->m_next != list->m_head) // 如果待刪除結點不是最後一個結點 { // 將currNode的後一個結點delNode作為刪除結點, delNode = currNode->m_next; currNode->m_next = delNode->m_next; //從連結串列中刪除delNode // 並將delNode的資料域儲存到delNode中 delElem = currNode->m_data; // delElem儲存currNode的資料域 currNode->m_data = delNode->m_data; // 真正刪除的結點其實是currNode下一個結點, 因此用currNode儲存下一個結點的資料域 } else // 否則待刪除結點是最後一個結點 { // 直接將最後一個結點刪除即可, 應該把其前一個結點的指標域賦值為空 delNode = currNode; // 下面應該將currnNode的前一個結點的指標域賦值為空[時間複雜度O(n)] CirLinkListNode *prevNode = FindPrevNode(list, currNode); prevNode->m_next = list->m_head; /// BUG1 最後一個結點的後一個結點 } free(delNode); list->m_length--; // 結點數目減少一個 list->m_head->m_data--; // 頭結點的資料域同樣儲存著結點總數 return delElem; } ///*//////////////////////////////////////////////////////////////////////////// /// /// 其他函式 /// /// 顯示單鏈表的資訊 /// void ShowList(CirLinkList *list /// /// 刪除連結串列list中prevNode結點之後的指標個指標 /// void SetNode(CirLinkList *list, int position, ElemType data) /// /// 獲取單鏈表list第position個結點的資料域 /// ElemType GetNode(CirLinkList *list, int position) /// /// 獲取單鏈表list的長度[即元素個數] /// int LengthCirLinkList(CirLinkList *list) /// /// 判斷當前連結串列是否是空連結串列 /// bool IsEmptyCirLinkList(CirLinkList *list) ///*//////////////////////////////////////////////////////////////////////////// /** void ShowCirLinkList(CirLinkList *list) 引數 list : 指向一個連結串列指標,此處傳入表頭地址 返回值 無 功能 顯示單鏈表的資訊 */ void ShowList(CirLinkList *list) { // assert(list->m_head != NULL) if(list->m_head == NULL) // 單鏈表可能沒有被初始化 { fprintf(stderr, "you can't SHOW the list without the list INITLINKLIST...\n"); return ; } printf("there are %d data in list\n", list->m_length); if(list->m_length == 0) { return ; } CirLinkListNode *pNode = list->m_head->m_next; // 從頭指標開始遍歷 while(pNode != list->m_head) //開始遍歷單鏈表 { printf("%d ", pNode->m_data); pNode = pNode->m_next; } printf("\n"); // ElemType data; // for(int pos = 0; pos < list->m_length; pos++) // { // data = GetNode(list, pos); // printf("%d ", data); // } // printf("\n"); } /** void SetNode(CirLinkList *list, int position, ElemType data) 引數 list : 指向一個連結串列指標,此處傳入表頭地址 positon : 待修改的結點的資料 data : 待更正的新資料域 返回值 無 功能 修改單鏈表list第position個結點的資料域為data */ void SetNode(CirLinkList *list, int position, ElemType data) { CirLinkListNode *pNode = FindPosNode(list, position); // 找到單鏈表的第position個結點 pNode->m_data = data; } /** ElemType GetNode(CirLinkList *list, int position 引數 list : 指向一個連結串列指標,此處傳入表頭地址 positon : 待查詢的結點的位置 返回值 獲取到的結點資料 功能 獲取單鏈表list第position個結點的資料域 */ ElemType GetNode(CirLinkList *list, int position) { CirLinkListNode *pNode = FindPosNode(list, position); // 找到單鏈表的第position個結點 return pNode->m_data; } /** int LengthCirLinkList(CirLinkList *list) 引數 list : 指向一個連結串列指標,此處傳入表頭地址 返回值 單鏈表的長度 功能 獲取單鏈表的長度 */ int LengthCirLinkList(CirLinkList *list) { return list->m_length; } /** bool IsEmptyCirLinkList(CirLinkList *list) 引數 list : 指向一個連結串列指標,此處傳入表頭地址 返回值 如果單鏈表是空表,返回true 否則返回false 功能 獲取單鏈表的長度 */ bool IsEmptyCirLinkList(CirLinkList *list) { return (list->m_head->m_next == list->m_head); // return (list->m_tail == this->m_head); } /** CirLinkListNode* GetFisrtNode(CirLinkList *list) 引數 list : 指向一個連結串列指標,此處傳入表頭地址 返回值 返回頭指標 功能 獲取迴圈連結串列的頭指標 */ CirLinkListNode* GetFisrtNode(CirLinkList *list) { return list->m_head->m_next; // return list->m_tail->m_next->m_next; // 只設尾指標的時候使用此程式碼 } /** CirLinkListNode* GetHeadNode(CirLinkList *list) 引數 list : 指向一個連結串列指標,此處傳入表頭地址 返回值 返回頭結點的地址 功能 獲取指向頭結點的指標 */ CirLinkListNode* GetHeadNode(CirLinkList *list) { return list->m_head; // return list->m_tail->m_next; // 只設尾指標的時候使用此程式碼 } #define LIST_SIZE 7 // 主函式 int main(void) { int pos; printf("TEST 1...\n"); CirLinkList *plist = CreateCirLinkList( ); // 建立單鏈表 for(int pos = 0; pos < LIST_SIZE; pos++) // 迴圈向單鏈表中插入資料 { InsertNode(plist, pos, pos + 1); } ShowList(plist); // 插入結束後顯示單鏈表的資訊 DeleteNode(plist, 0); // 刪除第一個元素 ShowList(plist); DeleteNode(plist, 1); // 刪除第二個元素 ShowList(plist); ClearCirLinkList(plist); // 將單鏈表清空 ShowList(plist); DestroyCirLinkList(plist); // 將單鏈表銷燬 plist = NULL; printf("\n\nTEST 2...\n"); CirLinkList list; InitCirLinkList(&list); // 初始化單鏈表 for(int pos = 0; pos < LIST_SIZE; pos++) // 訓話向單鏈表中插入資料 { InsertNode(&list, pos, pos + 1); } ShowList(&list); // 顯示單鏈表 ClearCirLinkList(&list); // 清空單鏈表 // FinitLinkList(&list); // ERROR== list->m_head->m_next == NULL ShowList(&list); printf("\n\nTEST 3...\n"); CirLinkListNode *prevNode = list.m_head; CirLinkListNode *addNode = NULL; for(int pos = 0; pos < LIST_SIZE; pos++) { if((addNode = AddNode(&list, prevNode, pos + 1)) != NULL) { prevNode = addNode; } } ShowList(&list); while(IsEmptyCirLinkList(&list) != true) // 迴圈刪除單鏈表中的資料 { //printf("%p == %p\n", list.m_head->m_next, list.m_head); DeleteCurrNode(&list, list.m_head->m_next); } ShowList(&list); // 顯示單鏈表 return EXIT_SUCCESS; }