C語言-----數據結構從門到精通

1.數據結構基本概念

?????????數據結構是計算機中存儲、組織數據的方式,旨在提高數據的訪問和操作效率。它是實現高效算法和程序設計的基石。???????

????????目標:通過思維導圖了解數據結構的知識點,并掌握。

1.1邏輯結構

????????邏輯結構主要四種類型:

? ? ? ? ?集合:結構中的數據元素之間除了同屬于一個集合的關系外,別無其他關系。
? ? ? ?? 線性結構:結構中的數據元素之間存在一個對一個的關系,如線性表。
? ? ? ?? 樹形結構:結構中的元素之間存在一個對多個的關系,像二叉樹等。
? ? ? ? ?圖狀結構或網狀結構:結構中的數據元素之間存在多個對多個的關系。????????

1.2物理結構

? ? ? ? 物理結構主要兩種類型的特點:

????????順序存儲結構:數據元素在存儲器中是順序存放的,邏輯上相鄰的數據元素在物理位置上也相鄰。
????????鏈式存儲結構:數據元素在存儲器中的位置是任意的,邏輯上相鄰的數據元素在物理位置上不一定相鄰,通過指針來表示元素之間的關系。

1.3抽象數據類型(ADT)

????????抽象數據類型是指一個數學模型以及定義在該模型上的一組操作。它將數據和操作封裝在一起,使用者只需要關心它的功能,而不需要了解其內部實現細節。

????????在數據結構設計中,ADT有助于提高程序的模塊化和可維護性。

? ? ? ? 可以簡單理解成:接口,封裝,模塊化。

1.4數據結構常用的關鍵字及函數

? ? ? ? 數據結構核心必須要掌握常用的關鍵字,常用函數的原型以及內存的資源分配,對內存的操作時需要注意:內存申請,內存使用的狀態,內存的釋放,避免數據溢出而導致的程序異常,這里我們就簡單介紹下struct、union、typedef 。

1.4.1數據結構常用關鍵字及函數定義

1.4.2數據結構體及聯合體解析

1.4.2.1 struct解析

????????struct代碼示例:

//struct原型定義的格式為:struct 結構體名 { 成員列表 };?#include <stdio.h> // 引入標準輸入輸出庫//1. struct Point定義了一個名為 Point 的結構體struct Point {int x; // int x;:結構體成員 x,用于存儲點的 x 坐標int y; // int y;:結構體成員 y,用于存儲點的 y 坐標
};int main() {//2. 聲明結構體變量:    
// struct Point p1;:聲明一個 Point 類型的變量 p1,用于存儲一個點的坐標struct Point p1;  //3. 初始化結構體 p1 的成員變量 p1.x = 10; // p1.x = 10;:將 p1 的 x 坐標設置為 10p1.y = 20; // p1.y = 20;:將 p1 的 y 坐標設置為 20//4. 訪問并打印 p1 的坐標
//p1.x 和 p1.y 分別訪問 p1 的 x 坐標和 y 坐標printf("Point p1: (%d, %d)\n", p1.x, p1.y); // 輸出點 p1 的坐標return 0; // 程序結束
}
1.4.2.2 union解析

????????union代碼示例:

/*
1.union原型:
2.UnionName是union的名稱
3.type1, type2, ..., typen是union中成員的數據類型,member1, member2, ..., membern是union的成員名稱*/union UnionName {type1 member1;type2 member2;// ...typen membern;
};#include <stdio.h>
#include <string.h>//1. 定義一個 union
// union Data 定義了一個名為 Data 的 union,及三個成員:一個整數 i,一個浮點數 f,一個字符數組 str
// union 中的所有成員共享同一塊內存空間,因此 union 的大小等于其最大成員的大小union Data {int i;          // 整數成員float f;        // 浮點數成員char str[20];   // 字符數組成員
};int main() {//2. 定義 union 變量及聲明:
// union Data data;:聲明一個 Data 類型的變量 data,用于存儲不同類型的數據union Data data;//3. 訪問 union 成員:data.i = 10;    // data.i = 10;:將 data 的整數成員 i 設置為 10printf("data.i: %d\n", data.i);  // 使用 printf 函數輸出 data.i 的值//4. 修改 union 成員data.f = 220.5; // 將浮點數成員 f 設置為 220.5printf("data.f: %f\n", data.f);  // 輸出浮點數成員 f 的值//5. 再次訪問 union 成員
由于 union 成員共享同一塊內存空間,初始化 f 會覆蓋之前的 i 的值。printf("data.i: %d\n", data.i);  // 輸出整數成員 i 的值,可以看到它已經被覆蓋//6. 使用 str 成員strcpy(data.str, "hello glang");  // 將字符串 "Hello glang" 復制到字符數組成員 strprintf("data.str: %s\n", data.str);  // 輸出字符數組成員 data.str 的值//7. 再次訪問 union 成員
// 由于 union 成員共享同一塊內存空間,初始化 str 會覆蓋之前的 i 和 f 的值printf("data.i: %d\n", data.i);  return 0;
}
1.4.2.3 typedef 解析

????????代碼示例:

/*
typedef的基本語法existing_type是已存在的數據類型,new_type_name是你為這個數據類型定義的新名稱typedef existing_type new_type_name;
*///1. 為基本數據類型int定義別名Integer
typedef int Integer;//2. 為基本數據類型float定義別名Real
typedef float Real;//3. 為指向int類型的指針定義別名IntPtr
typedef int *IntPtr;//4. 為指向char指針的指針(即char**)定義別名StringArray,可用于表示字符串數組
typedef char **StringArray;//5. 為包含10個int元素的數組定義別名ArrayOfInt
typedef int ArrayOfInt[10];//6. 為匿名結構體定義別名Point,該結構體包含兩個int類型的成員x和y
typedef struct {int x; // x坐標int y; // y坐標
} Point;//7. 為指向函數的指針定義別名Operation,該函數接受兩個int參數并返回一個int值
typedef int (*Operation)(int, int);//8. Integer a = 10; // 等同于 int a = 10;//9. Real b = 20.5f; // 等同于 float b = 20.5f;//10. IntPtr p = &a; // 等同于 int *p = &a;//11. StringArray strings = {"Hello", "World"}; // 等同于 char *strings[] = {"Hello", "World"};//12. ArrayOfInt numbers = {1, 2, 3, 4, 5, 6, 7, 8, 9, 0}; // 等同于 int numbers[10] = {1, 2, 
3, 4, 5, 6, 7, 8, 9, 0};//13. Point p1 = {1, 2}; // 等同于 struct {int x; int y;} p1 = {1, 2};//14. 假設有一個函數add,它接受兩個int參數并返回它們的和int add(int x, int y) {return x + y;
}Operation op = add; // 等同于 int (*op)(int, int) = add;int sum = op(5, 3); // 調用add函數,等同于 int sum = add(5, 3);
1.4.2.4?struct、union、typedef 聯合應用

? ? ? ? 代碼示例:

# include <stdio.h>
# include <string.h>// 定義一個結構體,用于表示二維平面上的一個點
struct Point {// 點的 x 坐標int x;// 點的 y 坐標int y;
};// 定義一個聯合體,用于表示不同類型的數據
union Data {// 整數成員int i;// 浮點數成員float f;// 字符數組成員char str[20];
};// 使用 typedef 為結構體和聯合體定義別名,方便后續使用
typedef struct Point Point;
typedef union Data Data;int main() {// 使用別名定義結構體變量Point p1;// 設置 x 坐標為 10p1.x = 10;// 設置 y 坐標為 20p1.y = 20;// 輸出點 p1 的坐標printf("Point p1: (%d, %d)\n", p1.x, p1.y);// 使用別名定義聯合體變量Data data;// 設置整數成員 i 的值為 10data.i = 10;// 輸出整數成員 i 的值printf("data.i: %d\n", data.i);// 設置浮點數成員 f 的值為 220.5data.f = 220.5;// 輸出浮點數成員 f 的值printf("data.f: %f\n", data.f);// 設置字符數組成員 str 的值為 "C Programming"strcpy(data.str,  "C Programming");// 輸出字符數組成員 str 的值printf("data.str:  %s\n", data.str);// 再次訪問聯合體成員 i 和 f,由于聯合體的特性,這些值可能已經被覆蓋printf("data.i after str: %d\n", data.i);printf("data.f after str: %f\n", data.f);return 0; // 返回 0 表示程序成功結束
}

2基礎數據結構

????????目標:通過思維導圖了解數據基礎結構核心的知識點,并掌握。

2.1數組

2.1.1數組的基礎概念及操作

2.1.2數組實現與解析? ? ??

2.1.2.1數組實現操作步驟

(1)插入元素函數?insert

a.參數

  • arr[]:目標數組。
  • size:指向數組當前大小的指針。
  • index:插入位置的索引。
  • value:要插入的值。

b.功能

  • 檢查數組是否已滿,如果已滿則輸出錯誤信息并返回。
  • 檢查索引是否有效,如果無效則輸出錯誤信息并返回。
  • 將插入位置之后的元素向后移動一位。
  • 在指定位置插入新元素。
  • 更新數組大小。

(2)刪除元素函數?delete

a.參數

  • arr[]:目標數組。
  • size:指向數組當前大小的指針。
  • index:刪除位置的索引。

b.功能

  • 檢查數組是否為空,如果為空則輸出錯誤信息并返回。
  • 檢查索引是否有效,如果無效則輸出錯誤信息并返回。
  • 將刪除位置之后的元素向前移動一位。
  • 更新數組大小。

(3)通過索引查找元素函數?searchByIndex

a.參數

  • arr[]:目標數組。
  • size:數組當前大小。
  • index:要查找的索引。

b.功能

  • 檢查索引是否有效,如果無效則輸出錯誤信息并返回?-1
  • 返回指定索引的元素。

(4)順序查找元素函數?searchByValue

a.參數

  • arr[]:目標數組。
  • size:數組當前大小。
  • value:要查找的值。

b.功能

  • 遍歷數組查找指定值。
  • 如果找到,返回找到的元素索引。
  • 如果未找到,返回?-1

(5)主函數?main

a.功能

  • 定義一個大小為100的數組?arr
  • 初始化數組大小?size?為0。
  • 插入元素到數組中。
  • 查找元素并打印結果。
  • 刪除元素并打印數組內容。

(6)運行結果解釋:

  • insert(arr, &size, 0, 10); insert(arr, &size, 1, 20); insert(arr, &size, 2, 30);:依次在索引?0,?1,?2?處插入值?10,?20,?30,此時數組內容為?[10, 20, 30]
  • printf("Element at index 1: %d\n", searchByIndex(arr, size, 1));:輸出索引?1?處的元素?20
  • printf("Index of element 20: %d\n", searchByValue(arr, size, 20));:輸出值?20?的索引?1
  • delete(arr, &size, 1);:刪除索引?1?處的元素?20,此時數組內容為?[10, 30]
  • printf("Array after deletion: ");:打印數組內容?[10, 30]
2.1.2.2數組代碼實現實例

通過下圖代碼了解數據結構中的數組實現原理。

#include <stdio.h>// 插入元素到數組指定位置
void insert(int arr[], int *size, int index, int value) {// 檢查數組是否已滿if (*size >= 100) {printf("Array is full, cannot insert.\n");return;}// 檢查索引是否有效if (index < 0 || index > *size) {printf("Invalid index.\n");return;}// 將插入位置之后的元素向后移動一位for (int i = *size; i > index; i--) {arr[i] = arr[i - 1];}// 在指定位置插入新元素arr[index] = value;// 更新數組大小(*size)++;
}// 從數組指定位置刪除元素
void delete(int arr[], int *size, int index) {// 檢查數組是否為空if (*size == 0) {printf("Array is empty, cannot delete.\n");return;}// 檢查索引是否有效if (index < 0 || index >= *size) {printf("Invalid index.\n");return;}// 將刪除位置之后的元素向前移動一位for (int i = index; i < *size - 1; i++) {arr[i] = arr[i + 1];}// 更新數組大小(*size)--;
}// 通過索引查找元素
int searchByIndex(int arr[], int size, int index) {// 檢查索引是否有效if (index < 0 || index >= size) {printf("Invalid index.\n");return -1;}// 返回指定索引的元素return arr[index];
}// 順序查找元素
int searchByValue(int arr[], int size, int value) {// 遍歷數組查找指定值for (int i = 0; i < size; i++) {if (arr[i] == value) {// 返回找到的元素索引return i;}}// 如果未找到,返回-1return -1;
}int main() {int arr[100]; // 定義一個大小為100的數組int size = 0; // 初始化數組大小為0// 插入元素insert(arr, &size, 0, 10); // 在索引0處插入值10insert(arr, &size, 1, 20); // 在索引1處插入值20insert(arr, &size, 2, 30); // 在索引2處插入值30// 查找元素printf("Element at index 1: %d\n", searchByIndex(arr, size, 1)); // 查找索引1處的元素printf("Index of element 20: %d\n", searchByValue(arr, size, 20)); // 查找值為20的元素索引// 刪除元素delete(arr, &size, 1); // 刪除索引1處的元素// 打印數組printf("Array after deletion: ");for (int i = 0; i < size; i++) {printf("%d ", arr[i]); // 打印數組中的元素}printf("\n");return 0;
}

2.2鏈表

2.2.1鏈表結構特點與操作

????????

2.2.2鏈表實現與解析

2.2.2.1鏈表實現操作步驟

(1)結構體定義

? Node?結構體表示單鏈表中的節點,包含一個整數?data?和一個指向下一個節點的指針?next

(2)插入節點函數

a. insertAtHead:在單鏈表頭部插入節點。

  • 分配內存給新節點,如果內存分配失敗則輸出錯誤信息并返回。
  • 設置新節點的數據。
  • 將新節點的?next?指針指向當前頭節點。
  • 更新頭節點為新節點。

b. insertAtTail:在單鏈表尾部插入節點。

  • 分配內存給新節點,如果內存分配失敗則輸出錯誤信息并返回。
  • 設置新節點的數據。
  • 將新節點的?next?指針初始化為?NULL
  • 如果鏈表為空,直接將新節點設置為頭節點。
  • 否則,從頭節點開始遍歷,找到最后一個節點并將最后一個節點的?next?指針指向新節點。

(3)刪除節點函數

a. deleteNode:刪除單鏈表中的節點。

  • 如果鏈表為空,直接返回。

  • 如果要刪除的節點是頭節點,保存頭節點,更新頭節點為下一個節點,并釋放原頭節點的內存。

  • 否則,從頭節點開始遍歷,找到要刪除節點的前一個節點。

  • 如果找到了要刪除的節點,保存要刪除的節點,將前一個節點的?next?指針指向要刪除節點的下一個節點,并釋放要刪除節點的內存。

(4)查找節點函數

a. searchNode:查找單鏈表中的節點。

????????從頭節點開始遍歷。

????????如果找到目標節點,返回目標節點。

????????如果未找到目標節點,返回?NULL

(5)打印鏈表函數

?a. printList:打印單鏈表。

????????從頭節點開始遍歷。

????????打印當前節點的數據。

????????打印換行符。

(6)主函數

  • 初始化鏈表頭節點為?NULL
  • 在頭部插入節點?10,?20,?30
  • 在尾部插入節點?40,?50
  • 打印鏈表。
  • 查找值為?30?的節點并打印結果。
  • 刪除值為?20?的節點。
  • 打印鏈表。

(7)運行結果解釋:

  • 插入節點

    • insertAtHead(&head, 10); insertAtHead(&head, 20); insertAtHead(&head, 30);:依次在頭部插入值?10,?20,?30,此時鏈表內容為?[30, 20, 10]

    • insertAtTail(&head, 40); insertAtTail(&head, 50);:依次在尾部插入值?40,?50,此時鏈表內容為?[30, 20, 10, 40, 50]

  • 打印鏈表

    • printf("Linked list after insertions: "); printList(head);:輸出鏈表內容?[30, 20, 10, 40, 50]

  • 查找節點

    • Node *foundNode = searchNode(head, 30);:查找值為?30?的節點。

    • if (foundNode != NULL) { printf("Node with value 30 found.\n"); }:輸出?Node with value 30 found.

  • 刪除節點

    • deleteNode(&head, 20);:刪除值為?20?的節點,此時鏈表內容為?[30, 10, 40, 50]

  • 打印鏈表

    • printf("Linked list after deletion: "); printList(head);:輸出鏈表內容?[30, 10, 40, 50]

    2.2.2.2鏈表代碼實現示例
    #include <stdio.h>
    #include <stdlib.h>// 定義單鏈表節點結構體
    typedef struct Node {int data;       // 節點數據域struct Node *next;  // 指向下一個節點的指針
    } Node;// 在單鏈表頭部插入節點
    void insertAtHead(Node **head, int value) {Node *newNode = (Node *)malloc(sizeof(Node));  // 分配新節點的內存newNode->data = value;  // 設置新節點的數據newNode->next = *head;  // 將新節點的next指針指向當前頭節點*head = newNode;  // 更新頭節點為新節點
    }// 在單鏈表尾部插入節點
    void insertAtTail(Node **head, int value) {Node *newNode = (Node *)malloc(sizeof(Node));  // 分配新節點的內存newNode->data = value;  // 設置新節點的數據newNode->next = NULL;  // 新節點的next指針初始化為NULLif (*head == NULL) {  // 如果鏈表為空*head = newNode;  // 直接將新節點設置為頭節點return;}Node *current = *head;  // 從頭節點開始遍歷while (current->next != NULL) {  // 找到最后一個節點current = current->next;}current->next = newNode;  // 將最后一個節點的next指針指向新節點
    }// 刪除單鏈表中的節點
    void deleteNode(Node **head, int value) {if (*head == NULL) {  // 如果鏈表為空return;}if ((*head)->data == value) {  // 如果要刪除的節點是頭節點Node *temp = *head;  // 保存頭節點*head = (*head)->next;  // 更新頭節點為下一個節點free(temp);  // 釋放原頭節點的內存return;}Node *current = *head;  // 從頭節點開始遍歷while (current->next != NULL && current->next->data != value) {  // 找到要刪除節點的前一個節點current = current->next;}if (current->next != NULL) {  // 如果找到了要刪除的節點Node *temp = current->next;  // 保存要刪除的節點current->next = current->next->next;  // 將前一個節點的next指針指向要刪除節點的下一個節點free(temp);  // 釋放要刪除節點的內存}
    }// 查找單鏈表中的節點
    Node *searchNode(Node *head, int value) {Node *current = head;  // 從頭節點開始遍歷while (current != NULL) {  // 遍歷鏈表if (current->data == value) {  // 如果找到目標節點return current;  // 返回目標節點}current = current->next;  // 繼續遍歷下一個節點}return NULL;  // 如果未找到目標節點,返回NULL
    }// 打印單鏈表
    void printList(Node *head) {Node *current = head;  // 從頭節點開始遍歷while (current != NULL) {  // 遍歷鏈表printf("%d ", current->data);  // 打印當前節點的數據current = current->next;  // 繼續遍歷下一個節點}printf("\n");  // 打印換行符
    }int main() {Node *head = NULL;  // 初始化鏈表頭節點為NULL// 在頭部插入節點insertAtHead(&head, 10);  // 在頭部插入值為10的節點insertAtHead(&head, 20);  // 在頭部插入值為20的節點insertAtHead(&head, 30);  // 在頭部插入值為30的節點// 在尾部插入節點insertAtTail(&head, 40);  // 在尾部插入值為40的節點insertAtTail(&head, 50);  // 在尾部插入值為50的節點// 打印鏈表printf("Linked list after insertions: ");printList(head);  // 打印鏈表// 查找節點Node *foundNode = searchNode(head, 30);  // 查找值為30的節點if (foundNode != NULL) {  // 如果找到了節點printf("Node with value 30 found.\n");  // 打印提示信息} else {  // 如果未找到節點printf("Node with value 30 not found.\n");  // 打印提示信息}// 刪除節點deleteNode(&head, 20);  // 刪除值為20的節點// 打印鏈表printf("Linked list after deletion: ");printList(head);  // 打印鏈表return 0;
    }
    

    2.3棧與隊列

    2.3.1棧與隊列的基本概念

    2.3.2棧與隊列實現與解析

    2.3.2.1順序棧實現操作步驟

    (1)宏定義MAX_SIZE?定義了棧的最大容量。

    (2)結構體定義Stack?結構體包含一個數組?data?和一個整數?top,用于存儲棧中的元素和指向棧頂的位置。

    (3)初始化函數initStack?將棧頂指針?top?初始化為?-1,表示棧為空。

    (4)判斷棧狀態isEmpty?和?isFull?分別用于判斷棧是否為空或已滿。

    (5)棧操作函數

    • push:將元素壓入棧頂,檢查棧是否已滿以避免溢出。

    • pop:彈出棧頂元素,檢查棧是否為空以避免下溢。

    • peek:查看棧頂元素但不彈出,同樣需要檢查棧是否為空。

    (6)主函數:演示了棧的基本操作,包括入棧、查看棧頂元素和出棧。

    2.3.2.2順序棧代碼實現示例
    #include <stdio.h>
    #include <stdlib.h>// 定義棧的最大容量
    #define MAX_SIZE 100// 定義棧結構體,包含一個數組和一個指向棧頂的指針
    typedef struct {int data[MAX_SIZE]; // 存儲棧中元素的數組int top;            // 棧頂指針,初始值為-1表示空棧
    } Stack;// 初始化棧,將棧頂指針設置為-1
    void initStack(Stack *s) {s->top = -1;
    }// 判斷棧是否為空
    int isEmpty(Stack *s) {return s->top == -1;
    }// 判斷棧是否已滿
    int isFull(Stack *s) {return s->top == MAX_SIZE - 1;
    }// 入棧操作:將元素壓入棧頂
    void push(Stack *s, int value) {if (isFull(s)) { // 檢查棧是否已滿printf("Stack Overflow\n"); // 棧滿時輸出錯誤信息return;}s->data[++s->top] = value; // 將元素存入棧頂,并更新棧頂指針
    }// 出棧操作:彈出棧頂元素
    int pop(Stack *s) {if (isEmpty(s)) { // 檢查棧是否為空printf("Stack Underflow\n"); // 棧空時輸出錯誤信息return -1;}return s->data[s->top--]; // 返回棧頂元素,并更新棧頂指針
    }// 查看棧頂元素但不出棧
    int peek(Stack *s) {if (isEmpty(s)) { // 檢查棧是否為空printf("Stack is empty\n"); // 棧空時輸出錯誤信息return -1;}return s->data[s->top]; // 返回棧頂元素
    }// 主函數,用于測試棧的基本操作
    int main() {Stack s;initStack(&s); // 初始化棧// 測試入棧操作push(&s, 1);push(&s, 2);push(&s, 3);// 查看棧頂元素printf("Top element: %d\n", peek(&s));// 彈出棧頂元素printf("Popped element: %d\n", pop(&s));// 再次查看棧頂元素printf("Top element after pop: %d\n", peek(&s));return 0;
    }
    ? 2.3.2.3鏈棧實現操作步驟

    ?(1)結構體定義

    • Node?結構體表示鏈表中的節點,每個節點包含一個整數?data?和一個指向下一個節點的指針?next
    • Stack?結構體表示棧,棧由一個指向鏈表頭節點(即棧頂)的指針?top?組成。

    ?(2)初始化函數initStack?將棧頂指針?top?初始化為?NULL,表示棧為空。

    ?(3)判斷棧狀態isEmpty?通過檢查棧頂指針是否為?NULL?來判斷棧是否為空。

    (4)棧操作函數

    • push:創建新節點并將其插入到棧頂。首先分配內存給新節點,如果內存分配失敗則輸出錯誤信息并返回。然后設置新節點的數據,并將其?next?指針指向當前棧頂,最后更新棧頂指針為新節點。
    • pop:移除棧頂元素并返回其值。如果棧為空則輸出錯誤信息并返回?-1。否則,保存當前棧頂節點,獲取其值,更新棧頂指針為下一個節點,并釋放原棧頂節點的內存。
    • peek:查看棧頂元素但不彈出。如果棧為空則輸出錯誤信息并返回?-1,否則返回棧頂元素的值。

    (5)主函數:演示了棧的基本操作,包括入棧、查看棧頂元素和出棧。

    (6)運行結果解釋:

    • push(&s, 1); push(&s, 2); push(&s, 3);:依次將?1,?2,?3?壓入棧中,此時棧頂元素為?3
    • printf("Top element: %d\n", peek(&s));:輸出棧頂元素?3
    • printf("Popped element: %d\n", pop(&s));:彈出棧頂元素?3?并輸出。
    • printf("Top element after pop: %d\n", peek(&s));:再次查看棧頂元素,此時棧頂元素為?2
    ? 2.3.2.4鏈棧代碼實現示例
    #include <stdio.h>
    #include <stdlib.h>// 定義鏈表節點結構體,每個節點包含一個整數數據和指向下一個節點的指針
    typedef struct Node {int data;                // 節點存儲的數據struct Node *next;       // 指向下一個節點的指針
    } Node;// 定義棧結構體,棧由一個指向鏈表頭節點(即棧頂)的指針組成
    typedef struct {Node *top;               // 指向棧頂元素的指針
    } Stack;// 初始化棧,將棧頂指針設置為NULL,表示空棧
    void initStack(Stack *s) {s->top = NULL;
    }// 判斷棧是否為空,如果棧頂指針為NULL,則棧為空
    int isEmpty(Stack *s) {return s->top == NULL;
    }// 入棧操作:創建新節點并將其插入到棧頂
    void push(Stack *s, int value) {// 分配內存給新節點Node *newNode = (Node *)malloc(sizeof(Node));if (newNode == NULL) { // 如果內存分配失敗,輸出錯誤信息并返回printf("Memory allocation failed\n");return;}newNode->data = value;  // 設置新節點的數據newNode->next = s->top; // 新節點的next指針指向當前棧頂s->top = newNode;       // 更新棧頂指針為新節點
    }// 出棧操作:移除棧頂元素并返回其值
    int pop(Stack *s) {if (isEmpty(s)) { // 如果棧為空,輸出錯誤信息并返回-1printf("Stack Underflow\n");return -1;}Node *temp = s->top;   // 保存當前棧頂節點int value = temp->data; // 獲取棧頂元素的值s->top = temp->next;   // 更新棧頂指針為下一個節點free(temp);            // 釋放原棧頂節點的內存return value;          // 返回棧頂元素的值
    }// 查看棧頂元素但不出棧
    int peek(Stack *s) {if (isEmpty(s)) { // 如果棧為空,輸出錯誤信息并返回-1printf("Stack is empty\n");return -1;}return s->top->data; // 返回棧頂元素的值
    }// 主函數,用于測試棧的基本操作
    int main() {Stack s;initStack(&s); // 初始化棧// 測試入棧操作push(&s, 1);push(&s, 2);push(&s, 3);// 查看棧頂元素printf("Top element: %d\n", peek(&s));// 彈出棧頂元素printf("Popped element: %d\n", pop(&s));// 再次查看棧頂元素printf("Top element after pop: %d\n", peek(&s));return 0;
    }
    2.3.2.5順序隊列實現操作步驟

    (1)宏定義MAX_SIZE?定義了隊列的最大容量。

    (2)結構體定義Queue?結構體包含一個數組?data?和兩個整數?front?和?rear,用于存儲隊列中的元素和指向隊頭和隊尾的位置。

    1. 初始化函數initQueue?將?front?和?rear?初始化為?-1,表示隊列為空。

    2. 判斷隊列狀態

    • isEmpty?通過檢查?front?是否為?-1?來判斷隊列是否為空。
    • isFull?通過檢查?(rear + 1) % MAX_SIZE?是否等于?front?來判斷隊列是否已滿。

    (3)隊列操作函數

    • enqueue:將元素添加到隊尾。首先檢查隊列是否已滿,如果已滿則輸出錯誤信息并返回。如果隊列為空,則將?front?和?rear?都設置為?0,否則更新?rear?指針并使用模運算實現循環隊列。最后將元素存入隊尾。
    • dequeue:移除隊頭元素并返回其值。如果隊列為空則輸出錯誤信息并返回?-1。否則,獲取隊頭元素的值,如果隊列只有一個元素,則重置?front?和?rear?為?-1,否則更新?front?指針并使用模運算實現循環隊列。
    • peek:查看隊頭元素但不移除。如果隊列為空則輸出錯誤信息并返回?-1,否則返回隊頭元素的值。

    (4)主函數:演示了隊列的基本操作,包括入隊、查看隊頭元素和出隊。

    (5)運行結果解釋:

    • enqueue(&q, 1); enqueue(&q, 2); enqueue(&q, 3);:依次將?1,?2,?3?入隊,此時隊頭元素為?1
    • printf("Front element: %d\n", peek(&q));:輸出隊頭元素?1
    • printf("Dequeued element: %d\n", dequeue(&q));:出隊隊頭元素?1?并輸出。
    • printf("Front element after dequeue: %d\n", peek(&q));:再次查看隊頭元素,此時隊頭元素為?2
    2.3.2.6順序隊列代碼實現示例
    #include <stdio.h>
    #include <stdlib.h>// 定義隊列的最大容量
    #define MAX_SIZE 100// 定義隊列結構體,包含一個數組和兩個指針(front和rear)
    typedef struct {int data[MAX_SIZE]; // 存儲隊列中元素的數組int front, rear;    // front指向隊頭,rear指向隊尾
    } Queue;// 初始化隊列,將front和rear設置為-1,表示隊列為空
    void initQueue(Queue *q) {q->front = q->rear = -1;
    }// 判斷隊列是否為空,如果front為-1,則隊列為空
    int isEmpty(Queue *q) {return q->front == -1;
    }// 判斷隊列是否已滿,如果(rear + 1) % MAX_SIZE等于front,則隊列已滿
    int isFull(Queue *q) {return (q->rear + 1) % MAX_SIZE == q->front;
    }// 入隊操作:將元素添加到隊尾
    void enqueue(Queue *q, int value) {if (isFull(q)) { // 檢查隊列是否已滿printf("Queue Overflow\n"); // 隊列滿時輸出錯誤信息return;}if (isEmpty(q)) { // 如果隊列為空,front和rear都指向0q->front = q->rear = 0;} else {q->rear = (q->rear + 1) % MAX_SIZE; // 更新rear指針,使用模運算實現循環隊列}q->data[q->rear] = value; // 將元素存入隊尾
    }// 出隊操作:移除隊頭元素并返回其值
    int dequeue(Queue *q) {if (isEmpty(q)) { // 檢查隊列是否為空printf("Queue Underflow\n"); // 隊列空時輸出錯誤信息return -1;}int value = q->data[q->front]; // 獲取隊頭元素的值if (q->front == q->rear) { // 如果隊列只有一個元素,重置front和rear為-1q->front = q->rear = -1;} else {q->front = (q->front + 1) % MAX_SIZE; // 更新front指針,使用模運算實現循環隊列}return value; // 返回隊頭元素的值
    }// 查看隊頭元素但不出隊
    int peek(Queue *q) {if (isEmpty(q)) { // 檢查隊列是否為空printf("Queue is empty\n"); // 隊列空時輸出錯誤信息return -1;}return q->data[q->front]; // 返回隊頭元素的值
    }// 主函數,用于測試隊列的基本操作
    int main() {Queue q;initQueue(&q); // 初始化隊列// 測試入隊操作enqueue(&q, 1);enqueue(&q, 2);enqueue(&q, 3);// 查看隊頭元素printf("Front element: %d\n", peek(&q));// 出隊操作printf("Dequeued element: %d\n", dequeue());// 再次查看隊頭元素printf("Front element after dequeue: %d\n", peek(&q));return 0;
    }
    2.3.2.7鏈式隊列實現操作步驟

    (1)結構體定義

    • Node?結構體表示鏈表中的節點,每個節點包含一個整數?data?和一個指向下一個節點的指針?next
    • Queue?結構體表示隊列,隊列由兩個指針?front?和?rear?組成,分別指向隊頭和隊尾。

    (2)初始化函數initQueue?將?front?和?rear?初始化為?NULL,表示隊列為空。

    (3)判斷隊列狀態isEmpty?通過檢查?front?是否為?NULL?來判斷隊列是否為空。

    (4)隊列操作函數

    • enqueue:將元素添加到隊尾。首先分配內存給新節點,如果內存分配失敗則輸出錯誤信息并返回。然后設置新節點的數據,并將其?next?指針初始化為?NULL。如果隊列為空,則將?front?和?rear?都指向新節點,否則將當前隊尾節點的?next?指針指向新節點,并更新?rear?指針為新節點。
    • dequeue:移除隊頭元素并返回其值。如果隊列為空則輸出錯誤信息并返回?-1。否則,保存當前隊頭節點,獲取其值,更新?front?指針為下一個節點。如果隊列為空,則更新?rear?指針也為?NULL,并釋放原隊頭節點的內存。
    • peek:查看隊頭元素但不移除。如果隊列為空則輸出錯誤信息并返回?-1,否則返回隊頭元素的值。

    (5)主函數:演示了隊列的基本操作,包括入隊、查看隊頭元素和出隊。

    (6)運行結果解釋:

    • enqueue(&q, 1); enqueue(&q, 2); enqueue(&q, 3);:依次將?1,?2,?3?入隊,此時隊頭元素為?1
    • printf("Front element: %d\n", peek(&q));:輸出隊頭元素?1
    • printf("Dequeued element: %d\n", dequeue(&q));:出隊隊頭元素?1?并輸出。
    • printf("Front element after dequeue: %d\n", peek(&q));:再次查看隊頭元素,此時隊頭元素為?2

    2.3.2.8鏈式隊列代碼實現示例

    #include <stdio.h>
    #include <stdlib.h>// 定義鏈表節點結構體,每個節點包含一個整數數據和指向下一個節點的指針
    typedef struct Node {int data;                // 節點存儲的數據struct Node *next;       // 指向下一個節點的指針
    } Node;// 定義隊列結構體,隊列由兩個指針(front和rear)組成
    typedef struct {Node *front;             // 指向隊頭元素的指針Node *rear;              // 指向隊尾元素的指針
    } Queue;// 初始化隊列,將front和rear設置為NULL,表示隊列為空
    void initQueue(Queue *q) {q->front = q->rear = NULL;
    }// 判斷隊列是否為空,如果front為NULL,則隊列為空
    int isEmpty(Queue *q) {return q->front == NULL;
    }// 入隊操作:將元素添加到隊尾
    void enqueue(Queue *q, int value) {// 分配內存給新節點Node *newNode = (Node *)malloc(sizeof(Node));if (newNode == NULL) { // 如果內存分配失敗,輸出錯誤信息并返回printf("Memory allocation failed\n");return;}newNode->data = value;  // 設置新節點的數據newNode->next = NULL;   // 新節點的next指針初始化為NULLif (isEmpty(q)) { // 如果隊列為空,front和rear都指向新節點q->front = q->rear = newNode;} else {q->rear->next = newNode; // 將當前隊尾節點的next指針指向新節點q->rear = newNode;       // 更新rear指針為新節點}
    }// 出隊操作:移除隊頭元素并返回其值
    int dequeue(Queue *q) {if (isEmpty(q)) { // 如果隊列為空,輸出錯誤信息并返回-1printf("Queue Underflow\n");return -1;}Node *temp = q->front;   // 保存當前隊頭節點int value = temp->data; // 獲取隊頭元素的值q->front = q->front->next; // 更新front指針為下一個節點if (q->front == NULL) { // 如果隊列為空,更新rear指針也為NULLq->rear = NULL;}free(temp);            // 釋放原隊頭節點的內存return value;          // 返回隊頭元素的值
    }// 查看隊頭元素但不出隊
    int peek(Queue *q) {if (isEmpty(q)) { // 如果隊列為空,輸出錯誤信息并返回-1printf("Queue is empty\n");return -1;}return q->front->data; // 返回隊頭元素的值
    }// 主函數,用于測試隊列的基本操作
    int main() {Queue q;initQueue(&q); // 初始化隊列// 測試入隊操作enqueue(&q, 1);enqueue(&q, 2);enqueue(&q, 3);// 查看隊頭元素printf("Front element: %d\n", peek(&q));// 出隊操作printf("Dequeued element: %d\n", dequeue(&q));// 再次查看隊頭元素printf("Front element after dequeue: %d\n", peek(&q));return 0;
    }

    3.高級數據結構

    ???????目標:通過思維導圖了解高級數據基礎結構核心的知識點,并掌握。

    3.1樹形結構

    ? ? ? ? 樹形結構基礎概念與操作。

    3.1.1二叉樹的概念


    ?

    3.1.2二叉樹實現與解析

    3.1.2.1二叉樹節點結構體定義
    // 定義了一個二叉樹節點的結構體 BiNode //通過 typedef 關鍵字定義了一個指向 BiNode 結構體的指針類型 BiTree,方便后續使用//typedef existing_type new_type_name; 關鍵字的原型//existing_type 是已有的數據類型,new_type_name 是你為該數據類型定義的新名稱typedef struct BiNode {int data;             // 定義一個整型節點數據域 datastruct BiNode *lchild; // 指向左子節點的lchild指針struct BiNode *rchild; // 指向右子節點的rchild指針
    } BiNode, *BiTree;/*
    
    3.1.2.2二叉樹存儲結構定義

    (1)數組表示法

    // 定義二叉樹數組的最大容量#define MAX_SIZE 100  // 定義二叉樹數組結構體typedef struct {// 一個整型數組,用于存儲二叉樹的節點數據,數組的大小為 MAX_SIZE,即100int data[MAX_SIZE];  // size:一個整型變量,用于記錄當前二叉樹的節點個數int size;    //定義了一個名為 BinaryTreeArray 的結構體,用于表示一個二叉樹的數組存儲結構
    //結構體包含兩個成員:} BinaryTreeArray;

    (2)鏈表表示法

    // 定義二叉樹節點名為 BiNode 的結構體,用于表示二叉樹的節點,結構體包含三個成員typedef struct BiNode {// 一個整型變量(data),用于存儲節點的值int data;  // 一個指向 BiNode 結構體的(lchild)指針,用于指向左子節點struct BiNode *lchild;// 一個指向 BiNode 結構體的(rchild)指針,用于指向右子節點struct BiNode *rchild;} BiNode, *BiTree;/*通過這個結構體,可以構建二叉樹的數據結構,并進行相關的操作,如插入、刪除、遍歷等。此外,代碼中還使用了 typedef 關鍵字,為 BiNode 結構體定義了一個別名 BiTree,使得 BiTree 可以作為指向 BiNode 結構體的指針類型使用。這樣,在聲明變量時可以直接使用 BiTree,而不需要每次都寫完整的 struct BiNode **/

    3.1.3平衡樹的概念

    3.1.4平衡樹實現與解析

    3.1.4.1平衡樹節點結構體定義
    // 定義平衡樹節點結構體
    // 定義了一個名為 AVLNode 的結構體,用于表示平衡樹的節點,結構體包含四個成員typedef struct AVLNode {// 節點數據域,用于存儲節點的值int data;             // 指向左子節點的指針,指向 AVLNode 結構體的指針struct AVLNode *left;      // 指向右子節點的指針,指向 AVLNode 結構體的指針struct AVLNode *right;     // 節點的高度,用于平衡因子的計算,節點的高度是指從該節點到其最遠葉子節點的最長路徑上的節點數int height;      //使用了 typedef 關鍵字,為 AVLNode 結構體定義了一個別名 AVLTree,使得 AVLTree 可以作為指向 AVLNode 結構體的指針類型使用
    //這樣,在聲明變量時可以直接使用 AVLTree,而不需要每次都寫完整的 struct AVLNode *} AVLNode, *AVLTree;
    3.1.4.2紅黑樹樹節點結構體定義
    // 定義紅黑樹節點顏色typedef enum {RED,BLACK//枚舉(Color )定義了紅黑樹節點的顏色,包括 RED 和 BLACK} Color;// 定義紅黑樹節點結構體typedef struct RBNode {int data;                   // 節點數據struct RBNode *left;        // 指向左子節點的指針struct RBNode *right;       // 指向右子節點的指針struct RBNode *parent;      // 指向父節點的指針Color color;                // 節點顏色,可以是 RED 或 BLACK
    } RBNode, *RBTree;

    3.1.5堆的概念

    3.1.6堆實現與解析

    #include <stdio.h>
    #include <stdlib.h>// 定義最大堆結構體
    typedef struct {// 存儲堆元素的數組int* array;     // 堆的最大容量int capacity;   // 當前堆的大小int size;       
    } MaxHeap;// 創建一個最大堆
    MaxHeap* createMaxHeap(int capacity) {// 分配內存給 MaxHeap 結構體MaxHeap* heap = (MaxHeap*)malloc(sizeof(MaxHeap));// 分配內存給存儲堆元素的數組heap->array = (int*)malloc(capacity * sizeof(int));// 設置堆的最大容量heap->capacity = capacity;// 初始化堆的大小為 0heap->size = 0;// 返回創建的堆return heap;
    }// 交換兩個元素的值
    void swap(int* a, int* b) {// 臨時變量存儲 a 的值int temp = *a;// 將 b 的值賦給 a*a = *b;// 將臨時變量的值賦給 b*b = temp;
    }// 向上調整堆
    void heapifyUp(MaxHeap* heap, int index) {// 計算父節點的索引int parent = (index - 1) / 2;// 當索引大于 0 且當前節點的值大于父節點的值時while (index > 0 && heap->array[index] > heap->array[parent]) {// 交換當前節點和父節點的值swap(&heap->array[index], &heap->array[parent]);// 更新當前節點的索引為父節點的索引index = parent;// 重新計算父節點的索引parent = (index - 1) / 2;}
    }// 向下調整堆
    void heapifyDown(MaxHeap* heap, int index) {// 計算左子節點的索引int leftChild = 2 * index + 1;// 計算右子節點的索引int rightChild = 2 * index + 2;// 初始化最大節點的索引為當前節點的索引int largest = index;// 如果左子節點存在且左子節點的值大于最大節點的值if (leftChild < heap->size && heap->array[leftChild] > heap->array[largest]) {// 更新最大節點的索引為左子節點的索引largest = leftChild;}// 如果右子節點存在且右子節點的值大于最大節點的值if (rightChild < heap->size && heap->array[rightChild] > heap->array[largest]) {// 更新最大節點的索引為右子節點的索引largest = rightChild;}// 如果最大節點的索引不是當前節點的索引if (largest != index) {// 交換當前節點和最大節點的值swap(&heap->array[index], &heap->array[largest]);// 遞歸調用 heapifyDown 函數,繼續向下調整堆heapifyDown(heap, largest);}
    }// 插入元素到堆中
    void insert(MaxHeap* heap, int value) {// 如果堆已滿if (heap->size == heap->capacity) {// 輸出提示信息printf("Heap is full. Cannot insert more elements.\n");// 返回return;}// 將元素插入到堆的末尾heap->array[heap->size] = value;// 向上調整堆heapifyUp(heap, heap->size);// 增加堆的大小heap->size++;
    }// 刪除堆頂元素
    int deleteMax(MaxHeap* heap) {// 如果堆為空if (heap->size == 0) {// 輸出提示信息printf("Heap is empty. Cannot delete element.\n");// 返回 -1 表示錯誤return -1;}// 獲取堆頂元素的值int max = heap->array[0];// 將堆的最后一個元素移到堆頂heap->array[0] = heap->array[heap->size - 1];// 減少堆的大小heap->size--;// 向下調整堆heapifyDown(heap, 0);// 返回刪除的堆頂元素的值return max;
    }// 獲取堆頂元素
    int peek(MaxHeap* heap) {// 如果堆為空if (heap->size == 0) {// 輸出提示信息printf("Heap is empty. No element to peek.\n");// 返回 -1 表示錯誤return -1;}// 返回堆頂元素的值return heap->array[0];
    }// 打印堆中的元素
    void printHeap(MaxHeap* heap) {// 遍歷堆中的元素for (int i = 0; i < heap->size; i++) {// 輸出元素的值printf("%d ", heap->array[i]);}// 輸出換行符printf("\n");
    }// 釋放堆的內存
    void destroyHeap(MaxHeap* heap) {// 釋放存儲堆元素的數組的內存free(heap->array);// 釋放 MaxHeap 結構體的內存free(heap);
    }int main() {// 創建一個最大堆,容量為 10MaxHeap* heap = createMaxHeap(10);// 插入元素到堆中insert(heap, 3);insert(heap, 4);insert(heap, 9);insert(heap, 5);insert(heap, 2);// 打印堆中的元素printf("Max Heap: ");printHeap(heap);// 獲取堆頂元素并輸出printf("Peek: %d\n", peek(heap));// 刪除堆頂元素并輸出printf("Deleted: %d\n", deleteMax(heap));// 打印刪除堆頂元素后的堆printf("Max Heap after deletion: ");printHeap(heap);// 釋放堆的內存destroyHeap(heap);return 0;
    }
    

    3.2圖結構基礎概念

    ????????圖(Graph)是一種非線性的數據結構,由頂點(Vertex)和邊(Edge)組成。頂點表示圖中的節點,邊表示節點之間的連接關系。圖可以用來表示各種實際問題,如社交網絡、交通網絡、計算機網絡等,常見的有鄰接矩陣和鄰接表。

    3.2.1圖結構頂點(Vertex)

    ? ? ? ? 基本概念如下所示:

    3.2.1.1圖結構頂點結構體定義
    // 定義一個結構體 Vertex,用于表示圖中的頂點
    typedef struct Vertex {int id;        // 頂點的標識符char *name;    // 頂點的名字int visited;   // 標記頂點是否被訪問過,0表示未訪問,1表示已訪問
    } Vertex;
    
    3.2.1.2圖結構頂點實現與解析
    // 頂點結構體定義
    typedef struct Vertex {int id;                // 頂點的唯一標識符char *name;            // 頂點的名稱(可選)int visited;           // 標記頂點是否被訪問過,0表示未訪問,1表示已訪問// 可以添加其他屬性,如權重、顏色等
    } Vertex;// 創建一個新的頂點
    Vertex *createVertex(int id, char *name) {// 分配內存以存儲新的頂點結構體Vertex *newVertex = (Vertex *)malloc(sizeof(Vertex));// 檢查內存分配是否成功if (newVertex == NULL) {printf("內存分配失敗\n");// 如果內存分配失敗,程序終止exit(1);}// 設置頂點的唯一標識符newVertex->id = id;// 設置頂點的名稱newVertex->name = name;// 初始化頂點的訪問狀態為未訪問newVertex->visited = 0;// 返回新創建的頂點指針return newVertex;
    }// 打印頂點信息
    void printVertex(Vertex *vertex) {// 打印頂點的唯一標識符printf("頂點ID: %d\n", vertex->id);// 如果頂點有名稱,打印頂點的名稱if (vertex->name != NULL) {printf("頂點名稱: %s\n", vertex->name);}// 打印頂點的訪問狀態printf("是否被訪問: %s\n", vertex->visited ? "是" : "否");
    }int main() {// 創建頂點Vertex *v1 = createVertex(1, "頂點1");Vertex *v2 = createVertex(2, "頂點2");Vertex *v3 = createVertex(3, "頂點3");// 打印頂點信息printVertex(v1);printVertex(v2);printVertex(v3);// 釋放內存free(v1);free(v2);free(v3);return 0;
    }

    3.2.2圖結構邊(Edge)

    ????????基本概念如下所示:

    3.2.2.1圖結構邊定義

    ????????圖結構的邊可以通過多種方式定義,具體取決于圖的表示方法(如鄰接矩陣或鄰接表)。

    //1. 使用結構體定義圖邊// 定義一個結構體 Edge,用于表示圖中的邊
    typedef struct Edge {int dest;           // 邊的目標頂點int weight;         // 邊的權重(如果是有權圖)struct Edge* next;  // 指向下一個邊的指針
    } Edge;//2. 使用結構體定義帶有源頂點和目標頂點的邊typedef struct Edge {int src;            // 邊的源頂點int dest;           // 邊的目標頂點int weight;         // 邊的權重(如果是有權圖)struct Edge* next;  // 指向下一個邊的指針
    } Edge;//3. 使用二維數組定義邊(適用于鄰接矩陣)
    #define MAX_VERTICES 100typedef struct {int matrix[MAX_VERTICES][MAX_VERTICES]; // 鄰接矩陣int numVertices;                        // 頂點數量
    } GraphMatrix;
    3.2.2.2圖結構邊實現與解析
    #include <stdio.h>
    #include <stdlib.h>// 定義邊結構體
    typedef struct Edge {int dest;           // 邊的目標頂點int weight;         // 邊的權重(如果是有權圖)struct Edge* next;  // 指向下一個邊的指針
    } Edge;//1. 創建一個新的邊
    Edge* createEdge(int dest, int weight) {// 分配內存以存儲新的邊結構體Edge* newEdge = (Edge*)malloc(sizeof(Edge));// 檢查內存分配是否成功if (newEdge == NULL) {printf("內存分配失敗\n");// 如果內存分配失敗,程序終止exit(1);}// 設置邊的目標頂點newEdge->dest = dest;// 設置邊的權重newEdge->weight = weight;// 將下一個邊指針初始化為NULLnewEdge->next = NULL;// 返回新創建的邊指針return newEdge;
    }//2. 插入邊到鄰接表中
    void insertEdge(Edge** adjList, int src, int dest, int weight) {// 創建一個新的邊Edge* newEdge = createEdge(dest, weight);// 將新邊的下一個邊指針指向當前頂點的鄰接表newEdge->next = adjList[src];// 將新邊插入到當前頂點的鄰接表的頭部adjList[src] = newEdge;
    }//3. 刪除邊
    void deleteEdge(Edge** adjList, int src, int dest) {// 獲取當前頂點的鄰接表Edge* current = adjList[src];// 初始化前一個邊指針為NULLEdge* prev = NULL;// 遍歷鄰接表,找到目標邊while (current != NULL && current->dest != dest) {prev = current;current = current->next;}// 如果目標邊不存在,打印錯誤信息并返回if (current == NULL) {printf("邊不存在\n");return;}// 如果目標邊是鄰接表的第一個邊if (prev == NULL) {// 將鄰接表的頭指針指向下一個邊adjList[src] = current->next;} else {// 將前一個邊的下一個邊指針指向目標邊的下一個邊prev->next = current->next;}// 釋放目標邊的內存free(current);
    }//4. 查找邊
    Edge* findEdge(Edge* adjList, int src, int dest) {// 獲取當前頂點的鄰接表Edge* current = adjList;// 遍歷鄰接表,找到目標邊while (current != NULL && current->dest != dest) {current = current->next;}// 返回目標邊指針return current;
    }//5. 遍歷邊
    void traverseEdges(Edge* adjList) {// 獲取當前頂點的鄰接表Edge* current = adjList;// 遍歷鄰接表,打印每條邊的目標頂點和權重while (current != NULL) {printf("目標頂點: %d, 權重: %d\n", current->dest, current->weight);current = current->next;}
    }//6. 更新邊的權重
    void updateEdgeWeight(Edge* adjList, int src, int dest, int newWeight) {// 查找目標邊Edge* edge = findEdge(adjList, src, dest);// 如果目標邊存在,更新其權重if (edge != NULL) {edge->weight = newWeight;} else {// 如果目標邊不存在,打印錯誤信息printf("邊不存在\n");}
    }//7. 判斷邊是否存在
    int edgeExists(Edge* adjList, int src, int dest) {// 查找目標邊return findEdge(adjList, src, dest) != NULL;
    }//8. 獲取邊的權重
    int getEdgeWeight(Edge* adjList, int src, int dest) {// 查找目標邊Edge* edge = findEdge(adjList, src, dest);// 如果目標邊存在,返回其權重if (edge != NULL) {return edge->weight;} else {// 如果目標邊不存在,打印錯誤信息并返回-1printf("邊不存在\n");return -1;}
    }int main() {// 假設圖有5個頂點int numVertices = 5;// 定義一個鄰接表數組,每個元素指向一個頂點的鄰接表Edge* adjList[numVertices];// 初始化鄰接表,將每個頂點的鄰接表指針初始化為NULLfor (int i = 0; i < numVertices; i++) {adjList[i] = NULL;}// 插入邊insertEdge(adjList, 0, 1, 10);insertEdge(adjList, 0, 2, 20);insertEdge(adjList, 1, 3, 30);insertEdge(adjList, 2, 4, 40);// 遍歷邊printf("遍歷邊:\n");traverseEdges(adjList[0]);// 更新邊的權重updateEdgeWeight(adjList[0], 0, 1, 50);// 查找邊Edge* edge = findEdge(adjList[0], 0, 1);if (edge != NULL) {printf("找到邊: 目標頂點: %d, 權重: %d\n", edge->dest, edge->weight);}// 判斷邊是否存在if (edgeExists(adjList[0], 0, 1)) {printf("邊存在\n");} else {printf("邊不存在\n");}// 獲取邊的權重int weight = getEdgeWeight(adjList[0], 0, 1);if (weight != -1) {printf("邊的權重: %d\n", weight);}// 刪除邊deleteEdge(adjList, 0, 1);// 再次遍歷邊printf("再次遍歷邊:\n");traverseEdges(adjList[0]);return 0;
    }
    

    3.2.3圖類型解析與實現

    ????????基本概念如下所示:

    3.2.3.1無向圖
    #include <stdio.h>
    #include <stdlib.h>// 定義最大頂點數
    #define MAX_VERTEX_NUM 100// 邊的結構體
    typedef struct Edge {int dest;           // 目標頂點struct Edge* next;  // 指向下一個邊的指針
    } Edge;// 圖的結構體
    typedef struct Graph {int numVertices;    // 頂點數量Edge* adjLists[MAX_VERTEX_NUM]; // 鄰接表數組
    } Graph;// 創建一個新的邊
    Edge* createEdge(int dest) {// 分配內存以存儲新的邊結構體Edge* newEdge = (Edge*)malloc(sizeof(Edge));// 設置邊的目標頂點newEdge->dest = dest;// 將下一個邊指針初始化為NULLnewEdge->next = NULL;// 返回新創建的邊指針return newEdge;
    }// 創建圖
    Graph* createGraph(int vertices) {// 分配內存以存儲圖結構體Graph* graph = (Graph*)malloc(sizeof(Graph));// 設置圖的頂點數量graph->numVertices = vertices;// 初始化鄰接表for (int i = 0; i < vertices; i++) {// 將每個頂點的鄰接表指針初始化為NULLgraph->adjLists[i] = NULL;}// 返回新創建的圖指針return graph;
    }// 添加邊到圖中(無向圖,所以添加兩次)
    void addEdge(Graph* graph, int src, int dest) {// 從src到dest// 創建一個新的邊Edge* newEdge = createEdge(dest);// 將新邊的下一個邊指針指向當前頂點的鄰接表newEdge->next = graph->adjLists[src];// 將新邊插入到當前頂點的鄰接表的頭部graph->adjLists[src] = newEdge;// 從dest到src// 創建一個新的邊newEdge = createEdge(src);// 將新邊的下一個邊指針指向當前頂點的鄰接表newEdge->next = graph->adjLists[dest];// 將新邊插入到當前頂點的鄰接表的頭部graph->adjLists[dest] = newEdge;
    }// 打印圖的鄰接表
    void printGraph(Graph* graph) {// 遍歷每個頂點for (int i = 0; i < graph->numVertices; i++) {// 獲取當前頂點的鄰接表Edge* edge = graph->adjLists[i];// 打印當前頂點的編號printf("頂點 %d 的鄰接頂點: ", i);// 遍歷當前頂點的鄰接表while (edge != NULL) {// 打印鄰接頂點的編號printf("%d ", edge->dest);// 移動到下一個鄰接頂點edge = edge->next;}// 換行printf("\n");}
    }int main() {// 創建一個有5個頂點的圖Graph* graph = createGraph(5);// 添加邊addEdge(graph, 0, 1);addEdge(graph, 0, 4);addEdge(graph, 1, 2);addEdge(graph, 1, 3);addEdge(graph, 1, 4);addEdge(graph, 2, 3);addEdge(graph, 3, 4);// 打印圖printGraph(graph);return 0;
    }
    
    3.2.3.2有向圖
    #include <stdio.h>
    #include <stdlib.h>// 定義最大頂點數
    #define MAX_VERTEX_NUM 100// 邊的結構體
    typedef struct Edge {int dest;           // 目標頂點struct Edge* next;  // 指向下一個邊的指針
    } Edge;// 圖的結構體
    typedef struct Graph {int numVertices;    // 頂點數量Edge* adjLists[MAX_VERTEX_NUM]; // 鄰接表數組
    } Graph;// 創建一個新的邊
    Edge* createEdge(int dest) {// 分配內存以存儲新的邊結構體Edge* newEdge = (Edge*)malloc(sizeof(Edge));// 設置邊的目標頂點newEdge->dest = dest;// 將下一個邊指針初始化為NULLnewEdge->next = NULL;// 返回新創建的邊指針return newEdge;
    }// 創建圖
    Graph* createGraph(int vertices) {// 分配內存以存儲圖結構體Graph* graph = (Graph*)malloc(sizeof(Graph));// 設置圖的頂點數量graph->numVertices = vertices;// 初始化鄰接表for (int i = 0; i < vertices; i++) {// 將每個頂點的鄰接表指針初始化為NULLgraph->adjLists[i] = NULL;}// 返回新創建的圖指針return graph;
    }// 添加邊到圖中(有向圖,只添加一次)
    void addEdge(Graph* graph, int src, int dest) {// 從src到dest// 創建一個新的邊Edge* newEdge = createEdge(dest);// 將新邊的下一個邊指針指向當前頂點的鄰接表newEdge->next = graph->adjLists[src];// 將新邊插入到當前頂點的鄰接表的頭部graph->adjLists[src] = newEdge;
    }// 打印圖的鄰接表
    void printGraph(Graph* graph) {// 遍歷每個頂點for (int i = 0; i < graph->numVertices; i++) {// 獲取當前頂點的鄰接表Edge* edge = graph->adjLists[i];// 打印當前頂點的編號printf("頂點 %d 的鄰接頂點: ", i);// 遍歷當前頂點的鄰接表while (edge != NULL) {// 打印鄰接頂點的編號printf("%d ", edge->dest);// 移動到下一個鄰接頂點edge = edge->next;}// 換行printf("\n");}
    }int main() {// 創建一個有5個頂點的圖Graph* graph = createGraph(5);// 添加邊addEdge(graph, 0, 1);addEdge(graph, 0, 4);addEdge(graph, 1, 2);addEdge(graph, 1, 3);addEdge(graph, 2, 3);addEdge(graph, 3, 4);// 打印圖printGraph(graph);return 0;
    }
    
    3.2.3.3加權圖
    #include <stdio.h>
    #include <stdlib.h>// 定義最大頂點數
    #define MAX_VERTEX_NUM 100// 邊的結構體,包含目標頂點和權重
    typedef struct Edge {int dest;           // 目標頂點int weight;         // 邊的權重struct Edge* next;  // 指向下一個邊的指針
    } Edge;// 圖的結構體
    typedef struct Graph {int numVertices;    // 頂點數量Edge* adjLists[MAX_VERTEX_NUM]; // 鄰接表數組
    } Graph;// 創建一個新的帶權重的邊
    Edge* createEdge(int dest, int weight) {// 分配內存以存儲新的邊結構體Edge* newEdge = (Edge*)malloc(sizeof(Edge));// 設置邊的目標頂點newEdge->dest = dest;// 設置邊的權重newEdge->weight = weight;// 將下一個邊指針初始化為NULLnewEdge->next = NULL;// 返回新創建的邊指針return newEdge;
    }// 創建圖
    Graph* createGraph(int vertices) {// 分配內存以存儲圖結構體Graph* graph = (Graph*)malloc(sizeof(Graph));// 設置圖的頂點數量graph->numVertices = vertices;// 初始化鄰接表for (int i = 0; i < vertices; i++) {// 將每個頂點的鄰接表指針初始化為NULLgraph->adjLists[i] = NULL;}// 返回新創建的圖指針return graph;
    }// 添加帶權重的邊到圖中(無向圖,所以添加兩次)
    void addEdge(Graph* graph, int src, int dest, int weight) {// 從src到dest// 創建一個新的邊Edge* newEdge = createEdge(dest, weight);// 將新邊的下一個邊指針指向當前頂點的鄰接表newEdge->next = graph->adjLists[src];// 將新邊插入到當前頂點的鄰接表的頭部graph->adjLists[src] = newEdge;// 從dest到src// 創建一個新的邊newEdge = createEdge(src, weight);// 將新邊的下一個邊指針指向當前頂點的鄰接表newEdge->next = graph->adjLists[dest];// 將新邊插入到當前頂點的鄰接表的頭部graph->adjLists[dest] = newEdge;
    }// 打印圖的鄰接表
    void printGraph(Graph* graph) {// 遍歷每個頂點for (int i = 0; i < graph->numVertices; i++) {// 獲取當前頂點的鄰接表Edge* edge = graph->adjLists[i];// 打印當前頂點的編號printf("頂點 %d 的鄰接頂點及權重: ", i);// 遍歷當前頂點的鄰接表while (edge != NULL) {// 打印鄰接頂點的編號和權重printf("(%d, %d) ", edge->dest, edge->weight);// 移動到下一個鄰接頂點edge = edge->next;}// 換行printf("\n");}
    }int main() {// 創建一個有5個頂點的圖Graph* graph = createGraph(5);// 添加帶權重的邊addEdge(graph, 0, 1, 10);addEdge(graph, 0, 4, 20);addEdge(graph, 1, 2, 30);addEdge(graph, 1, 3, 40);addEdge(graph, 1, 4, 50);addEdge(graph, 2, 3, 60);addEdge(graph, 3, 4, 70);// 打印圖printGraph(graph);return 0;
    }
    
    3.2.3.4無權圖
    #include <stdio.h>
    #include <stdlib.h>// 定義最大頂點數
    #define MAX_VERTEX_NUM 100// 邊的結構體
    typedef struct Edge {int dest;           // 目標頂點struct Edge* next;  // 指向下一個邊的指針
    } Edge;// 圖的結構體
    typedef struct Graph {int numVertices;    // 頂點數量Edge* adjLists[MAX_VERTEX_NUM]; // 鄰接表數組
    } Graph;// 創建一個新的邊
    Edge* createEdge(int dest) {// 分配內存以存儲新的邊結構體Edge* newEdge = (Edge*)malloc(sizeof(Edge));// 設置邊的目標頂點newEdge->dest = dest;// 將下一個邊指針初始化為NULLnewEdge->next = NULL;// 返回新創建的邊指針return newEdge;
    }// 創建圖
    Graph* createGraph(int vertices) {// 分配內存以存儲圖結構體Graph* graph = (Graph*)malloc(sizeof(Graph));// 設置圖的頂點數量graph->numVertices = vertices;// 初始化鄰接表for (int i = 0; i < vertices; i++) {// 將每個頂點的鄰接表指針初始化為NULLgraph->adjLists[i] = NULL;}// 返回新創建的圖指針return graph;
    }// 添加邊到圖中(無向圖,所以添加兩次)
    void addEdge(Graph* graph, int src, int dest) {// 從src到dest// 創建一個新的邊Edge* newEdge = createEdge(dest);// 將新邊的下一個邊指針指向當前頂點的鄰接表newEdge->next = graph->adjLists[src];// 將新邊插入到當前頂點的鄰接表的頭部graph->adjLists[src] = newEdge;// 從dest到src// 創建一個新的邊newEdge = createEdge(src);// 將新邊的下一個邊指針指向當前頂點的鄰接表newEdge->next = graph->adjLists[dest];// 將新邊插入到當前頂點的鄰接表的頭部graph->adjLists[dest] = newEdge;
    }// 打印圖的鄰接表
    void printGraph(Graph* graph) {// 遍歷每個頂點for (int i = 0; i < graph->numVertices; i++) {// 獲取當前頂點的鄰接表Edge* edge = graph->adjLists[i];// 打印當前頂點的編號printf("頂點 %d 的鄰接頂點: ", i);// 遍歷當前頂點的鄰接表while (edge != NULL) {// 打印鄰接頂點的編號printf("%d ", edge->dest);// 移動到下一個鄰接頂點edge = edge->next;}// 換行printf("\n");}
    }int main() {// 創建一個有5個頂點的圖Graph* graph = createGraph(5);// 添加邊addEdge(graph, 0, 1);addEdge(graph, 0, 4);addEdge(graph, 1, 2);addEdge(graph, 1, 3);addEdge(graph, 2, 3);addEdge(graph, 3, 4);// 打印圖printGraph(graph);return 0;
    }
    
    3.2.3.5稠密圖
    #include <stdio.h>
    #include <stdbool.h>// 定義最大頂點數
    #define MAX_VERTEX_NUM 100// 圖的結構體,使用鄰接矩陣表示
    typedef struct Graph {int numVertices;    // 頂點數量bool adjMatrix[MAX_VERTEX_NUM][MAX_VERTEX_NUM]; // 鄰接矩陣
    } Graph;// 創建圖
    Graph* createGraph(int vertices) {// 分配內存以存儲圖結構體Graph* graph = (Graph*)malloc(sizeof(Graph));// 設置圖的頂點數量graph->numVertices = vertices;// 初始化鄰接矩陣為false(表示無連接)for (int i = 0; i < vertices; i++) {for (int j = 0; j < vertices; j++) {graph->adjMatrix[i][j] = false;}}// 返回新創建的圖指針return graph;
    }// 添加邊到圖中(無向圖)
    void addEdge(Graph* graph, int src, int dest) {// 檢查源頂點和目標頂點是否在有效范圍內if (src >= 0 && src < graph->numVertices && dest >= 0 && dest < graph->numVertices) {// 在鄰接矩陣中標記源頂點到目標頂點的邊graph->adjMatrix[src][dest] = true;// 在鄰接矩陣中標記目標頂點到源頂點的邊(因為是無向圖)graph->adjMatrix[dest][src] = true;}
    }// 打印圖的鄰接矩陣
    void printGraph(Graph* graph) {// 遍歷鄰接矩陣的每一行for (int i = 0; i < graph->numVertices; i++) {// 遍歷鄰接矩陣的每一列for (int j = 0; j < graph->numVertices; j++) {// 打印鄰接矩陣中的元素(0或1)printf("%d ", graph->adjMatrix[i][j]);}// 換行printf("\n");}
    }int main() {// 創建一個有5個頂點的圖Graph* graph = createGraph(5);// 添加邊addEdge(graph, 0, 1);addEdge(graph, 0, 4);addEdge(graph, 1, 2);addEdge(graph, 1, 3);addEdge(graph, 1, 4);addEdge(graph, 2, 3);addEdge(graph, 3, 4);// 打印圖的鄰接矩陣printGraph(graph);return 0;
    }
    
    3.2.3.6稀疏圖
    #include <stdio.h>
    #include <stdlib.h>// 定義最大頂點數
    #define MAX_VERTEX_NUM 100// 邊的結構體
    typedef struct Edge {int dest;           // 目標頂點struct Edge* next;  // 指向下一個邊的指針
    } Edge;// 圖的結構體,使用鄰接表表示
    typedef struct Graph {int numVertices;    // 頂點數量Edge* adjLists[MAX_VERTEX_NUM]; // 鄰接表數組
    } Graph;// 創建一個新的邊
    Edge* createEdge(int dest) {// 分配內存以存儲新的邊結構體Edge* newEdge = (Edge*)malloc(sizeof(Edge));// 設置邊的目標頂點newEdge->dest = dest;// 將下一個邊指針初始化為NULLnewEdge->next = NULL;// 返回新創建的邊指針return newEdge;
    }// 創建圖
    Graph* createGraph(int vertices) {// 分配內存以存儲圖結構體Graph* graph = (Graph*)malloc(sizeof(Graph));// 設置圖的頂點數量graph->numVertices = vertices;// 初始化鄰接表為空for (int i = 0; i < vertices; i++) {// 將每個頂點的鄰接表指針初始化為NULLgraph->adjLists[i] = NULL;}// 返回新創建的圖指針return graph;
    }// 添加邊到圖中(無向圖)
    void addEdge(Graph* graph, int src, int dest) {// 檢查源頂點和目標頂點是否在有效范圍內if (src >= 0 && src < graph->numVertices && dest >= 0 && dest < graph->numVertices) {// 從src到dest// 創建一個新的邊Edge* newEdge = createEdge(dest);// 將新邊的下一個邊指針指向當前頂點的鄰接表newEdge->next = graph->adjLists[src];// 將新邊插入到當前頂點的鄰接表的頭部graph->adjLists[src] = newEdge;// 如果是無向圖,還需要從dest到src// 創建一個新的邊newEdge = createEdge(src);// 將新邊的下一個邊指針指向當前頂點的鄰接表newEdge->next = graph->adjLists[dest];// 將新邊插入到當前頂點的鄰接表的頭部graph->adjLists[dest] = newEdge;}
    }// 打印圖的鄰接表
    void printGraph(Graph* graph) {// 遍歷每個頂點for (int i = 0; i < graph->numVertices; i++) {// 獲取當前頂點的鄰接表Edge* edge = graph->adjLists[i];// 打印當前頂點的編號printf("頂點 %d 的鄰接頂點: ", i);// 遍歷當前頂點的鄰接表while (edge != NULL) {// 打印鄰接頂點的編號printf("%d ", edge->dest);// 移動到下一個鄰接頂點edge = edge->next;}// 換行printf("\n");}
    }int main() {// 創建一個有5個頂點的圖Graph* graph = createGraph(5);// 添加邊addEdge(graph, 0, 1);addEdge(graph, 0, 4);addEdge(graph, 1, 2);addEdge(graph, 1, 3);addEdge(graph, 2, 3);addEdge(graph, 3, 4);// 打印圖的鄰接表printGraph(graph);return 0;
    }
    
    3.2.3.7完全圖
    #include <stdio.h>
    #include <stdbool.h>// 定義最大頂點數
    #define MAX_VERTEX_NUM 100// 圖的結構體,使用鄰接矩陣表示
    typedef struct Graph {int numVertices;    // 頂點數量bool adjMatrix[MAX_VERTEX_NUM][MAX_VERTEX_NUM]; // 鄰接矩陣
    } Graph;// 創建完全圖
    Graph* createCompleteGraph(int vertices) {// 分配內存以存儲圖結構體Graph* graph = (Graph*)malloc(sizeof(Graph));// 設置圖的頂點數量graph->numVertices = vertices;// 初始化鄰接矩陣,完全圖中每對頂點間都有邊for (int i = 0; i < vertices; i++) {for (int j = 0; j < vertices; j++) {if (i == j) {// 頂點不與自身相連graph->adjMatrix[i][j] = false;} else {// 其他情況都有邊相連graph->adjMatrix[i][j] = true;}}}// 返回新創建的圖指針return graph;
    }// 打印圖的鄰接矩陣
    void printGraph(Graph* graph) {// 遍歷鄰接矩陣的每一行for (int i = 0; i < graph->numVertices; i++) {// 遍歷鄰接矩陣的每一列for (int j = 0; j < graph->numVertices; j++) {// 打印鄰接矩陣中的元素(0或1)printf("%d ", graph->adjMatrix[i][j]);}// 換行printf("\n");}
    }int main() {// 創建一個有5個頂點的完全圖Graph* graph = createCompleteGraph(5);// 打印圖的鄰接矩陣printGraph(graph);return 0;
    }
    
    3.2.3.8強連通圖
    #include <stdio.h>
    #include <stdlib.h>// 定義最大頂點數
    #define MAX_VERTEX_NUM 100// 邊的結構體
    typedef struct Edge {int dest;           // 目標頂點struct Edge* next;  // 指向下一個邊的指針
    } Edge;// 圖的結構體,使用鄰接表表示
    typedef struct Graph {int numVertices;    // 頂點數量Edge* adjLists[MAX_VERTEX_NUM]; // 鄰接表數組
    } Graph;// 創建一個新的邊
    Edge* createEdge(int dest) {// 分配內存以存儲新的邊結構體Edge* newEdge = (Edge*)malloc(sizeof(Edge));// 設置邊的目標頂點newEdge->dest = dest;// 將下一個邊指針初始化為NULLnewEdge->next = NULL;// 返回新創建的邊指針return newEdge;
    }// 創建圖
    Graph* createGraph(int vertices) {// 分配內存以存儲圖結構體Graph* graph = (Graph*)malloc(sizeof(Graph));// 設置圖的頂點數量graph->numVertices = vertices;// 初始化鄰接表為空for (int i = 0; i < vertices; i++) {// 將每個頂點的鄰接表指針初始化為NULLgraph->adjLists[i] = NULL;}// 返回新創建的圖指針return graph;
    }// 添加邊到圖中(有向圖)
    void addEdge(Graph* graph, int src, int dest) {// 檢查源頂點和目標頂點是否在有效范圍內if (src >= 0 && src < graph->numVertices && dest >= 0 && dest < graph->numVertices) {// 從src到dest// 創建一個新的邊Edge* newEdge = createEdge(dest);// 將新邊的下一個邊指針指向當前頂點的鄰接表newEdge->next = graph->adjLists[src];// 將新邊插入到當前頂點的鄰接表的頭部graph->adjLists[src] = newEdge;}
    }// 打印圖的鄰接表
    void printGraph(Graph* graph) {for (int i = 0; i < graph->numVertices; i++) {Edge* edge = graph->adjLists[i];printf("頂點 %d 的鄰接頂點: ", i);while (edge != NULL) {printf("%d ", edge->dest);edge = edge->next;}printf("\n");}
    }int main() {// 創建一個有4個頂點的圖Graph* graph = createGraph(4);// 添加邊以形成強連通圖addEdge(graph, 0, 1);addEdge(graph, 1, 2);addEdge(graph, 2, 3);addEdge(graph, 3, 0);addEdge(graph, 1, 3);addEdge(graph, 3, 1);addEdge(graph, 2, 0);addEdge(graph, 0, 2);// 打印圖的鄰接表printGraph(graph);return 0;
    }
    
    3.2.3.9并查集
    #include <stdio.h>
    #include <stdlib.h>// 定義最大頂點數
    #define MAX_VERTEX_NUM 100// 并查集結構體
    typedef struct UnionFind {int parent[MAX_VERTEX_NUM]; // 存儲每個頂點的父節點int rank[MAX_VERTEX_NUM];   // 存儲每個頂點的秩(樹的高度)
    } UnionFind;// 初始化并查集
    void initUnionFind(UnionFind* uf, int vertices) {for (int i = 0; i < vertices; i++) {uf->parent[i] = i; // 初始時,每個頂點的父節點是它自己uf->rank[i] = 0;   // 初始時,每個頂點的秩為0}
    }// 查找頂點的根節點
    int find(UnionFind* uf, int x) {if (uf->parent[x] != x) {uf->parent[x] = find(uf, uf->parent[x]); // 路徑壓縮}return uf->parent[x];
    }// 合并兩個集合
    void unionSets(UnionFind* uf, int x, int y) {int rootX = find(uf, x);int rootY = find(uf, y);if (rootX != rootY) {if (uf->rank[rootX] < uf->rank[rootY]) {uf->parent[rootX] = rootY; // 將秩小的樹合并到秩大的樹上} else if (uf->rank[rootX] > uf->rank[rootY]) {uf->parent[rootY] = rootX;} else {uf->parent[rootY] = rootX;uf->rank[rootX]++; // 秩相同,合并后秩加1}}
    }// 檢查兩個頂點是否在同一個集合中
    int isConnected(UnionFind* uf, int x, int y) {return find(uf, x) == find(uf, y);
    }int main() {// 創建一個有4個頂點的圖int vertices = 4;UnionFind uf;initUnionFind(&uf, vertices);// 添加邊以形成強連通圖unionSets(&uf, 0, 1);unionSets(&uf, 1, 2);unionSets(&uf, 2, 3);unionSets(&uf, 3, 0);unionSets(&uf, 1, 3);unionSets(&uf, 3, 1);unionSets(&uf, 2, 0);unionSets(&uf, 0, 2);// 檢查頂點是否連通printf("頂點0和頂點1是否連通: %d\n", isConnected(&uf, 0, 1));printf("頂點0和頂點2是否連通: %d\n", isConnected(&uf, 0, 2));printf("頂點0和頂點3是否連通: %d\n", isConnected(&uf, 0, 3));return 0;
    }
    

    3.2.4圖的表示方法

    ? ? ? ? 圖的表示方法概念如下所示:? ? ? ??

    3.2.4.1領接矩陣
    #include <stdio.h>
    #include <stdbool.h>// 定義最大頂點數
    #define MAX_VERTEX_NUM 100// 圖的結構體,使用鄰接矩陣表示
    typedef struct Graph {int numVertices;    // 頂點數量bool adjMatrix[MAX_VERTEX_NUM][MAX_VERTEX_NUM]; // 鄰接矩陣
    } Graph;// 創建圖
    Graph* createGraph(int vertices) {// 分配內存以存儲圖結構體Graph* graph = (Graph*)malloc(sizeof(Graph));// 設置圖的頂點數量graph->numVertices = vertices;// 初始化鄰接矩陣,無連接時設為falsefor (int i = 0; i < vertices; i++) {for (int j = 0; j < vertices; j++) {graph->adjMatrix[i][j] = false;}}// 返回新創建的圖指針return graph;
    }// 添加邊到圖中(無向圖)
    void addEdge(Graph* graph, int src, int dest) {// 檢查源頂點和目標頂點是否在有效范圍內if (src >= 0 && src < graph->numVertices && dest >= 0 && dest < graph->numVertices) {// 設置鄰接矩陣中對應位置為true,表示有邊連接graph->adjMatrix[src][dest] = true;// 如果是無向圖,對稱位置也需要設置為truegraph->adjMatrix[dest][src] = true;}
    }// 打印圖的鄰接矩陣
    void printGraph(Graph* graph) {for (int i = 0; i < graph->numVertices; i++) {for (int j = 0; j < graph->numVertices; j++) {// 打印鄰接矩陣中的元素(0或1)printf("%d ", graph->adjMatrix[i][j]);}// 換行printf("\n");}
    }int main() {// 創建一個有5個頂點的圖Graph* graph = createGraph(5);// 添加邊addEdge(graph, 0, 1);addEdge(graph, 0, 4);addEdge(graph, 1, 2);addEdge(graph, 1, 3);addEdge(graph, 1, 4);addEdge(graph, 2, 3);addEdge(graph, 3, 4);// 打印圖的鄰接矩陣printGraph(graph);return 0;
    }
    
    3.2.4.2鄰接表
    #include <stdio.h>
    #include <stdlib.h>// 定義最大頂點數
    #define MAX_VERTEX_NUM 100// 邊的結構體
    typedef struct Edge {int dest;           // 目標頂點struct Edge* next;  // 指向下一個邊的指針
    } Edge;// 圖的結構體,使用鄰接表表示
    typedef struct Graph {int numVertices;    // 頂點數量Edge* adjLists[MAX_VERTEX_NUM]; // 鄰接表數組
    } Graph;// 創建一個新的邊
    Edge* createEdge(int dest) {// 分配內存以存儲新的邊結構體Edge* newEdge = (Edge*)malloc(sizeof(Edge));// 設置邊的目標頂點newEdge->dest = dest;// 將下一個邊指針初始化為NULLnewEdge->next = NULL;// 返回新創建的邊指針return newEdge;
    }// 創建圖
    Graph* createGraph(int vertices) {// 分配內存以存儲圖結構體Graph* graph = (Graph*)malloc(sizeof(Graph));// 設置圖的頂點數量graph->numVertices = vertices;// 初始化鄰接表為空for (int i = 0; i < vertices; i++) {// 將每個頂點的鄰接表指針初始化為NULLgraph->adjLists[i] = NULL;}// 返回新創建的圖指針return graph;
    }// 添加邊到圖中(有向圖)
    void addEdge(Graph* graph, int src, int dest) {// 檢查源頂點和目標頂點是否在有效范圍內if (src >= 0 && src < graph->numVertices && dest >= 0 && dest < graph->numVertices) {// 從src到dest// 創建一個新的邊Edge* newEdge = createEdge(dest);// 將新邊的下一個邊指針指向當前頂點的鄰接表newEdge->next = graph->adjLists[src];// 將新邊插入到當前頂點的鄰接表的頭部graph->adjLists[src] = newEdge;}
    }// 打印圖的鄰接表
    void printGraph(Graph* graph) {for (int i = 0; i < graph->numVertices; i++) {Edge* edge = graph->adjLists[i];printf("頂點 %d 的鄰接頂點: ", i);while (edge != NULL) {printf("%d ", edge->dest);edge = edge->next;}printf("\n");}
    }int main() {// 創建一個有4個頂點的圖Graph* graph = createGraph(4);// 添加邊以形成特定的圖結構addEdge(graph, 0, 1);addEdge(graph, 0, 2);addEdge(graph, 1, 2);addEdge(graph, 2, 0);addEdge(graph, 2, 3);addEdge(graph, 3, 3);// 打印圖的鄰接表printGraph(graph);return 0;
    }
    
    3.2..4.3邊緣列表
    #include <stdio.h>
    #include <stdlib.h>// 定義最大邊數
    #define MAX_EDGE_NUM 100// 邊的結構體
    typedef struct Edge {int src;    // 邊的起始頂點int dest;   // 邊的目標頂點// 可選:int weight; // 邊的權重(如果是有權圖)struct Edge* next; // 指向下一個邊的指針
    } Edge;// 圖的結構體,使用邊緣列表表示
    typedef struct Graph {int numVertices;    // 頂點數量int numEdges;       // 邊的數量Edge* edgeList[MAX_EDGE_NUM]; // 邊緣列表數組
    } Graph;// 創建一個新的邊
    Edge* createEdge(int src, int dest) {// 分配內存以存儲新的邊結構體Edge* newEdge = (Edge*)malloc(sizeof(Edge));// 設置邊的起始頂點newEdge->src = src;// 設置邊的目標頂點newEdge->dest = dest;// 將下一個邊指針初始化為NULLnewEdge->next = NULL;// 返回新創建的邊指針return newEdge;
    }// 創建圖
    Graph* createGraph(int vertices, int edges) {// 分配內存以存儲圖結構體Graph* graph = (Graph*)malloc(sizeof(Graph));// 設置圖的頂點數量graph->numVertices = vertices;// 設置圖的邊數量graph->numEdges = edges;// 初始化邊緣列表為空for (int i = 0; i < edges; i++) {// 將每個邊緣列表指針初始化為NULLgraph->edgeList[i] = NULL;}// 返回新創建的圖指針return graph;
    }// 添加邊到圖中
    void addEdge(Graph* graph, int index, int src, int dest) {// 檢查索引是否在有效范圍內if (index >= 0 && index < graph->numEdges) {// 在指定索引處創建一個新的邊graph->edgeList[index] = createEdge(src, dest);}
    }// 打印圖的邊緣列表
    void printGraph(Graph* graph) {printf("圖的邊緣列表:\n");for (int i = 0; i < graph->numEdges; i++) {Edge* edge = graph->edgeList[i];if (edge != NULL) {// 打印每條邊的起始頂點和目標頂點printf("邊 %d: %d -> %d\n", i, edge->src, edge->dest);}}
    }int main() {// 創建一個有4個頂點和5條邊的圖Graph* graph = createGraph(4, 5);// 添加邊以形成特定的圖結構addEdge(graph, 0, 0, 1);addEdge(graph, 1, 0, 2);addEdge(graph, 2, 1, 2);addEdge(graph, 3, 2, 0);addEdge(graph, 4, 2, 3);// 打印圖的邊緣列表printGraph(graph);return 0;
    }
    

    3.3散列表(哈希表)

    ? ? ? ? 基礎概念如下所示:

    3.3.1散列表結構定義

    #include <stdio.h>
    #include <stdlib.h>
    #include <string.h>#define TABLE_SIZE 10 // 散列表的大小// 鍵值對結構體
    typedef struct {char* key;       // 鍵int value;       // 值
    } KeyValuePair;// 散列表節點結構體
    typedef struct HashNode {KeyValuePair pair; // 存儲鍵值對struct HashNode* next; // 指向下一個節點的指針,用于處理沖突
    } HashNode;// 散列表結構體
    typedef struct {HashNode* buckets[TABLE_SIZE]; // 哈希表數組,每個元素是一個鏈表的頭節點
    } HashTable;
    

    3.3.2哈希函數定義

    // 將字符串鍵轉換為整數索引
    unsigned int hashFunction(const char* key) {unsigned int hash = 0; // 初始化哈希值為0while (*key) { // 遍歷字符串中的每個字符hash = (hash << 5) + *key++; // 將當前哈希值左移5位,并加上當前字符的ASCII值}return hash % TABLE_SIZE; // 返回哈希值對散列表大小取模的結果,確保索引在散列表范圍內
    }
    

    3.3.3創建和初始化散列表

    // 創建哈希表
    HashTable* createHashTable() {// 分配內存以存儲哈希表結構體HashTable* table = (HashTable*)malloc(sizeof(HashTable));// 初始化哈希表的每個桶為空for (int i = 0; i < TABLE_SIZE; i++) {table->buckets[i] = NULL;}// 返回創建的哈希表指針return table;
    }
    

    3.3.4插入或更新鍵值對

    // 插入或更新鍵值對
    void insertOrUpdate(HashTable* table, const char* key, int value) {unsigned int index = hashFunction(key); // 計算鍵的哈希值,獲取對應的桶索引HashNode* node = table->buckets[index]; // 獲取桶的頭節點// 搜索鍵是否已存在,若存在則更新值while (node != NULL) {if (strcmp(node->pair.key, key) == 0) { // 比較當前節點的鍵與傳入的鍵node->pair.value = value; // 更新值return; // 結束函數}node = node->next; // 移動到下一個節點}// 若鍵不存在,則創建新節點并插入node = (HashNode*)malloc(sizeof(HashNode)); // 分配新節點的內存if (node == NULL) { // 檢查內存分配是否成功fprintf(stderr, "內存分配失敗\n"); // 輸出錯誤信息exit(1); // 終止程序}node->pair.key = strdup(key); // 復制鍵字符串到新節點node->pair.value = value; // 設置新節點的值node->next = table->buckets[index]; // 將新節點的下一個指針指向當前桶的頭節點table->buckets[index] = node; // 將新節點設置為桶的頭節點
    }
    

    3.3.5查找鍵對應的值

    // 在哈希表中查找鍵對應的值
    int find(HashTable* table, const char* key) {unsigned int index = hashFunction(key); // 計算鍵的哈希值,獲取對應的桶索引HashNode* node = table->buckets[index]; // 獲取桶的頭節點while (node != NULL) { // 遍歷桶中的鏈表if (strcmp(node->pair.key, key) == 0) { // 比較當前節點的鍵與傳入的鍵return node->pair.value; // 如果找到匹配的鍵,返回對應的值}node = node->next; // 移動到下一個節點}return -1; // 表示未找到
    }
    

    3.3.6散列表代碼匯總

    #include <stdio.h>
    #include <stdlib.h>
    #include <string.h>#define TABLE_SIZE 10// 鍵值對結構體
    typedef struct {char* key;       // 鍵int value;       // 值
    } KeyValuePair;// 散列表節點結構體(鏈地址法處理沖突)
    typedef struct HashNode {KeyValuePair pair; // 存儲鍵值對struct HashNode* next; // 指向下一個節點的指針,用于處理沖突
    } HashNode;// 散列表結構體
    typedef struct {HashNode* buckets[TABLE_SIZE]; // 哈希表數組,每個元素是一個鏈表的頭節點
    } HashTable;// 哈希函數
    unsigned int hashFunction(const char* key) {unsigned int hash = 0;while (*key) {hash = (hash << 5) + *key++; // 將當前哈希值左移5位,并加上當前字符的ASCII值}return hash % TABLE_SIZE; // 返回哈希值對散列表大小取模的結果,確保索引在散列表范圍內
    }// 創建散列表
    HashTable* createHashTable() {HashTable* table = (HashTable*)malloc(sizeof(HashTable)); // 分配內存以存儲哈希表結構體if (table == NULL) {fprintf(stderr, "內存分配失敗\n"); // 錯誤處理exit(1);}for (int i = 0; i < TABLE_SIZE; i++) {table->buckets[i] = NULL; // 初始化哈希表的每個桶為空}return table;
    }// 插入或更新鍵值對
    void insertOrUpdate(HashTable* table, const char* key, int value) {unsigned int index = hashFunction(key); // 計算鍵的哈希值,獲取對應的桶索引HashNode* node = table->buckets[index]; // 獲取桶的頭節點while (node!= NULL) { // 遍歷桶中的鏈表if (strcmp(node->pair.key, key) == 0) { // 比較當前節點的鍵與傳入的鍵node->pair.value = value; // 如果鍵已存在,更新值return;}node = node->next; // 移動到下一個節點}node = (HashNode*)malloc(sizeof(HashNode)); // 分配新節點的內存if (node == NULL) {fprintf(stderr, "內存分配失敗\n");exit(1);}node->pair.key = strdup(key); // 復制鍵字符串到新節點node->pair.value = value; // 設置新節點的值node->next = table->buckets[index]; // 將新節點的下一個指針指向當前桶的頭節點table->buckets[index] = node; // 將新節點設置為桶的頭節點
    }// 查找鍵對應的值
    int find(HashTable* table, const char* key) {unsigned int index = hashFunction(key); // 計算鍵的哈希值,獲取對應的桶索引HashNode* node = table->buckets[index]; // 獲取桶的頭節點while (node!= NULL) { // 遍歷桶中的鏈表if (strcmp(node->pair.key, key) == 0) { // 比較當前節點的鍵與傳入的鍵return node->pair.value; // 如果找到匹配的鍵,返回對應的值}node = node->next; // 移動到下一個節點}return -1; // 表示未找到
    }// 刪除鍵值對
    void delete(HashTable* table, const char* key) {unsigned int index = hashFunction(key); // 計算鍵的哈希值,獲取對應的桶索引HashNode* node = table->buckets[index]; // 獲取桶的頭節點HashNode* prev = NULL; // 用于記錄當前節點的前一個節點while (node!= NULL) { // 遍歷桶中的鏈表if (strcmp(node->pair.key, key) == 0) { // 比較當前節點的鍵與傳入的鍵if (prev == NULL) { // 如果當前節點是桶的頭節點table->buckets[index] = node->next; // 將桶的頭節點指向下一個節點} else {prev->next = node->next; // 將前一個節點的下一個指針指向當前節點的下一個節點}free(node->pair.key); // 釋放鍵字符串的內存free(node); // 釋放當前節點的內存return;}prev = node; // 記錄當前節點為前一個節點node = node->next; // 移動到下一個節點}
    }// 打印散列表(用于調試)
    void printHashTable(HashTable* table) {for (int i = 0; i < TABLE_SIZE; i++) {HashNode* node = table->buckets[i];printf("Bucket %d: ", i);while (node!= NULL) {printf("(%s, %d) -> ", node->pair.key, node->pair.value);node = node->next;}printf("NULL\n");}
    }// 釋放散列表內存
    void freeHashTable(HashTable* table) {for (int i = 0; i < TABLE_SIZE; i++) {HashNode* node = table->buckets[i];while (node!= NULL) {HashNode* next = node->next;free(node->pair.key); // 釋放鍵字符串的內存free(node); // 釋放當前節點的內存node = next; // 移動到下一個節點}}free(table); // 釋放哈希表結構體的內存
    }int main() {HashTable* table = createHashTable(); // 創建哈希表insertOrUpdate(table, "apple", 1); // 插入鍵值對insertOrUpdate(table, "banana", 2);insertOrUpdate(table, "orange", 3);printf("apple: %d\n", find(table, "apple")); // 查找鍵對應的值printf("banana: %d\n", find(table, "banana"));printf("orange: %d\n", find(table, "orange"));delete(table, "banana"); // 刪除鍵值對printf("After deleting banana:\n");printHashTable(table); // 打印哈希表freeHashTable(table); // 釋放哈希表內存return 0;
    }
    

    4.特殊數據結構

    ? ? ? ? 基礎概念如下所示:

    4.1Trie樹(又稱前綴樹)

    #include <stdio.h>
    #include <stdlib.h>
    #include <string.h>#define ALPHABET_SIZE 26 // 字母表大小,假設只處理小寫字母// Trie樹節點結構體
    typedef struct TrieNode {struct TrieNode* children[ALPHABET_SIZE]; // 子節點指針數組,每個元素對應一個字母int isEndOfWord; // 標記該節點是否為單詞的結尾
    } TrieNode;// 創建新的Trie樹節點
    TrieNode* createNode() {TrieNode* node = (TrieNode*)malloc(sizeof(TrieNode)); // 分配內存if (node) {node->isEndOfWord = 0; // 初始化節點,不是單詞結尾for (int i = 0; i < ALPHABET_SIZE; i++) {node->children[i] = NULL; // 初始化子節點指針為空}}return node;
    }// 插入字符串到Trie樹
    void insert(TrieNode* root, const char* key) {TrieNode* node = root;for (int i = 0; key[i] != '\0'; i++) {int index = key[i] - 'a'; // 計算字符在字母表中的索引if (!node->children[index]) {node->children[index] = createNode(); // 如果子節點不存在,創建新節點}node = node->children[index]; // 移動到下一個節點}node->isEndOfWord = 1; // 標記當前節點為單詞結尾
    }// 在Trie樹中搜索字符串
    int search(TrieNode* root, const char* key) {TrieNode* node = root;for (int i = 0; key[i] != '\0'; i++) {int index = key[i] - 'a'; // 計算字符在字母表中的索引if (!node->children[index]) {return 0; // 如果子節點不存在,說明字符串不存在}node = node->children[index]; // 移動到下一個節點}return (node != NULL && node->isEndOfWord); // 返回是否找到單詞
    }// 釋放Trie樹的內存
    void freeTrie(TrieNode* root) {if (root == NULL) {return;}for (int i = 0; i < ALPHABET_SIZE; i++) {if (root->children[i]) {freeTrie(root->children[i]); // 遞歸釋放子節點內存}}free(root); // 釋放當前節點內存
    }int main() {TrieNode* root = createNode(); // 創建Trie樹的根節點// 插入一些單詞insert(root, "apple");insert(root, "banana");insert(root, "cherry");// 搜索單詞printf("Search 'apple': %s\n", search(root, "apple") ? "Found" : "Not Found");printf("Search 'banana': %s\n", search(root, "banana") ? "Found" : "Not Found");printf("Search 'cherry': %s\n", search(root, "cherry") ? "Found" : "Not Found");printf("Search 'grape': %s\n", search(root, "grape") ? "Found" : "Not Found");// 釋放Trie樹的內存freeTrie(root);return 0;
    }
    

    4.2并查集

    ????????是一種數據結構,用于處理一些不相交集合的合并及查詢問題。它主要支持以下兩種操作

    ? ? ? ? ?(1)查找(Find):確定某個元素屬于哪個集合。

    ? ? ? ? (2)合并(Union):將兩個集合合并成一個集合。

    #include <stdio.h>
    #include <stdlib.h>#define MAX_SIZE 100 // 并查集的最大元素數量// 并查集結構體
    typedef struct {int parent[MAX_SIZE]; // 父節點數組,用于存儲每個元素的父節點int rank[MAX_SIZE];   // 秩數組,用于存儲每個集合的秩(樹的高度)
    } DisjointSet;// 初始化并查集
    void makeSet(DisjointSet* ds, int n) {for (int i = 0; i < n; i++) {ds->parent[i] = i; // 初始時每個元素的父節點是自己ds->rank[i] = 0;   // 初始時秩為0}
    }// 查找根節點,并進行路徑壓縮
    int find(DisjointSet* ds, int x) {if (ds->parent[x] != x) {ds->parent[x] = find(ds, ds->parent[x]); // 路徑壓縮,將x的父節點直接指向根節點}return ds->parent[x];
    }// 合并兩個集合,按秩合并
    void unionSets(DisjointSet* ds, int x, int y) {int rootX = find(ds, x);int rootY = find(ds, y);if (rootX != rootY) {if (ds->rank[rootX] < ds->rank[rootY]) {ds->parent[rootX] = rootY; // 將秩小的集合合并到秩大的集合} else if (ds->rank[rootX] > ds->rank[rootY]) {ds->parent[rootY] = rootX;} else {ds->parent[rootY] = rootX; // 秩相等時,任選一個作為根節點,并增加其秩ds->rank[rootX]++;}}
    }// 示例使用
    int main() {DisjointSet ds;int n = 10; // 假設有10個元素makeSet(&ds, n); // 初始化并查集unionSets(&ds, 1, 2); // 合并元素1和元素2所在的集合unionSets(&ds, 3, 4); // 合并元素3和元素4所在的集合unionSets(&ds, 1, 4); // 合并元素1和元素4所在的集合printf("元素1和元素4是否在同一個集合中?%s\n",find(&ds, 1) == find(&ds, 4) ? "是" : "否"); // 查找元素1和元素4是否在同一個集合中printf("元素1和元素5是否在同一個集合中?%s\n",find(&ds, 1) == find(&ds, 5) ? "是" : "否"); // 查找元素1和元素5是否在同一個集合中return 0;
    }
    

    4.3跳表

    ????????是一種基于鏈表的數據結構,用于高效地實現有序集合的插入、刪除和查找操作。它通過在鏈表中添加多級索引來提高查找效率,類似于二分查找的思想。

    #include <stdio.h>
    #include <stdlib.h>
    #include <time.h>#define MAX_LEVEL 16 // 跳表的最大層數// 跳表節點結構體
    typedef struct SkipListNode {int key; // 鍵int value; // 值struct SkipListNode* forward[MAX_LEVEL]; // 指向不同層的下一個節點
    } SkipListNode;// 跳表結構體
    typedef struct {SkipListNode* header; // 跳表的頭節點int level; // 跳表的當前層數
    } SkipList;// 創建新的跳表節點
    SkipListNode* createNode(int key, int value, int level) {SkipListNode* node = (SkipListNode*)malloc(sizeof(SkipListNode)); // 分配內存if (node) {node->key = key; // 設置鍵node->value = value; // 設置值for (int i = 0; i < level; i++) {node->forward[i] = NULL; // 初始化指向不同層的下一個節點的指針為NULL}}return node;
    }// 創建新的跳表
    SkipList* createSkipList() {SkipList* list = (SkipList*)malloc(sizeof(SkipList)); // 分配內存if (list) {list->header = createNode(0, 0, MAX_LEVEL); // 創建頭節點,鍵和值都為0,層數為最大層數list->level = 1; // 初始時跳表的層數為1}return list;
    }// 生成隨機層數
    int randomLevel() {int level = 1;while (rand() < RAND_MAX / 2 && level < MAX_LEVEL) {level++; // 以一定概率增加層數,直到達到最大層數}return level;
    }// 插入鍵值對到跳表
    void insert(SkipList* list, int key, int value) {SkipListNode* update[MAX_LEVEL]; // 用于記錄插入位置的前驅節點SkipListNode* current = list->header;for (int i = list->level - 1; i >= 0; i--) {while (current->forward[i] && current->forward[i]->key < key) {current = current->forward[i]; // 在當前層找到插入位置的前驅節點}update[i] = current;}current = current->forward[0];if (current == NULL || current->key != key) {int newLevel = randomLevel(); // 生成新節點的隨機層數if (newLevel > list->level) {for (int i = list->level; i < newLevel; i++) {update[i] = list->header; // 更新新層的前驅節點為頭節點}list->level = newLevel; // 更新跳表的層數}SkipListNode* newNode = createNode(key, value, newLevel); // 創建新節點for (int i = 0; i < newLevel; i++) {newNode->forward[i] = update[i]->forward[i]; // 更新新節點在各層的指針update[i]->forward[i] = newNode;}} else {current->value = value; // 如果鍵已存在,更新值}
    }// 在跳表中查找鍵對應的值
    int search(SkipList* list, int key) {SkipListNode* current = list->header;for (int i = list->level - 1; i >= 0; i--) {while (current->forward[i] && current->forward[i]->key < key) {current = current->forward[i]; // 在當前層查找鍵}}current = current->forward[0];if (current && current->key == key) {return current->value; // 如果找到鍵,返回對應的值} else {return -1; // 如果未找到鍵,返回-1}
    }// 刪除跳表中的鍵值對
    void delete(SkipList* list, int key) {SkipListNode* update[MAX_LEVEL]; // 用于記錄刪除位置的前驅節點SkipListNode* current = list->header;for (int i = list->level - 1; i >= 0; i--) {while (current->forward[i] && current->forward[i]->key < key) {current = current->forward[i]; // 在當前層找到刪除位置的前驅節點}update[i] = current;}current = current->forward[0];if (current && current->key == key) {for (int i = 0; i < list->level; i++) {if (update[i]->forward[i] != current) {break; // 如果在某一層沒有找到要刪除的節點,跳出循環}update[i]->forward[i] = current->forward[i]; // 更新前驅節點的指針}free(current); // 釋放被刪除節點的內存while (list->level > 1 && list->header->forward[list->level - 1] == NULL) {list->level--; // 如果某一層沒有節點,減少跳表的層數}}
    }// 釋放跳表的內存
    void freeSkipList(SkipList* list) {SkipListNode* current = list->header;SkipListNode* next;while (current) {next = current->forward[0]; // 保存下一個節點的指針free(current); // 釋放當前節點的內存current = next; // 移動到下一個節點}free(list); // 釋放跳表結構體的內存
    }// 打印跳表(用于調試)
    void printSkipList(SkipList* list) {for (int i = list->level - 1; i >= 0; i--) {SkipListNode* current = list->header->forward[i];printf("Level %d: ", i + 1);while (current) {printf("(%d, %d) -> ", current->key, current->value); // 打印當前節點的鍵值對current = current->forward[i]; // 移動到下一個節點}printf("NULL\n"); // 打印當前層的結束標志}
    }int main() {srand(time(NULL)); // 初始化隨機數生成器SkipList* list = createSkipList(); // 創建跳表// 插入一些鍵值對insert(list, 1, 10);insert(list, 2, 20);insert(list, 3, 30);insert(list, 4, 40);insert(list, 5, 50);// 打印跳表printSkipList(list);// 查找鍵對應的值printf("Search key 3: %d\n", search(list, 3));printf("Search key 6: %d\n", search(list, 6));// 刪除鍵值對delete(list, 3);// 打印跳表printSkipList(list);// 釋放跳表的內存freeSkipList(list);return 0;
    }
    

    5.文件組織和外部存儲數據結構

    ? ? ? ? 基本概念定義如下所示:

    5.1順序文件

    5.1.1 順序文件的特點

    • 簡單性:順序文件的結構簡單,易于理解和實現。
    • 順序訪問效率高:如果數據的訪問模式是順序的,那么順序文件的訪問效率很高。
    • 不適合隨機訪問:由于數據是按照順序存儲的,因此隨機訪問(即直接訪問文件中的某個特定位置的數據)效率較低。

    5.1.2 順序文件的操作

    • 寫入數據:數據按照順序依次寫入文件。
    • 讀取數據:數據按照順序依次從文件中讀取。

    5.1.3 順序文件的實現解析

    #include <stdio.h>  #define MAX_RECORDS 100  // 定義最大記錄數// 定義學生結構體,包含ID、姓名和成績
    typedef struct {int id;  // 學生IDchar name[50];  // 學生姓名,最多49個字符加上結束符float score;  // 學生成績
    } Student;// 順序寫入文件函數
    void writeSequentialFile(const char* filename) {FILE* file = fopen(filename, "wb");  // 以二進制寫模式打開文件if (file == NULL) {  // 如果文件打開失敗perror("無法打開文件");  // 打印錯誤信息return;  // 返回}Student students[MAX_RECORDS];  // 創建學生數組// 循環填充學生數組for (int i = 0; i < MAX_RECORDS; i++) {students[i].id = i + 1;  // 設置IDsprintf(students[i].name, "學生%d", i + 1);  // 設置姓名students[i].score = (float)(i + 1) * 10;  // 設置成績fwrite(&students[i], sizeof(Student), 1, file);  // 寫入文件}fclose(file);  // 關閉文件
    }// 順序讀取文件函數
    void readSequentialFile(const char* filename) {FILE* file = fopen(filename, "rb");  // 以二進制讀模式打開文件if (file == NULL) {  // 如果文件打開失敗perror("無法打開文件");  // 打印錯誤信息return;  // 返回}Student student;  // 創建學生變量用于存儲讀取的數據// 循環讀取文件直到文件結束while (fread(&student, sizeof(Student), 1, file) == 1) {printf("ID: %d, 姓名: %s, 成績: %.2f\n", student.id, student.name, student.score);  // 打印學生信息}fclose(file);  // 關閉文件
    }int main() {const char* filename = "students.dat";  // 定義文件名writeSequentialFile(filename);  // 寫入文件readSequentialFile(filename);  // 讀取文件return 0;  // 程序結束
    }
    

    5.2 索引文件

    索引文件是一種通過建立索引來提高數據查找速度的文件組織方式。索引指向數據在文件中的位置,使得可以快速定位到特定的數據記錄。

    5.2.1 索引文件的特點

    • 查找效率高:通過索引可以快速定位到數據記錄,提高了查找效率。
    • 增加了存儲空間:需要額外的存儲空間來存儲索引。
    • 維護成本:當數據發生變化時,需要更新索引,增加了維護成本。

    5.2.2 索引文件的操作

    • 寫入數據:數據按照順序依次寫入文件,同時更新索引。
    • 讀取數據:通過索引快速定位到數據記錄,然后讀取數據。

    5.2.3 索引文件的實現與解析

    #include <stdio.h>
    #include <stdlib.h>#define MAX_RECORDS 100  // 定義最大記錄數// 定義學生結構體,包含ID、姓名和成績
    typedef struct {int id;  // 學生IDchar name[50];  // 學生姓名,最多49個字符加上結束符float score;  // 學生成績
    } Student;// 定義索引條目結構體,包含ID和文件偏移量
    typedef struct {int id;  // 記錄IDlong offset;  // 文件中的偏移量
    } IndexEntry;// 寫入索引文件函數
    void writeIndexedFile(const char* filename) {FILE* dataFile = fopen(filename, "wb");  // 以二進制寫模式打開數據文件if (dataFile == NULL) {  // 如果數據文件打開失敗perror("無法打開數據文件");  // 打印錯誤信息return;  // 返回}FILE* indexFile = fopen("index.dat", "wb");  // 以二進制寫模式打開索引文件if (indexFile == NULL) {  // 如果索引文件打開失敗perror("無法打開索引文件");  // 打印錯誤信息fclose(dataFile);  // 關閉已打開的數據文件return;  // 返回}Student students[MAX_RECORDS];  // 創建學生數組IndexEntry index[MAX_RECORDS];  // 創建索引條目數組// 循環填充學生數組和索引數組,并寫入文件for (int i = 0; i < MAX_RECORDS; i++) {students[i].id = i + 1;  // 設置IDsprintf(students[i].name, "學生%d", i + 1);  // 設置姓名students[i].score = (float)(i + 1) * 10;  // 設置成績fwrite(&students[i], sizeof(Student), 1, dataFile);  // 寫入數據文件index[i].id = students[i].id;  // 設置索引IDindex[i].offset = ftell(dataFile) - sizeof(Student);  // 計算并設置文件偏移量fwrite(&index[i], sizeof(IndexEntry), 1, indexFile);  // 寫入索引文件}fclose(dataFile);  // 關閉數據文件fclose(indexFile);  // 關閉索引文件
    }// 讀取索引文件函數
    void readIndexedFile(const char* filename, int id) {FILE* dataFile = fopen(filename, "rb");  // 以二進制讀模式打開數據文件if (dataFile == NULL) {  // 如果數據文件打開失敗perror("無法打開數據文件");  // 打印錯誤信息return;  // 返回}FILE* indexFile = fopen("index.dat", "rb");  // 以二進制讀模式打開索引文件if (indexFile == NULL) {  // 如果索引文件打開失敗perror("無法打開索引文件");  // 打印錯誤信息fclose(dataFile);  // 關閉已打開的數據文件return;  // 返回}IndexEntry index;  // 創建索引條目變量用于存儲讀取的數據// 循環讀取索引文件直到找到匹配的ID或文件結束while (fread(&index, sizeof(IndexEntry), 1, indexFile) == 1) {if (index.id == id) {  // 如果找到匹配的IDfseek(dataFile, index.offset, SEEK_SET);  // 定位到數據文件中的記錄位置Student student;  // 創建學生變量用于存儲讀取的數據fread(&student, sizeof(Student), 1, dataFile);  // 讀取學生信息printf("ID: %d, 姓名: %s, 成績: %.2f\n", student.id, student.name, student.score);  // 打印學生信息break;  // 結束循環}}fclose(dataFile);  // 關閉數據文件fclose(indexFile);  // 關閉索引文件
    }int main() {const char* filename = "students.dat";  // 定義數據文件名writeIndexedFile(filename);  // 寫入索引文件readIndexedFile(filename, 50);  // 讀取ID為50的學生信息return 0;  // 程序結束
    }

    本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
    如若轉載,請注明出處:http://www.pswp.cn/news/894676.shtml
    繁體地址,請注明出處:http://hk.pswp.cn/news/894676.shtml
    英文地址,請注明出處:http://en.pswp.cn/news/894676.shtml

    如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

    相關文章

    華為小米vivo向上,蘋果榮耀OPPO向下

    日前&#xff0c;Counterpoint發布的手機銷量月度報告顯示&#xff0c;中國智能手機銷量在2024年第四季度同比下降3.2%&#xff0c;成為2024年唯一出現同比下滑的季度。而對于各大智能手機品牌來說&#xff0c;他們的市場份額和格局也在悄然發生變化。 華為逆勢向上 在2024年第…

    每日一博 - 三高系統架構設計:高性能、高并發、高可用性解析

    文章目錄 引言一、高性能篇1.1 高性能的核心意義1.2 影響系統性能的因素1.3 高性能優化方法論1.3.1 讀優化&#xff1a;緩存與數據庫的結合1.3.2 寫優化&#xff1a;異步化處理 1.4 高性能優化實踐1.4.1 本地緩存 vs 分布式緩存1.4.2 數據庫優化 二、高并發篇2.1 高并發的核心意…

    吳恩達深度學習——有效運作神經網絡

    內容來自https://www.bilibili.com/video/BV1FT4y1E74V&#xff0c;僅為本人學習所用。 文章目錄 訓練集、驗證集、測試集偏差、方差正則化正則化參數為什么正則化可以減少過擬合Dropout正則化Inverted Dropout其他的正則化方法數據增廣Early stopping 歸一化梯度消失與梯度爆…

    20【變量的深度理解】

    一說起變量&#xff0c;懂點編程的都知道&#xff0c;但是在理解上可能還不夠深 變量就是存儲空間&#xff0c;電腦上的存儲空間有永久&#xff08;硬盤&#xff09;和臨時&#xff08;內存條&#xff09;兩種&#xff0c;永久數據重啟電腦后依舊存在&#xff0c;臨時數據只…

    RESTful API的設計原則與這些原則在Java中的應用

    RESTful API 是基于 REST&#xff08;Representational State Transfer&#xff09; 架構風格設計的 API&#xff0c;其核心目標是提高系統的可伸縮性、簡潔性和可維護性。以下是 RESTful API 的設計原則及在 Java 中的實現方法&#xff1a; 一、RESTful API 的核心設計原則 客…

    【apt源】RK3588 平臺ubuntu20.04更換apt源

    RK3588芯片使用的是aarch64架構&#xff0c;因此在Ubuntu 20.04上更換apt源時需要使用針對aarch64架構的源地址。以下是針對RK3588芯片在Ubuntu 20.04上更換apt源到清華源的正確步驟&#xff1a; 步驟一&#xff1a;打開終端 在Ubuntu 20.04中&#xff0c;按下Ctrl Alt T打…

    k8s二進制集群之Kube ApiServer部署

    創建kube工作目錄(僅在主節點上創建即可)同樣在我們的部署主機上創建apiserver證書請求文件根據證書文件生成apiserver證書僅接著創建TLS所需要的TOKEN創建apiserver服務的配置文件(僅在主節點上創建即可)創建apiserver服務管理配置文件對所有master節點分發證書 & TOK…

    基于RK3588/RK3576+MCU STM32+AI的儲能電站電池簇管理系統設計與實現

    伴隨近年來新型儲能技術的高質量規模化發展&#xff0c;儲能電站作為新能源領域的重要載體&#xff0c; 旨在配合逐步邁進智能電網時代&#xff0c;滿足電力系統能源結構與分布的創新升級&#xff0c;給予相應規模 電池管理系統的設計與實現以新的挑戰。同時&#xff0c;電子系…

    K8s 分布式存儲后端(K8s Distributed Storage Backend)

    K8s 分布式存儲后端 在 K8s 中實現分布式存儲后端對于管理跨集群的持久數據、確保高可用性、可擴展性和可靠性至關重要。在 K8s 環境中&#xff0c;應用程序通常被容器化并跨多個節點部署。雖然 K8s 可以有效處理無狀態應用程序&#xff0c;但有狀態應用程序需要持久存儲來維護…

    FFmpeg:多媒體處理的瑞士軍刀

    FFmpeg&#xff1a;多媒體處理的瑞士軍刀 前言 FFmpeg 是一個功能強大且跨平臺的開源多媒體框架&#xff0c;廣泛應用于音視頻處理領域。 它由多個庫和工具組成&#xff0c;能夠處理各種音視頻格式&#xff0c;涵蓋編碼、解碼、轉碼、流處理等多種操作。 無論是專業視頻編輯…

    unordered_map/set的哈希封裝

    【C筆記】unordered_map/set的哈希封裝 &#x1f525;個人主頁&#xff1a;大白的編程日記 &#x1f525;專欄&#xff1a;C筆記 文章目錄 【C筆記】unordered_map/set的哈希封裝前言一. 源碼及框架分析二.迭代器三.operator[]四.使用哈希表封裝unordered_map/set后言 前言 哈…

    編程AI深度實戰:大模型哪個好? Mistral vs Qwen vs Deepseek vs Llama

    ?? 系列文章&#xff1a; 編程AI深度實戰&#xff1a;私有模型deep seek r1&#xff0c;必會ollama-CSDN博客 編程AI深度實戰&#xff1a;自己的AI&#xff0c;必會LangChain-CSDN博客 編程AI深度實戰&#xff1a;給vim裝上AI-CSDN博客 編程AI深度實戰&#xff1a;火的編…

    neo4j-community-5.26.0 install in window10

    在住處電腦重新配置一下neo4j, 1.先至官方下載 Neo4j Desktop Download | Free Graph Database Download Neo4j Deployment Center - Graph Database & Analytics 2.配置java jdk jdk 21 官網下載 Java Downloads | Oracle 中國 path: 4.查看java -version 版本 5.n…

    【怎么用系列】短視頻戒除—1—對推薦算法進行干擾

    如今推薦算法已經滲透到人們生活的方方面面&#xff0c;尤其是抖音等短視頻核心就是推薦算法。 【短視頻的危害】 1> 會讓人變笨&#xff0c;慢慢讓人喪失注意力與專注力 2> 讓人喪失閱讀長文的能力 3> 讓人沉浸在一個又一個快感與嗨點當中。當我們刷短視頻時&#x…

    網絡原理(5)—— 數據鏈路層詳解

    目錄 一. 以太網 1.1 認識以太網 1.2 網卡與以太網 1.3 以太網幀格式 二. 認識MAC地址 三. MAC地址 與 IP地址 的區別 4.1 定義 4.2 分配方式 4.3 工作層次 4.4 地址格式 4.5 尋址方式 四. ARP協議 4.1 引入 4.2 ARP的概念 4.3 ARP工作原理 五. MTU 與 MSS …

    【從零開始的LeetCode-算法】922. 按奇偶排序數組 II

    給定一個非負整數數組 nums&#xff0c; nums 中一半整數是 奇數 &#xff0c;一半整數是 偶數 。 對數組進行排序&#xff0c;以便當 nums[i] 為奇數時&#xff0c;i 也是 奇數 &#xff1b;當 nums[i] 為偶數時&#xff0c; i 也是 偶數 。 你可以返回 任何滿足上述條件的…

    設計一個特殊token以從1億詞表中動態采樣8192個詞來表達當前序列

    為了設計一個特殊token以從1億詞表中動態采樣8192個詞來表達當前序列&#xff0c;可以采用以下分步方案&#xff1a; 1. 特殊token的設計與作用 定義特殊token&#xff1a;在輸入序列前添加一個特殊標記&#xff0c;如[SUBVOCAB]。該token的嵌入包含觸發子詞表采樣的元信息。…

    兩晉南北朝 僑置州郡由來

    僑置的核心思想是面向人管理 而不是面向土地 1. 北雍州 西晉于長安置雍州&#xff0c;永嘉之亂&#xff0c;沒于劉、石。苻秦之亂&#xff0c;雍州流民南出樊沔&#xff0c;孝武于襄陽僑立雍州。此時稱長安為北雍州。

    H264原始碼流格式分析

    1.H264碼流結構組成 H.264裸碼流&#xff08;Raw Bitstream&#xff09;數據主要由一系列的NALU&#xff08;網絡抽象層單元&#xff09;組成。每個NALU包含一個NAL頭和一個RBSP&#xff08;原始字節序列載荷&#xff09;。 1.1 H.264碼流層次 H.264碼流的結構可以分為兩個層…

    【C語言設計模式學習筆記1】面向接口編程/簡單工廠模式/多態

    面向接口編程可以提供更高級的抽象&#xff0c;實現的時候&#xff0c;外部不需要知道內部的具體實現&#xff0c;最簡單的是使用簡單工廠模式來進行實現&#xff0c;比如一個Sensor具有多種表示形式&#xff0c;這時候可以在給Sensor結構體添加一個enum類型的type&#xff0c;…