宣告:所有理論筆記**於《大話資料結構》
線性表的鏈式儲存結構
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情況,...