国产毛片a精品毛-国产毛片黄片-国产毛片久久国产-国产毛片久久精品-青娱乐极品在线-青娱乐精品

C語言的那些小秘密之鏈表(一)

發布時間:2016-2-18 14:16    發布者:designapp
關鍵詞: C語言 , 鏈表

鏈表,一個對于學習過C語言的人都是再熟悉不過的概念了,可能很多學習過鏈表的人都覺得鏈表沒什么值得太在意的地方,可是如果你走進linux內核,去看看linux內核里面鏈表的實現方式,你不得不為之驚嘆。可能有人會覺得linux內核鏈表實現方式僅此而已,但是你要知道,如果你沒有見到這樣的實現方式之前,能寫出那樣的鏈表嘛?所以在寫鏈表的文章時,我深知自己不可能用一篇文章來講解完鏈表的知識點,所以我特地分為三個部分(單鏈表、雙鏈表、linux內核鏈表,而其中linux內核鏈表單獨拿出來講是因為它的特殊性,在后面的博客中我們再來細談它)來進行講解,盡可能用簡短的文字描述加上簡單易懂的代碼來向讀者講解我所理解的鏈表,希望我所講的鏈表能都對你有所幫助。接下來言歸正傳,開始我們的鏈表之旅。
那么什么是鏈表呢?鏈表是一種物理存儲單元上非連續、非順序的存儲結構,數據元素的邏輯順序是通過鏈表中的指針鏈接次序實現的。鏈表由一系列結點組成,即鏈表中的每個元素,結點可以在運行時動態生成。每個結點均由兩個部分所組成:一個是存儲數據元素的數據域,另一個是存儲相鄰結點地址的指針域。相比于線性表順序結構,鏈表比較方便插入和刪除操作。
對于鏈表我們可以將其分為單鏈表、雙向鏈表和循環鏈表等。首先我們先講講單鏈表。
所謂單鏈表,是指數據結點是單向排列的。一個單鏈表結點,其結構類型分為兩部分:
1、數據域:用來存儲本身數據
2、指針域:用來存儲下一個結點地址或者說指向其直接后繼的指針。
如下圖所示:


注意:如果有圖中的紅色箭頭部分,則變為了單向循環鏈表。
那什么又是雙鏈表呢?雙向鏈表其實是單鏈表的改進。當我們對單鏈表進行操作時,有時你要對某個結點的直接前驅進行操作時,又必須從表頭開始查找。這是由單鏈表結點的結構所限制的。因為單鏈表每個結點只有一個存儲直接后繼結點地址的鏈域,那么能不能定義一個既有存儲直接后繼結點地址的鏈域,又有存儲直接前驅結點地址的鏈域的這樣一個雙鏈域結點結構呢?這就是雙向鏈表。
在雙向鏈表中,結點除含有數據域外,還有兩個鏈域,一個存儲直接后繼結點地址,一般稱之為右鏈域;一個存儲直接前驅結點地址,一般稱之為左鏈域。
如下圖所示:


注意:如果有圖中的紅色箭頭部分,則變為了雙向循環鏈表。
看完了上面的介紹之后,我想讀者對于鏈表算是有了一個大致的了解。那么接下來我們的任務就是學習如何使用鏈表,我們從最簡單的代碼開始,教會讀者來學習使用鏈表,首先我們來看一個單鏈表的創建。為了便于講解,我在此就把所有的代碼放到一個源文件中來執行了,當然讀者在實際編寫代碼的過程中不管是為了維護還是閱讀都應該使用頭文件,而不要在一個源文件中一條龍似地寫到底。
#include
#include
#include
#include
#define N 3
#undef _EXAM_ASSERT_TEST_ //禁用
//#define _EXAM_ASSERT_TEST_ //啟用
#ifdef _EXAM_ASSERT_TEST_ //啟用斷言測試
void assert_report( const char * file_name, const char * function_name, unsigned int line_no )
{
printf( "\n[EXAM]Error Report file_name: %s, function_name: %s, line %u\n",
file_name, function_name, line_no );
abort();
}
#define ASSERT_REPORT( condition ) \
do{ \
if ( condition ) \
NULL; \
else \
assert_report( __FILE__, __func__, __LINE__ ); \
}while(0)
#else // 禁用斷言測試
#define ASSERT_REPORT( condition ) NULL
#endif /* end of ASSERT */
typedef enum _SListReturn
{
SLIST_RETURN_OK
}SListReturn;
typedef struct node
{
char name[10];
int score;
struct node *link;
}stud;
stud * creat(int n)
{
stud *p,*h,*s;
int i;
if((h=(stud *)malloc(sizeof(stud)))==NULL)
{
printf("分配內存空間失敗!");
exit(0);
}
h->name[0]='\0';
h->score=0;
h->link=NULL;
p=h;
for(i=0;i
{
if((s= (stud *) malloc(sizeof(stud)))==NULL)
{
printf("分配內存空間失敗!");
exit(0);
}
p->link=s;
printf("請輸入第%d個人的姓名:",i+1);
scanf("%s",s->name);
printf("請輸入第%d個人的成績:",i+1);
scanf("%d",&s->score);
s->link=NULL;
p=s;
}
return h;
}
SListReturn destroy(stud* head)
{
stud* tmp,*next;
tmp=head;
while(tmp!=NULL)
{
next=tmp->link;
tmp->link=NULL;
free(tmp);
tmp=next;
}
return SLIST_RETURN_OK;
}
SListReturn print(stud* head)
{
stud* tmp=head->link;
while(tmp!=NULL)
{
printf("%s的成績為%d\t",tmp->name,tmp->score);
tmp=tmp->link;
}
return SLIST_RETURN_OK;
}
void main()
{
int number;
stud *head;
number=N;
head=creat(number);
ASSERT_REPORT(print(head)==SLIST_RETURN_OK);
ASSERT_REPORT(destroy(head)==SLIST_RETURN_OK);
}
運行結果為:
root@ubuntu:/home/paixu# ./tt
請輸入第1個人的姓名:rewq
請輸入第1個人的成績:123
請輸入第2個人的姓名:fdsa
請輸入第2個人的成績:456
請輸入第3個人的姓名:vcxz
請輸入第3個人的成績:789
rewq成績為123 fdsa成績為456 vcxz成績為789
看了上面的代碼,如果讀過我之前寫的那篇《C語言的那些小秘密之斷言》的讀者就知道,在這段代碼的紅色部分我們使用了自己實現的斷言,學以致用嘛,所有特此在這里拿出來使用下,如果還沒有看那篇文章的讀者,我建議你看看,畢竟斷言還是很有用的。代碼的藍色部分也算是一個特色點,因為以前可能我們在自己的代碼中都是返回一些int型值或者NULL之類的,使得代碼的返回值不能夠直觀的體現出運行結果,也使得代碼的可讀性比較差,所有為了改善我們的代碼,我們要學習自己定義返回類型,做到盡可能的從各個方面去改善我們編寫的代碼。在這里我們自己用枚舉型的數據結構來定義了返回類型,因為代碼的關系,我這里僅僅使用了一個返回類型SLIST_RETURN_OK,根據自己代碼的需要讀者可以自己添加編寫更多的返回值,我僅僅是在這里舉出一個例子。如:
typedef enum _SListReturn
{
SLIST_RETURN_OK,
SLIST_RETURN_STOP,
SLIST_RETURN_FAIL
}SListReturn;
現在我們從以上代碼中選出部分代碼來加注釋進行講解。
stud *p,*h,*s; /* *h保存表頭結點的指針,*p指向當前結點的前一個結點,*s指向當前結點*/
h->link=NULL; /*把表頭結點的鏈域置空*/
p=h; /*p指向表頭結點*/
p->link=s; /*把s的地址賦給p所指向的結點的鏈域,這樣就把p和s所指向的結點連接起來了*/
在代碼中除了創建鏈表的函數外,我們還使用了兩個函數,一個是SListReturn print(stud* head)函數,通過該函數我們打印輸出鏈表中的數據。另一個函數是SListReturn destroy(stud* head),通過該函數我們對申請的鏈表空間進行釋放。通過以上函數我想讀者應該知道了如何創建一個鏈表和處理鏈表中的數據,在這里我僅僅是對數據做了打印操作,如果讀者有興趣可以進行其他的操作。
以上代碼實現了單鏈表的創建,但是鏈表的常用操作還有查找、插入、刪除等沒有講解,刪除操作與插入操作類似,就不在這里一一講解了,在此我們以查找和插入為例進行講解,但是讀者在編寫刪除操作的時候別忘了把刪除的結點釋放掉。接下來我們就來看看一段查找和插入的操作。
代碼功能為在查找到的結點后面添加一個新的結點。
#include
#include
#include
#include
#define N 3 /*N為人數*/
//#undef _EXAM_ASSERT_TEST_ //禁用
#define _EXAM_ASSERT_TEST_ //啟用
#ifdef _EXAM_ASSERT_TEST_ //啟用斷言測試
void assert_report( const char * file_name, const char * function_name, unsigned int line_no )
{
printf( "\n[EXAM]Error Report file_name: %s, function_name: %s, line %u\n",
file_name, function_name, line_no );
abort();
}
#define ASSERT_REPORT( condition ) \
do{ \
if ( condition ) \
NULL; \
else \
assert_report( __FILE__, __func__, __LINE__ ); \
}while(0)
#else // 禁用斷言測試
#define ASSERT_REPORT( condition ) NULL
#endif /* end of ASSERT */
typedef enum _SListReturn
{
SLIST_RETURN_OK,
}SListReturn;
typedef struct node
{
char name[20];
int score;
struct node *link;
}stud;
stud * creat(int n)
{
stud *p,*h,*s;
int i;
if((h=(stud *)malloc(sizeof(stud)))==NULL)
{
printf("分配內存空間失敗!");
exit(0);
}
h->name[0]='\0'; /*把表頭結點的數據域置空*/
h->score=0;
h->link=NULL; /*把表頭結點的鏈域置空*/
p=h; /*p指向表頭結點*/
for(i=0;i
{
if((s= (stud *) malloc(sizeof(stud)))==NULL)
{
printf("分配內存空間失敗!");
exit(0);
}
p->link=s; /*把s的地址賦給p所指向的結點的鏈域,這樣就把p和s所指向的結點連接起來了*/
printf("請輸入第%d個人的姓名:",i+1);
scanf("%s",s->name); /*姓名*/
printf("請輸入第%d個人的成績:",i+1);
scanf("%d",&s->score); /*分數*/
s->link=NULL;
p=s;
}
return(h);
}
stud * search(stud *h,char *x) /*查找函數*/
{
stud *p;
char *y;
p=h->link;
while(p!=NULL)
{
y=p->name;
if(strcmp(y,x)==0)
return(p);
else p=p->link;
}
if(p==NULL)
printf("沒有查找到該數據!");
}
SListReturn insert(stud *p) /*插入函數,在指針p后插入*/
{
char stu_name[20];
int stu_score;
stud *s; /*指針s是保存新結點地址的*/
if((s= (stud *) malloc(sizeof(stud)))==NULL)
{
printf("分配內存空間失敗!");
exit(0);
}
printf("請輸入你要插入的人的姓名:");
scanf("%s",stu_name);
printf("請輸入你要插入的人的成績:");
scanf("%d",&stu_score);
strcpy(s->name,stu_name); /*把指針stuname所指向的數組元素拷貝給新結點的數據域*/
s->score=stu_score;
s->link=p->link; /*把新結點的鏈域指向原來p結點的后繼結點*/
p->link=s; /*p結點的鏈域指向新結點*/
return SLIST_RETURN_OK;
}
SListReturn destroy(stud* head)
{
stud* tmp,*next;
tmp=head;
int i=0;
while(tmp!=NULL)
{
next=tmp->link;
tmp->link=NULL;
free(tmp);
tmp=next;
i++;
printf("第%d次釋放\n",i);
}
return SLIST_RETURN_OK;
}
SListReturn print(stud* head)
{
stud* tmp=head->link;
while(tmp!=NULL)
{
printf("%s成績為%d\n",tmp->name,tmp->score);
tmp=tmp->link;
}
return SLIST_RETURN_OK;
}
void main()
{
int number; /*保存人數的變量*/
char fname[10]; /*保存輸入的要查找的人的姓名*/
stud *head,*searchpoint; /*head是保存單鏈表的表頭結點地址的指針*/
number=N;
head=creat(number); /*把所新建的單鏈表表頭地址賦給head*/
printf("請輸入你要查找的人的姓名:");
scanf("%s",fname);
searchpoint=search(head,fname); /*查找并返回查找到的結點指針*/
insert(searchpoint); /*調用插入函數*/
print(head);
destroy(head);
//ASSERT_REPORT(print(head)==SLIST_RETURN_OK);
//ASSERT_REPORT(destroy(head)==SLIST_RETURN_OK);
}
運行結果為:


以防圖片打開失敗,在此特地加上文字描述。
請輸入第1個人的姓名:rewq
請輸入第1個人的成績:123
請輸入第2個人的姓名:fdsa
請輸入第2個人的成績:456
請輸入第3個人的姓名:vcxz
請輸入第3個人的成績:789
請輸入你要查找的人的姓名:fdsa
請輸入你要插入的人的姓名:ghjk
請輸入你要插入的人的成績:369
rewq成績為123
fdsa成績為456
ghjk成績為369
vcxz成績為789
第1次釋放
第2次釋放
第3次釋放
第4次釋放
第5次釋放
Press any key to continue
代碼中的關鍵部分都加了注釋來進行說明,所以在此就不做一一講解了,只說幾個值得注意的地方,那就是destroy()函數的實現,可能有很多人對這兒的操作不是很熟悉,因為對于釋放成功與否都沒有能夠直觀的顯示出來,就算寫對了也還是不太確信,這個時候我們就要自己來加點東西了。所以在此特地教讀者一個方法,來進行簡單的驗證,通過i++;、 printf("第%d次釋放\n",i);語句來實現打印釋放了多少個結點,和我們創建的結點數目進行比較即可,在本代碼中我們一開始創建了4個結點(注意:包括頭結點),之后又插入了一個結點,所以總需要釋放5個結點,看看打印的結果就知道我們的函數實現正確了,當然還有很多驗證的方法,在此僅是例舉一個簡單的方法。
總不能沒玩沒了的寫下去吧,所以暫時到此為止,到下一篇博客我們接著講。由于本人水平有限,博客中的不妥或錯誤之處在所難免,殷切希望讀者批評指正。同時也歡迎讀者共同探討相關的內容,如果樂意交流的話請留下你寶貴的意見。
本文地址:http://m.qingdxww.cn/thread-160882-1-1.html     【打印本頁】

本站部分文章為轉載或網友發布,目的在于傳遞和分享信息,并不代表本網贊同其觀點和對其真實性負責;文章版權歸原作者及原出處所有,如涉及作品內容、版權和其它問題,我們將根據著作權人的要求,第一時間更正或刪除。
您需要登錄后才可以發表評論 登錄 | 立即注冊

廠商推薦

  • Microchip視頻專區
  • Dev Tool Bits——使用MPLAB® Discover瀏覽資源
  • Dev Tool Bits——使用條件軟件斷點宏來節省時間和空間
  • Dev Tool Bits——使用DVRT協議查看項目中的數據
  • Dev Tool Bits——使用MPLAB® Data Visualizer進行功率監視
  • 貿澤電子(Mouser)專區
關于我們  -  服務條款  -  使用指南  -  站點地圖  -  友情鏈接  -  聯系我們
電子工程網 © 版權所有   京ICP備16069177號 | 京公網安備11010502021702
快速回復 返回頂部 返回列表
主站蜘蛛池模板: 亚洲精品高清视频 | 看全大色黄大色黄大片一级爽 | 日日操夜夜操狠狠操 | 亚洲一卡2卡3卡4卡272 | 日韩一区二区久久久久久 | 欧美xxxx新一区二区三区 | 噜噜噜久久 | 四虎影视在线麻豆国产 | 四虎永久精品免费观看 | 少妇太爽了在线观看 | 日韩大片免费在线观看 | 日韩欧美精品综合一区二区三区 | 天天久久狠狠伊人第一麻豆 | 三面娜迦泰剧全集在线观看 | 亚洲福利视频一区二区三区 | 国产一区二区免费不卡在线播放 | 在线观看日韩一区 | 国产三级精品三级在专区 | 国产高清视频在线播放 | 天天干天天插 | 国产欧美视频一区二区三区 | 国产精品高清一区二区 | 日本一区二区三区视频在线观看 | 国产97在线观看 | 在线 色 | 日韩一区二区三区四区 | www.九九热| 亚洲国产高清视频在线观看 | 分享一个无毒不卡的网站 | 一级片在线观看视频 | 精品国产香蕉伊思人在线又爽又黄 | 蓝军出击电视剧在线观看 | 国产精品香蕉夜间视频免费播放 | 成人麻豆视频 | 欧美福利一区二区三区 | 国产精品一区二区三区四区 | 久久中文字幕一区二区三区 | 久久九九99热这里只有精品 | 曰批免费动漫视频播放免费 | 麻豆xxxxhd videos| 国产精品好好热在线观看 |