數據結構導論(第二章線性表)

1、線性表的基本概念

線性表的邏輯結構 線性表是一種最簡單、最多見的數據結構算法

線性表是由n(n≥0)個數據元素(結點)a1,a2,a3,……an組成的有限序列。 數據元素的個數n定義爲表的長度。 當n=0時,稱爲空表數據結構

將非空的線性表(n>0)記做:L=(a1,a2,a3,……,an) a1:起始結點,an:終端結點。 a1稱爲a2的直接前驅,a3稱爲a2的直接後繼函數

線性表的特色:spa

  • 線性表中只有1個起始結點,1個終端結點, 起始結點沒有直接前驅,有1個直接後繼。
  • 終端結點有1個直接前驅,沒有直接後繼。
  • 除這2個結點外,每一個結點都有且只有1個直接前驅和1個直接後繼

2、線性表的順序存儲

線性表順序存儲的方法:將表中的結點依次存放在計算機內存 中的一組連續存儲單元中。指針

數據元素在線性表中的鄰接關係決定其在存儲空間中的存儲位置;即邏輯結構中相鄰的結點,其存儲位置也相鄰。code

 用順序存儲實現的線性表稱爲順序表。blog

順序表上的基本運算 : 插入(Insert)、刪除(Delete)、定位(Locate)內存

=======================插入=========================it

線性表的插入運算:是指在表的位置i上,插入一個新結點x,使 長度爲n的線性表(a1,a2,……,ai,……,an) 變爲長度爲n+1的線性表(a1,……,x,……,an)class

插入時的主義事項:

  • 當表空間已滿,不可再作插入操做。
  • 當插入位置是非法位置,不可作正常的插入操做。

順序表插入操做過程

  • 將表中位置爲n ,n-1,…,i上的結點,依次後移到位置n+1,n,…,i+1 上,空出位置i。
  • 在位置i上插入新結點x。 (當插入位置i=n+1時,無須移動結點,直接將x插入表的末尾)
  • 該順序表長度加1

插入算法的分析:

  • 假設線性表中含有n個數據元素,在進行插入操做時,有n+1個位置可插入,每一個位置插入數據的機率是:1/(n+1); 在位置i插入時,要移動n-i+1個數據。
  • 假定在n+1個位置上插入元素的可能性均等, 則平均移動元素的個數爲:

  

 

 須要移動:10-4+1=7 ;7個節點

======================刪除=====================

線性表的刪除運算:是指將表的位置i的結點刪去,使長度爲n的線性 表(a1,a2,……,ai,……,an)變成長度爲n-1的線性表 (a1,a2,……,ai-1,ai+1,……,an)

注意事項:當要刪除元素的位置i不在表長範圍內時,爲非法位置,不能作正 常的刪除操做

順序表刪除操做過程

  • 若i=n,則只要刪除終端結點,無須移動結點;
  • 若1≤i≤n-1,則必須將表中位置 i+1,i+2,……,n的結點,依次前移到 位置i,i+1,…,n-1上,以填補刪除操做形成的空缺。 (僅當刪除位置i=n時, 才無須移動結點,直接令表長度-1便可)
  • 該表長度減1

假設線性表中含有n個數據元素,在進行刪除操做時,有n個位置可刪除, 在位置i刪除時,要移動n-i個數據。

 

 

 

 結論:順序存儲結構表示的線性表,在作插入或刪除操做時,平均 須要移動大約一半的數據元素。當線性表的數據元素量較大, 而且常常要對其作插入或刪除操做時,這一點須要考慮

===================定位=====================

定位:從第一個元素a1起,依次和x比較, 直到找到一個與x相等的數據元素,則 返回它在順序表中的存儲下標或序號;當查遍整個表都沒找到與x相等的元 素,返回0。

定位算法的分析:

定位運算的功能是查找出線性表L中值等於x的結點序號的最小值,當不存 在這種結點時,結果爲0。

i從0開始,做爲掃描順序表時的下標: 

  • 最好狀況下,第1個元素就是x值,此時查找比較次數爲1。 
  • 最壞狀況下,最後1個元素是x值,此時查找比較次數爲n。 
  • 故平均查找長度爲(n+1)/2。

順序表的特色:

  • 設線性表中全部結點的類型相同,則每一個結點所佔用 存儲空間大小亦相同。
  • 假設表中每一個結點佔用L個存儲單元,其中第一個單元 的存儲地址則是該結點的存儲地址。
  • 並設表中開始結點a1的存儲地址是d,那麼結點ai的存 儲地址LOC(ai)

 順序表的優勢:

  • 無需爲表示結點間的邏輯關係而增長額外存儲空間 
  • 能夠方便地隨機存取表中的任一結點

順序表的缺點:

  • • 插入和刪除運算不方便,必須移動大量的結點
  • • 順序表要求佔用連續的空間,存儲分配只能預先進 行,所以當表長變化較大時,難以肯定合適的存儲 規模

3、線性表的鏈式存儲

結點結構:

 鏈表的具體存儲表示爲:

  • 用一組任意的存儲單元來存放。
  • 鏈表中結點的邏輯次序和物理次序不必定相同,還必須存儲指示其後繼 結點的地址信息

=================單鏈表===================

 

  • 單鏈表:全部結點經過指針連接而組成。 
  • NULL:空指針。
  • Head:頭指針。
  • 尾指針:指向最後一個元素(尾結點)的指針。

單鏈表的通常圖示法:因爲咱們經常只注重結點間的邏輯順序,不關心每一個結點的實際位置,能夠用 箭頭來表示鏈域中的指針

單鏈表各個結點在內存中的存儲位置(物理位置)並不必定連續

 

 頭結點:通常不存數據,利用Head存放該結點地址。在單鏈表中,增長頭結點的目的是方便運算

單鏈表特色總結:

  • 鏈表由頭指針惟一肯定,單鏈表能夠用頭指針的名字來命名。頭指針名是head3的鏈表可稱爲表head3。 
  • 起始節點又稱爲首結點,無直接前驅,故在無頭結點時,設頭指針head指向首結點。
  • 終端結點又稱尾結點,無直接後繼,故終端結點的指針域爲空,即NULL。 
  • 除頭結點以外的結點爲表結點,爲運算操做方便,頭結點中不存數據。

 

 單鏈表的實現:

 

 

一、初始化

  • 創建一個空的單鏈表L,InitiateLinkList(L) 一個空的單鏈表是一個頭指針和一個頭結點構成的
  • 假設已定義指針變量t,令t指向一個頭結點 並令頭結點的next爲NULL 
  • 注意:產生頭結點時由malloc函數產生一個新節點; 動態分配內存函數malloc函數格式:(數據類型*)malloc(sizeof(數據類型))  如:int *p;p=(int *)malloc(sizeof(int))
空表由一個頭指針和一個頭結點組成。算法描述以下:
LinkList InitiateLinkList( )
//創建一個空的單鏈表
{
LinkList head; //頭指針
head=malloc (sizeof (Node) ) ; //動態構建一結點,它是頭結點
head->next=NULL;
return head;
}
在算法中,變量head是鏈表的頭指針,它指向新建立的結點,即頭結點。一個空單鏈表僅有一個頭結點,它的指針域爲NULL。

二、求表長

在單鏈表存儲結構中,線性表的長度等於單鏈表所 含結點的個數(不含頭結點)

步驟:

  • 1,令計數器j爲0
  • 2,令p指向頭結點
  • 3,當下一個結點不空時,j加1,p指向下一個結點
  • 4,j的值即爲鏈表中結點個數,即表長度
int lengthLinklist (LinkList head){ 
    Node *p;
    p=head; j=0;
    while( p->next != NULL )
    { p=p->next;
    j++;
    }
    return(j);
}

三、讀表元素步驟:查找第i個結點

  • 一、令計數器j爲0
  • 二、令p指向頭結點
  • 三、當下一個結點不空時,而且j<i時,j加1,p指向下一個結點
  • 四、若是j等於i,則p所指結點爲要找的第i結點不然,鏈表中無第i結點
Node * GetlinkList( LinkList head, int i ){ 
    Node *p;
    p=head->next; int c=1;
    while ((c<i) && (p!=NULL) ){ p=p->next;
        c++;
    }
    if(i= =c) return(p);
    else return NULL;
}

四、刪除節點

算法思路(此算法描述刪除第i個結點)

① 找到第i-1個結點;若存在繼續,不然結束;

② 刪除第i個結點,並釋放對應的內存,結束

刪除運算是將表的第i個結點刪去。

(1)找到ai-1的存儲位置p

(2)令p->next指向ai的直接後繼結點

(3)釋放結點ai的空間,將其歸還給"存儲池" 。

算法的實現

在單鏈表中刪除第 i 個結點的基本操做爲:找到線 性表中第i-1個結點,修改其指向後繼的指針。

 

 

void DeleteLinklist(LinkList head, int i)
    //刪除表head的第i個結點
    {
    Node *q;
    if(i==1) q=head;
    else q=GetLinklist(head, i-1); //先找待刪結點的直接前驅
    if(q !== NULL && q->next != NULL) //若直接前驅存在且待刪結點存在
    {
        p=q->next; //p指向待刪結點
        q->next=p->next; //移出待刪結點
        free(p); //釋放已移出結點p的空間
    }
    else exit (「找不到要刪除的結點」); //結點不存在
}

注意:free(p)是必不可少的,由於當一個結點從鏈表移出後,若是不釋放它的空間,它將變 成一個無用的結點,它會一直佔用着系統內存空間,其餘程序將沒法使用這塊空間

 5. 定位

 定位運算是對給定表元素的值,找出這個元素的位置。對 於單鏈表,給定一個結點的值,找出這個結點是單鏈表的 第幾個結點。定位運算又稱爲按值查找

 具體步驟:

  • 一、令p指向頭結點
  • 二、令i=0
  • 三、當下一個結點不空時,p指向下一個結點,同時i的值加1
  • 四、直到p指向的結點的值爲x,返回i+1的值。
  • 五、若是找不到結點值爲x的話,返回值爲0

 

int LocateLinklist(LinkList head, DataType x)
    //求表head中第一個值等於x的結點的序號,若不存在這種結點,返回結果爲0
    {
    Node *p=head; //p是工做指針
    p=p->next; //初始時p指向首結點
    int i=0; //i表明結點的序號,這裏置初值爲
    while (p != NULL && p->data != x) //訪問鏈表
    {
    i++;
    p=p->next;
    }
    if (p!=NULL) return i+1;
    else return 0;
}

六、插入

 插入運算是將值爲x的新結點插入到表的第i個結點 的位置上,即插入到ai-1與ai之間。

具體步驟:

  • (1)找到ai-1存儲位置p
  • (2)生成一個數據域爲x的新結點*s
  • (3)令結點*p的指針域指向新結點
  • (4)新結點的指針域指向結點ai

 

 

 

void InsertLinklist (LinkList head, DataType x, int i)
    //在表head的第i個數據元素結點以前插入一個以x爲值的新結點
    {
    Node *p,*q;
    if (i==1) q=head;
    else q=GetLinklist (head, i-1); //找第 i-1個數據元素結點
    if (q==NULL) //第i-1個結點不存在
    exit(「找不到插入的位置」);
    else
    {
        p=malloc(sizeof (Node) );p->data=x; //生成新結點
        p->next=q->next; //新結點鏈域指向*q的後繼結點
        q->next=p; //修改*q的鏈域
    }
}

注意:連接操做p->next=q->next和q->next=p兩條語句的執行順序不能顛倒,不然結點 *q的鏈域值(即指向原表第i個結點的指針)將丟失。

七、清除單鏈表中值爲x的重複結點

步驟:

  • 1)找到值爲x的第一個結點位置,p指向該結點
  • 2)從p所指結點開始向後查找, 若存在值爲x的結點,令q指向x結點前一個 執行刪除操做 繼續查找直到鏈表末尾
void PurgeLinklist(LinkList head)
    //刪除表head中多餘的重複結點
    {
        Node *p,*q,*r;
        q=head->next; //q指示當前檢查結點的位置,置其初值指向首結點
        while(q!=NULL) //當前檢查結點*q不是尾結點時,尋找並刪除它的重複結點
    {
        p=q; //工做指針p指向*q
        while(p->next!=NULL) //當*p的後繼結點存在時,將其數據域與*q數據域比較
        { if (p->next->data==q->data) //若*(p->next)是*q的重複結點
            {
                r=p->next; //r指向待刪結點
                p->next = r->next; //移出結點* (p->next),p->next指向原來* (p->next)的後繼結點
                free (r);
            }
            else p=p->next; //不然,讓p指向下一個結點
        }
            q=q->next; //更新檢查結點
    }
}

 4、順序表的算法

線性表的基本運算

1,初始化 Initiate(L)     創建一個空表L=(),L不含數據元素。  

2,求表長度 Length(L)   返回線性表L的長度。

三、取表元 Get(L,i)    返回線性表第i個數據元素,當i不知足 1≤i≤Length(L)時,返回一特殊值  

四、定位 Locate(L,x)   查找線性表中數據元素值等於x的結點序號,如有多 個數據元素值與x相等,運算結果爲這些結點中序號 的最小值,若找不到該結點,則運算結果爲0  

五、插入 Insert(L,x,i)  在線性表L的第i個數據元素以前插入一個值爲x的新數據 元素,參數i的合法取值範圍是1≤i≤n+1。操做結束後線性 表L由(a1,a2,…,ai-1, ai,ai+1,.…,an )變爲(a1,a2, …,ai-1,x, ai,ai+1,.…,an ),表長度加 1。

六、刪除 Delete(L,i)  刪除線性表L的第i個數據元素ai,i的有效取值範圍是1≤i≤n。 刪除後線性表L由(a1,a2,…,ai-1, ai,ai+1,.…,an )變爲 (a1,a2,…,ai-1,ai+1,.…,an ),表長度減1

5、其它鏈表 

一、循環鏈表

  • 普通鏈表的終端結點的next值爲NULL
  • 循環鏈表的終端結點的next指向頭結點
  • 在循環鏈表中,從任一結點出發可以掃描整個鏈表

如何找到循環鏈表的尾結點

在循環鏈表中附設一個rear指針指向尾結點 適用於常常適用頭尾結點的鏈表操做中

 

 二、雙向循環鏈表

在鏈表中設置兩個指針域, 一個指向後繼結點 一個指向前驅結點 這樣的鏈表叫作雙向鏈表

 

 

 

 

 7、順序實現與鏈式實現的比較

線性表與鏈表的優缺點:

(1)單鏈表的每一個結點包括數據域與指針域,指針域須要佔用額外空間。

(2)從總體考慮,順序表要預分配存儲空間,若是預先分配得過大,將形成 浪費,若分配得太小,又將發生上溢;單鏈表不須要預先分配空間,只 要內存空間沒有耗盡,單鏈表中的結點個數就沒有限制。

 

相關文章
相關標籤/搜索