色哟哟视频在线观看-色哟哟视频在线-色哟哟欧美15最新在线-色哟哟免费在线观看-国产l精品国产亚洲区在线观看-国产l精品国产亚洲区久久

0
  • 聊天消息
  • 系統消息
  • 評論與回復
登錄后你可以
  • 下載海量資料
  • 學習在線課程
  • 觀看技術視頻
  • 寫文章/發帖/加入社區
會員中心
創作中心

完善資料讓更多小伙伴認識你,還能領取20積分哦,立即完善>

3天內不再提示

鏈表結點的數據結構該如何定義

電子工程師 ? 來源:互聯網 ? 作者:佚名 ? 2017-09-20 16:28 ? 次閱讀

近日周立功教授公開了數年的心血之作《程序設計與數據結構》,電子版已無償性分享到電子工程師與高校群體下載,經周立功教授授權,特對本書內容進行連載。

>>>1.1.1 數據與p_next分離

由于鏈表只關心p_next指針,因此完全沒有必要在鏈表結點中定義數據域,那么只保留p_next指針就好了。鏈表結點的數據結構(slist.h)定義如下:

1 typedef struct _slist_node{

2 struct _slist_node *p_next; //指向下一個結點的指針

3 }slist_node_t;

由于結點中沒有任何數據,因此節省了內存空間,其示意圖詳見3.10。

圖3.10 鏈表示意圖

當用戶需要使用鏈表管理數據時,僅需關聯數據和鏈表結點,最簡單的方式是將數據和鏈表結點打包在一起。以int類型數據為例,首先將鏈表結點作為它的一個成員,再添加與用戶相關的int類型數據,該結構體定義如下:

1 typedef struct _slist_int{

2 slist_node_t node; //包含鏈表結點

3 int data; // int類型數據

4 }slist_int_t;

由此可見,無論是什么數據,鏈表結點只是用戶數據記錄的一個成員。當調用鏈表接口時,僅需將node的地址作為鏈表接口參數即可。在定義鏈表結點的數據結構時,由于僅刪除了data成員,因此還是可以直接使用原來的slist_add_tail()函數,管理int型數據的范例程序詳見程序清單3.14。

程序清單3.14管理int型數據的范例程序

1 #include

2 typedef struct _slist_int{

3 slist_node_t node;

4 int data;

5 }slist_int_t;

6

7 int main (void)

8 {

9 slist_node_t head = {NULL};

10 slist_int_t node1, node2, node3;

11 slist_node_t *p_tmp;

12

13 node1.data = 1;

14 slist_add_tail(&head, &node1.node);

15 node2.data = 2;

16 slist_add_tail(&head, &node2.node);

17 node3.data = 3;

18 slist_add_tail(&head, &node3.node);

19 p_tmp = head.p_next;

20 while (p_tmp != NULL){

21 printf("%d ", ((slist_int_t *)p_tmp)->data);

22 p_tmp = p_tmp->p_next;

23 }

24 return 0;

25 }

由于用戶需要初始化headNULL,且遍歷時需要操作各個結點的p_next指針。而將數據和p_next分離的目的就是使各自的功能職責分離,鏈表只需要關心p_next的處理,用戶只關心數據的處理。因此,對于用戶來說,鏈表結點的定義就是一個“黑盒子”,只能通過鏈表提供的接口訪問鏈表,不應該訪問鏈表結點的具體成員。

為了完成頭結點的初始賦值,應該提供一個初始化函數,其本質上就是將頭結點中的p_next成員設置為NULL。鏈表初始化函數原型為:

int slist_init (slist_node_t *p_head);

由于頭結點的類型與其它普通結點的類型一樣,因此很容易讓用戶誤以為,這是初始化所有結點的函數。實際上,頭結點與普通結點的含義是不一樣的,由于只要獲取頭結點就可以遍歷整個鏈表,因此頭結點往往是被鏈表的擁有者持有,而普通結點僅僅代表單一的一個結點。為了避免用戶將頭結點和其它結點混淆,需要再定義一個頭結點類型(slist.h):

typedef slist_node_t slist_head_t;

基于此,將鏈表初始化函數原型(slist.h)修改為

int slist_init (slist_head_t *p_head);

其中,p_head指向待初始化的鏈表頭結點,slist_init()函數的實現詳見程序清單3.15。

程序清單3.15鏈表初始化函數

1 int slist_init (slist_head_t *p_head)

2 {

3 if (p_head == NULL){

4 return -1;

5 }

6 p_head -> p_next = NULL;

7 return 0;

8 }

在向鏈表添加結點前,需要初始化頭結點。即

slist_node_t head;

slist_init(&head);

由于重新定義了頭結點的類型,因此添加結點的函數原型也應該進行相應的修改。

int slist_add_tail (slist_head_t *p_head, slist_node_t *p_node);

其中,p_head指向鏈表頭結點,p_node為新增的結點,slist_add_tail()函數的實現詳見程序清單3.16。

程序清單3.16新增結點范例程序

1 int slist_add_tail (slist_head_t *p_head, slist_node_t *p_node)

2 {

3 slist_node_t *p_tmp;

4

5 if ((p_head == NULL) || (p_node == NULL)){

6 return -1;

7 }

8 p_tmp = p_head;

9 while (p_tmp -> p_next != NULL){

10 p_tmp = p_tmp -> p_next;

11 }

12 p_tmp -> p_next = p_node;

13 p_node -> p_next = NULL;

14 return 0;

15 }

同理,當前鏈表的遍歷采用的還是直接訪問結點成員的方式,其核心代碼如下:

1 slist_node_t *p_tmp = head.p_next;

2 while (p_tmp != NULL){

3 printf("%d ", ((slist_int_t *)p_tmp)->data);

4 p_tmp = p_tmp->p_next;

5 }

這里主要對鏈表作了三個操作:(1)得到第一個用戶結點;(2)得到當前結點的下一個結點;(3)判斷鏈表是否結束,與結束標記(NULL)比較。

基于此,將分別提供三個對應的接口來實現這些功能,避免用戶直接訪問結點成員。它們的函數原型為(slist.h)

slist_node_t *slist_begin_get (slist_head_t *p_head); //獲取開始位置第一個用戶結點

slist_node_t *slist_next_get (slist_head_t *p_head, slist_node_t *p_pos);//獲取某一結點的后一結點

slist_node_t *slist_end_get (slist_head_t *p_head); //結束位置,尾結點下一個結點的位置

其實現代碼詳見程序清單3.17。

程序清單3.17遍歷相關函數實現

1 slist_node_t *slist_next_get (slist_head_t *p_head, slist_node_t *p_pos)

2 {

3 if (p_pos) { //找到p_pos指向的結點

4 return p_pos->p_next;

5 }

6 return NULL;

7 }

8

9 slist_node_t *slist_begin_get (slist_head_t *p_head)

10 {

11 return slist_next_get(p_head, p_head);

12 }

13

14 slist_node_t *slist_end_get (slist_head_t *p_head)

15 {

16 return NULL;

17 }

程序中獲取的第一個用戶結點,其實質上就是頭結點的下一個結點因此可以直接調用slist_next_get()實現。盡管slist_next_get()在實現時并沒有用到參數p_head,但還是將p_head參數傳進來了,因為實現其它的功能時將會用到p_head參數比如,判斷p_pos是否在鏈表中。當有了這些接口函數后,即可完成遍歷,詳見程序清單3.18。

程序清單3.18使用各個接口函數實現遍歷的范例程序

1 slist_node_t *p_tmp = slist_begin_get(&head);

2 slist_node_t *p_end = slist_end_get(&head);

3 while (p_tmp != p_end){

4 printf("%d ", ((slist_int_t *)p_tmp)->data);

5 p_tmp = slist_next_get(&head, p_tmp);

6 }

由此可見,slist_begin_get()和slist_end_get()的返回值決定了當前有效結點的范圍,其范圍為一個半開半閉的空間,:[begin,end),包括begin但是不包括end。當begin與end相等時,表明當前鏈表為空,沒有一個有效結點。

程序清單3.18所示的遍歷程序中,只有printf()語句才是用戶實際關心的語句,其它語句都是固定的模式,為此可以封裝一個通用的遍歷函數,便于用戶順序處理與各個鏈表結點相關聯的數據。顯然,只有使用鏈表的用戶才知道數據的具體含義,對數據的實際處理應該交由用戶完成,比如,程序清單3.18中的打印語句,因此訪問數據的行為應該由用戶定義,定義一個回調函數,通過參數傳遞給遍歷函數,每遍歷到一個結點時,都調用該回調函數處理對數據進行處理。遍歷鏈表的函數原型(slist.h)為:

typedef int (*slist_node_process_t) (void *p_arg, slist_node_t *p_node);

int slist_foreach(slist_head_t *p_head,

slist_node_process_t pfn_node_process,

void *p_arg);

其中,p_head指向鏈表頭結點,pfn_node_process為結點處理回調函數。每遍歷到一個結點時,都會調用pfn_node_process指向的函數,便于用戶根據需要自行處理結點數據。當調用該回調函數時,會自動將用戶參數p_arg作為回調函數的第1個參數,將指向當前遍歷到的結點的指針作為回調函數的第2個參數。

當遍歷到某個結點時,用戶可能希望終止遍歷,此時只要在回調函數中返回負值即可。一般地,若要繼續遍歷,函數執行結束后返回0。slist_foreach()函數的實現詳見程序清單3.19。

程序清單3.19遍歷鏈表范例程序

1 int slist_foreach( slist_head_t *p_head,

2 slist_node_process_t pfn_node_process,

3 void *p_arg);

4

5 {

6 slist_node_t *p_tmp, *p_end;

7 int ret;

8

9 if ((p_head == NULL) || (pfn_node_process == NULL)){

10 return -1;

11 }

12 p_tmp = slist_begin_get(p_head);

13 p_end = slist_end_get(p_head);

14 while (p_tmp != p_end){

15 ret = pfn_node_process(p_arg, p_tmp);

16 if (ret < 0)???? return ret;????? ???????????? //?不再繼續遍歷

17 p_tmp = slist_next_get(p_head, p_tmp); //繼續下一個結點

18 }

19 return 0;

20 }

現在可以使用這些接口函數迭代如程序清單3.14所示的功能詳見程序清單3.20。

程序清單3.20管理int型數據的范例程序

1 #include

2 #include "slist.h"

3

4 typedef struct _slist_int {

5 slist_node_t node; //包含鏈表結點

6 int data; // int類型數據

7 }slist_int_t;

8

9 int list_node_process (void *p_arg, slist_node_t *p_node)

10 {

11 printf("%d ", ((slist_int_t *)p_node)->data);

12 return 0;

13 }

14

15 int main(void)

16 {

17 slist_head_t head; //定義鏈表頭結點

18 slist_int_t nodel, node2, node3;

19 slist_init(&head);

20

21 node1.data = 1;

22 slist_add_tail(&head, &(node1.node));

23 node2.data = 2;

24 slist_add_tail(&head, &(node2.node));

25 node3.data = 3;

26 slist_add_tail(&head, &(node3.node));

27 slist_foreach(&head, list_node_process, NULL); //遍歷鏈表,用戶參數為NULL

28 return 0;

29 }
聲明:本文內容及配圖由入駐作者撰寫或者入駐合作網站授權轉載。文章觀點僅代表作者本人,不代表電子發燒友網立場。文章及其配圖僅供工程師學習之用,如有內容侵權或者其他違規問題,請聯系本站處理。 舉報投訴
  • 電子工程師
    +關注

    關注

    252

    文章

    769

    瀏覽量

    95652
  • 數據結構
    +關注

    關注

    3

    文章

    573

    瀏覽量

    40154
  • 周立功
    +關注

    關注

    38

    文章

    130

    瀏覽量

    37672
  • 鏈表
    +關注

    關注

    0

    文章

    80

    瀏覽量

    10572

原文標題:周立功:數據與p_next分離

文章出處:【微信號:Zlgmcu7890,微信公眾號:周立功單片機】歡迎添加關注!文章轉載請注明出處。

收藏 人收藏

    評論

    相關推薦

    數據結構中最簡單的鏈表

    數據結構作為嵌入式工程師必修課程之一,今天,我們就來講一講數據結構中最簡單的鏈表,包含鏈表的初始化、插入和遍歷操作。 鏈表在項目開發中使用的
    發表于 06-13 17:40 ?375次閱讀

    數據結構:單鏈表的排序

    給定一個單鏈表的頭結點head(結點有值),長度為n的無序單鏈表,對其按升序排序后,返回新鏈表
    的頭像 發表于 11-30 13:56 ?1612次閱讀
    <b class='flag-5'>數據結構</b>:單<b class='flag-5'>鏈表</b>的排序

    數據結構:刪除有序鏈表的重復節點

    給定一個有序單鏈表(從小到大有序)的頭結點head(結點有值),刪除鏈表中的重復元素,使鏈表
    的頭像 發表于 12-05 15:46 ?965次閱讀
    <b class='flag-5'>數據結構</b>:刪除有序<b class='flag-5'>鏈表</b>的重復節點

    數據結構

    1.數據結構的概念 所謂數據結構是指由某一數據對象及對象中所有數據成員之間的關系組成的集合。成員之間的關系有很多種,最常見的是前后件關系。
    發表于 03-04 14:13

    Linux Kernel數據結構:鏈表

    Linux Kernel數據結構鏈表原創 2016年10月20日 22:58:25標簽:LINUX/kernel/鏈表 數據結構數據結構
    發表于 09-25 16:41

    數據結構之鏈式棧介紹

    數據結構之鏈式棧鏈式棧鏈式棧的定義鏈式棧操作的實現鏈式棧初始化鏈式棧入棧鏈式棧出棧鏈式棧初始化鏈式棧鏈式棧無棧滿問題,空間可以擴充插入與刪除僅在棧頂處執行鏈式棧的棧頂在鏈頭鏈式棧的定義 //
    發表于 12-17 08:11

    數據結構鏈表的基本操作

    嵌入式學習基礎-數據結構鏈表的基本操作鏈表節點采用結構體的方式進行定義,下面是最基礎的定義只有一
    發表于 12-22 08:05

    在RT-Thread中普通鏈表和侵入式鏈表有何區別

    普通鏈表學習數據結構的時候寫的鏈表是下面這個樣子侵入式鏈表在 RT-Thread 以及 Linux 內核中鏈表是這樣
    發表于 04-11 15:15

    Linux內核中的數據結構的一點認識

    成員,那么到時候鏈表中沒有任何數據,這樣的鏈表有什么用呢?其實這就是內核鏈表設計的巧妙之處,因為在整個內核中需要使用鏈表來存放的
    發表于 04-20 16:42

    RT-Thread中侵入式鏈表的應用有哪些呢

    侵入式鏈表普通鏈表學習數據結構的時候寫的鏈表是下面這個樣子的typedef struct LNode{int data;/* 數據
    發表于 12-05 13:59

    算法與數據結構——雙向鏈表

    第三章為算法與數據結構,本文為3.3 雙向鏈表
    的頭像 發表于 09-19 17:56 ?7304次閱讀
    算法與<b class='flag-5'>數據結構</b>——雙向<b class='flag-5'>鏈表</b>

    鏈表的基礎知識

    在學習數據結構的時候,最開始接觸到的一種數據結構就是線性表,對于線性表的定義是: **零個或多個數據元素的有限序列** ,那對于線性表來講,又分為順序存儲
    的頭像 發表于 01-20 17:00 ?1099次閱讀
    <b class='flag-5'>鏈表</b>的基礎知識

    LeetCode876鏈表的中間結點介紹

    給定一個頭結點為 head 的非空單鏈表,返回鏈表的中間結點。
    的頭像 發表于 01-11 17:58 ?833次閱讀
    LeetCode876<b class='flag-5'>鏈表</b>的中間<b class='flag-5'>結點</b>介紹

    Linux內核的鏈表數據結構

    Linux內核實現了自己的鏈表數據結構,它的設計與傳統的方式不同,非常巧妙也很通用。
    的頭像 發表于 03-24 11:34 ?850次閱讀
    Linux內核的<b class='flag-5'>鏈表</b><b class='flag-5'>數據結構</b>

    鏈表數據結構基本概念

    的必要元素。 頭節點: 頭結點是為了操作的統一和方便而設立的,放在第一元素的結點之前,其數據域一般無意義(也可存放鏈表的長度)。 有了頭結點
    的頭像 發表于 07-27 11:14 ?814次閱讀
    <b class='flag-5'>鏈表</b><b class='flag-5'>數據結構</b>基本概念
    主站蜘蛛池模板: china chinese中国人玩| 国产精品成人影院在线观看| 夜夜躁婷婷AV蜜桃视频| 亚洲毛片网| 一个人高清在线观看日本免费| 原神美女被超污app| 91亚洲精品| 成年人视频免费在线观看| 国产成人在线网站| 国语自产视频在线不卡| 九九热这里有精品| 免费看欧美一级特黄a大片| 欧美日韩精品不卡在线观看| 色戒西瓜视频| 亚洲国产精品天堂在线播放| 在线视频 国产精品 中文字幕| 99手机在线视频| 国产超碰人人爱被IOS解锁| 国色天香社区视频免费高清3| 开心久久激情| 欧美最猛12teevideos| 午夜aaaa| 中文无码第3页不卡av| xxnx日本| 黑人强伦姧人妻日韩那庞大的| 老师你奶真大下面水真多| 日本三级按摩推拿按摩| 亚洲黄视频在线观看| 99E久热只有精品8在线直播| 国产99久久九九精品无码不卡| 精品熟女少妇AV免费观看| 欧美尤物射精集锦| 亚洲欧美成人在线| gogo亚洲肉体艺术照片9090| 国产偷国产偷亚洲高清SWAG| 男女啪啪抽搐呻吟高潮动态图| 偷尝禁果H1V1幸运的山熊| 最新无码国产在线视频| 国产精品99久久久久久宅男AV| 恋夜影院支持安卓视频美女| 甜性涩爱快播|