1 線性表
必會概念:
鏈式存儲 前驅 後繼 表長
鏈表的本質:
LinkList L; L = create_LinkList(10);
此時L看起來是一個線性表,其實它本質只是一個節點的引用,指向線性表表頭節點.通過這個引用我們可以遍歷整張線性表
1.1數據結構如何應用到運算
邏輯結構定義算法,存儲結構運行算法
線性表常用邏輯算法:
- 初始化:Init_List(L) 構造一個空表
- 求表長:Length_List(L) 返回表含有的元素的個數
- 取表元:Get_List(L,i) L非空,i正確時,返回地i個表元的值或地址
- 查找(定位):已知值,查下標. Locate_List(L,x) 返回x在L中首次出現的序號或地址,否則返回-1
- 插入:Insert_List(L,i,x) L存在,i正確.是L[i]=x; L.len()++; L[i]之後的表元下標依次加一
- 刪除: Delete_List(L,i) L存在,i正確時 L[i] = L[i+1] ;L.len()-- ;L[i]之後的表元下標依次減一
1.2 順序表
列表在內存中按連續物理地址存儲,知道了地址就一定能訪問到對應的元素
優點與好處
- 已知表頭地址Loc(
$a_0$
),表元類型(所佔字節) d比特,則瞬間可求第(i+1)個元素的地址.Loc($a_i$
)=Loc($a_0$
)+i*d , 0≤i≤n - 所以順序存儲用來查找,訪問,修改元素非常方便
1.3 順序表的實現
距離是元素間相距長度,個數是下標相減再加1.
Capacity(L) = L.MAXSIZE ;下標n必須時刻≤MAXSIZE ;設插入下標爲i,最末下標爲n; i必須時刻≤n+1;
1.3.1 動態初始化
- 構造一個空表.
- 動態分配存儲空間,先確定表容量,但具體地址對應的表元在後續代碼裏確定
- 定義表指針last=-1,隨L.len()增加而增加
1.3.2 插入運算
- 用for循環實現:j=n+1 從
$L(n)$
起,元素依次向後挪動一位.$L(j)$
=$L(j-1)$
; j–; - 插入x: L(i) = x;
- 修改表尾指針:L.last = L.len()++;
- 算法複雜度計算: 主要耗時都發生在元素移動時.每次移動(n-i+1)個元素,即移動(n-i+1)次,假設插入到底i個元素是等概率事件,則對應的概率
$P_i$
=1/(n+1). - 總移動次數 =
$\sum_{i=1}^{n+1}=P_i(n-i+1)=\frac{n}{2}$
,即時間複雜度是O(n)
1.3.3 刪除操作
- 0≤i≤n,否則報錯,已經包含了i=-1,L爲null時的情景.
- for循環實現:j=;j≤n;從L[i]起,L[j] = L[j+1];j++;
- 算法複雜度:O(n)
1.3.4 查找操作
找列表中值爲x的第一個元素下標
- for循環,j=0; L[j]==x ; 否則j++;
- 若j==n,返回"無"
- 算法複雜度:`
1.4 線性表的鏈式存儲與運算實現
先修知識
malloc與new的區別
new會自動根據類型分配內存地址,並返回同類型的指針給引用
int *p1,*p2;
p1 = new int;
p2 = new int[100];
malloc只能動態分配內存,意思就是不會自動分配,必須人爲設定內存大小與返回的指針類型
int *p;
p = (int*)malloc(sizeof(int)*100);
->的用法
->是間接引用符,是二目訪問符,類似於成員符 .
typedef struct Node{
int a;
double b;
}node, *linkList;
*linkList = &node;
//struct Node *p = &node;
//此時
linkList->a等價於(*linkList).a;
struct的用法
自定義類型struct
typedef struct Node{
Elemtype a;
Node *next;
}node,linkList; //結構變量node等有點像靜態類,這裏借用java編程思想描述,Node是父類,node,linkList是子類.
或
struct Node node,linkList;//按模板類型Node定義具體類型node,linkList
寫一個struct Node,編譯器只是讀取它,並不會爲它賦予內存空間.邏輯合理,它只是一個模板.
1 線性表
必會概念:
鏈式存儲 前驅 後繼 表長
鏈表的本質:
LinkList L; L = create_LinkList(10);
此時L看起來是一個線性表,其實它本質只是一個節點的引用,指向線性表表頭節點.通過這個引用我們可以遍歷整張線性表
1.1數據結構如何應用到運算
邏輯結構定義算法,存儲結構運行算法
線性表常用邏輯算法:
- 初始化:Init_List(L) 構造一個空表
- 求表長:Length_List(L) 返回表含有的元素的個數
- 取表元:Get_List(L,i) L非空,i正確時,返回地i個表元的值或地址
- 查找(定位):已知值,查下標. Locate_List(L,x) 返回x在L中首次出現的序號或地址,否則返回-1
- 插入:Insert_List(L,i,x) L存在,i正確.是L[i]=x; L.len()++; L[i]之後的表元下標依次加一
- 刪除: Delete_List(L,i) L存在,i正確時 L[i] = L[i+1] ;L.len()-- ;L[i]之後的表元下標依次減一
1.2 順序表
列表在內存中按連續物理地址存儲,知道了地址就一定能訪問到對應的元素
優點與好處
- 已知表頭地址Loc(
$a_0$
),表元類型(所佔字節) d比特,則瞬間可求第(i+1)個元素的地址.Loc($a_i$
)=Loc($a_0$
)+i*d , 0≤i≤n - 所以順序存儲用來查找,訪問,修改元素非常方便
1.3 順序表的實現
距離是元素間相距長度,個數是下標相減再加1.
Capacity(L) = L.MAXSIZE ;下標n必須時刻≤MAXSIZE ;設插入下標爲i,最末下標爲n; i必須時刻≤n+1;
1.3.1 動態初始化
- 構造一個空表.
- 動態分配存儲空間,先確定表容量,但具體地址對應的表元在後續代碼裏確定
- 定義表指針last=-1,隨L.len()增加而增加
1.3.2 插入運算
- 用for循環實現:j=n+1 從
$L(n)$
起,元素依次向後挪動一位.$L(j)$
=$L(j-1)$
; j–; - 插入x: L(i) = x;
- 修改表尾指針:L.last = L.len()++;
- 算法複雜度計算: 主要耗時都發生在元素移動時.每次移動(n-i+1)個元素,即移動(n-i+1)次,假設插入到底i個元素是等概率事件,則對應的概率
$P_i$
=1/(n+1). - 總移動次數 =
$\sum_{i=1}^{n+1}=P_i(n-i+1)=\frac{n}{2}$
,即時間複雜度是O(n)
1.3.3 刪除操作
- 0≤i≤n,否則報錯,已經包含了i=-1,L爲null時的情景.
- for循環實現:j=;j≤n;從L[i]起,L[j] = L[j+1];j++;
- 算法複雜度:O(n)
1.3.4 查找操作
找列表中值爲x的第一個元素下標
- for循環,j=0; L[j]==x ; 否則j++;
- 若j==n,返回"無"
- 算法複雜度:`
1.4 線性表的鏈式存儲與運算實現
先修知識
malloc與new的區別
new會自動根據類型分配內存地址,並返回同類型的指針給引用
int *p1,*p2;
p1 = new int;
p2 = new int[100];
malloc只能動態分配內存,意思就是不會自動分配,必須人爲設定內存大小與返回的指針類型
int *p;
p = (int*)malloc(sizeof(int)*100);
->的用法
->是間接引用符,是二目訪問符,類似於成員符 .
typedef struct Node{
int a;
double b;
}node, *linkList;
*linkList = &node;
//struct Node *p = &node;
//此時
linkList->a等價於(*linkList).a;
struct的用法
自定義類型struct
typedef struct Node{
Elemtype a;
Node *next;
}node,linkList; //結構變量node等有點像靜態類,這裏借用java編程思想描述,Node是父類,node,linkList是子類.
或
struct Node node,linkList;//按模板類型Node定義具體類型node,linkList
寫一個struct Node,編譯器只是讀取它,並不會爲它賦予內存空間.邏輯合理,它只是一個模板.
棧
後進先出,前插法
抽象:表L; 新節點s(malloc,s->a=x); s->next=L(指向表頭);L=s(表頭指向s,s再指向next裏保存的舊錶頭地址)
linkList create_stack(int flag){
linkList L = null; //linkList是指針子類,故L也是指針,又L=null靜態聲明,省去了malloc分配.
// 同時! L==null;第一個節點s->next=L,巧妙地使最後一個節點的next爲null.不會出現野指針.
node *s;
int x;
scanf("%d",&x);
int cout=1;
while(cout<=flag){
s=(Node*)malloc(sizeof(node));//每次循環都會手動new一個s.因爲新的s節點的next後繼是上一節點,所以新的s節點是上一節點的前驅.即新的s節點是前插的.滿足棧後進先出的形式.
s->data=x;
s->next=L;//s上鍊指向L的表頭,此時s還不算在L內
L = s;//讓s做L的表頭,即巧妙地讓L永遠指向新的表頭元素.
scanf("%d",&x);
cout++;
}
return L;
}
隊列
後進後出,尾插法
表L永遠指向表頭,我們可以用副表*r先指向第一個節點(s作表頭時),它與L都是指向第一個節點的引用.
linkList create_quene(int flag){
linkList L=null;
node *s,*r=null;
int x;
scanf("%d",&x);
int cout=1;
while(cout<=flag){
s = malloc(sizeof(node)); //new一個s,new完對s的屬性賦值
s->a = x;
if(L=null) L=s;
else r->next = s;上鍊
r = s; //此處r,L都是s的軟引用.L佔了r的光,之後一條龍的鏈表全靠r接上.L最多指向表頭,其餘上鍊操作有r來完成
scanf("%d",&x);
cout++;
}
//定義表尾節點的後繼節點爲空.
}
1.4.1 節點
單鏈表由一個個節點構成
typedef struct node{
int data;
struct node *next;
}LNode
node LNode = {};