鏈表總結

(鏈表的具體相關操做請參考本博客文章中的鏈表一類)javascript

鏈表(Linked list是一種常見的基礎數據結構,是一種線性表,是一種物理存儲單元上非連續、非順序的存儲結構。鏈表由一系列結點(鏈表中每個元素稱爲結點)組成,結點能夠在運行時動態生成。每一個結點包括存儲數據元素的數據域和存儲下一個結點地址的指針域兩個部分。 相比於線性表順序結構,操做複雜。數據元素的邏輯順序也是經過鏈表中的指針連接次序實現的。使用鏈表結構能夠克服數組鏈表須要預先知道數據大小的缺點,鏈表結構能夠充分利用計算機內存空間,實現靈活的內存動態管理。可是鏈表失去了數組隨機讀取的優勢,同時鏈表因爲增長告終點的指針域,空間開銷比較大。在計算機科學中,鏈表做爲一種基礎的數據結構能夠用來生成其它類型的數據結構。鏈表一般由一連串節點組成,每一個節點包含任意的實例數據(data fields)和一或兩個用來指向上一個/或下一個節點的位置的連接("links")。鏈表最明顯的好處就是,常規數組排列關聯項目的方式可能不一樣於這些數據項目在記憶體或磁盤上順序,數據的存取每每要在不一樣的排列順序中轉換。而鏈表是一種自我指示數據類型,由於它包含指向另外一個相同類型的數據的指針(連接)。鏈表容許插入和移除表上任意位置上的節點,可是不容許隨機存取。鏈表有不少種不一樣的類型:單向鏈表,雙向鏈表以及循環鏈表。鏈表能夠在多種編程語言中實現。像Lisp和Scheme這樣的語言的內建數據類型中就包含了鏈表的存取和操做。java

    

特色

線性表的鏈式存儲表示的特色是用一組任意的存儲單元存儲線性表的數據元素(這組存儲單元能夠是連續的,也能夠是不連續的)。所以,爲了表示每一個數據元素與其直接後繼數據元素 之間的邏輯關係,對數據元素 來講,除了存儲其自己的信息以外,還需存儲一個指示其直接後繼的信息(即直接後繼的存儲位置)。由這兩部分信息組成一個"結點"(如概述旁的圖所示),表示線性表中一個數據元素。線性表的鏈式存儲表示,有一個缺點就是要找一個數,必需要從頭開始找起,十分麻煩。node

圖7-3是單鏈表的結構:編程

 
                                             

基本操做:數組

1)創建:數據結構

順序建鏈表:編程語言

 

struct node *head;
struct node *creat(int n)
{
    int i;
    struct node *tail,*p;
    head=(struct node *)malloc(sizeof(struct node));
    head->next=NULL;
    tail=head;
    for(i=0;i<n;i++)
    {
        p=(struct node *)malloc(sizeof(struct node));
        scanf("%d",&p->data);
        p->next=NULL;
        tail->next=p;
        tail=p;
    }
    return(head);
};

 

逆序建鏈表:函數

struct node *head;
struct node *creat(int n)
{
    int i;
    struct node *p;
    head=(struct node *)malloc(sizeof(struct node));
    head->next=NULL;
    for(i=0;i<n;i++)
    {
        p=(struct node *)malloc(sizeof(struct node));
        scanf("%d",&p->data);
        p->next=head->next;
        head->next=p;
    }
    return(head);
};


2)查找:工具

從鏈表中刪除一個節點有三種狀況,即刪除鏈表頭節點、刪除鏈表的中
間節點、刪除鏈表的尾節點。題目給出的是學生姓名,則應在鏈表中從頭至尾依此查找各節
點,並與各節點的學生姓名比較,若相同,則查找成功,不然,找不到節點。因爲刪除的節
點可能在鏈表的頭,會對鏈表的頭指針形成丟失,因此定義刪除節點的函數的返回值定義爲
返回結構體類型的指針。學習

struct node *delet(head,pstr)//以he a d 爲頭指針,刪除pstr所在節點
struct node *head;
char *pstr;
{
    struct node *temp,*p;
    temp = head;// 鏈表的頭指針
    if (head==NULL) //鏈表爲空
        printf("\nList is null!\n");
    else //非空表
    {
        temp = head ;
        while (strcmp(temp->str,pstr)!=0&&temp->next!=NULL)// 若節點的字符串與輸入字符串不一樣,而且未到鏈表尾
        {
            p=temp;
            temp = temp->next; //  跟蹤鏈表的增加,即指針後移
        }
        if(strcmp(temp->str,pstr)==0 ) //找到字符串
        {
            if(temp==head)
            {
                //表頭節點
                printf("delete string :%s\n",temp->str);
                head = head->next;
                free (temp) ; //釋放被刪節點
            }
            else
            {
                p->next=temp->next; //表中節點
                printf("delete string :%s\n",temp->str);
                free( temp ) ;
            }
        }
        else printf("\nno find string!\n");//沒找 到要刪除的字符串
    }
    return(head) ;
//返回表頭指針*
}

3. 鏈表的插入
首先定義鏈表的結構:
struct
{
int num; /*學生學號* /
char str[20]; /*姓名* /
struct node *next;
} ;
在創建的單鏈表中,插入節點有三種狀況,如圖7 - 5所示。

插入的節點能夠在表頭、表中或表尾。假定咱們按照以學號爲順序創建鏈表,則插入的節點依次與表中節點相比較,找到插入位置。因爲插入的節點可能在鏈表的頭,會對鏈表的頭指針形成修改,因此定義插入節點的函數的返回值定義爲返回結構體類型的指針。節點的
插入函數以下:

struct node *insert(head,pstr,n) //插入學號爲n、姓名爲p s t r 的節點
struct node *head; //鏈表的頭指針
char *pstr;
int n;
{
    struct node *p1,*p2,*p3;
    p1=(struct node*)malloc(sizeof(struct node));
    分配//一個新節點
    strcpy(p1->str,pstr); // 寫入節點的姓名字串
    p1->num=n; // 學號
    p2=head;
    if (head==NULL) // 空表
    {
        head=p1;
        p1->next=NULL;//新節點插入表頭
    }
    else
    {
        //非空表
        while(n>p2->num&&p2->next!=NULL)
//輸入的學號小於節點的學號,而且未到表尾
        {
            p3=p2;
            p2=p2->next; //跟蹤鏈表增加
        }
        if (n<=p2->num) //找到插入位置
            if (head==p2) // 插入位置在表頭
            {
                head=p1;
                p1->next=p2;
            }
            else
            {
                //插入位置在表中
                p3->next=p1;
                p1->next=p2;
            }
        else
        {
            //插入位置在表尾
            p2->next=p1;
            p1->next=NULL;
        }
    }
    return ( head ) ; // 返回鏈表的頭指針
}

循環鏈表是與單鏈表同樣,是一種鏈式的存儲結構,所不一樣的是,循環鏈表的最後一個結點的指針是指向該循環鏈表的第一個結點或者表頭結點,從而構成一個環形的鏈。

循環鏈表的運算與單鏈表的運算基本一致。所不一樣的有如下幾點:

一、在創建一個循環鏈表時,必須使其最後一個結點的指針指向表頭結點,而不是象單鏈表那樣置爲NULL。此種狀況還使用於在最後一個結點後插入一個新的結點。

二、在判斷是否到表尾時,是判斷該結點鏈域的值是不是表頭結點,當鏈域值等於表頭指針時,說明已到表尾。而非象單鏈表那樣判斷鏈域值是否爲NULL。

雙向鏈表

雙向鏈表實際上是單鏈表的改進。

當咱們對單鏈表進行操做時,有時你要對某個結點的直接前驅進行操做時,又必須從表頭開始查找。這是由單鏈表結點的結構所限制的。由於單鏈表每一個結點只有一個存儲直接後繼結點地址的鏈域,那麼能不能定義一個既有存儲直接後繼結點地址的鏈域,又有存儲直接前驅結點地址的鏈域的這樣一個雙鏈域結點結構呢?這就是雙向鏈表

在雙向鏈表中,結點除含有數據域外,還有兩個鏈域,一個存儲直接後繼結點地址,通常稱之爲右鏈域;一個存儲直接前驅結點地址,通常稱之爲左鏈域。

應用舉例概述

約瑟夫環問題:已知n我的(以編號1,2,3...n分別表示)圍坐在一張圓桌周圍。從編號爲k的人開始報數,數到m的那我的出列;他的下一我的又從1開始報數,數到m的那我的又出列;依此規律重複下去,直到圓桌周圍的人所有出列。例如:n = 9,k = 1,m = 5。

參考代碼

#include<stdio.h>
#include<malloc.h>
#defineN41
#defineM5

typedef struct node*link;
struct node
{
    int item;
    link next;
};
link NODE(intitem,linknext)
{
    linkt=malloc(sizeof*t);
    t->item=item;
    t->next=next;
    return t;
}
int main(void)
{
    int i;
    link t=NODE(1,NULL);
    t->next=t;
    for(i=2; i<=N; i++)
t=t->next=NODE(i,t->next);
while(t!=t->next)
{
    for(i=1; i<M; i++)
            t=t->next;
        t->next=t->next->next;
    }
    printf("%d\n",t->item);
    return0;
}

 

其餘相關結語與我的總結

C語言是學習數據結構的很好的學習工具。理解了C中用結構體描述數據結構,那麼對於理解其C++描述,Java描述都就垂手可得了!鏈表的提出主要在於順序存儲中的插入和刪除的時間複雜度是線性時間的,而鏈表的操做則能夠是常數時間的複雜度。對於鏈表的插入與刪除操做,我的作了一點總結,適用於各類鏈表以下:

插入操做處理順序:中間節點的邏輯,後節點邏輯,前節點邏輯。按照這個順序處理能夠完成任何鏈表的插入操做。

刪除操做的處理順序:前節點邏輯,後節點邏輯,中間節點邏輯。

按照此順序能夠處理任何鏈表的刪除操做。

若是不存在其中的某個節點略過便可。

上面的總結,你們能夠看到一個現象,就是插入的順序和刪除的順序剛好是相反的,頗有意思!

相關文章
相關標籤/搜索