1. 程式人生 > >數據結構之鏈表及實現

數據結構之鏈表及實現

oid void scan pan 尋找 最大 執行 連續 邏輯

線性表的鏈式表示和實現

線性表的順序存儲結構的特點是邏輯關系上相鄰的兩個元素在物理位置上也相鄰。正由於這種特點,在做插入和刪除操作時,需移動大量元素。

鏈式存儲:不要求邏輯上相鄰的元素在物理位置上也相鄰,特點是用一組任意的存儲單元存儲線性表的數據元素(可以是連續的,也可以是不連續的)。

為了表示每個數據元素ai與其直接後繼元素a i+1 之間的邏輯關系,對數據元素a來說,除了存儲其本身的信息之外,還需要存儲一個指示其直接後繼的信息(即直接後繼的存儲位置)。這兩部分信息組成數據元素ai的存儲映像,稱為結點。包括兩個域:數據域和指針域。每個結點只包含一個指針域,故又稱為線性鏈表或單鏈表

頭結點

:在單鏈表的第一個元素結點之前設置的一個結點,數據域可以不存在任何信息,指針域指向單鏈表第一個元素的結點。

首元結點:單鏈表中第一個有數據元素的結點,如果單鏈表有頭結點,則首元結點為頭結點的下一個結點;如果單鏈表沒有頭結點,則首元結點就是單鏈表的第一個結點。

頭指針:指示鏈表中第一個結點(即第一個數據元素的存儲映像)的存儲位置。如果單鏈表有頭結點,則頭指針指向頭結點如果單鏈表沒有頭結點,則頭指針指向首元結點。

用線性鏈表表示線性表是,數據元素之間的邏輯關系是由結點中的指針指示的。單鏈表可由頭指針唯一確定。

//------ 線性表的單鏈表存儲結構 ------ 
typedef struct
LNode{ ElemType data; struct LNode *next; }LNode,*LinkList; //------ 線性表的靜態單鏈表存儲結構 (用數組)------ /* 假設S為SLinkList型變量,則是S[0].cur指示第一個結點在數組中的位置,若設i=S[0].cur, 則S[i].data存儲線性表表的第一個數據元素,且S[i].cur指示第二個結點在數組中的位置。 i=S[i].cur 的操作實為指針後移(類似p=p->next) */ #define MAXSIZE 100 //鏈表的最大長度 typedef struct LNode{ ElemType data;
int cur; }component,SLinkList[MAXSIZE]; //------ 單鏈表 GetElem ------ Status GetElem_L(LinkList L,int i,ElemType &e){ //L為帶頭結點的單鏈表的頭指針 //當第i個元素存在時,其值賦給e並返回OK,否則返回ERROR p = L->next;j=1; //初始化,p指向第一個結點,j為計數器 while(p && j < i){ // 順指針向後查找,直到p指向第i個元素或者p為空 p = p->next; ++j; } if(!p || j > i) return ERROR; //第i個元素不存在 e = p->data; //取第i個元素 return OK; } //GetElem_L //------ 單鏈表 ListInsert------ Status ListInsert_L(LinkList L,int i,ElemType &e){ //在帶頭結點的單鏈線性表中第i個位置插入元素e p = L; j = 0; while(p && j < i-1){ //尋找第i-1個結點 p = p->next; ++j; } if(!p || j > i-1) return ERROR;//i小於1或者大於表長加1 s = (LinkList)malloc(sizeof(LNode)); //生成新結點 s->data = e; s->next = p->next; p->next = s; return OK; }//LinkInsert_L //------ 單鏈表 ListDelete------ Status ListDelete_L(){ //在帶頭結點的單鏈線性表L中,刪除第i個元素,並由e返回其值 p = L; j = 0; while(p->next && j < i-1){//尋找第i個結點,並令p指向其前驅 p = p->next; ++j; } if(!(p->next) || j > i-1) return ERROR;//刪除位置不合理 q = p->next; p->next = q->next; //刪除並釋放結點 e = q->data; free(q); return OK; }//ListDelete_L //------ 單鏈表 逆向建立單鏈表------ void CreateList_L(LinkList &L,int n){ //逆位序輸入n個元素的值,建立帶表頭結點的單鏈線性表L L = (LinkList)malloc(sizeof(LNode)); L->next = NULL; //先建立一個帶頭結點的單鏈表 for(i = n;i > 0;--i){ p =(LinkList)malloc(sizeof(LNode));//生成新結點 scanf(&p->data); //輸入元素值 p->next = L->next; L->next = p; //插入到表頭 } }//CreateList_L //------ 單鏈表的合並------ void MergeList_L(LinkList &La,LinkList &Lb,LinkList &Lc){ //已知單鏈表La和Lb的元素按值非遞減有序排列 //歸並La和Lb得到新的單鏈表Lc,Lc的元素也按值非遞減排列有序 pa = La->next; pb = Lb->next; Lc = pc = La; //用La的頭結點作為Lc的頭結點 while(pa && pb){ if(pa->data <= pb->data){ pc->next = pa; pc = pa; pa = pa->next; } else{ pc->next = pb;pc = pb; pb = pb->next; } } pc->next = pa? pa : pb; //插入剩余段 free(Lb); //釋放Lb的頭結點 } //MergeList_Sqq

循環鏈表:表中最後一個結點的指針域指向頭結點,整個鏈表形成一個環。

操作和線性鏈表基本一致,差別僅在於算法中的循環條件不是p或p->next是否為空,而是它們是否等於頭指針。

在單鏈表中,NextElem的執行時間為O(1) ,而PriorElem的執行時間為O(n)。為了克服單鏈表這種單向性的缺點,可以利用雙向鏈表。

雙向鏈表:雙向鏈表的結點中有兩個指針域,其一指向直接後繼,另一指向直接前驅。

//------ 線性表的雙向鏈表存儲結構 ------ 
typedef struct DuLNode{
    ElemType data;
    struct DuLNode *prior;
    struct DuLNode *next;    
}DuLNode,*DuLinkList; 

//雙向鏈表的ListLength GetElem LocateElem僅僅設計一個方向的指針,與線性鏈表操作相同 

//------ 雙向鏈表 insert------ 
Status ListInseret_DuL(DuLinkList &L,int i,ElemType e){
    //在帶頭結點的雙鏈循環線性表中第i個位置之前插入元素e
    //i的合法值為1<=i<=表長+1
    if(!(p = GetElemP_DuL(L,i))) //在L中確定插入位置
        return ERROR; 
    
    if(!(s = (DuLinkList)malloc(sizeof(DuLNode)))) return ERROR;
    
    s->data = e;
    s->prior = p->prior; p->prior->next = s;
    s->next = p; p->prior = s;
    return OK;
}//ListInseret_DuL

//------ 雙向鏈表 delete------ 
Status ListDelete_DuL(DuLinkList &L,int i,ElemType &e){
    //刪除帶頭結點的雙鏈循環鏈表L的第i個元素, i的合法值為1<=i<=表長+1
    if(!(p = GetElemP_DuL(L,i)))  return ERROR;//p=NULL ,即第i個元素不存在
    e = p->data;
    p->prior->next = p->next;
    p->next->prior = p->prior;
    free(p);
    return OK; 
}//ListDelete_DuL

數據結構之鏈表及實現