資料結構與演算法 第四天 線性表 鍊錶結構(二)

2021-09-12 17:11:36 字數 3504 閱讀 9482

宣告:所有理論筆記**於《大話資料結構》

線性表的鏈式儲存結構

1 線性表的順序儲存結構

定義:用一組任意的儲存單元儲存線性表的資料元素,這組儲存單元可以時連續的,也可以是不連續的。

為了表示每個資料元素ai與其本身的資訊之外,還需儲存乙個指示其直接後繼的資訊(即直接後繼的儲存位置)。我們把儲存資料元素資訊的域稱為資料域,把儲存直接後繼位置的域稱為指標域。指標域中儲存的資訊稱作指標域或鏈。這兩部分資訊組成資料元素ai的儲存映像,稱為節點(node)。

我們把連表中第乙個結點的儲存位置叫做頭指標。線性表最後乙個結點指標為「空」。

有時候為了方便地對鍊錶進行操作,會在單鏈表的第乙個結點前附設乙個結點,稱為頭結點。頭結點資料域可以不儲存任何資訊

線性表的順序儲存的結構**。

typedef int elemtype;   //elemtype型別根據實際情況而定,這裡假設為int

typedef struct node node;

typedef struct node *linklist;  //定義linklist

結點由存放資料元素的資料域和存放後繼結點位址的指標域組成。

2 順序儲存結構的插入與刪除

2.1 獲取鍊錶第i個資料的演算法思路:

1.宣告乙個指標p指向鍊錶第乙個結點,初始化j從1開始;

2.當j**:

#define ok 1

#define error 0

#define true 1

#define false 0

typedef int status;

/** 初始條件:順序線性表l已存在,1<= i <= listlength(l)

* 操作結果:用e返回l中第i個資料元素的值

*/status getelem(sqlist l, int i, elemtype *e)

if(!p || j > i)

*e = p->data;   //取第i個結點的資料

return ok;

}由於這個演算法的時間複雜度取決於i的位置,當i=1時,則不需要遍歷,第乙個就取出來了,而當i=n時則遍歷n-1次才可以。因此最壞情況的時間複雜度時o(n)。

由於單鏈表的結構中沒有定義表長,所以不能事先知道要迴圈多少次,因此也就不方便使用for迴圈。器主要核心思想就是「工作指標後移」,這其實也是很多演算法的常用技術。

2.2 單鏈表的插入

單鏈表第i個資料插入結點的演算法思路:

1. 宣告一指標p指向煉表頭結點,初始化j從1開始;

2. 當jdata;

6. 單鏈表的插入標準語句s->next = p->next; p->next = s;

7. 返回成功

實現**如下

/* 初始條件: 順序線性表l已存在, 1<=i<=listlength(l),

* 操作結果: 在l中第i個位置之前插入新的資料元素e,l的長度加1

*/status listinsert(sqlist *l, int i, elemtype e)

if(!p || j > i)

s = (linklist)malloc(sizeof(node)); //生成新結點(c標準函式)

s->data = e;

s->next = p-next;   //將p的後繼結點賦值給s的後繼

p->next = s;    //將s賦值給p的後繼

return ok;

}2.3 刪除操作

單鏈表第i個資料刪除結點的演算法思路:

1. 宣告一指標p指向煉表頭結點,初始化j從1開始;

2. 當jnext賦值給q;

5. 單鏈表的刪除標準語句p->next = q->next;

6. 將q結點中的資料賦值給e,作為返回;

7. 釋放q結點;

8. 返回成功。

實現**如下:

/*** 初始條件:順序線性表l已存在, 1 <= i <= listlength(l)

* 操作結果:刪除l的第i個資料元素,並用e返回其值,l的長度減1

*/status listdelete(sqlist *l, int i, elemtype *e)

if(!(p->next) || j > i)

q = p->next;

p->next = q->next;  //將q的後繼賦值給p的後繼

*e = q->data;   //將q結點中的資料給e

free(q);        //讓系統**此結點,釋放記憶體

return ok;

}插入和刪除的時間複雜度都是o(n)。如果在我們不知道第i個結點的指標位置,單鏈表資料結構在插入和刪除操作上,與線性表的順序結構是沒有太大優勢的。但如果,我們希望從第i個位置,插入10個結點,對與順序儲存結構意味著,每一次插入都需要移動n-i個結點,每次都是o(n)。而單鏈表,我們只需要在第一次時,找到第i個位置的指標,此時為o(n),接下來只是簡單地通過賦值移動指標而已,時間複雜度都是o(1)。顯然,對於插入或刪除資料越頻繁的操作,單鏈表的效率優勢就越是明顯。

2.4 單鏈表的整表建立

單鏈表整表建立的演算法思路:

1. 宣告一指標p和計數器變數i;

2. 初始化一空鍊錶l;

3. 讓l的頭結點的指標指向null,即建立乙個帶頭結點的單鏈表;

4. 迴圈:

a 生成一新結點賦值給p;

b 隨機生成一數字賦值給p的資料域p->data;

c 將p插入到頭結點與前一新結點之間。

實現**如下:

//隨機產生n個元素的值,建立帶表頭結點的單鏈線性表l(頭插法)

void createlisthead(linklist *l, int n)

}尾插法實現如下

//隨機產生n個元素的值,建立帶表頭結點的單鏈表l(尾插法)

void createlisttail(linklist *l, int n)

r->next = null;

}2.5 單鏈表的整表刪除

單鏈表整表刪除的演算法思路如下:

1. 宣告一結點p和q;

2. 將第乙個結點賦值給p;

3. 迴圈:

a 將下乙個結點賦值給q;

b 釋放p;

c 將q賦值給p。

演算法實現如下:

//初始條件:順序線性表l已存在,操作結果:將l重置為空表

status clearlist(linklist *l)

(*l)->next = null;  //頭結點指標域為空

return ok;

}3. 實現中遇到的問題

3.1 插入元素後列印,無限列印0?

q = (linklist)malloc(sizeof(node)); //生成新結點

q->data = e;

q->next = p->next;  //把p的後繼給新生成元素的後繼

p->next = p;    //把新生成元素作為p的後繼

在實現插入的時候最後把p賦給了p->next,正確的應該把q賦給->next.

難道這就是書中用s不用q的原因?

資料結構與演算法 線性表 鍊錶

我畫了20張圖,終於讓女朋友學會了翻轉鍊錶 碼海 該文章介紹了鍊錶的基礎知識,程式區域性性原理 重點關注 尾插法 public class linkedlist tmp.next newnode val 尾部插入 public class linkedlist public static void ...

資料結構與演算法 線性表 鍊錶

書接上文 前篇提到的順序表是線性表的一種表示形式,但是在頻繁的增刪操作中 順序表是不夠打的 這就要引出今天的鍊錶了 三 迴圈鍊錶的基本演算法 四 雙向鍊錶的基本演算法總結 除頭結點和尾結點外,每個結點有且只有乙個直接前驅和直接後繼 最後乙個結點的指標為null 邏輯關係 資料元素之間的邏輯關係是由結...

學習資料結構的第四天 雙向鍊錶

建立輔助節點temp指向表頭,建立兩乙個節點temp2存放temp.next 遍歷鍊錶,若temp.next null,遍歷到最後 temp.next.no 新節點.no,可以新增,並且退出迴圈 temp.next.no 新節點.no,表示已經存在 新增節點,如果是temp.next null情況,...