使用指標實現的線性表——鏈表

來源:互聯網
上載者:User

前一小節介紹使用數組實現了線性表,這一小節使用指標來實現:

先看那12個函數:

#include <stdio.h>#include <malloc.h>typedef int ElemType;typedef struct LNode{//存放的資料ElemType data;//指向下個節點的指標LNode *next;}LNode,*LinkList;//初始化鏈表bool initList(LinkList *lst){*lst = (LinkList)malloc(sizeof(LNode));if(NULL == lst)return false;(*lst)->next = NULL;return true;}//刪除鏈表void deleteList(LinkList *lst){LinkList p = (*lst)->next;LinkList q;while(p){q = p->next;free(p);p = q;}free(*lst);*lst = NULL;}//清空鏈表void clearList(LinkList *lst){LinkList p = (*lst)->next;LinkList q;while(p){q = p->next;free(p);p = q;}(*lst)->next = NULL;}//判斷鏈表是否為空白bool is_empty(LinkList *lst){if(NULL == (*lst)->next){printf("the list is empty! \n");return true;}else{printf("the list is not empty! \n");return false;}}//遍曆鏈表並列印void printList(LinkList *lst){printf("list elements are: ");LinkList p = (*lst)->next;while(p){printf("%d",p->data);p = p->next;}printf("\n");}//計算鏈表中的元素個數int listLength(LinkList *lst){int cnt = 0;LinkList p = (*lst)->next;while(p){++cnt;p = p->next;}return cnt;}//在指定位置插入元素bool insertElem(LinkList *lst,int index,ElemType *e){int cnt = 0;LinkList p = (*lst);LinkList q = (LinkList)malloc(sizeof(LNode));while(p){if(index == cnt){//新插入節點的指標指向當前節點的指標指向的元素q->next = p->next;//當前節點的指標指向新插入的節點p->next = q;//存入資料q->data = *e;return true;}++cnt;p = p->next;}return false;}//刪除某個位置的元素bool deleteElem(LinkList *lst,int index,ElemType *e){int cnt = 0;LinkList p = (*lst)->next;//儲存當前節點的前一個節點LinkList q = *lst;while(p){if(index == cnt){//擷取即將刪除的節點的元素*e = p->data;//當前節點的前一個節點指向當前節點的後一個節點q->next = p->next;//釋放當前節點free(p);break;}p = p->next;q = q->next;++cnt;}return false;}//擷取某個元素的位置int locateElem(LinkList *lst,ElemType e){int index = 0;LinkList p = (*lst)->next;while(p){if(e == p->data)return index;++index;p = p->next;}return -1;}//擷取某個位置的元素bool getElem(LinkList *lst,int index,ElemType *e){int cnt = 0;LinkList p = (*lst)->next;while(p){if(index == cnt){*e = p->data;return true;}++cnt;p = p->next;}return false;}//擷取下一個元素bool nextElem(LinkList *lst,ElemType curr,ElemType *nxt){LinkList p = (*lst)->next;while(p){if(curr == p->data){*nxt = p->next->data;return true;}p = p->next;}return false;}//擷取前一個元素bool priorElem(LinkList *lst,ElemType curr,ElemType *pre){LinkList p = (*lst)->next;LinkList q = (*lst);while(p){if(curr == p->data){*pre = q->data;return true;}q = q->next;p = p->next;}return false;}void inputList(LinkList *lst,int n,int* arr){for(int i = n-1; i >= 0; --i){LinkList pnew = (LinkList)malloc(sizeof(LNode));pnew->data = arr[i];pnew->next = (*lst)->next;(*lst)->next = pnew;}}

 

其中最後一個並不是必須的,只為了輸入的時候方便。

對指標不太熟悉的朋友可能有點不明白參數是怎麼傳遞的,這裡通過一個具體的例子來說明一下。

void reset(int *x){*x = 0;}

 

在主函數中:

int a = 1;printf("before reset: %d\n",a);reset(&a);printf("after reset: %d\n",a);

這個程式相信大家都見過,但是我還是要仔細地說明一下編譯器是怎麼對待函數調用的:它會將實參複製一份給形參,然後對形參進行操作。所以,形參的改變是不會影響實參的,如果想改變主調函數中某個實參的值,必須將它的地址傳遞給被調函數—雖然這裡還是會發生實參複製給形參的情況,但是由於複製的是地址,所以被掉函數中,只要對地址進行解引,就能操作主調主調函數中的值了。
再來看我們的程式:

typedef struct LNode{ElemType data;LNode *next;}LNode,*LinkList;

定義了指向結構體的指標,當我們的操作並不需要修改指標本身時,我們大可以傳遞指標本身:

//判斷鏈表是否為空白bool is_empty(LinkList lst){if(NULL == (lst)->next){printf("the list is empty! \n");return true;}else{printf("the list is not empty! \n");return false;}}

但是如果我們的操作需要修改這個指標時,那麼我們只能給函數傳遞這個指標的地址,然後在函數中通過解引操作“*”來或得指標本身,然後在進行記憶體配置、插入、刪除等操作。所以我們的很多函數的型參都有LinkList *lst。而在主函數中,將這個指標的地址傳遞給函數,比如initList(&myList);
與“值”傳遞相比,傳遞指標看上去很麻煩,但是它有一個明顯的效率優勢:當直接傳遞值時,會複製一個值,如果複製的是一個複雜的結構體,那麼代價是非常大的;但如果複製的僅僅是一個指標,那麼只需要4個位元組(因為我們的電腦是32位的,所以地址也是32位,就是4個位元組)。
相比之下,如果使用C++中的“引用”概念就要方便的多:直接將實參與形參關聯起來,通過形參的改變,就能改變實參,而且並沒有任何“複製”發生。

與數組實現的線性表相比,鏈表的優勢在於可以方便的插入、刪除元素;但是對於隨機訪問某個元素,卻非常緩慢,必須要通過從頭開始遍曆整個鏈表開始,所以程式中總是出現

LinkList p = l->next;while(p){//具體操作p = p->next;}

 

的結構。總體上來說,二者各有利弊,如果的資料需要頻繁的插入、刪除,那麼選擇鏈表;如果需要頻繁的隨機範文,那麼選擇順序表。

其實鏈表還有另外幾種複雜的結構,比如:迴圈鏈表、雙向鏈表、雙向迴圈鏈表等等,由於基本的原理是相同的,這裡只對他們進行簡要的介紹。

迴圈鏈表很簡單,就是最後一個節點的指標next重新指向鏈表頭。它的好處在於不論你現在處於鏈表的什麼位置,總能遍曆到鏈表的所有元素。程式部分的改動也比較小,主要是在初始化時,將next指向鏈表頭,在遍曆鏈表的過程中,迴圈的停止條件變為:while(p != (*lst))。

雙向鏈表是在基本鏈表的基礎上,增加一個指向前一個節點的指標prior,在插入、刪除操作時,指標的指向需要仔細:

 

#include <stdio.h>#include <malloc.h>#define ElemType inttypedef struct DNode{ElemType data;DNode *next;DNode *prior;}DNode,*DLinkList;bool initDList(DLinkList* dlst){*dlst = (DLinkList)malloc(sizeof(DNode));if(*dlst == NULL)return false;(*dlst)->next = NULL;(*dlst)->prior = NULL;return true;}void deleteDList(DLinkList* dlst){DLinkList p = (*dlst)->next;DLinkList q;while(p){q = p->next;free(p);p = q;}free(*dlst);*dlst = NULL;}bool insertElem(DLinkList* dlst,int index,ElemType e){int cnt = 0;DLinkList p = *dlst;while(p){if(index == cnt){DLinkList pnew = (DLinkList)malloc(sizeof(DNode));pnew->data = e;pnew->next = p->next;pnew->prior = p;p->next = pnew;//注意:當第一次插入元素或者在最後一個位置插入元素時,p->next是空的,並沒有節點if(pnew->next!=NULL)pnew->next->prior = pnew;return true;}p = p->next;++cnt;}return false;}void deleteElem(DLinkList* dlst,int index,ElemType *e){int cnt = 0;DLinkList p = (*dlst)->next;while(p){if(index == cnt){*e = p->data;p->prior->next = p->next;//如果刪除的是鏈表末尾的元素,則不需要這個步驟if(p->next != NULL)p->next->prior = p->prior;free(p);break;}++cnt;p = p->next;}}void printList(DLinkList* dlst){printf("elements are: ");DLinkList p = (*dlst)->next;while(p){printf("%d\t",p->data);p = p->next;}printf("\n");}

這裡給出了比較詳細的代碼。尤其需要注意,當你第一次向鏈表中插入元素,或者插入元素的位置在鏈表的末尾,或者刪除鏈表的最後一個元素時,此時p->是為NULL的,所以不能取p->next->prior 。

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.