線性表
定義
由同類型數據元素構成有序序列的線性結構。
- 表中元素個數稱爲線性表的長度
- 線性表沒有元素時,稱爲空表
- 表起始位置稱表頭,表結束位置稱表尾
抽象數據類型描述
- 類型名稱:線性表(List)
- 數據對象集:線性表是
n(≥0) 個元素構成的有序序列(a1,a2,...,an) - 操作集:線性表
L∈List ,整數i 表示位置,元素X∈ElementType ,線性表的主要操作有:
List MakeEmpty()
:初始化一個空線性表LElementType FindKth(int K, List L)
:根據位序K,返回相應的元素int Find(ElementType X, List L)
:在線性表L中查找X第一次出現的位置void Insert(ElementType X, int i, ListL)
:在位序i前插入一個新元素Xvoid Delete(int i, List L)
:刪除指定位序i的元素int Length(List L)
:返回線性表L的長度n
順序存儲實現
利用數組的連續存儲空間順序存放線性表的個元素。
線性表的代碼定義
typedef struct LNode *List;
struct LNode {
ElementType Data[MAXSIZE];
int Last;
};
struct LNode L;
List Ptrl;
- 訪問下標爲i的元素:
L.Data[[i]
或Ptrl->Data[i]
- 線性表的長度:
L.Last + 1
或Ptrl->Last + 1
主要操作的實現
初始化(建立空的順序表)
List MakeEmpty() {
List PtrL;
PtrL = (List)malloc(sizeof(struct LNode));
PtrL-Last = -1;
return PtrL;
}
查找
int Find(ElementType X, List PtrL) {
int i = 0;
while (i <= PtrL->Last && PtrL->Data[i] != X)
i++;
if (i > PtrL->Last) // 說明沒有找到,返回-1
return -1;
return i; // 找到後返回的是存儲位置
}
查找成功的平均比較次數爲
插入
在第
void Insert(ElementType X, int i, List PtrL) {
int j;
if (PtrL->Last == MAXSIZE - 1) { // 表空間已滿,不能插入
printf("表滿");
return;
}
if (i < 1 || i > PtrL->Last + 2) { // 位置只能從第一個到末尾之間
printf("位置不合法");
return;
}
for (j = PtrL->Last; j >= i - 1; j--) // 將ai ~ an倒序向後移動
PtrL->Data[j + 1] = PtrL->Data[j] ;
PtrL->Data[i - 1] = X; // 新元素插入
PtrL->Last++; // Last仍然指代最後元素的下標
}
元素的向後移動:
* 平均移動次數爲
* 平均時間性能爲
刪除
刪除表的第
void Delete(int i, List PtrL) {
int j;
if (i < 1 || i > PtrL->Last + 1) {
printf("不存在第%d個元素", i);
return;
}
for (j = i; j <= PtrL->Last; j++)
PtrL->Data[j - 1] = PtrL->Data[j]; // 將ai+1 ~ an 順序向前移動
PtrL->Last--; // Last仍然指代最後元素的下標
}
鏈式存儲實現
不要求邏輯上相鄰的兩個元素物理上也相鄰。通過“鏈”建立起數據元素之間的邏輯關係。
線性表的代碼定義
typeof struct LNode *List;
struct LNode {
ElementType Data;
List Next;
};
struct LNode L;
List PtrL;
主要操作的實現
求表長
int Length(List PtrL) {
List p = PtrL; // p指向表的第一個結點
int len = 0;
while (p != NULL) {
p = p->Next;
len++; // 當前P指向的是第len個結點
}
return len;
}
時間性能爲
查找
按序號查:FindKth
List FindKth(int K, List PtrL) {
List p = PtrL;
int i = 1;
while (p != NULL && i < k) {
p = p->Next;
i++;
}
if (i == k) // 說明找到第K個元素
return P;
return NULL;
}
平均時間性能爲
按值查找:Find
List Find(ElementType X, List PtrL) {
List p = PtrL;
while (p != NULL && p->Data != X) // 如果在鏈表中沒有元素等於X,那麼會返回NULL
p = p->Next;
return p;
}
平均時間性能爲
插入
在第
1. 先構造一個新結點,用s指向
2. 再找到鏈表的第
3. 然後修改指針,插入結點(p之後插入新結點是S)
// 不帶頭結點的鏈表
List Insert(ElementType X, int i, List PtrL) {
List p, s;
if (i == 1) { // 新結點插入在表頭
s = (List)malloc(sizeof(struct LNode)); // 創建一個新結點
s->Data = x;
s->Next = PtrL;
return s;
}
p = FindKth(i - 1, PtrL); // 查找到第i-1個結點
if (p == NULL) { // 第i-1個不存在,不能插入
printf("參數i錯誤");
return NULL;
}
s = (List)malloc(sizeof(struct LNode)); // 創建一個新結點
s->Data = x;
s->Next = p->Next; // 新結點插入在第i-1個結點後面
p->Next = s;
return PtrL;
}
平均查找次數爲
刪除
刪除鏈表的第
1. 先找到鏈表的第
2. 再用指針s指向要被刪除的結點(p的下一個結點)
3. 然後修改指針,刪除s所指結點
4. 最後釋放s所指結點的空間
List Delete(int i, List PtrL) {
List p, s;
if (i == 1) { // 如果刪除的是表的第一個結點
s = PtrL; // s指向第一個結點
if (PtrL != NULL) // 從鏈表中刪除
PtrL = PtrL->Next;
else
return NULL;
free(s);
return PtrL;
}
p = FindKth(i - 1, PtrL); // 查找第i-1個結點
if (P == NULL || p->Next == NULL) {
printf("第%d個結點不存在", i - 1);
return NULL;
}
s = p->Next; // s指向第i個結點
p->Next = s->Next; // 從鏈表中刪除
free(s); // 釋放被刪除的結點
return PtrL;
}
平均查找次數爲
廣義表(Generalized List)
- 廣義表是線性表的推廣
- 對於線性表而言,n個元素都是基本的單元素
- 廣義表中,這些元素不僅可以是單元素也可以是另一個廣義表
代碼定義
typedef struct GNode *GList;
struct GNode {
int Tag; // 標誌域:0表示結點是單元素,1表示結點是廣義表
union { // 子表指針域Sublist與單元素數據域Data複用,即共用存儲空間
ElementType Data;
GList SubList;
} URegion;
GList Next; // 指向後繼結點
};
多重鏈表
鏈表中的結點可能同時隸屬於多個鏈。
- 多重鏈表中結點的指針域會有多個,如前面例子包含了Next和SubList兩個指針域
- 但包含兩個指針域的鏈表並不一定是多重鏈表,比如雙向鏈表不是多重鏈表
多重鏈表有廣泛的的用途:基本上如樹、圖這樣相對複雜的數據結構都可以採用多重鏈表方式實現存儲。
多重鏈表存儲矩陣
採用一種典型的多重鏈表——十字鏈表來存儲稀疏矩陣:
* 只存儲矩陣非0元素項,結點的數據域:行座標Row、列座標Col、數值Value
* 每個結點通過兩個指針域,把同行、同列串起來:
* 行指針(或稱爲向右指針)Right
* 列指針(或稱爲向下指針)Down
- 用一個標識域Tag來區分頭結點與非0元素結點
- 頭結點的標識域爲”Head”,矩陣非0元素結點的標識值爲”Term”