日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 人文社科 > 生活经验 >内容正文

生活经验

数据结构和算法:(3)3.2线性表的链式存储结构

發布時間:2023/11/27 生活经验 27 豆豆
生活随笔 收集整理的這篇文章主要介紹了 数据结构和算法:(3)3.2线性表的链式存储结构 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

????? 線性表的鏈式存儲結構的特點是用一組任意的存儲單元存儲線性表的數據元素也就是說你這個可以放在A地點,這個可以放在E地點,A地點和E地點中間可以隔開一個C地點和D地點,這樣是允許的),這組存儲單元可以存在內存中未被占用的任意位置。

????? 比起順序存儲結構每個順序存儲結構只需要存儲一個位置就可以了。現在鏈式存儲結構中,除了要存儲數據元素信息外,還要存儲他的后繼元素的存儲地址(指針)。也就是說除了存儲其本身的信息外,還需存儲一個指示其直接后繼的存儲位置的信息。

????? 我們把存儲數據元素信息的域稱為數據域(域就是地方的意思),把存儲直接后繼位置的域稱為指針域。指針域中存儲的信息稱為指針或鏈,這兩部分信息組成數據元素稱為存儲映像,稱為結點(Node)。

????? n個結點鏈接成一個鏈表,即為線性表(a1,a2,a3,...,an)的鏈式存儲結構。

????? 因為此鏈表的每個結點中只包含一個指針域,所以叫做單鏈表。(后邊還有其它鏈表)如下圖:

?

對于線性表來說,總得有個頭有個尾(這樣你才能知道從哪里開始從那里結束),鏈表也不例外,我們把鏈表中的第一個結點的存儲位置叫做頭指針,最后一個結點指針為空(NULL)表示結束。

頭指針與頭結點的異同:(頭結點的數據域一般不存儲任何信息。(第一個的特權))

--頭指針:

  • 頭指針是指鏈表指向第一個結點的指針,若鏈表有頭結點的話,那么頭指針是在頭結點之前指向頭結點的指針。
  • 頭指針具有標識作用,所以常用頭指針冠以鏈表的名字(指針變量的名字)。(意思就是使用指針變量的名字的我們叫做這個鏈表的名字)
  • 無論鏈表是否為空,頭指針均不為空。(因為頭指針為空就沒有這個鏈表了)(空鏈表也是一個鏈表)
  • 頭指針是鏈表的必要元素。

--頭結點:

  • 頭結點是為了操作的統一和方便而設立的,放在第一個元素的結點之前,其數據域一般無意義(但也可以用來存放鏈表的長度)(就是說他的數據域可以寫一個變量,一個變量存放鏈表的長度,那么通過索引他就可以知道鏈表的當前長度)。
  • 有了頭結點,對在第一個元素結點前插入結點和刪除第一個結點的操作與其它結點的操作就統一了。
  • 頭結點不一定是鏈表的必要元素。

(如下圖:單鏈表圖例)

頭指針指向頭結點,頭結點的數據域一般是空的(一般是沒有任何東西的,但你也可以存放當前這個鏈表的長度,(因為數據結構都是可以自己定義的)),然后頭結點指向我們的第一結點(就是有元素的a1的位置),接著往下。

(空鏈表的圖如下:)

空鏈表是頭指針、頭結點、NULL。(如果沒有頭結點的話就是頭指針指向NULL)

我們在C語言中可以用結構指針來描述單鏈表。如下:

//1我們這里定義一個結點
typedef struct Node//2typedef是取別名
{ElemType data;//4數據域struct Node* Next;//5指針域(他是指向結點類型的指針,因為他下一個元素就是結點,這個結點類型就是ode,加上一個*號表示他是一個指針)
}Node;
typedef struct Node* LinkList;// 3Node*表明他是一個指針,取別名為LinkList(6有點像define,也就是說這是類型的別名。只要在文章中出現LinkList他就相當于Node*,指向一個Node 的指針)
//我們看到結點由存放數據元素的數據域和存放后繼結點地址的指針域組成。

假設P是指向線性表第i個元素的指針,則該結點ai的數據域我們可以用P->data的值是一個數據元素,結點ai的指針域(可以指向下一個結點的地址)可以用P->next(next是指向下一個結點的指針)來表示。

P->next的值是一個指針。

那么P->next指向的是誰呢?當然指向第i+1個元素!也就是指向ai+1的指針。即P->data=ai,則P->next->data=ai+1(P->next下個結點的地址也是一個結點結構,那么結點結構就有一個data,所以這個就是取下一個結點的數據他的元素的值)。

---1、單鏈表的讀取

在單鏈表中我們要找的第i個元素到底在哪,我們必須從第一個結點開始挨個找。

因此對于單鏈表實現獲取第i個元素的數據操作GetElem的算法思路是:

  • 聲明一個結點p指向鏈表第一個結點,初始化j從1開始;
  • 當j<i時,就遍歷鏈表,讓p的指針向后移動,不斷指向下一個結點,j+1;
  • 若到鏈表末尾p為空,則說明第i個元素不存在;
  • 否則查找成功,返回結點p的數據。

GetElem.c代碼如下:

//初始條件:順序線性表L已經存在,1<=i<=Listlength(L)
//操作結果:用e返回L中第i個數據元素的值。
Status GetElem(LinkList L,int i,ElemType *e)
{int j;LinkList p;//由前可知,LinkList就是鏈表(Node*取了別名)Node*指向了一個p,p這里就是一個指針(指向一個結點的指針)(聲明一個指針p)p=L->next;//讓p指向L鏈表的第一個結點j=1;//我們把當前計數器設置為1,初始化while(p&&j<i)//若p且j小于i(要符合兩個條件)表示未找到{p=p->next;//未找到指向下一個++j;}if(!p||j>i)//左邊為假,說明這時候已經到了結束的地方了,鏈表已經整個索引結束還沒中找到他,p應該是等于NULL為空,那么!p就為真,我們并沒有找到鏈表已經結束了,所以他會返回一個錯誤。第二個情況j>i{return ERROR;}*e=p->data;//排除以上兩種情況就是找到了,就可以把他的元素給了e變量。return OK;//表示查找成功
}
//就是從頭開始找,直到第i個元素為止。

//因為上述單鏈表的結構中沒有定義表長,所以不能實現知道要循環多少次,因此也就不能方便使用for來控制循環。(最主要的就是工作指針后移)

2、單鏈表的插入

假設存儲元素e的結點為s,要實現結點p、p->next和s之間邏輯關系的變化,如下圖:

由上圖我們可以看到,在元素ai這個存放的地址處,我們說了單鏈表需要用兩個單元的空間來存放一個結點,前邊的空間呢是這個存放的元素,第二個空間呢是存放下一個結點的位置的地址,那假設ai為結點p那么p->next就是ai后邊的地址(ai后邊空間存放的)p有兩個產權,一個是ai一個是next(地址指向就是下一個),s就是計劃要插入的結點,也就是元素e所在的結點,那么如何將結點s插入到ai和ai+1之間呢?

我們思考后發現壓根就不用驚動其他的結點,只需要讓s->nest和p->next的指針做一點改變。(我們只需要p->next指向s結點,讓s->next指向我們原來p->next指向的ai+1的結點就可以了。)

  • (1)s->next=p->next;
  • (2)p->next=s

PS:切記不能將上述兩個語句調換順序,因為如果先執行p->next的話會被覆蓋為s的地址,那么再執行s->next=p->next其實就等于s->next=s了。

單鏈表第i個數據插入結點的算法思路:

  • 聲明一結點p指向指向鏈表頭結點,初始化j從1開始;
  • 當j<1時,就遍歷鏈表,讓p的指針向后移動,不斷指向下一結點,j累加1;
  • 若到鏈表末尾p為空,則說明第i個元素不存在;
  • 否則查找成功,在系統中生成一個空結點s;(我們需要在一個鏈表里面插入一個元素那我們應該生成一個空的結點,然后把元素放在這個結點的第一個位置,然后將第二個位置指向下一個元素;讓他的上一個元素指向他的這個位置,就可以了。)
  • 將數據元素e賦值給s->data;
  • 單鏈表的插入剛才兩個標準語句;
  • 返回成功。

代碼如下:

//初始條件:順序線性表L已經存在,1<=i<=Listlength(L)
//操作結果:在L中第i個位置之前插入新的數據元素e,L的長度加1。
Status GetElem(LinkList *L,int i,ElemType *e)
{int j;LinkList p,s;//由前可知,LinkList就是鏈表(Node*取了別名)Node*指向了一個p,p這里就是一個指針(指向一個結點的指針)(聲明一個指針p)p=*L;j=1;//我們把當前計數器設置為1,初始化
//while循環是為了尋找第i個結點。 (j從1開始便利直到i的時候,如果找到了她就等于i嘛,循環就會退出。)while(p&&j<i)//若p且j小于i(要符合兩個條件)表示未找到(&&其中一個為假的時候循環就會退出,要么他永遠都找不到,要么p為NULL已經指向了結尾。那么也就是退出,在下邊再判斷){p=p->next;//未找到指向下一個++j;}if(!p||j>i)//左邊為假,說明這時候已經到了結束的地方了,鏈表已經整個索引結束還沒中找到他,p應該是等于NULL為空,那么!p就為真,我們并沒有找到鏈表已經結束了,所以他會返回一個錯誤。第二個情況j>i,已經找不到了。{return ERROR;}s=(LinkList)malloc(sizeof(Node));//那么我們找到這個位置之后就用malloc生成一個新的結點,我們這個結點時Node一個結構,不再寫8個字節還是16個字節用sizeof提高了代碼的靈活性。(在編寫代碼要經常考慮靈活性和普遍性問題),(LinkList)他的意思是再把他強制轉化為LinkList的形式,這是一個確保。s->data=e;//賦值s->next=p->next;p->next=sreturn OK;//表示查找成功
}
//就是從頭開始找,直到第i個元素為止。

3、單鏈表的刪除操作

由上圖我們可以看出,插入前與插入后的變化。刪除的話我們假設a1是結點p,a2叫做結點q,我們想把結點q給他去掉,那么a1就指向了a3,a2在哪里?不用管他。我們沒有必要說把a2給清零,直接把a2的指針給去掉就可以了(a1就直接忽視a2就直接指向a3,我們不用關心a2會不會飄到哪里,在那里駐扎;不用管他啦,因為在內存中都會有一些隨隨機的數據,只要他的地址沒有用到,他的這個數據就是隨機的,就是沒用的,就是隨即可能被覆蓋的。所以不用他)。

  • 假設元素a2的結點為q,要實現結點q刪除單鏈表的操作,其實就是將他的前繼結點的指針繞過指向后繼結點即可。
  • 那我們所要做的,實際上就是一布操作:
  • 可以這樣:p->next=p->next->next;
  • 或者也可以這樣:q=p->next; p->next=q->next;

單鏈表第i個數據刪除結點的算法思路:

  • 聲明結點p指向鏈表第一個結點,初始化j=1;
  • 當j<1時,就遍歷鏈表,讓p的指針向后移動,不斷指向下一個結點,j累加1;
  • 若到鏈表末尾p為空,則說明第i個元素不存在;
  • 否則查找成功,將欲刪除結點p->next賦值給q;
  • 單鏈表的刪除標準語句p->next=q->next;
  • 將結點中的數據賦值給e,作為返回;
  • 釋放q結點。

代碼如下:

//初始條件:順序線性表L已經存在,1<=i<=Listlength(L)
//操作結果:刪除L的第i個數據元素,并用e返回其值,L的長度-1。
Status GetElem(LinkList *L,int i,ElemType *e)
{int j;LinkList p,q;//由前可知,LinkList就是鏈表(Node*取了別名)Node*指向了一個p,p這里就是一個指針(指向一個結點的指針)(聲明一個指針p)p=*L;j=1;//我們把當前計數器設置為1,初始化
//while循環是為了尋找第i個結點。 (j從1開始便利直到i的時候,如果找到了她就等于i嘛,循環就會退出。)while(p->next&&j<i)//若p且j小于i(要符合兩個條件)表示未找到(&&其中一個為假的時候循環就會退出,要么他永遠都找不到,要么p為NULL已經指向了結尾。那么也就是退出,在下邊再判斷){p=p->next;//未找到指向下一個++j;}if(!p->next||j>i)//左邊為假,說明這時候已經到了結束的地方了,鏈表已經整個索引結束還沒中找到他,p應該是等于NULL為空,那么!p就為真,我們并沒有找到鏈表已經結束了,所以他會返回一個錯誤。第二個情況j>i,已經找不到了。{return ERROR;}q=p->next;p->next=q->next;*e=q->data;free(q);//或者delete q;return OK;//表示查找成功
}
//就是從頭開始找,直到第i個元素為止。

效率對比:我們希望從第i個位置開始,插入連續10個元素,對于順序存儲結構意味著每一次插入都需要移動n-i個位置,所以每次都是O(n)。而單鏈表我們只需要在第一次時,找到第i個位置的指針,此時為O(n),接下來只是簡單的通過賦值移動指針而已,時間復雜度都是O(1)。所以,對于插入和刪除越頻繁的操作,單鏈表的效率優勢就越明顯。(這樣我們在存儲一大堆數據的時候就不會只是想到數組,可以考慮一下單鏈表或雙鏈表)

總結

以上是生活随笔為你收集整理的数据结构和算法:(3)3.2线性表的链式存储结构的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。