上面這張圖來自課件,發現這張圖將計算機四大基礎學科之間的關系很好的體現出來了,故貼在這。
第一章 緒論
1.1 數據結構的基本概念
數據元素、數據項
數據元素是數據的基本單位。數據元素由數據項組成。
如,在飯館排隊的顧客,可用以下數據元素和數據項表示:
數據結構、數據對象
數據結構是相互之間存在一種或多種特定關系的數據元素的集合。
數據對象是具有相同性質的數據元素的集合,是數據的一個子集。
索引存儲:
第二章 線性表
2.1 線性表的定義和操作
2.1.1 線性表的定義(邏輯結構)
線性表是具有相同數據類型的 n 個數據元素的有限序列,其中 n 為表長,當 n = 0 時,線性表是一個空表。若用 L 命名線性表,則一般表示為:L = {a1, a2, ... , an}。
位序:也就是元素在線性表中的位置。位序從 1 開始,在編程時,數組從 0 開始。
表頭元素為 a1 和表尾元素為 a2。
除頭尾元素外,每個元素都有一對直接前驅和直接后驅。
2.1.2 線性表的基本操作
InitList(&L)
:初始化表。構造一個空的線性表 L,並分配內存空間。
DestroyList(&L)
:銷毀表,並釋放線性表 L 占用的內存空間。
ListInsert(&L, i, &e)
:插入操作。在表 L 的第 i 個位置插入指定元素 e 。
ListDelete(&L, i, &e)
:刪除操作。刪除表 L 中第 i 個位置的元素,並用 e 返回刪除元素的值。
LocateElem(L, e)
:按值查找。在表 L 中查找具有給定元素值的元素。
GetElem(L, i)
:按位查找。獲取表 L 中第 i 個位置的元素的值
其他常用操作
Length(L)
:求表長。返回線性表 L 的長度,即表中元素的個數。
PrintList(L)
:打印表。按順序輸出線性表 L 的所有元素值。
Empty(L)
:判斷是否為空。若 L 為空表,則返回 true,否則返回 false。
Tips:
操作數據結構的思路:創銷、增刪改查。
實際開發中,可根據實際需求定義其他操作。
關於引用 &
:引用運算符是 C++ 中的運算符,C 中沒有這種操作。
比起學會“How”(怎么做),更重要的是想明白“Why”(為什么要這么做)。
2.2 線性表的順序表示
如何獲取數據元素的長度?
使用sizeof()
函數。
2.2.1 順序表的定義
用順序存儲的方式實現線性表。順序存儲,也就是將邏輯上相鄰的元素存儲在相鄰的物理位置上。
2.2.2 順序表的實現
靜態分配
用靜態分配的方式實現順序表,表的長度后續不可再發生改變。
代碼實現示例:
#define MaxSize 10 // 定義最大長度
typedef struct {
int data[MaxSize]; // ElemType = int, * 用靜態的“數組”存訪數據元素
int length; // 順序表的當前長度
} SqList;
// 初始化順序表
void InitList(SqList &L) {
L.length = 0; // 順序表初始化為0
}
int main() {
SqList L; // 聲明一個順序表
InitList(L); // 初始化順序表
return 0;
}
動態分配
malloc()
函數的作用:會申請一片存儲空間,並返回存儲空間第一個位置的地址,也就是該位置的指針。
#define InitSize 10 // 順序表的初始長度
typedef struct {
int * data; // ElemType = int, * 聲明動態分配數組的指針
int MaxSize; // 順序表的最大容量
int length; // 順序表的當前長度
} SqList;
// 初始化順序表
void InitList(SqList &L) {
// 用 malloc 函數申請一片連續的存儲空間
L.data = (int *)malloc(InitSize * sizeof(int));
L.length = 0;
L.MaxSize = InitSize;
}
// 增加動態數組的長度
void IncreaseSize(SqList &L, int len) {
int * p = L.data;
L.data = (int *)malloc((L.MaxSize + len) * sizeof(int));
for (int i = 0; i < L.length; i++)
L.data[i] = p[i]; // 將數據復制到新區域
L.MaxSize = L.MaxSize + len; // 順序表最大長度增加len
free(p); // 釋放原來的內存空間
}
int main() {
SqList L; // 聲明一個順序表
InitList(L); // 初始化順序表
IncreaseSize(L, 5);
return 0;
}
2.2.3 順序表的特點
- 可隨機訪問,查找元素所需時間復雜度為
O(1)
。 - 存儲密度高,每個節點只存儲數據元素。
- 拓展容量不方便(即使使用動態分配的方式實現,拓展長度的時間復雜度也比較高,因為需要把數據復制到新的區域)。
- 插入刪除操作不方便,需移動大量元素:
O(n)
。
靜態分配與動態分配的對比:
靜態分配無法拓展容量,動態分配可以拓展容量。
2.2.4 順序表的基本操作
1. 插入
#define MaxSize 10 // 定義最大長度
typedef struct {
int data[MaxSize]; // 用靜態的“數組”存訪數據元素
int length; // 順序表的當前長度
} SqList;
bool ListInsert(SqList &L, int i, int e) {
if (i < 1 || i > L.length+1) // 判斷i的范圍是否有效
return false;
if (L.length >= MaxSize) // 判斷存儲空間是否已滿
return false;
for (int j = L.length; j >= i; j--) // 將第i個元素之后的元素后移
L.data[j] = L.data[j-1];
L.data[i-1] = e; // 在位置i處放入e
L.length++; // 長度+1
return true;
}
int main() {
SqList L;
InitList(L);
ListInsert(L, 3, 3);
return 0;
}
時間復雜度分析
- 最好的情況:在表尾插入,無需移動元素。O(1) 。
- 最壞的情況:在表頭插入,需移動所有元素。O(n) 。
- 平均情況:所有位置插入的可能性都為 p = 1/(n+1) ,則平均時間復雜度 = O[np + (n-1)p + ··· + 0*p] = O(n/2),也就是 O(n) 。
2. 刪除
#define MaxSize 10 // 定義最大長度
typedef struct {
int data[MaxSize]; // 用靜態的“數組”存訪數據元素
int length; // 順序表的當前長度
} SqList;
bool ListInsert(SqList &L, int i, int e) {
if (i < 1 || i > L.length+1) // 判斷i的范圍是否有效
return false;
if (L.length >= MaxSize) // 判斷存儲空間是否已滿
return false;
for (int j = L.length; j >= i; j--) // 將第i個元素之后的元素后移
L.data[j] = L.data[j-1];
L.data[i-1] = e; // 在位置i處放入e
L.length++; // 長度+1
return true;
}
bool ListDelete(SqList &L, int i, int &e) {
if (i < 1 || i > L.length)
return false;
e = L.data[i-1]; // 將被刪除的元素賦值給e
for (int j = i; j < L.length; j++) //將第i個位置后的元素前移
L.data[j-1] = L.data[j];
L.length--;
return true;
}
int main() {
SqList L;
InitList(L);
int e = -1;
if (ListDelete(L, 3, e))
printf("已刪除第3個元素,刪除元素值為%d\n", e);
else
printf("位序i不合法,刪除失敗\n");
return 0;
}
時間復雜度分析
- 最好時間復雜度:刪除表尾,O(1)。
- 最壞時間復雜度:刪除表頭,后續 n-1 個元素全部前移,O(n)。
- 平均時間復雜度:每個元素被刪除的概率都為 p = 1/n,O[(n-1)p + (n-2)p + ··· + 0*p] = O[(n-1)/2] = O(n)
3. 查找
a. 按位查找
GetElem(L, i)
/* 靜態分配的按位查找 */
#define MaxSize 10 // 定義最大長度
typedef struct {
ElemType data[MaxSize]; // 用靜態的“數組”存訪數據元素
int length; // 順序表的當前長度
} SqList;
ElemType GetElem(SqList L, int i) {
return L.data[i-1];
}
/* 動態分配的按位查找 */
#define InitSize 10 // 順序表的初始長度
typedef struct {
ElemType * data; // ElemType = int, * 聲明動態分配數組的指針
int MaxSize; // 順序表的最大容量
int length; // 順序表的當前長度
} SqList;
ElemType GetElem(SqList L, int i) {
return L.data[i-1];
}
時間復雜度分析
由於元素連續存放,可以根據起始地址和元素大小立刻找到元素——隨機存取特性。
最好時間復雜度 = 最壞時間復雜度 = 平均時間復雜度 = O(1)
b. 按值查找
LocateElem(L, e)
#define InitSize 10 // 順序表的初始長度
typedef struct {
ElemType * data; // ElemType = int, * 聲明動態分配數組的指針
int MaxSize; // 順序表的最大容量
int length; // 順序表的當前長度
} SqList;
// 查找第一個元素值為e的元素,並返回其位序
int LocateElem(SqList L, ElemType e) {
for (int i = 0; i < L.length; i++)
if (L.data[i] == e)
return i+1; // 數組下標為i的元素值等於e,返回其位序i+1
return 0; // 沒有查找到
}
時間復雜度分析
- 最好時間復雜度:目標在表頭,O(1) 。
- 最壞時間復雜度:目標在表尾,O(n) 。
- 平均時間復雜度:目標元素在任何位置的概率都為 p = 1/n,則平均時間復雜度為 O[1 * 1/n + 2 * 2/n + ··· + n * 1/n] = O[(n+1)/2] = O(n) 。
C 中不能用
==
判斷兩個結構體是否相等