《大話數(shù)據(jù)結(jié)構(gòu)》-程杰 著 閱讀筆記day3 2017-10-2 周一

嘮嗑

時隔兩周再來寫這篇筆記诱告,是因為對于我這種大學本著及格就好的學神來說這一章節(jié)還是有點難度和思考恒削,有些東西知道羡藐,但是具體細節(jié)已經(jīng)沒有概念了,所以需要認真思考和總結(jié)一下落萎,所以在此還是想跟各位學生黨朋友說一句,學習要有目標炭剪,有些東西你覺得沒什么用可能是你還沒接觸到练链,等到真的需要你的這一些知識的時候就悔之晚矣了。

好了奴拦,話不多說媒鼓,展開這次的筆記,第三章-線性表。

第三章 線性表

線性表(list):零個或多個數(shù)據(jù)元素的有限序列绿鸣。

這里有幾個需要注意的關(guān)鍵點:

  • 序列:顧名思義是有順序的數(shù)列疚沐,數(shù)據(jù)元素之間是有順序的。就是說潮模,一列隊伍在排隊亮蛔,第一個前面沒有人,最后一個后面沒有人擎厢,其他中間的人有且只有一個前驅(qū)和后繼究流。
  • 有限:線性表強調(diào)是有限的,事實上动遭,在計算機中處理的對象都是有限的芬探,那種無限的數(shù)列,只存在于數(shù)學的概念中厘惦。
  • 數(shù)據(jù)元素的類型需要相同:這就相當于占座偷仿,書包和人能一樣嗎?能算一個數(shù)列嗎宵蕉?
數(shù)學語言定義
若將線性表記為(a1,...,a(i-1),a(i),a(i+1),...,a(n))酝静,則表中a(i-1)領(lǐng)先于a(i),a(i)領(lǐng)先于a(i+1)国裳,稱a(i-1)是a(i)的直接前驅(qū)元素形入,a(i)是a(i+1)的直接后繼元素。并且各元素有且只有一個直接前驅(qū)和直接后繼缝左。

所以線性表元素的個數(shù)n(n>=0)定義為線性表的長度亿遂,當n=0時,稱為空表渺杉。

在非空表中的每個數(shù)據(jù)元素都有一個確定的位置蛇数,稱i為數(shù)據(jù)元素a(i)在線性表中的位序。

線性表的抽象數(shù)據(jù)類型

現(xiàn)在我們思考一下是越,線性表會有一些什么操作耳舅?

拿一個現(xiàn)實中的例子來說明吧:

我想大家都有學生時代排隊的經(jīng)歷,那么一個班的學生該怎么排隊呢倚评?按身高從矮到高還是按照名字拼音還是按照姓氏筆畫呢浦徊?這個過程就是線性表的創(chuàng)建和初始化

一開始可能沒有經(jīng)驗按照姓氏筆畫來排隊天梧,然后發(fā)現(xiàn)有高有矮盔性,隊伍很難看,這個時候就需要重新排隊呢岗,這就是線性表重置為空表的操作冕香。

那么隊伍排好了蛹尝,會有什么操作呢?會不會有說隊伍第幾個人出來一下的操作悉尾?會不會有說隊伍報數(shù)看看有多少人的操作突那?會不會有其他班的老師來問xxx是不是你們班的學生這樣的操作?更或者會不會來個插班生构眯,按照身高直接排在隊伍中呢愕难?

以上的集中情況都是比較常見的線性表操作,分別是根據(jù)位序得到數(shù)據(jù)元素鸵赖,查找某個數(shù)據(jù)元素是否存在务漩,獲得線性表長度插入和刪除數(shù)據(jù)它褪。

所以饵骨,線性表的抽象數(shù)據(jù)類型定義如下:

ADT 線性表 (List)
Data
      線性表的數(shù)據(jù)對象集合為(a1,a2,...,a(n)),每個元素的類型均為DataType茫打。其中居触,除第一個
元素a1外,每一個元素有且只有一個直接前驅(qū)元素老赤,除了最后一個元素a(n)外轮洋,每一個元素有且只有一個直接
后繼元素。數(shù)據(jù)元素之間的關(guān)系是一對一關(guān)系抬旺。
Operation
      InitList(*L);                 初始化操作弊予,建立一個空的線性表L。
      ListEmpty(L);                 判斷線性表是否為空开财。
      ClearList(*L);                將線性表清空汉柒。
      GetElem(L,i,*e);              將線性表中的第i個元素值返回給e。
      LocateElem(L,e);              在線性表L中查找與給定值e相等的元素责鳍,如果查到返回
該元素在表中的位序碾褂;否則返回0表示失敗。
      ListInsert(*L,i,e);           在線性表L中的第i個位置插入新元素e历葛。
      ListDelete(*L,i,*e);          刪除線性表L中第i個位置元素正塌,并用e返回其值。
      ListLength(L);                返回線性表L的長度恤溶。
endADT

以上就是線性表的最基本操作乓诽,對于更復雜的操作,完全可以通過基本操作的組合來實現(xiàn)咒程。

線性表的順序存儲結(jié)構(gòu)

前面說過问裕,計算機的物理存儲分為兩種,一種是順序存儲孵坚,一種是鏈式存儲粮宛。先來說說比較簡單的線性表的順序存儲結(jié)構(gòu)。

線性表的順序存儲結(jié)構(gòu)卖宠,指的是用一段地址連續(xù)的存儲單元一次存儲線性表的數(shù)據(jù)元素巍杈。

順序存儲結(jié)構(gòu),說白了扛伍,其實就是在內(nèi)存中找了塊地方筷畦,通過占位的方式,把一段連續(xù)的內(nèi)存給占了刺洒,然后把相同數(shù)據(jù)類型的數(shù)據(jù)元素依次放入這塊內(nèi)存中鳖宾。同時,數(shù)據(jù)類型相同逆航,就直接可以使用一維數(shù)組來實現(xiàn)順序存儲結(jié)構(gòu)鼎文,即把第一個數(shù)據(jù)元素存在數(shù)組下標為0的位置中,依次把線性表相鄰的元素存儲在數(shù)組中相鄰的位置因俐。

那么拇惋,我們可以思考一下,這里的關(guān)鍵點:

  1. 第一個是首位置抹剩,也就是這一塊內(nèi)存的起始位置撑帖。
  2. 第二個是我們需要多大的內(nèi)存,這是需要預估的澳眷,多了浪費胡嘿,少了容易溢出,所以數(shù)組的長度就是這個最大存儲容量钳踊。

接下來衷敌,我們看看線性表的順序存儲的結(jié)構(gòu)代碼

#define MAXSIZE 20                     /*存儲空間初始分配量*/
typedef int ElemType;                  /*ElemType類型根據(jù)實際情況而定,這里假設為int*/
typedef struct
{
    ElemType data[MAXSIZE];            /*數(shù)組存儲數(shù)據(jù)元素箍土,最大值為MAXSIZE*/
    int length逢享;                       /*線性表當前長度*/
}SqList;

這里吴藻,我們發(fā)現(xiàn)描述順序存儲結(jié)構(gòu)需要三個屬性:
- 存儲空間的起始位置:數(shù)組data瞒爬,它的存儲位置就是存儲空間的存儲位置。
- 線性表的最大存儲容量:數(shù)組長度MAXSIZE沟堡。
- 線性表的當前長度:length侧但。

注意:需要區(qū)別一下數(shù)組長度和線性表長度。數(shù)組長度是存放線性表的存儲空間的長度航罗,內(nèi)存分配后這個量一般是不變的(當然排除動態(tài)分配數(shù)組禀横,比如OC中可變數(shù)組,這會帶來性能上的一定損耗)粥血。線性表的長度是線性表中數(shù)據(jù)元素的個數(shù)柏锄,隨著線性表的插入刪除酿箭,這個量是變化的。所以趾娃,在任何時刻缭嫡,線性表的長度應該小于等于數(shù)組的長度,也就是我們需要估算數(shù)組長度多少比較合適抬闷。

線性表順序結(jié)構(gòu)的存取時間復雜度

已經(jīng)知道了順序結(jié)構(gòu)是連續(xù)的妇蛀,那么我們想想如果要計算存取的時間復雜度,只需要了解一下內(nèi)存中的地址的計算方法笤成,就可以很輕松的得出時間復雜度评架。

線性表的起始是1,數(shù)組的起始下標是0炕泳,所以線性表的第i個元素是要存儲在數(shù)組下標為i-1的位置纵诞,即數(shù)據(jù)元素和數(shù)組下標有如下對應關(guān)系:

對應關(guān)系.png
對應關(guān)系.png

存儲器中的每個存儲單元都有自己的編號,這個編號稱為地址喊崖。當我們開辟內(nèi)存后挣磨,第一個位置已經(jīng)確定,那么后面的位置是不是荤懂,只要首地址+長度*間距數(shù)茁裙,就可以計算出來呢?公式如下:

LOC(a(i)) = LOC(a1)+(i-1)*c                 /*c表示數(shù)據(jù)類型的長度*/

通過這個公式就可以隨時計算出線性表中任意位置的地址节仿,那么我們對每個線性表位置的存入或者取出數(shù)據(jù)晤锥,對于計算機來說都是相等的時間,也就是一個常熟廊宪,所以它的存取時間性能為O(1)矾瘾。

也因此,我們?nèi)绻玫骄€性表中的第i個位置的數(shù)據(jù)元素箭启,其實也是很簡單的壕翩,只要取得數(shù)組的i-1下標的值,用代碼來寫的話傅寡,如下:

#define OK 1
#define ERROR 0
typedef int Status;
/*
   Status是函數(shù)的類型放妈,其值是函數(shù)結(jié)果狀態(tài)代碼
   初始條件:順序線性表已經(jīng)存在,1<=i<=ListLength(L)
   操作結(jié)果:用e返回L中第i個數(shù)據(jù)元素的值
*/
Status GetElem(SqList L, int i,ElemType *e)
{
   if(L.length == 0 || i<1 || i>L.length)
          return ERROR;
   *e = L.data[i-1];
   return OK;
}
線性表順序結(jié)構(gòu)的插入與刪除

我們來想一下荐操,如果一個隊伍排在那芜抒,有人插隊,會有什么情況發(fā)生托启,肯定是后面的人每個人都要往后退一個位置宅倒。順序結(jié)構(gòu)的線性表插入數(shù)據(jù)的實現(xiàn)過程也就是這樣。

插入算法的思路:
1.如果插入位置不合理屯耸,拋出異常拐迁;
2.如果線性表長度大于等于數(shù)組長度蹭劈,則拋出異常或動態(tài)增加容量唠亚;
3.從最后一個元素開始向前遍歷到第i個位置链方,分別將它們都向后移動一個位置;
4.將要插入數(shù)據(jù)填入位置i處灶搜;
5.表長加1;
代碼:
Status ListInsert(SqList *L,int i,ElemType e)
{
   int k;
   if (L->length == MAXSIZE)     /*順序線性表已經(jīng)滿了*/
       return ERROR;
   if (i<1 || i>L->length+1)     /*當i不在范圍內(nèi)時*/
       return ERROR;
   if (i<=L->length)             /*若插入數(shù)據(jù)位置不在表尾*/
   {
      for(k=L->length-1;k>=i-1;k--)    /*將要插入位置后數(shù)據(jù)元素向后移動一位*/
      {
           L->data[k+1] = L->data[k];
      }
   }
   L->data[i-1] = e;           /*新元素插入i*/
   L->length++;                /*表長加1*/
   return OK;
}

同樣的道理工窍,如果一個隊伍中有一個走了割卖,那是不是后面的人都可以上前一位呢,這也就是順序線性表的刪除實現(xiàn)過程患雏。

刪除算法的思路:
1.如果刪除位置不合理鹏溯,拋出異常;
2.取出刪除元素淹仑;
3.從刪除元素位置開始遍歷到最后一個元素位置丙挽,分別將它們都向前移動一個位置;
4.表長減1.
代碼:
Status ListDelete(SqList *L,int i,ElemType *e)
{
     int k;
     if (L->length == 0)            /*線性表為空*/
         return ERROR;
     if (i < 1 || i > L->length)        /*刪除位置不正確*/
         return ERROR;
     *e = L->data[i-1];
     if (i < L->length)
     {
            for (k = i; k < L->length;k++)
            {
                  L->data[k-1] = L->data[k];
            }
     }
     L->length--;
     return OK;
}

在這里的話就是注意線性表的下標和數(shù)組的下標匀借,別搞混了應該是挺簡單的颜阐。

現(xiàn)在,來分析一下插入刪除的時間復雜度吓肋,最好的情況時間復雜度是O(1)凳怨,最壞的情況時間復雜度是O(n),至于平均的情況為(n-1)/2是鬼,時間復雜度還是O(n)肤舞。

這樣的話,我們就可以總結(jié)一下順序結(jié)構(gòu)存儲線性表的優(yōu)缺點了:

  • 優(yōu)點:無須為表示表中元素之間的邏輯關(guān)系而增加額外的存儲空間均蜜;
    可以快速的存取表中任一位置的元素李剖。
  • 缺點:插入和刪除操作需要移動大量位置(可以想想如果插入的不是一個元素而是多個連續(xù)的元素);當線性表長度變化較大時囤耳,難以確定存儲空間的容量篙顺;容易造成存儲空間的“碎片”。

接下來紫皇,我們就來分析一下線性表的鏈式存儲結(jié)構(gòu)慰安,又有什么不同和優(yōu)缺點。

線性表的鏈式存儲結(jié)構(gòu)

既然知道順序結(jié)構(gòu)的插入和刪除這么麻煩聪铺,那有沒有什么方法可以解決呢化焕?首先我們思考一下為什么順序結(jié)構(gòu)會有這種問題,原因在于相鄰兩元素的存儲位置也具有鄰里關(guān)系铃剔,在內(nèi)存中的位置也是挨著的撒桨,中間沒有空隙查刻,這就導致插入的時候,需要移動n-i個元素凤类,刪除也一樣穗泵。

那么解決辦法也就可以思考得出,就是那不然大家都不要挨著了谜疤,全部散開佃延,這樣的話,插入和刪除就不需要移動這么多元素夷磕,那么數(shù)據(jù)元素就不單單存數(shù)據(jù)元素的信息履肃,也需要存它的后繼元素的存儲地址。這種形式也就是單鏈表坐桩。

為了表示每個數(shù)據(jù)元素a(i)與其直接后繼數(shù)據(jù)元素a(i+1)之間的邏輯關(guān)系尺棋,對數(shù)據(jù)元素a(i)來說,除了存儲
其本身的信息之外绵跷,還需存儲一個指示其直接后繼的信息(即直接后繼的存儲位置)膘螟。我們把存儲數(shù)據(jù)元素信
息的域稱為數(shù)據(jù)域,把存儲直接后繼位置的域稱為指針域碾局。指針域中存儲的信息稱作指針或連荆残。這兩部分信息
組成數(shù)據(jù)元素a(i)的存儲映像,稱為結(jié)點(Node)擦俐。
n個結(jié)點(a(i)的存儲映像)鏈結(jié)成一個鏈表脊阴,即為線性表的鏈式存儲結(jié)構(gòu),因為此鏈表的每個結(jié)點只包含一
個指針域蚯瞧,所以叫做單鏈表嘿期。

我們把鏈表中第一個結(jié)點的存儲位置叫做頭指針。整個鏈表的存取就必須是從頭指針開始進行了埋合,之后的每一個結(jié)點备徐,就是上一個結(jié)點的后繼指針指向的位置,如此甚颂,最后一個結(jié)點的后繼指針就為空(NULL或^符號表示)蜜猾。

有時,我們習慣在單鏈表的第一個結(jié)點前附設一個結(jié)點振诬,稱為頭結(jié)點蹭睡。頭結(jié)點的數(shù)據(jù)域可以不存儲任何信息,或者存儲如線性表的長度等附加信息赶么,頭結(jié)點的指針域指向第一個結(jié)點的指針肩豁。

頭指針:頭指針是指鏈表指向第一個結(jié)點的指針,若鏈表有頭結(jié)點,則是指向頭結(jié)點的指針清钥;頭指針具有標識
作用琼锋,所以常用頭指針冠以鏈表的名字;無論鏈表是否為空祟昭,頭指針均不為空缕坎。頭指針是鏈表的必須要素。
頭結(jié)點:頭結(jié)點是為了操作的統(tǒng)一和方便而設立的篡悟,放在第一元素的結(jié)點之前谜叹,其數(shù)據(jù)域一般無意義(也可存
放鏈表的長度);有了頭結(jié)點恰力,對在第一元素結(jié)點前插入結(jié)點和刪除第一結(jié)點叉谜,其操作和其他結(jié)點的操作就統(tǒng)一
了;頭結(jié)點不一定是鏈表必須要素踩萎。
單鏈表
單鏈表

帶頭結(jié)點的單鏈表
帶頭結(jié)點的單鏈表

空聯(lián)表
空聯(lián)表
/*線性表的單鏈表存儲結(jié)構(gòu)*/
typedef struct Node
{
      ElemType data;
      struct Node *next;
} Node;
typedef struct Node *LinkList;   /*定義LinkList*/

從定義可以看出來,結(jié)點由存放數(shù)據(jù)元素的數(shù)據(jù)域和存放后繼結(jié)點地址的指針域組成很钓。

總結(jié)

這篇就寫到這吧香府,寫的是真累,回到老家码倦,發(fā)現(xiàn)書桌沒了企孩,一直是坐在床沿邊寫,太累了袁稽,這一章節(jié)的后一半單鏈表的讀取勿璃,插入刪除,以及靜態(tài)鏈表推汽,雙向鏈表补疑,循環(huán)鏈表,就下一篇再寫吧歹撒,初步預估后天會寫好發(fā)出來莲组,希望各位大佬多多諒解。

最后祝祖國母親68周歲快樂暖夭,祝大家國慶锹杈,中秋雙節(jié)快樂!有時間的話可以多陪陪爸媽迈着,反正我是打算在家宅8天竭望,哈哈!裕菠!


Better Late Than Never!
努力是為了當機會來臨時不會錯失機會咬清。
共勉!

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市枫振,隨后出現(xiàn)的幾起案子喻圃,更是在濱河造成了極大的恐慌,老刑警劉巖粪滤,帶你破解...
    沈念sama閱讀 212,718評論 6 492
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件斧拍,死亡現(xiàn)場離奇詭異,居然都是意外死亡杖小,警方通過查閱死者的電腦和手機肆汹,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,683評論 3 385
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來予权,“玉大人昂勉,你說我怎么就攤上這事∩ㄏ伲” “怎么了岗照?”我有些...
    開封第一講書人閱讀 158,207評論 0 348
  • 文/不壞的土叔 我叫張陵,是天一觀的道長笆环。 經(jīng)常有香客問我攒至,道長,這世上最難降的妖魔是什么躁劣? 我笑而不...
    開封第一講書人閱讀 56,755評論 1 284
  • 正文 為了忘掉前任,我火速辦了婚禮账忘,結(jié)果婚禮上志膀,老公的妹妹穿的比我還像新娘。我一直安慰自己鳖擒,他們只是感情好放航,可當我...
    茶點故事閱讀 65,862評論 6 386
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般祖秒。 火紅的嫁衣襯著肌膚如雪竭缝。 梳的紋絲不亂的頭發(fā)上抬纸,一...
    開封第一講書人閱讀 50,050評論 1 291
  • 那天,我揣著相機與錄音命黔,去河邊找鬼纷铣。 笑死,一個胖子當著我的面吹牛槐秧,可吹牛的內(nèi)容都是我干的启搂。 我是一名探鬼主播撼短,決...
    沈念sama閱讀 39,136評論 3 410
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了泥兰?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 37,882評論 0 268
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎雁刷,沒想到半個月后覆劈,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 44,330評論 1 303
  • 正文 獨居荒郊野嶺守林人離奇死亡沛励,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 36,651評論 2 327
  • 正文 我和宋清朗相戀三年责语,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片侯勉。...
    茶點故事閱讀 38,789評論 1 341
  • 序言:一個原本活蹦亂跳的男人離奇死亡鹦筹,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出址貌,到底是詐尸還是另有隱情铐拐,我是刑警寧澤徘键,帶...
    沈念sama閱讀 34,477評論 4 333
  • 正文 年R本政府宣布,位于F島的核電站遍蟋,受9級特大地震影響吹害,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜虚青,卻給世界環(huán)境...
    茶點故事閱讀 40,135評論 3 317
  • 文/蒙蒙 一它呀、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧棒厘,春花似錦纵穿、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,864評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至何乎,卻和暖如春句惯,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背支救。 一陣腳步聲響...
    開封第一講書人閱讀 32,099評論 1 267
  • 我被黑心中介騙來泰國打工抢野, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人各墨。 一個月前我還...
    沈念sama閱讀 46,598評論 2 362
  • 正文 我出身青樓指孤,卻偏偏與公主長得像,于是被迫代替她去往敵國和親贬堵。 傳聞我的和親對象是個殘疾皇子邓厕,可洞房花燭夜當晚...
    茶點故事閱讀 43,697評論 2 351

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