數(shù)據(jù)結構之鏈表及應用

**程序 = 數(shù)據(jù)結構 + 算法 **

數(shù)據(jù)結構的設計對于程序的結構和性能都至關重要泛鸟。合理的數(shù)據(jù)結構不僅可以降低算法的復雜性雁歌,使程序更易于理解和維護沾凄,并且可以極大地提升程序的性能挖腰。

數(shù)據(jù)結構綜述.png

上圖已經(jīng)列出了常用的數(shù)據(jù)結構和基本的操作词身,本文主要講述線性結構的鏈表的創(chuàng)建和增刪改查等基本操作屡穗。
首先明確幾個名詞和其對應的英文單詞:
list(表)贴捡、queue(隊列)、stack(棧)村砂、tree(樹)烂斋、graph(圖)
link(鏈式)、sequence(順序)础废、node(節(jié)點)

  1. 順序表
    順序表比較簡單汛骂,不是本文的重點,在這里只做簡單的介紹评腺。比較著急的童鞋可直接跳到第二部分帘瞭。
    順序表和數(shù)組其實是差不多的,只不過數(shù)組是存儲在椵锛ィ空間蝶念,而表需要在堆空間申請內(nèi)存空間(請參考這篇介紹:內(nèi)存管理).
    定義順序表的結構體:
typedef struct _seqlist_
{
        int *data;//可以理解為int data[tlen],指向內(nèi)存片段的首地址
        int clen;//順序表當前長度
        int tlen;//順序表總長度
}seqlist_st;

順序表基本操作:

  • 初始化函數(shù)
seqlist_st *seqlist_init(int len)//傳入表的總長度,類似于數(shù)組的大小
{
        seqlist_st *list = NULL;

        list = (seqlist_st *)malloc(sizeof(seqlist_st));//向內(nèi)存申請表頭結構體的內(nèi)存空間
        list->data = (int *)malloc(sizeof(int) * len);//申請順序表的內(nèi)存大小芋绸,類似于 int data[len]
        list->clen = 0;//表的當前長度
        list->tlen = len;//表的總長度

        return list;
}
  • 銷毀函數(shù)
 int seqlist_destroy(seqlist_st *list)
{
        free(list->data);//先把結構體內(nèi)部申請到的內(nèi)存釋放
        free(list);//再釋放表頭結構體的內(nèi)存地址

        return 0;
}

  • 順序表的插入操作:由于順序表的內(nèi)存地址是連續(xù)的媒殉,所以插入操作比較麻煩,需要將插入位置及之后位置的數(shù)值全部后移一位摔敛。這里只簡單地將元素插入尾部廷蓉。
int seqlist_insert(seqlist_st *list,int value)
{
        if(list->clen >= list->tlen)//判斷鏈表長度是否超過申請的內(nèi)存大小
                return -1;
        list->data[list->clen++] = value;//clen記得要執(zhí)行 +1 操作噢!

        return 0;
}

  • 順序表的刪除操作同樣需要將之后的元素全部前移一個位置:
int seqlist_delete(seqlist_st *list, int value)
{
        int i, j;

        for(i=0; i < list->clen; i++)
        {
                if(list->data[i] == value)//刪除表中所以得value單元
                {
                        for(j=i; j+1 < list->clen; j++)//將value元素之后的所以單元前移一個位置
                                list->data[j] = list->data[j+1];    
                        list->clen--;//將當前長度 -1
                        i--;//請認真理解此處為何要 -1
                }
        }

        return 0;
}

  • 將表中的第一個old替換為new,若不存在old則返回 -1
int seqlist_modify(seqlist_st *list,int old,int new)
{
        int i;
        for(i = 0;i < list->clen;i++)
        {
                if(list->data[i]== old)//找到第一個old并將其替換為new
                        break;
        }

        if(i>=list->clen)
                return -1;

        list->data[i]=new;

        return 0;
}```
 - 查
  看表中是否存在此值

int seqlist_search(seqlist_st *list,int value)
{
int i;

    for(i = 0;i<list->clen;i++)
    {
            if(list->data[i]==value)//查找第一個vlaue值
                    break;
    }

    if(i>=list->clen)
            return 0;

    return 1;

}

  這里給出一個show()函數(shù)和main()函數(shù)以方便測試順序表的基本操作是否正確:

int seqlist_show(seqlist_st *list)
{
int i=0;
for(i=0;i<list->clen;i++)
printf("%5d ",list->data[i]);
printf("\n");

    return 0;

}```

int main(int argc, const char *argv[])
{
        seqlist_st *list = NULL;
        int value = 100;

        list = seqlist_init(10);

        while(0 == seqlist_insert(list,value))
                value += 100;
 
        seqlist_show(list);
        seqlist_delete(list,600);
        seqlist_show(list);
        printf("search(300)= %d \n",seqlist_search(list,300));
        printf("modify(200,300)= %d \n",seqlist_modify(list,200,300));
        seqlist_show(list);
        printf("search(200)= %d \n",seqlist_search(list,200));
        seqlist_delete(list,300);
        seqlist_show(list);
        seqlist_destroy(list);

        return 0;
}
  1. 鏈表
    鏈表和順序表的本質區(qū)別在于:鏈式表在堆內(nèi)存中不是順序存儲的马昙,而是鏈式存儲的桃犬,即相鄰的兩個單元在物理位置上并不相鄰,也即地址并不連續(xù)行楞。而是前一個節(jié)點里面存儲的后一個節(jié)點的內(nèi)存地址攒暇,通過此種方式實現(xiàn)存儲和訪問。
    鏈式表的創(chuàng)建和基本的操作我們直接在下面的完整的程序中進行說明:
鏈表的內(nèi)存視圖
#include <stdio.h>
#include <stdlib.h>

typedef struct _linknode_//鏈表的節(jié)點結構體
{
    int data;//存儲節(jié)點的值
    struct _linknode_ *next;//存儲后續(xù)單元的地址
}linknode_t;

typedef struct _linklist_//鏈表的結構體
{
    struct _linknode_ *head;//鏈表頭的地址
    int clen;//鏈表當前的長度
    int tlen;//鏈表的總長度
}linklist_t;

int linklist_destroy(linklist_t *list);
linklist_t *linklist_init(int len);
int linklist_insert(linklist_t *list,int value);
linknode_t *creat_linknode(int value);
int linklist_show(linklist_t *list);
int linklist_delete(linklist_t *list,int value);
linknode_t * linklist_search(linklist_t *list,int value);
int linklist_modify(linklist_t *list,int old,int new);
int linklist_insertend(linklist_t *list,int value);
int linklist_insertmid(linklist_t *list,int local_value,int destin_value);

int main(int argc, const char *argv[])
{
    int value = 100;
    linknode_t *ret=NULL;
    linklist_t *list = NULL;//鏈表首地址

    list = linklist_init(10);//初始化鏈表敢伸,傳入鏈表長度

    while(0 == linklist_insertend(list,value))//使用尾差法將值插入表中
        value +=100;

    linklist_show(list);//查看表

    linklist_delete(list,600);//刪除表中值為600的節(jié)點
  
    linklist_show(list);
    ret = linklist_search(list,600);//查找鏈表中值為600的節(jié)點,返回其結構體地址

    if(ret == NULL)
        printf("search : NULL \n");
    else
        printf("%d \n",ret->data);

    linklist_modify(list,500,543);//修改
    ret = linklist_search(list,543);

    if(ret == NULL)
        printf("search : NULL \n");
    else
        printf("%d \n",ret->data);
    
    linklist_insertmid(list,400,678);//將678插入在400的下一節(jié)點

    linklist_show(list);
 
    linklist_destroy(list);//一定記得要銷毀鏈表釋放內(nèi)存噢恒削,不然會產(chǎn)生內(nèi)存泄露喔池颈。

    return 0;
}

linklist_t *linklist_init(int len)//鏈表的初始化
{
    linklist_t *list = NULL;

    list = (linklist_t *)malloc(sizeof(linklist_t));//鏈表的結構體地址
    list->head = creat_linknode(0);//鏈表的頭結點尾序,
    list->clen = 0;
    list->tlen = len;

    return list;
}

int linklist_destroy(linklist_t *list)//鏈表的銷毀
{
    linknode_t *p = list->head;
    linknode_t *temp = NULL;

    while(NULL != p)
    {
        temp = p;//從頭結點開始逐個釋放申請到的內(nèi)存空間
        p = p->next;
        free(temp);
    }
    free(list);

    return 0;
}

int linklist_insert(linklist_t *list,int value)//鏈表擦頭插發(fā),即將value值插入鏈表的第一個位置(頭結點之后)
{
    linknode_t *new = NULL;

    if(list->clen>=list->tlen)
        return -1;

    new = creat_linknode(value);
    new->next=list->head->next;//新建節(jié)點的next指向原頭節(jié)點的后繼節(jié)點
    list->head->next=new;//頭結點的next指向新建的節(jié)點躯砰,則完成了頭插法

    list->clen++;

    return 0;
}

linknode_t *creat_linknode(int value)
{
    linknode_t  *node = NULL;

    node = (linknode_t *)malloc(sizeof(linknode_t));//申請內(nèi)存空間每币,創(chuàng)建節(jié)點,鏈式表只在需要用時才去動態(tài)申請內(nèi)存空間琢歇,順序表和數(shù)組都是在一開始即初始化就申請了所需的全部內(nèi)存
    node->data = value;
    node->next = NULL;

    return node;//返回申請到的節(jié)點的內(nèi)存空間地址
}

int linklist_show(linklist_t *list)//輔助函數(shù)兰怠,順序打印鏈表的值
{
    int i;
    linknode_t *p = list->head->next;
    for(i=0;i<list->clen;i++)   
    {
        printf("%5d ",p->data);
        p=p->next;
    }
    putchar('\n');
    return 0;
}

int linklist_delete(linklist_t *list,int value)
{
    linknode_t *p = list->head;
    linknode_t *tmp = NULL;

    while(NULL != p->next)//找到需要刪除的節(jié)點的地址:注意,我們需要定位到其前一個節(jié)點李茫,即 p->next->data == value 的p揭保,請認真思考為什么
    {
        if(p->next->data == value)//
                break;
        p=p->next;
    }

    if(NULL == p->next)  
        return -1;
    
    tmp = p->next;//將要刪除的節(jié)點的地址(p->next)先暫存
    p->next = tmp->next;//將其前一個節(jié)點p的next指向其后繼節(jié)點,現(xiàn)在才可以釋放其內(nèi)存
    free(tmp);

    list->clen--;

    return 0;
}

 linknode_t * linklist_search(linklist_t *list,int value)
{
    linknode_t *p = list->head->next;
    while(NULL != p)
    {
        if(p->data == value)//搜索鏈表中值為value的節(jié)點魄宏,并將其地址返回秸侣,如不存在,則返回null
            return p;
        p=p->next;
    }

    return NULL;
}
int linklist_modify(linklist_t *list,int old,int new)
{
    linknode_t *p = list->head->next;
    while(NULL != p)
    {
        if(p->data == old)//找到old節(jié)點宠互,將其data值替換為指定的new
        {
            p->data = new;
            break;
        }       
            p=p->next;
    }
    
    if(NULL == p)//若沒有找到old節(jié)點味榛, 即鏈表中不存在值為old的結點,則返回-1
        return -1;

    return 0;
}
int linklist_insertend(linklist_t *list,int value)
{
    linknode_t *p = list->head;
    linknode_t *new = NULL;
    
    while(NULL != p->next)//找到最后一個節(jié)點
        p = p->next;
    
    if(list->clen >= list->tlen)
        return -1;

    new = creat_linknode(value);//創(chuàng)建新的節(jié)點
    p->next = new;//將找到的最后一個節(jié)點的next指向新創(chuàng)建的節(jié)點予跌,這樣新創(chuàng)建的節(jié)點就添加在整個鏈表的最后了
    list->clen++;

    return 0;
}

int linklist_insertmid(linklist_t *list,int local_value,int destin_value)
{
    linknode_t *p = list->head->next;
    linknode_t *new = NULL;
    linknode_t *tmp = NULL;

    if(list->clen >= list->tlen)
        return -1;

    while(NULL != p)
    {
        if(p->data == local_value)//找到值為local_value的節(jié)點的地址
        {
            tmp = p->next;//先將其后繼節(jié)點的地址存儲起來
            new = creat_linknode(destin_value);//創(chuàng)建新的節(jié)點
            p->next = new;//使local節(jié)點的next指向新插入的節(jié)點
            new->next = tmp;//新創(chuàng)建的節(jié)點的next指向原local的后繼節(jié)點搏色,完成了插入操作
            list->clen++;
            break;
        }
        p = p->next;
    }

    return 0;
}

上述實現(xiàn)的鏈表是單向鏈表,即只能從前一個節(jié)點找到后一節(jié)點券册,此外還有雙向鏈表(即能從前一個節(jié)點找到后一個節(jié)點频轿,也能從后一個節(jié)點找到前一個節(jié)點,即一個節(jié)點結構體不僅存了前一個節(jié)點的地址還存了后繼節(jié)點的地址)汁掠、循環(huán)鏈表(即尾節(jié)點的next不為null,而是存了第一個節(jié)點的地址)等略吨。只需在此基礎上稍加修改即可實現(xiàn)。我們可以根據(jù)具體的需求來選擇相應的數(shù)據(jù)結構考阱。

最后編輯于
?著作權歸作者所有,轉載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末翠忠,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子乞榨,更是在濱河造成了極大的恐慌秽之,老刑警劉巖,帶你破解...
    沈念sama閱讀 219,427評論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件吃既,死亡現(xiàn)場離奇詭異考榨,居然都是意外死亡,警方通過查閱死者的電腦和手機鹦倚,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,551評論 3 395
  • 文/潘曉璐 我一進店門河质,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事掀鹅∩⑿荩” “怎么了?”我有些...
    開封第一講書人閱讀 165,747評論 0 356
  • 文/不壞的土叔 我叫張陵乐尊,是天一觀的道長戚丸。 經(jīng)常有香客問我,道長扔嵌,這世上最難降的妖魔是什么限府? 我笑而不...
    開封第一講書人閱讀 58,939評論 1 295
  • 正文 為了忘掉前任,我火速辦了婚禮痢缎,結果婚禮上胁勺,老公的妹妹穿的比我還像新娘。我一直安慰自己牺弄,他們只是感情好姻几,可當我...
    茶點故事閱讀 67,955評論 6 392
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪务漩。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,737評論 1 305
  • 那天络拌,我揣著相機與錄音,去河邊找鬼回溺。 笑死春贸,一個胖子當著我的面吹牛,可吹牛的內(nèi)容都是我干的遗遵。 我是一名探鬼主播萍恕,決...
    沈念sama閱讀 40,448評論 3 420
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼车要!你這毒婦竟也來了允粤?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 39,352評論 0 276
  • 序言:老撾萬榮一對情侶失蹤翼岁,失蹤者是張志新(化名)和其女友劉穎类垫,沒想到半個月后,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體琅坡,經(jīng)...
    沈念sama閱讀 45,834評論 1 317
  • 正文 獨居荒郊野嶺守林人離奇死亡悉患,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,992評論 3 338
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了榆俺。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片售躁。...
    茶點故事閱讀 40,133評論 1 351
  • 序言:一個原本活蹦亂跳的男人離奇死亡坞淮,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出陪捷,到底是詐尸還是另有隱情碾盐,我是刑警寧澤,帶...
    沈念sama閱讀 35,815評論 5 346
  • 正文 年R本政府宣布揩局,位于F島的核電站,受9級特大地震影響掀虎,放射性物質發(fā)生泄漏凌盯。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 41,477評論 3 331
  • 文/蒙蒙 一烹玉、第九天 我趴在偏房一處隱蔽的房頂上張望驰怎。 院中可真熱鬧,春花似錦二打、人聲如沸县忌。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,022評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽症杏。三九已至,卻和暖如春瑞信,著一層夾襖步出監(jiān)牢的瞬間厉颤,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,147評論 1 272
  • 我被黑心中介騙來泰國打工凡简, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留逼友,地道東北人。 一個月前我還...
    沈念sama閱讀 48,398評論 3 373
  • 正文 我出身青樓秤涩,卻偏偏與公主長得像帜乞,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子筐眷,可洞房花燭夜當晚...
    茶點故事閱讀 45,077評論 2 355

推薦閱讀更多精彩內(nèi)容

  • 鏈表(Linked List)是一種物理存儲單元上非連續(xù)黎烈、非順序的存儲結構,數(shù)據(jù)元素的邏輯順序是通過鏈表中的指針鏈...
    蓋世英雄_ix4n04閱讀 614評論 0 0
  • /*Linklist h; 定義頭指針*/ /*ListNode *p; 定義指向某結點的指針,兩者類型相同*/ ...
    鐘美怡閱讀 284評論 0 1
  • 鏈表是線性表的一種浊竟。線性表是最基本怨喘、最簡單、也是最常用的一種數(shù)據(jù)結構振定。 線性表中數(shù)據(jù)元素之間的關系是一對一的關系必怜,...
    騎摩托馬斯閱讀 656評論 0 3
  • 大二了,大二了后频,一切都是原來的樣子梳庆,一切又都變了樣暖途。 還沒準備好就被時間扔回了學校,迷茫膏执,無助...
    啦里嘩稀閱讀 177評論 0 0
  • 說明 Docker必須安裝在64位平臺上驻售,且內(nèi)核版本必須高于3.10,一般裝于CentOS 7上 安裝 使用腳本自...
    夢想做小猿閱讀 189評論 0 0