03.線性表(二)鏈式儲存結構.單鏈表1,鏈式單鏈

來源:互聯網
上載者:User

03.線性表(二)鏈式儲存結構.單鏈表1,鏈式單鏈
鏈式儲存結構.單鏈表11.基本概念    為了表示每個資料元素ai與其直接後繼資料元素ai+1之間的邏輯關係,對資料元素ai來說,除了儲存其本身的資訊之外,還需儲存一個指示其直接後繼的資訊(即直接後繼的儲存位置)(1)資料域:儲存線性表資料元素資料資訊的域稱為資料域;(2)指標域:把儲存直接後繼位置(下一個資料元素的地址)的域稱為指標域,指標域中儲存的資訊為指標或鏈;(3)結點(Node):由資料域和指標域兩部分資訊組成資料元素ai的儲存映像,稱為結點。(4)頭指標:把鏈表中第一個結點的儲存位置叫做頭指標,因此整個鏈表的存取就必須從頭指標開始進行。(5)頭結點:為了更加方便地對鏈表進行操作,我們在單鏈表的第一個結點前附設一個結點,即為頭結點。頭結點的資料域可以不儲存任何資訊,也可以儲存如線性表的長度等附加資訊,頭結點的指標域儲存指向第一個結點的指標。

升華筆記一:頭指標和頭結點的異同?頭指標:a.頭指標是指鏈表指向第一個結點的儲存位置指標,若鏈表有頭結點,則是指向頭結點的指標。b.頭指標具有標識作用,所以常用頭指標冠以鏈表的名字;c.無論鏈表是否為空白,頭指標均不為空白。頭指標是鏈表的必要元素。頭結點:a.頭結點是為了操作的統一和方便而設立的,放在第一個元素的結點之前,其資料域一般無意義(也可以存放鏈表的長度);b.有了頭結點,對在第一元素結點前插入結點和刪除第一結點,其操作與其他結點的操作就同一了;c.頭結點不一定是鏈表必要素。 2.單鏈表     n個結點(ai的儲存映像)鏈結成一個鏈表,即為線性表(a1,a2....an)的鏈式儲存結構,因為此鏈表的每個結點只包含一個指標域,所以稱之為單鏈表。 3.鏈式儲存結構指標程式碼範例/*線性表的單鏈表格儲存體結構  *    結點由存放資料元素的資料域和存放後繼結點地址的指標域組成*/typedef int ElemType; 
typedef struct Node{    ElemType data;            //int類型成員變數,作為結點的資料域    struct Node *next;        //struct Node類型成員指標變數,作為結點的指標域}Node;typedef struct Node *LinkList;    //定義LinkList注釋:(1)Node相當於struct Node結構體;*LinkList相當於struct Node結構體(2)假設指標p是指向線性表第i個元素的指標,則結點ai資料域和指標域為p->data、p->next即:     p->data的值為該結點ai的資料域,內容是一個資料元素;     p->next的值是一個指標,指向第(i+1)個元素(結點)的儲存地址,即指向ai+1的指標即p->data=ai   p->next->data=ai+1 4.單鏈表的讀取、插入與刪除操作 (1)單鏈表的讀取操作實現操作: GetElem(LinkList L,int i,ElemType *e),從單鏈表L中取出第i個資料元素,存放到指標變數i指向的地址空間中。演算法思路:不像線性表的順序儲存結構可以很容易的從任意位置讀取一個元素,單鏈表的資料元素讀取必須從頭開始找,即工作指標p後移。a.聲明一個結點p指向鏈表第一個結點,初始化j從1開始遍曆b.當j<i時,就遍曆鏈表,讓p的指標向後移動,不斷指向下一結點,j累加1,直到鏈表末尾p為空白。則說明第i個元素不存在c.如果p為空白結點或者遍曆位置超過i,拋出異常d.尋找成功,返回結點p的資料。源碼實現:/*初始條件:順序線性表L已存在,1=《i<=ListLength(L) *操作結構:用e返回L中第i個資料元素的值*/#define OK 1#define ERROR 0typedef int Status; 
typedef int ElemType; 
typedef struct Node *LinkList;    //定義LinkList
Status GetElem(LinkList L,int i,ElemType *e){    int j;    LinkList p;        //聲明一個結點p    p=p->next;         //讓結點p指向鏈表L的第一個結點    j=1;    while(p&&j<i)     //從鏈表第一個結點開始遍曆,直到j=i-1,讓p指向儲存i-1位置所在的結點    {        p=p->next;     //讓p指向下一個結點        j++;    }    if(!p || j>i)        return ERROR;    *e=p->data;        //尋找成功,將鏈表中的第i個元素的資料存放區到指標e指向的空間    return OK;}注釋:p為新定義的一個結點,while(p&&j<i)的作用是使結點p從鏈表第一個結點開始遍曆,直到j=i-1,讓p指向儲存i-1位置所在的結點,直到鏈表末尾p為空白是說明第i個元素不存在。
(2)單鏈表插入操作實現操作: ListInsert(LinkList *L,int i,ElemType e),將資料元素e插入到單鏈表L第i個位置演算法思路: s->next =p->next; p->next=s;      a.聲明一個結點p指向鏈表第一個結點,初始化j從1開始遍曆b.當j<i時,就遍曆鏈表,讓p的指標向後移動,不斷指向下一結點,j累加1,直到鏈表末尾p為空白,則說明第i個元素不存在。c.如果p為空白結點或者遍曆位置超過i,拋出異常d.如果尋找成功,    -在系統中產生一個空結點s    -將資料元素e賦值給s->data;    -單鏈表的插入標準語句s->next=p->next,p->next=s源碼實現:/*初始條件:順序線性表L已存在,1=《i<=ListLength(L) *操作結構:將資料元素e插入到單鏈表L第i個位置*/#define OK 1#define ERROR 0typedef int Status; 
typedef int ElemType; 
typedef struct Node *LinkList;    //定義LinkList
Status ListInsert(LinkList *L,int i,ElemType e){    int j;    LinkList p,s;        //聲明一個結點p    p=*L;             j=1;    while(p&&j<i)     //從鏈表第一個結點開始遍曆,直到j=i-1,讓p指向儲存i-1位置所在的結點    {        p=p->next;     //讓p指向下一個結點        j++;    }    if(!p || j>i)        return ERROR;    s=(LinkList)malloc(sizeof(Node));    //產生新結點,即在記憶體中找了一小塊空地,準備用來存放e資料s結點    s->data=e;        //關鍵1:尋找成功,將資料元素e賦值給s結點的資料域   s->next =p->next;//關鍵2:將結點p的後繼結點賦值給s的後繼(p->next為指向結點p下一個儲存地址所在的結點)    p->next=s;        //關鍵3:設定結點p的後繼結點為結點s    return OK;}注釋:p為新定義的一個結點,while(p&&j<i)的作用是使結點p從鏈表第一個結點開始遍曆,直到j=i-1,讓p指向儲存i-1位置所在的結點,直到鏈表末尾p為空白是說明第i個元素不存在。 (3)單鏈表刪除操作實現操作: ListDelete(LinkList *L,int i,ElemType *e),刪除單鏈表L中第i個資料元素,並將資料存放至指標e指向的空間中演算法思路:{ p->next=p->next->next(或者q=p->next;p->next=q->next) }   a.聲明一個結點p指向鏈表第一個結點,初始化j從1開始遍曆b.當j<i時,就遍曆鏈表,讓p的指標向後移動,不斷指向下一結點,j累加1,直到鏈表末尾p為空白,則說明第i個元素不存在。c.如果p為空白結點或者遍曆位置超過i,拋出異常d.如果尋找成功    -將欲刪除的結點p->next賦值給q    -設定結點p的後繼結點為q->next,即p->next=q->next;    -將結點q的資料域資料存放至ee.釋放q結點,返回成功標識源碼實現:/*初始條件:順序線性表L已存在,1=《i<=ListLength(L) *操作結構:刪除單鏈表L中第i個資料元素,並將資料存放至指標e指向的空間中*/#define OK 1#define ERROR 0typedef int Status; 
typedef int ElemType; 
typedef struct Node *LinkList;    //定義LinkList
Status ListInsert(LinkList *L,int i,ElemType *e){    int j;    LinkList p,q;        //聲明一個結點p    p=*L;            j=1;    while(p->next&&j<i)     //從鏈表第一個結點開始遍曆,直到j=i-1,讓p指向儲存i-1位置所在的結點    {        p=p->next;     //讓p指向下一個結點        j++;    }    if(!p || j>i)        return ERROR;    q=p->next;            //設定q為p的後繼結點    p->next=q->next;   //將q的後繼結點設定為p的後繼結點    *e=q->data;    //將q結點中的資料給e    free(q);           //設定完成後,讓系統回收此結點,釋放記憶體    return OK;}
升華筆記二:s、p、s->data、s->next、p->next辨析?(1)LinkList p,s:聲明兩個結點p、s,包含資料域和指標域;(2)s->data:為結點s的資料域,其值為一個資料(3)s->next::為結點s的指標域,其值為下一個結點儲存地址(4)s->next=p->next:將結點p的下一個結點儲存地址賦值給s結點指標域(5)p->next=s; 將結點s設定為p的下一個結點事實上,(4)(5)我們可以這樣理解:    假設初始鏈表相連兩個結點p、q,即......-p-q-......,現在我們需要在結點p、q之間插入一個結點s,則    方法如下:由於p->next=q                        則s->next=q        //結點s後繼為q                            p->next=s        //結點p後繼為s,此時即可形成......-p-s-q-......
5.順序儲存結構效能分析(1)尋找:最好情況時間複雜度為O(1),最壞情況事件複雜度為O(2)(2)刪除、插入:單鏈表的插入和刪除主要由兩部分組成:第一部分是遍曆查詢第i個元素;第二部分就是插入和刪除元素    從整個演算法來說,我們很容易推匯出:它們的時間複雜度都是O(n)。如果在我們不知道第i個元素的指標位置,單鏈表資料結構在插入和刪除操作上,與線性表的順序儲存解僱是沒有太大的優勢。但是如果我們希望從第i個位置,插入10個元素,對於順序儲存結構意味著,每一次插入都需要移動n-i個元素,每次都是O(n)。而對於單鏈表,我們只需要在第一次找到第i個位置的指標,此時為O(n),接下來只是簡單地通過賦值移動指標而已,時間複雜度都是O(1)。總結:對於插入或刪除資料越頻繁的操作,單鏈表的效率優勢就越明顯。

相關文章

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.