18.11.14
(初學者食用)
之前的合併線性表是用順序儲存結構做的,這裡鏈式儲存結構是線性表的另一種構建方式。
總的來說, 這種線性表由乙個頭結點(通常是有的)和很多個結點(每個結點由資料域『data』和指標域『next』,也就是乙個存資料的地方和乙個存指標的地方)組成。
有頭結點是為了增強可讀性,它的好處是我們對第乙個結點做插入和刪除時不需要做特殊處理。(呼叫函式的時候通常是把頭結點的位址交給函式)
**1.查詢元素必須從頭指標開始找
2.做插入刪除不需要移動元素,時間複雜度o(1)
3.儲存空間不連續,結點之間指標相連**
開始的開始,先對結點的結構體進行封裝,以下都是用c語言實現的(只會c語言)
#include/*該檔案包含printf()等函式*/
#include//包含了exit()等函式
#include//包含了malloc(),free()等函式
typedef int elemtype;//定義elemtype 為 int
typedef struct nodenode,*pnode;//看,剛剛說的 後面 就是這裡,我們統一叫struct node 為node了,而且規定它的指標形式為*pnode;
以上我們寫好了標頭檔案,封裝好了結點,開始寫函式啦~
pnode clh(void)
這樣設定頭結點的函式就算做好了,現在得到的是乙個除了頭結點以外空空如也沒有靈魂的鍊錶,接下來就是往函式裡面插東西,使其飽滿
int insert(pnode l,int pos,int e)//出迴圈後,p會指向插入位置的前乙個結點,也就是pos-1的位置
if(!p||pdata=e;
pnew->next=p->next;//一定要先走這一步
p->next=pnew;
return 1;
}
現在是有肉體的鍊錶了,我們還需要增添一些函式使其更有活力,接下來是求鍊錶長度,思路很簡單,需要乙個定位指標,以及乙個計數器,定位指標每往後指乙個,計數器加乙個數,**實現如下
int listlength(pnode l)
return i;
}
求完長度,我們試著將鍊錶的每乙個值列印出來看,思路便是需要乙個定位指標,在不斷往後指的過程中列印所指到的每乙個數,**實現如下
void print(pnode l)
}
接下來我們要面對兩個比較相近的函式,刪除函式和獲取元素函式,咱們就只以刪除指定位置元素作為示例,**如下
int delete(pnode l,int pos,int *e)
if(!(p->next)||i>pos)
return 0;
s=p->next;//刪除過程
*e=s->data;
p->next=s->next;
free(s);//來也匆匆去也匆匆的s,一定記得free掉,程式不能毛躁
return 1;
}
可以發現,我們每次呼叫insert函式時,都要對前面的結點重新遍歷一遍,程式看那些重複的數值都看煩了,所以這就是這個函式的弊端,效率太低,當然這個insert函式是可以選插入指定位置的,但如果想讓函式輸出的順序和我們輸入的一樣,那現在我們考慮把加進來的插在前面,每來乙個數值就插在最前面,也就是 l->next 的位置,這樣我們只需要在列印函式的時候倒敘遍歷就好了
於是乎,我們就有了乙個新的前插函式 newinsert,**如下
int newinsert(pnode l,int e)
else
return 0;
}
還有與之相配套的倒敘列印函式,newprint,**如下
int newprint(pnode l)
for(i=len-1;i>=0;i--)
}printf("\n");
return 1;
}
//線性表鏈式儲存結構的實現
#include#include//包含了exit()等函式
#include//包含了malloc()等函式
typedef int elemtype;//定義elemtype 為 int
//開始設定結點的結構體指標
typedef struct nodenode,*pnode;//看,剛剛說的後面就是這裡,我們統一叫struct node 為node了,而且規定它的指標形式為*pnode;
//結點的結構體已經封裝好了,可以開始使用了
pnode clh(void);
int insert(pnode l,int pos,int e);
int listlength(pnode l);
void print(pnode l);
int newinsert(pnode l,int e);//前插演算法,插入成功返回1,否則返回0
int newprint(pnode l);//與前插演算法配套使用的列印函式,可倒敘列印出每乙個數值
int delete(pnode l,int pos,int *e);
int main()
print(l);
}printf("%d\n",listlength(l));
printf("刪除了第乙個資料:");
delete(l,1,&t);
print(l);
printf("輸入數值,當輸入-1時結束輸入:\n");
scanf("%d",&e);
while(e!=-1)
newprint(l1);
}pnode clh(void)
int insert(pnode l,int pos,int e)//出迴圈後,p會指向插入位置的前乙個結點,也就是pos-1的位置
if(!p||idata=e;
pnew->next=p->next;//一定要先走這一步
在呼叫上面程式的時候,我們會發現鍊錶的順序非常的混亂,我們採用了前插數值的方法,導致了整個鍊錶是倒過來的,倒敘列印倒像是欲蓋彌彰,呼叫刪除和獲取元素的時候比較容易出錯(其實是我自己寫**的時候容易忽略這個倒置的問題)其根本原因還是出在插入函式
說重點很多教材都會講到合併成順序鍊錶的問題,上面的程式為什麼會沒有,因為用上面的插入函式來實現建立鍊錶進而實現線性表的合併是很不明智的做法,上面的通過前插得來的鍊錶是沒有順序的,而合併成順序表,則要求我們對鍊錶進行排列,重新排序會使得程式效率變得及其低下,因為我們要不斷的重複遍歷這個混亂的鍊錶,再進行合併,所以,上述的前插函式,如果只是進行一些列印輸出刪除之類的操作是比較輕鬆,但是對合併可能就不是很友好了
所以下一期就是專門記錄鍊錶合併成順序表的問題
1.指向下乙個結點時用的是 l->next,而不是l->*next
2.插入新結點時,如果先執行p->next = pnew,會導致p->next原來所指的地方找不到了,所以必須先執行pnew->next=p->next
3.我們可以發現,在對鍊錶進行處理的時候,都是要從第乙個元素開始往後查詢,所以基本套路是需要乙個定位指標,即 pnode p=l->next; 這句,從第乙個元素依次往後遍歷
4.我們可以習慣性的在函式頭旁注釋函式的用途以及返回回來的是個什麼東西
5.會發現insert函式呼叫要好久,看到測試截圖後面大大的11.138秒了嗎,大部分都是它的功勞,因為它每次都要遍歷插入位置之前的全部結點,所以效率非常的低
6.單純採用前插演算法在合併成順序表的時候會非常難受,在插入函式時就排序可能是個不錯的選擇
線性表的鏈式儲存實現
include include 下面的單鏈表是帶頭節點的 typedef int elementtype struct listnode typedef struct listnode list 函式宣告 int insert list tmp,elementtype x,int pos void ...
(二)線性表的鏈式儲存實現
1.概念 1 為了儲存每個資料元素和其直接後繼元素之間的邏輯關係,每個元素除了儲存本身的資訊之外,還需儲存只是其直接後繼的資訊。n個結點鏈結成乙個鍊錶,每個結點只包含乙個指標域,又叫單鏈表。2 單鏈表正是通過每個結點的指標域將線性表的資料元素按其邏輯次序鏈結在一起。3 表頭結點 鍊錶中的第乙個結點,...
線性表的鏈式儲存
此方法雖然簡單,但是真寫起來太複雜了。線性表的鏈式儲存 include include struct lnode 線性表的初始化 void init l lnode l 線性表的後插建立 void create l1 lnode l n next null 線性表的後插建立 void create ...