c語言結構體鏈表


原文鏈接:http://zhina123.blog.163.com/blog/static/417895782012106036289/

引用自身的結構體,一個結構體中有一個或多個成員的基類型就是本結構體類型時,說明這個結構體可以引用自己,所以稱作引用自身的結構體。

例如下面的結構體:

 

struct link{ char ch; struct link *p} a;

 

p是一個可以指向struct link類型變量的指針成員,這樣,a.p=&a就是合法的表達式。那么,這有什么意義呢?

這樣的意義就是我們可以把分散存儲的數據項,用一個結構體成員鏈接起來(當然這也耗費了那個存儲指針的內存空間)看下面的程序

 

#include <stdio.h>
struct node{
 int data;
 struct node *next;
};
main(){
 struct node a,b,c,*p;//我們假設,這樣聲明的結構體變量a 、b、c在內存中並不是相臨的
 a.data=10;  b.data=20; c.data=30; 

 a.next=&b; b.next=&c; c.next='\0';
 p=&a;
 //結構體變量a地址賦給p 
 while(p){
 printf(" %d ",p->data);
 //每輸出一個值后,p自動指向本項的鏈接項 
/*這樣有了一個單獨保持鏈接的成員就把不相臨的存儲單元在邏輯上存儲在了一起*/ 

p=p->next;
 }
 printf("\n");
}

 

這樣的相鏈的數據存儲形式稱為鏈表!上面形成鏈表的方法是人為定義的,在程序執行過程中,不會生成新的存儲單元,所以也稱為“靜態鏈表”

下面看一種更方法使用的“動態鏈表”

前面的日志中提到了C語言動態存儲分配提供了“按需分配內存”的實現方法,有一個問題是,如果多次動態分配存儲單元,各存儲單元的地址不是一定是連續的,而所需要處理的批量數據往往是一個整體,各數據之間存在着順序關系,這樣,我們可以利用上面的鏈表把動態得到的存儲單元在邏輯上(而不是在物理上)連接起來,就可以實現我們需要的順序關系了。這時,是把鏈表技術與動態存儲分配結合了起來,所以這里我們給了鏈表一個新的名詞叫做“動態鏈表”

很自然,我們上面例子中的鏈表只有一個指向后面數據項的指針,如果有兩個呢?一個指后,一個指前,這樣就會出現“雙向鏈表”與“單向鏈表”的區別

下面我們主要看單向鏈表

事實上,單身鏈表中的每個存儲單元的數據(也就是結構體)的格式類型是相同的(包括數據成員和指針成員)

如下:

 

struct abc{int data,……struct abc *next;};

 

與單向鏈表有關的算法有以下幾類:

建立鏈表  輸出結點數據   插入結點數據 刪除結點數據

下面這個程序是示例

 

#include <stdio.h>
#include <stdlib.h>
struct slist{ int data; struct slist *next;};
typedef struct slist SLIST;

SLIST *creat_slist(){
/*該函數的作用是創建動態鏈表,函數的返回值是結構體指針變量,也就是新創建的動態鏈表的頭結點,需要注意的是,這里的頭結點沒有數據data,只有指針next指向動態鏈表的第一個結點*/
 int c;

/*用來臨時存儲結構體中的data*/ 
 SLIST *h,*s,*r;

/*聲明工作指針*/
 h=(SLIST *)malloc(sizeof(SLIST));
 /*動態獲取一個結構體的存儲空間*/
 r=h;

/*結構體指針r用來在下面的循環中使用h指針不變*/
 scanf("%d",&c);

/*得到一個結構體成員int data數據*/ 
 while(c!=-1){
 /*如果上面得到的int data數據不為-1就進入循環 */
  s=(SLIST *)malloc(sizeof(SLIST));
 /*循環中用結構體指針s獲取結點*/
  s->data=c;
 /*s成員data為c注意第一次進入循環時,這個c是在循環外部上面輸入的*/
     r->next=s;
   /*r指針本來與h相同,r的成員next在進入循環前是沒有指向的現在進入了循環得到了一個結點,就把r的next指向新的結點,這樣就使h頭結點指向了s*/
  r=s;
 /*r依次與最新的結點相同,為了依次用最新的存儲空間的next指向下一個獲得的結點*/
  scanf("%d",&c);
 }
 r->next='\0';
 /*退出循環后,r指向最后一個結點,而這個最后結點的成員next要指向'\0'*/
 return h;
 /*返回動態鏈表的頭結點指針*/
}
void print_slist(SLIST *head){
/*該函數是依次輸出動態鏈表的結點,參數是結構體指針變量,也就是
動態鏈表的頭結點*/ 
 SLIST *p;
 /*聲明一個工作指針,因為head不能自己往后依次移動,所以用指針
 p實現*/
 p=head->next;
 if(p=='\0')printf("Linklist is null!\n");/*空鏈表*/ 
 else{printf("head");/*非空鏈表*/ 
        do{ printf("->%d",p->data);
        p=p->next;}while(p!='\0');
 printf("->end\n");
 }
 
}

SLIST *insert_snode(SLIST *head,int x,int y){
/*該函數實現在鏈表值為x的結點前面插入一個結點,值為y參數有三個鏈表頭結點,值x,y*/ 
 SLIST *s,*p,*q;
 /*定義工作指針*/
 s=(SLIST *)malloc(sizeof(SLIST));
 s->data=y;
 /*上面這兩句先獲取了一個結構體動態存儲空間,並給其成員data賦值為y,但此時該空間並未成為鏈表的一個結點*/
 q=head;
 p=head->next;
 /*上面兩句初始化工作指針,就是把工作指針q與head相同,p指向head的next*/
 while((p!='\0')&&(p->data!=x)){
  /*這個循環是供查找到值為x的結點所在位置的,需要注意的是這里的並的兩個條件位置不能變,因為只有在p指向不為空的時候才能討論其data成員值是否為x,否則如果p的指向是空,程序確要先判斷p指向的data是不是x這樣會發生地址訪問錯誤,因為p本不指向結點,也就無從談成員data,所以判斷p指向的data是不是x是不對的*/
  q=p;p=p->next;
  /*滿足循環條件的話,p與q依次后移直到找到值為x的結點或到了鏈表的尾部*/
  }
  s->next=p;
  /*在p指向的結點前面插入,所以新的結點的next指向p*/
  q->next=s;
  /*q-next本指向p結點,現在令其指向s結點,實現了插入*/
  
 return head;/*頭指針並未變化,返回即可*/
}

SLIST *insert_bnode(SLIST *head,int x,int y){
/*該函數實現在鏈表值為x的結點后面插入一個結點,值為y參數有三個鏈表頭結點,值x,y*/ 
 SLIST *s,*p,*q;
 /*定義工作指針*/
 s=(SLIST *)malloc(sizeof(SLIST));
 s->data=y;
 /*上面這兩句先獲取了一個結構體動態存儲空間,並給其成員data賦值為y,但此時該空間並未成為鏈表的一個結點*/
 q=head;
 p=head->next;
 /*上面兩句初始化工作指針,就是把工作指針q與head相同,p指向head的next*/
 while((p!='\0')&&(p->data!=x)){
  /*這個循環是供查找到值為x的結點所在位置的,需要注意的是這里的並的兩個條件位置不能變,因為只有在p指向不為空的時候才能討論其data成員值是否為x,否則如果p的指向是空,程序確要先判斷p指向的data是不是x這樣會發生地址訪問錯誤,因為p本不指向結點,也就無從談成員data,所以判斷p指向的data是不是x是不對的*/
  q=p;p=p->next;
  /*滿足循環條件的話,p與q依次后移直到找到值為x的結點或到了鏈表的尾部*/
  }
  s->next=p->next;
  /*在p指向的結點后面插入,所以新的結點的next指向p*/
  p->next=s;
  /*p-next本指向p后面的結點,現在令其指向s結點,實現了后插入*/
  
 return head;/*頭指針並未變化,返回即可*/
}

SLIST *del_node(SLIST *head,int x){
/*該函數實現刪除鏈表值為x的結點,參數有兩個鏈表頭結點,值x */ 
 SLIST *s,*p,*q;
 /*定義工作指針*/
 q=head;
 p=head->next;
 /*上面兩句初始化工作指針,就是把工作指針q與head相同, p指向head的next*/
 while((p!='\0')&&(p->data!=x)){
  /*這個循環是供查找到值為x的結點所在位置的,需要注意的是這里的並的兩個條件位置不能變,因為只有在p指向不為空的時候才能討論其data成員值是否為x,否則如果p的指向是空,程序確要先判斷p指向的data是不是x這樣會發生地址訪問錯誤,因為p本不指向結點,也就無從談成員data,所以判斷p指向的data是不是x是不對的*/
  q=p;p=p->next;
  /*滿足循環條件的話,p與q依次后移直到找到值為x的結點或到了鏈表的尾部*/
  }
  q->next=p->next;
  /*把q->next置成p->next,*/
  free(p);
  /*釋放p的存儲空間,實現刪除*/
 return head;

/*頭指針並未變化,返回即可*/
}

main(){
 SLIST *head; int x,y;
 head=creat_slist();//創建鏈表函數
 print_slist(head);
 printf("please input x\n");  scanf("%d",&x);
 printf("please input y\n");  scanf("%d",&y);
 head=insert_snode(head,x,y);//結點前插入函數
 print_slist(head);
 printf("please input x\n");  scanf("%d",&x);
 printf("please input y\n");   scanf("%d",&y);
 head=insert_bnode(head,x,y);//結點后插入函數
 print_slist(head);
 printf("please input x\n"); scanf("%d",&x);
 head=del_node(head,x);//刪除結點函數

 print_slist(head);
}

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM