註:本文的主要目的是為了記錄自己的學習過程,也方便與大家做交流。轉載請註明來自:
http://blog.csdn.net/ab198604
迴圈鏈表在單向鏈表及雙向鏈表的基礎之上作了一進步的概念延伸,迴圈鏈表讓鏈表操作變的更加靈活。這是因為,單向鏈表與雙向鏈表都具有鏈表的頭結點與尾結點,如果我們進一步思考:
1單向鏈表的尾結點的next鏈域指向其頭結點 --> 形成了單向迴圈鏈表
2 雙向鏈表的尾結點的next鏈域指向其頭結點,頭結點的prev鏈域指向其尾結點 --> 形成了雙向迴圈鏈表
無論其如何變化,概念如何擴充,只要抓住其"鏈表"的本質,就可以很容易的理解迴圈鏈表的要領。迴圈鏈表並沒有明顯的頭結點和尾結點,因為鏈表上的任何一個元素均可以看成是頭、尾結點,這主要取決於實際的應用。因為區分一個鏈表是否是迴圈鏈表的主要依據在於判別它是否含有完整的"環",即它不存在像單鏈表及雙向鏈表的真正意義上的尾結點了。
本文主要講闡的是單向迴圈鏈表,形象如下所示:
一、單向迴圈鏈表的結構及介面定義
/* * clist.h * Author: zhm * date: 2013-01-04 */#ifndef CLIST_H_#define CLIST_H_#include <stdlib.h>/* define a structure for the element */typedef struct CListElmt_{ void *data; struct CListElmt_ *next;}CListElmt;
上述結構體定義了單向迴圈鏈表的每個結點元素,一個data域及指向其後續結點的next指標域。
typedef struct CList_{ int size; int (*match)(const void *key1, const void *key2); void (*destroy)(void *data); CListElmt *head;}CList;
上面結構體定義了一個單向迴圈鏈表結構,用於表示一個完整的單向迴圈鏈表。
/* public interface */void clist_init(CList *list, void (*destroy)(void *data));void clist_destroy(CList *list);int clist_ins_next(CList *list, CListElmt *element, const void *data);int clist_rem_next(CList *list, CListElmt *element, void **data);#define clist_size(list) ((list)->size)#define clist_head(list) ((list)->head)#define clist_data(element) ((element)->data)#define clist_next(element) ((element)->next)#endif
上面代碼定義了單向迴圈鏈表的一些基本的操作,主要有如下操作:
1 單向迴圈鏈表的初始化
2 單向迴圈鏈表的銷毀
3 插入操作
4 刪除操作
5 通用的宏定義操作,用以快速擷取鏈表中的元素個數,頭結點,data域及next域。
二、單向迴圈鏈表的介面細節實現
1 單向迴圈鏈表的初始化
這步比較簡單,看下代碼就明白了。
/* * clist.c * Author: zhm * date: 2013-01-04 */#include <stdlib.h>#include <string.h>#include "clist.h"/* clist_init */void clist_init(CList *list, void (*destroy)(void *data)){ list->size = 0; list->head = NULL; list->destroy = destroy; return;}
主要對CList單向迴圈鏈表結構體的初始化設定。
2 單向迴圈鏈表的銷毀
/* clist_destroy */void clist_destroy(CList *list){ void *data; while( clist_size(list) > 0 ) { if( clist_rem_next(list, list->head, (void **)&data) == 0 && list->destroy != NULL ) { //destroy the memory where the data point to. list->destroy(data); } } memset(list, 0, sizeof(CList)); return;}
代碼實現的主要思想在於,迭代鏈表中的每個元素並將其從鏈表中移除,對每個元素中的data資料域調用自己的destroy函數進行記憶體資源的回收。
3 插入操作
代碼如下:
/* clist_ins_next */int clist_ins_next(CList *list, CListElmt *element, const void *data){ CListElmt *new_element; /* allocate the memory */ new_element = (CListElmt *)malloc(sizeof(CListElmt)); if( new_element == NULL ) return -1; /* insert the new element into the list */ new_element->data = (void *)data; if( clist_size(list) == 0 ) { //when the list is empty. be care! new_element->next = new_element; //here, new_element is the head and the tail element; list->head = new_element; } else { //when the list is not empty. new_element->next = element->next; element->next = new_element; } /* Adjust the size */ list->size++; return 0;}
上述代碼中需要注意的地方如下:
(1) 需要注意當鏈表為空白時的情況
(2)當插入第一個元素時,它是頭結點,且它的next指標域指向其自身,形成一個閉環。
4 刪除操作
/* clist_rem_next */int clist_rem_next(CList *list, CListElmt *element, void **data){ CListElmt *old_element; //keep the element which to be removed. /* Do not remove from an empty list */ if( clist_size(list) == 0 ) return -1; /* Remove the element from the list */ *data = (void *)element->next->data; if( element->next == element ) //be care the condition. { /* when the element is the last element */ old_element = element; list->head = NULL; } else { /* remove the other element */ old_element = element->next; element->next = element->next->next; if( old_element == clist_head(list) ) //if the head element will be removed. list->head = old_element->next; } free(old_element); list->size--; return 0;}
刪除操作也很簡單,整體思路為,擷取要刪除的元素的data資料域,調整被刪除元素前後結點元素的next指標域,需要注意如下情況:
(1) 由於是迴圈鏈表,當刪除的元素為頭結點時,需要維護鏈表的head指標域
(2) 當被刪除的元素是最後一個元素時,也需要維護head指標域
三、單向迴圈鏈表的簡單應用舉例
本例的應用代碼從與《雙向鏈表》那篇博文類似,代碼很簡單,主要是為了應用單向迴圈鏈表的介面及其宏定義操作,完成單向迴圈鏈表的初始化,插入元素操作,刪除元素操作和銷毀元素操作。在本例中,用一個結構體表示一個長方體,結構體維護了三個變數:長、寬、高,代碼如下:
/* * main.c * author:zhm * date: 2013-01-04 */#include <stdio.h>#include <string.h>#include <stdlib.h>#include "clist.h"typedef struct Cuboid_{ int length; int width; int height;}Cuboid;
這個結構體表示一個長方體的一些屬性,其主要作用僅僅為了讓單向鏈表中元素的data域來表徵一個長方體。可以參照《雙向鏈表》那篇博文的第三部分:http://blog.csdn.net/ab198604/article/details/8275020
在給每個元素的data域賦值之前,需要執行個體化一個長方體對象,代碼如下:
Cuboid *cube_instance(const int length, const int width, const int height){ Cuboid *cb_ptr; cb_ptr = (Cuboid *)malloc(sizeof(Cuboid)); if( cb_ptr == NULL ) return NULL; cb_ptr->length = length; cb_ptr->width = width; cb_ptr->height = height; return cb_ptr;}
下面是main函數內容,具體代碼不再說明了,非常簡單,看了就明白。
/* main */int main(int argc, char **argv){ int i; int ret = 0; CList clist; CListElmt *p = NULL; Cuboid *cb1_ptr, *cb2_ptr, *cb3_ptr, *cb4_ptr, *cb5_ptr; Cuboid *cb_ptr; //cb1_ptr ~ cb5_ptr are the data of the 5 elements. cb1_ptr = cube_instance(1,2,3); cb2_ptr = cube_instance(6,10,8); cb3_ptr = cube_instance(5,20,30); cb4_ptr = cube_instance(17,100,25); cb5_ptr = cube_instance(3,6,9); //init the clist clist_init(&clist, destroy); //insert the above 5 element /* cb1 -> cb1 */ ret = clist_ins_next(&clist, NULL, (void *)cb1_ptr); if( ret != 0 ) { printf("insert cb1 error\n"); return -1; } /* cb1 -> cb2 -> cb1 */ p = clist_head(&clist); ret = clist_ins_next(&clist, p, (void *)cb2_ptr); if( ret != 0 ) { printf("insert cb2 error\n"); return -1; } /* cb1 -> cb2 ->cb3 ->cb1*/ p = clist_next(p); ret = clist_ins_next(&clist, p, (void *)cb3_ptr); if( ret != 0 ) { printf("insert cb3 error\n"); return -1; } /* cb1 -> cb2 -> cb3 -> cb4 ->cb1 */ p = clist_next(p); ret = clist_ins_next(&clist, p, (void *)cb4_ptr); if( ret != 0 ) { printf("insert cb4 error\n"); return -1; } /* cb1 -> cb2 -> cb3 -> cb4 ->cb5 -> cb1 */ p = clist_next(p); ret = clist_ins_next(&clist, p, (void *)cb5_ptr); if( ret != 0 ) { printf("insert cb5 error\n"); return -1; } p = clist_head(&clist); //get the head element for(i = 0; i < clist_size(&clist) + 1; i++ ) { cb_ptr = (Cuboid *)clist_data(p); // get the element's data, every data is a Cuboid 's pointer. printf("i = %d: ",i); printf("length = %d, width = %d, height = %d\n", cb_ptr->length, cb_ptr->width, cb_ptr->height); p = clist_next(p); //pointer to next element. } //remove the head element clist_rem_next(&clist, p, (void **)&cb_ptr); printf("the removed element: length = %d, width = %d, height = %d\n", cb_ptr->length, cb_ptr->width, cb_ptr->height); destroy(cb_ptr); //free the memeory //destroy the circle list clist_destroy(&clist); printf("after destroy the list, its size = %d\n", clist_size(&clist)); return 0;}
上述代碼的主要邏輯思路為:先執行個體化5個長方體,然後初始化一個單向迴圈鏈表,緊接著將5個長方體分別作為data域插入到鏈表中,然後刪除其中某個元素,最後銷毀整個迴圈鏈表。經過編譯後,運行程式,結果如下所示:
註:有需要的朋友可以留下郵箱,有時間我會把完整的調試過的代碼通過郵箱發給大家。