我所理解的鏈表(來自網絡)


          雖然使用數組可以很方便的查找每一個元素,但如果要插入或刪除元素時,因為要移動大量的元素,所以需要一定的運行時間,而且代碼也變的復雜。為了能夠方便的刪除和插入元素,我們一般不采用順序存取結構,而采用鏈表存取。根據鏈表中結點的性質,我把它分為兩種,一種是使用指針來指向它的后繼(為簡單起見,我暫時只講一下單向鏈表,至于雙向鏈表和循環鏈表,大家可以在單向鏈表的基礎上做一定擴展即可),每一個結點的空間由系統動態分配,我們稱之為動態鏈表;另一種是用游標(相當于指針)來指向它的后繼,每一個結點的空間由程序建立的“模擬空間分配站”來分配,這個“模擬空間分配站”實際上是一個事先給定足夠空間的結構數組,它為鏈表的結點分配空間,鏈表上釋放的結點空間再返回給“模擬空間分配, 站”,由于這個“模擬空間分配站”的空間是由系統事先靜態分配好空間的,所以稱這種鏈表為靜態鏈表。靜態鏈表滿足了那些不支持指針的高級語言(如BASIC和FORTRAN)需要鏈表的要求,它的使用方法和動態鏈表極為相似。 首先我們來了解動態鏈表。它的類型聲明和基本操作如下表所示: //-----------線性表的單鏈表存儲結構------------- typedef struct Node{ ??? ElemType data; ??? struct Node *next; } *LNode, *LinkList; //----------線性表的單鏈表基本操作------------ LinkList InitList(void); //構造一個空的線性表 ? void DestroyList(LinkList *L);//初始條件:線性表L已存在。 操作結果:銷毀線性表L。 ? LinkList MakeEmpty(LinkList L);//初始條件:線性表L已存在。 操作結果:將線性表L重置為空表。 ? int IsEmpty(LinkList L);//初始條件:線性表L已存在。 操作結果:判斷線性表是否為空表。 ? int ListLength(LinkList L);//初始條件:線性表L已存在。 操作結果:返回線性表L結點的個數。 ? LNode IsLast(LinkList L); //初始條件:線性表L已存在。 操作結果:返回線性表L的最后一個結點(尾結點)。 LNode NewLNode(ElemType X);//構造一個數據域為X的新結點 ? LNode FindPrefious(ElemType X, LinkList L);//初始條件:線性表L已存在。 操作結果:在線性表L中尋找值為X的結點,若找到則返回該結點的前驅,否則返回NULL。 ? void ListDelete(LNode Pre);//初始條件:線性表L中結點P已找到。 操作結果:刪除該結點。 ? void ListInsert(LNode Pre, LNode S);//初始條件:線性表L中結點P已找到,新結點S已構造。 操作結果:在該結點之前插入新結點X。 ? ----------線性表的單鏈表基本操作的算法實現------------ //我給鏈表設置了一個頭結點,雖然它的數據域毫無意義,但它作為一個指針卻意義非凡! //它的作用我們在下面的例程中可以領教 LinkList InitList(void) //構造一個空的線性表 { ??? LNode Head; ??????? ? ??? Head = (LNode)malloc(sizeof(struct Node)); //為鏈表的頭結點分配空間 ??? if(!Head) ??? { ??? ??? printf("Out of space!"); ??? ??? return NULL; ??? } ??? Head->next = NULL; ??? return Head;//返回頭結點 } ? void DestroyList(LinkList *L)//初始條件:線性表L已存在。 操作結果:銷毀線性表L。 { ?? LNode Head, P; ??? ??? if(*L)//若線性表L已存在 ??? { ??????? Head = *L; ??????? P = Head->next; ??? ??? while(!P)? //把鏈表中除頭結點外的所有結點釋放 ??????? { ??????? ??? free(Head); ??????? ??? Head = P; ??????????? P = Head->next;??????? ??? ?? } ??? ??? free(Head); //釋放頭結點 ??? } } ??? ? LinkList MakeEmpty(LinkList L)//初始條件:線性表L已存在。 操作結果:將線性表L重置為空表。 { ?? LNode Head, P; ? ??? Head = *L; ??? P = Head->next; ??? while(!P)//把鏈表中除頭結點外的所有結點釋放 ??? { ??? ??? free(Head); ??????? Head = P; ??????? P = Head->next; ??? ??? } ??? return (Head); //返回頭結點 } ? int IsEmpty(LinkList L);//初始條件:線性表L已存在。 操作結果:判斷線性表是否為空表。 { ??? return L->next == NULL; } ? int ListLength(LinkList L)//初始條件:線性表L已存在。 操作結果:返回線性表L結點的個數。 { ??? LNode P = L->next; ??? int num = 0; ??? ? ??? while(P) //累積線性表L結點的個數 ??? { ??? ??? num++; ??????? P = P->next; ??? } ?? return num;? //返回線性表L結點的個數 } ? //初始條件:線性表L已存在。 操作結果:返回線性表L的最后一個結點(尾結點)。 LNode IsLast(LinkList L) { ??? LNode P = L->next; ??? ??? if(P) ??? { ??? ??? while(P->next) //遍歷線性表L ??????????? P = P->next; ??? } ??? return P;? //返回線性表L的最后一個結點,若為空表則返回NULL } ? LNode NewLNode(ElemType X)//構造一個數據域為X的新結點 { ??? LNode S; ??? ??? S = (LNode)malloc(sizeof(struct Node))//為新結點分配空間 ??? if(!S) ??? { ??? ??? printf("Out of space!"); ??? ??? return NULL; ??? } ??? S->data = X; ??? S->next = NULL; ??? return S;//返回新結點 } ? //線性表L已存在。 操作結果:在線性表L中尋找值為X的結點,若找到則返回該結點的前驅,否則返回NULL。 LNode FindPrefious(ElemType X, LinkList L) { ??? LNode P = L; ??? ??? while(P->next && P->next->data != X)//遍歷鏈表尋找值為X的結點 ??????? P = P->next; ??? if(!P->next)? //如果找不到值為X的結點,返回NULL ??? ??? return NULL; ??? else???????? //若找到則返回該結點的前驅P ??? ??? return P;??? } ?????????????????????????? void ListDelete(LNode Pre)//初始條件:線性表L中結點P已找到。 操作結果:刪除該結點。 { ??? LNode P = Pre->next; ??? ??? Pre->next = P->next; ??? free(P); } //初始條件:線性表L中結點P已找到,新結點S已構造。。操作結果:在該結點之前插入新結點X。 void ListInsert(LNode Pre, LNode S) { ??? S->next = Pre->next; ??? Pre->next = S; } 注意:為操作方便起見,在執行函數void ListDelete(LNode Pre, LinkList L)

          和void ListInsert(LNode Pre, LNode X, LinkList L)時,通常把結點P的前驅Pre作為實參。而且這兩個函數通常與函數LNode FindPrefious(ElemType X, LinkList L)一起使用,即先查找結點,再執行插入或刪除操作。

          以上操作為線性單鏈表的最基本的操作,其他操作可以是上述操作的組合。如,執行“在線性表L中尋找值為X的結點,并刪除之”操作時,可先執行LNode FindPrefious(ElemType X, LinkList L),再執行void ListDelete(LNode Pre)。 又如,執行“把一個值為X的新結點插入結點P之前”,可先執行LNode FindPrefious(ElemType X, LinkList L),再執行LinkList NewLNode((ElemType X),最后執行void ListInsert(LNode Pre, LNode S)。

          我特意向大家推薦這種”堅持使用頭結點“和“鎖定前驅結點”的設計方法,這正是我和一般書本上有著不同理解的地方。有些書上的例程有時設置一個頭結點,還有些根本就不使用頭結點,也許是他們認為頭結點占地方罷,但我認為為了更方便的編程實現我們的意圖和更好的編程風格---主要指代碼清晰易讀,我再一次吐血推薦我的設計方法。因為這種設計方法就像一把萬能鑰匙---也許有些夸張了,呵呵!一般書上的例程在刪除或插入結點時,要分別討論鏈表頭,鏈表中和鏈表尾三種不同的情況,采取不同的操作;而我上面所列的”刪除“和”插入“操作可以全面搞定對鏈表中任意位置的結點(頭結點除外)插入和刪除功能。(除了”把新結點插入到鏈表尾“,但這可以組合執行LNode IsLast(LinkList L)和void ListInsert(LNode Pre, LNode S))。

          舉一個實際的例子。已知集合A={1,2,3,4,5},B={3,4,5,6,7,8,9,11},求集合A和B的交集。

          算法思路是先把集合A和B分別存入兩個單向鏈表LA和LB中,以LA的結點P為研究對象,遍歷LB,看是否有與其同值的結點,根據情況判斷是否執行刪除結點P的指令。最后得到的LA就是集合A和B的交集。

          具體的實現如下所示,因為函數部分已經包含在“線性表的單鏈表基本操作的算法實現“中,所以不做重復,只把其他的部分列出來。程序經過測試,可以運行。

          #include

          #include

          #include

          ?

          typedef int ElemType;

          typedef struct Node{

          ??? ElemType data;

          ??? struct Node *next;

          } *LNode, *LinkList;

          LinkList CreateList(ElemType a[], ElemType len);//用來構造一個鏈表

          。。。//其他函數聲明

          int main(void)

          {

          ?? LNode LA, LB, Pre,Flag;

          ?? ElemType X, A[5]={1,2,3,4,5}, B[8]={3,4,5,6,7,8,9,11};

          ?? //把集合A和B分別存入兩個單向鏈表LA和LB中

          ?? LA = CreateList(A, 5);

          ?? LB = CreateList(B, 8);

          ?? //以LA的結點P為研究對象,遍歷LB,看是否有與其同值的結點,根據情況判斷是否執行刪除結點P的指令

          ?? Pre = LA;

          ?? while(Pre->next)

          ?? {

          ??????? X = Pre->next->data;

          ??????? Flag = FindPrefious(X, LB);

          ??? ??? if(!Flag)

          ??????? ??? ListDelete(Pre);

          ??????? else

          ??????? ??? Pre = Pre->next;?

          ??? }

          ??? //輸出集合A和B的交集

          ? ? Pre = LA;

          ? ? printf("集合A和B的交集:\n");

          ? ??? if(!Pre->next)

          ? ? ??? printf("交集為空集!");

          ? ? else??????????

          ??? ? ??? while(Pre->next)

          ??? ?? {

          ??????????? X = Pre->next->data;

          ??????? ??? printf("%2d", X);

          ??????? ??? Pre = Pre->next;?

          ??????? }

          ??

          ? ??? system("pause");????

          ?? return 0;

          }

          ?

          LinkList CreateList(ElemType a[], ElemType len)//用來構造一個鏈表

          {

          ??? LNode L, S;

          ??? ElemType i;

          ??? ?

          ??? L = InitList(); //構造一個空的線性表

          ??? for(i=0; i

          ??? {

          ??????? S = NewLNode(a[i]); //構造一個數據域為a[i]的新結點

          ??? ??? ListInsert(L, S); //把新結點S插到頭結點后面。

          ??? }

          ??? return L;

          }??


          動態鏈表我們就先講到這里,實際上更讓我感興趣的是靜態鏈表。這種無需指針而有能夠實現鏈表功能的結構,對于那些不支持指針的高級語言來說,無疑是個巨大的福音。它既可以像數組一樣隨機存取數據---它本身就是一個數組,又具有鏈表方便地實現插入和刪除結點的功能;由于它是模擬的“動態分配空間”,實際上它的存儲空間是由系統一次性分配好了的,這樣在“動態分配空間”的時候,不需要內存管理程序,如果運行的Find函數相對較少,它實現的速度比動態鏈表要快很多;此外,他很少出現因為內存空間不夠的原因而導致程序不正常終止的情況,因為它的空間一早就分配好了,只要不超出鏈表的最大長度,空間就足夠。因此它真可以稱的上是一個“寶貝”。

          在鏈表的指針實現(即動態鏈表)中,有兩個重要的特點: 1.數據存儲在一組結構體中,每個結構包含有數據以及指向下一個結構體的指針. 2.一個新的結構體可以通過調用malloc()而從系統全局內存(global memory)得到,并可以通過調用 free()而被釋放. 靜態鏈表必須能夠模仿實現這兩條特性.滿足條件1的邏輯方法是要有一個全局的結構體數組.對于該數組中的任何單元(元素),其數組下標可以用來表示一個地址(結點).也就是說數組元素(結構體)包含有數據以及指向下一個結構體的游標---即下一個結點的數組下標.可以建立不同的鏈表,但實際上每一個鏈表都是結構體數組一部分元素的集合。 為了模擬條件2,我們需要建立一個“模擬空間分配站”,它是一個規模較大的結構體數組。我們可以建立不同的鏈表,實際上我們創造的每一個鏈表都來自這個“模擬空間分配站”,每一個結點都是該結構體數組的元素,每一個鏈表都是結構體數組一部分元素的集合。 它的類型聲明和基本操作如下表所示: //-------------------線性表的靜態單鏈表存儲結構------------------------ #define MAXSIZE 1000//鏈表的最大長度 typedef int Position; typedef int SLink; typedef struct Node{ ??? ElemType data; ??? Position next; } SLinkList[MAXSIZE]; //-------------------線性表的靜態單鏈表基本操作------------------------ static void InitSpace_SL(SLinkList Space);//構造一個“模擬空間分配站”,為全局變量 //初始條件:“模擬空間分配站”已存在。操作結果:"動態"分配空間 給結點P? static Position malloc_SL(void); //初始條件:“模擬空間分配站”已存在。操作結果:釋放結點P 的空間 到“模擬空間分配站” static void free_SL(Position P); Position MakeEmpty(SLink L);//初始條件:線性表L已存在。 操作結果:將線性表L重置為空表。 Position InitList(void); //構造一個空的線性表 void DestroyList(SLink L);//初始條件:線性表L已存在。 操作結果:銷毀線性表L。 int IsEmpty(SLink L);//初始條件:線性表L已存在。 操作結果:判斷線性表是否為空表。 int SListLength(SLink L);//初始條件:線性表L已存在。 操作結果:返回線性表L結點的個數。 Position NewSLNode(ElemType X);//構造一個數據域為X的新結點 //初始條件:線性表L和結點P已存在。操作結果:判斷P是否為鏈表L的結點 int LContaiP(SLink L, Position P); int IsLast(Position P); //初始條件:結點P已存在。 操作結果:判斷P是否為尾結點 Position FindPrefious(ElemType X, SLink L); //初始條件:線性表L已存在。操作結果:在線性表L中尋找值為X的結點,若找到則返回該結點的前驅,否則返回NULL。 void SListDelete(Position Pre);//初始條件:線性表L中結點P已找到。 操作結果:刪除該結點。 void SListInsert(Position Pre, Position S); //初始條件:線性表L中結點P已找到,新結點S已構造。操作結果:在該結點之前插入新結點X。 //-------------------線性表的靜態單鏈表基本操作的算法實現------------------------ static void InitSpace_SL(SLinkList Space)//構造一個“模擬空間分配站”,為全局變量 { ??? int i; ??? ??? for(i=0; i<MAXSIZE-1; i++) //每個結點的游標值均表示其后繼結點的數組下標 ??? { ??? ??? Space[i].next = i+1; ??? ??? Space[i].data = i+1; ??? } ??? Space[MAXSIZE-1].next = 0;//尾結點的后繼結點下標為0,即NULL } //初始條件:“模擬空間分配站”已存在。操作結果:"動態"分配空間 給結點P? static Position malloc_SL(void) { ??? Position P; ??? ??? P = Space[0].next;? //每一個結點的空間均從Space[0]這里取得,當前被取走的結點乃Space[0]的直接后繼 ??? Space[0].next = Space[P].next; //為P結點分配空間,實際上相當于出棧,Space[0]即棧頂 ??? return P;?? //把結點P從“模擬空間分配站”中取出來 ,并返回其值(實際上是一個數組下標) } ? //初始條件:“模擬空間分配站”已存在。操作結果:釋放結點P 的空間 到“模擬空間分配站” static void free_SL(Position P) { ??? ?Space[P].next = Space[0].next; ??? ?Space[0].next = P;//回收P結點的空間,實際上相當于入棧 ,Space[0]即棧頂 } ? Position MakeEmpty(SLink L)//初始條件:線性表L已存在。 操作結果:將線性表L重置為空表。 { ??? Position P = Space[L].next; ??? ??? while(P) ??? { ??? ??? free_SL(L); //從頭結點開始依次釋放回收結點的空間 ??????? L = P; ??????? P = Space[L].next; ??? }?? //最后使? Space[L].next = 0; } ? Position InitList(void) //構造一個空的線性表 { ??? SLink L; ??? ??? L = malloc_SL(); //為鏈表的頭結點分配空間 ??? if(!L) ??? { ??? ??? printf("Out of space!"); ??? ??? return 0; ??? } ??? Space[L].next = 0; //使頭結點的直接后繼為0,構造一個空的線性表 ??? return L; } ? void DestroyList(SLink L)//初始條件:線性表L已存在。 操作結果:銷毀線性表L。 { ??? Position P = Space[L].next; ??? ??? while(P) ??? { ??? ??? free_SL(L); //從頭結點開始依次釋放回收結點的空間 ??????? L = P; ??????? P = Space[L].next; ??? }? ??? ?free_SL(L);//把頭結點的空間也釋放回收,徹底銷毀線性表L } ? int IsEmpty(SLink L)//初始條件:線性表L已存在。 操作結果:判斷線性表是否為空表。 { ??? return Space[L].next == 0; } ? int SListLength(SLink L)//初始條件:線性表L已存在。 操作結果:返回線性表L結點的個數。 { ??? Position P = Space[L].next; ??? int num = 0; ??? ? ??? while(P) //累積線性表L結點的個數 ??? { ??? ??? num++; ??????? P = Space[P].next; ??? } ??? return num;? //返回線性表L結點的個數 } ? Position NewSLNode(ElemType X)//構造一個數據域為X的新結點 { ??? Position S; ??? ??? S = malloc_SL(); //為新結點分配空間? ??? if(!S) ??? { ??? ??? printf("Out of space!"); ??? ??? return 0; ??? } ??? Space[S].data = X; ??? Space[S].next = 0; ??? return S;//返回新結點 } ? //初始條件:線性表L和結點P已存在。操作結果:判斷P是否為鏈表L的結點 int LContaiP(SLink L, Position P) { ??? Position R = Space[L].next; ??? ??? while(R && R!=P) //遍歷整個鏈表 ??????? R = Space[R].next; ??? return R;//返回R,若P不是鏈表L的結點,則R=0,否則R不為0 } ? int IsLast(Position P) //初始條件:結點P已存在。 操作結果:判斷P是否為尾結點 { ??? return? Space[P].next == 0; } //初始條件:線性表L已存在。操作結果:在線性表L中尋找值為X的結點,若找到則返回該結點的前驅,否則返回NULL。 Position FindPrefious(ElemType X, SLink L) { ??? Position P = L; ??? ??? while(Space[P].next && Space[Space[P].next].data != X)//遍歷鏈表尋找值為X的結點 ??????? P = Space[P].next; ??? if(!Space[P].next)? //如果找不到值為X的結點,返回NULL ??? ??? return 0; ??? else???????? //若找到則返回該結點的前驅P ??? ??? return P;??? } ? void SListDelete(Position Pre)//初始條件:線性表L中結點P已找到。 操作結果:刪除該結點。 { ??? ?Position P; ??? ? ??? ?P = Space[Pre].next; //刪除結點P ??? ?Space[Pre].next = Space[P].next; ??? ?free_SL(P);//釋放回收結點P的空間 } //初始條件:線性表L中結點P已找到,新結點S已構造。操作結果:在該結點之前插入新結點X。 void SListInsert(Position Pre, Position S) { ??? ?Space[S].next = Space[Pre].next; ??? ?Space[Pre].next = S;

          }

          和動態鏈表一樣,以上操作為線性靜態單鏈表的最基本的操作,其他操作可以是上述操作的組合。

          例如要實現“判斷結點P是否為鏈表L的尾結點”操作,函數如下:

          int IsLLast(SLink L, Position P)

          {

          ??? if(LContaiP(L, P)

          ??? ??? return IsLast(P);

          ??? return 0;

          }

          如果你仔細的閱讀這些代碼,你會發現動態鏈表和靜態鏈表的基本操作的實現算法很相似,游標實現的接口和指針實現是一樣的。靜態鏈表可以代替動態鏈表實現,實際上在程序的其余部分不需要變化,而且速度更快。

          同樣的讓我們用一個實際的例子說明。已知集合A={1,2,3,4,5},B={3,4,5,6,7,8,9,11},求集合A和B的交集的非,即(A-B)并(B-A)。

          算法思路是先把集合A和B分別存入兩個單向鏈表LA和LB中,以LB的結點P為研究對象,遍歷LA,看是否有與其同值的結點,若有刪除與結點P相同的結點,否則把結點P插入鏈表LA。最后得到的LA就是集合A和B的交集的非。

          具體的實現如下所示,因為函數部分已經包含在“線性表的靜態單鏈表基本操作的算法實現“中,所以不做重復,只把其他的部分列出來。程序經過測試,可以運行。

          ?

          #include<stdio.h>

          #include<stdlib.h>

          #include<malloc.h>

          #define MAXSIZE 100//鏈表的最大長度

          typedef int Position;

          typedef int SLink;

          typedef int ElemType;

          typedef struct Node{

          ??? ElemType data;

          ??? Position next;

          } SLinkList[MAXSIZE];

          ?

          SLink CreateList(ElemType a[], ElemType len);//用來構造一個鏈表

          。。。//其他函數聲明

          int main(void)

          {

          ?? SLink LA, LB;

          ??? Position P, Pre, S;

          ?? ElemType X, A[5]={1,2,3,4,5}, B[8]={3,4,5,6,7,8,9,1};

          ?

          ?? InitSpace_SL(Space);//構造一個“模擬空間分配站”,為全局變量

          ?

          ?? //把集合A和B分別存入兩個單向鏈表LA和LB中

          ?? LA = CreateList(A, 5);

          ?? LB = CreateList(B, 8);

          ?? //以LB的結點P為研究對象,遍歷LA,看是否有與其同值的結點,若有刪除與結點P相同的結點,否則把結點P插入鏈表LA。

          ?? P = Space[LB].next;??

          ??? while(P)

          ?? {?

          ??????? X = Space[P].data;??? //把結點P的值賦給X

          ??? ??? Pre = FindPrefious(X, LA); //判斷LA中是否有與P同值的結點 ,返回同值結點的前驅? ???

          ??? ??? if(Pre)??? //若有,刪除結點PA??????????????????

          ??????? ??? SListDelete(Pre);

          ??? ??? else ?//否則

          ??????? {

          ??????? ??? ?S = NewSLNode(X); //構造一個數據域為X的新結點,即復制結點P到S

          ??????? ??? ?SListInsert(LA, S);? //把結點S插入鏈表LA

          ??????? }??? ?? ?

          ??????? P = Space[P].next;? //繼續分析鏈表中的下一個結點????????

          ??? }

          ??? //輸出集合A和B的交集的非

          ? ? Pre = LA;

          ? ??? printf("集合A和B的交集的非:\n");

          ? ??? if(!Space[Pre].next)

          ? ? ??? printf("交集的非為空集!");

          ? ? else?? //輸出鏈表LA的所有結點的值????????????

          ??? ? ??? while(Space[Pre].next)

          ??? ?? {

          ??????? ??? X = Space[Space[Pre].next].data;

          ??????? ??? printf("%d ", X);

          ??????? ??? Pre = Space[Pre].next;

          ??????? }

          ?? ??

          ? ??? system("pause");????

          ?? return 0;

          }

          ?

          SLink CreateList(ElemType a[], ElemType len)//用來構造一個鏈表

          {

          ??? SLink L, S;

          ??? int i;

          ??? ?

          ??? L = InitList(); //構造一個空的線性表

          ??? for(i=0; i<len; i++)

          ??? {?

          ??????? S = NewSLNode(a[i]); //構造一個數據域為a[i]的新結點

          ??? ??? SListInsert(L, S); //把新結點S插到頭結點后面。

          ??? }

          ??? return L;

          }?

          ?

          如果你用靜態鏈表去做“求集合A和B的交集”的題目,你會發現,它的主函數部分和用動態鏈表做幾乎一樣。

          提問:如果要同時求集合A和B的交集以及交集的非 ,又該怎么辦呢?

          提示:算法思路是先把集合A和B分別存入兩個單向鏈表LA和LB中,以LB的結點P為研究對象,遍歷LA,看是否有與其同值的結點,若有刪除與結點P相同的結點,否則把結點P插入鏈表LA;還要根據情況判斷是否執行刪除結點P的指令,以便得到A和B的交集。最后得到的LA就是集合A和B的交集的非;而LB是集合A和B的交集。

          主函數部分:

          int main(void)

          {

          ?? SLink LA, LB;

          ??? Position PreA, PreB, S;

          ?? ElemType X, A[5]={1,2,3,4,5}, B[8]={3,4,5,6,7,8,9,11};

          ?

          ?? InitSpace_SL(Space);//構造一個“模擬空間分配站”,為全局變量

          ?

          ?? //把集合A和B分別存入兩個單向鏈表LA和LB中

          ?? LA = CreateList(A, 5);

          ?? LB = CreateList(B, 8);

          ?? //以LB的結點P為研究對象,遍歷LA,看是否有與其同值的結點,若有刪除與結點P相同的結點,否則把結點P插入鏈表LA。

          ? //還要根據情況判斷是否執行刪除結點P的指令

          ?? PreB = LB; //PreB表示結點P的前驅,在所有的操作中,我們都不直接操作被處理的結點,而是操作其前驅??

          ??? while(Space[PreB].next)

          ?? {?

          ??????? X = Space[Space[PreB].next].data;? //把結點PB的值賦給X

          ??? ??? PreA = FindPrefious(X, LA); //判斷LA中是否有與PB同值的結點 ,返回同值結點的前驅

          ??? ??? if(PreA)? //若有,刪除結點PA,繼續分析鏈表中的下一個結點

          ??????? {????????????????????????????

          ??????? ??? SListDelete(PreA);

          ??????? ??? PreB = Space[PreB].next;?

          ??????? }

          ??? ??? else //否則

          ??????? {

          ??????? ??? ?S = NewSLNode(X); //構造一個數據域為X的新結點,即復制結點PB到S

          ??????? ??? ?SListInsert(LA, S);//把結點S插入鏈表LA???

          ??????? ??? ?SListDelete(PreB); //刪除鏈表LB的結點PB

          ??????? }??? ?? ??? ???

          ??? }

          ??? //輸出集合A和B的交集的非

          ? ? PreA = LA;

          ? ??? printf("集合A和B的交集的非:\n");

          ? ??? if(!Space[PreA].next)

          ? ? ??? printf("交集的非為空集!");

          ? ? else??????????

          ??? ? ??? while(Space[PreA].next)

          ??? ?? {

          ??????? ??? X = Space[Space[PreA].next].data;

          ??????? ??? printf("%d ", X);

          ??????? ??? PreA = Space[PreA].next;???

          ??????? }

          ?? //輸出集合A和B的交集?

          ??? PreB = LB;

          ? ??? printf("\n集合A和B的交集:\n");

          ? ??? if(!Space[PreB].next)

          ? ? ??? printf("交集為空集!");

          ? ? else??????????

          ??? ? ??? while(Space[PreB].next)

          ??? ?? {

          ??????? ??? X = Space[Space[PreB].next].data;

          ??????? ??? printf("%d ", X);

          ??????? ??? PreB = Space[PreB].next;???

          ??????? }?

          ? ??? system("pause");????

          ?? return 0;

          }


          posted on 2007-03-18 10:26 金家寶 閱讀(532) 評論(0)  編輯  收藏 所屬分類: 數據結構和算法


          只有注冊用戶登錄后才能發表評論。


          網站導航:
           
          主站蜘蛛池模板: 内黄县| 西贡区| 修水县| 永善县| 察雅县| 如皋市| 涟源市| 甘肃省| 格尔木市| 东阳市| 洛宁县| 石门县| 荔浦县| 南华县| 崇州市| 凤翔县| 遂宁市| 伊川县| 右玉县| 洛浦县| 松滋市| 通河县| 石屏县| 慈利县| 泊头市| 电白县| 大兴区| 胶南市| 汾阳市| 桑植县| 收藏| 乐昌市| 额敏县| 遂昌县| 英超| 江都市| 安阳县| 汶上县| 琼海市| 会宁县| 公主岭市|