C語言實例之雙向鏈表增刪改查
一、雙向鏈表介紹
雙向鏈表(Doubly Linked List)是一種常見的數(shù)據(jù)結(jié)構(gòu),在單鏈表的基礎(chǔ)上增加了向前遍歷的功能。與單向鏈表不同,雙向鏈表的每個節(jié)點除了包含指向下一個節(jié)點的指針外,還包含指向前一個節(jié)點的指針。


作用和原理:
(1)插入和刪除操作:由于雙向鏈表中每個節(jié)點都有指向前一個節(jié)點的指針,所以在雙向鏈表中進行插入或刪除操作時,相對于單向鏈表更加高效??梢酝ㄟ^修改前后節(jié)點的指針來完成插入和刪除,而無需遍歷鏈表。
(2)雙向遍歷:雙向鏈表支持從頭部到尾部以及從尾部到頭部的雙向遍歷。這在某些場景下非常有用,例如需要反向查找、刪除最后一個節(jié)點等。
(3)增加了靈活性:由于每個節(jié)點都具有指向前一個節(jié)點和后一個節(jié)點的指針,雙向鏈表在某些特定場景下更靈活。例如,需要在鏈表中間插入或刪除節(jié)點,或者需要修改前一個節(jié)點的信息。
雙向鏈表的原理很簡單。每個節(jié)點由數(shù)據(jù)域和兩個指針組成,其中一個指針指向前一個節(jié)點,一個指針指向后一個節(jié)點。頭節(jié)點指向鏈表的第一個節(jié)點,尾節(jié)點指向鏈表的最后一個節(jié)點。通過調(diào)整節(jié)點之間的指針,可以在雙向鏈表中執(zhí)行插入、刪除和遍歷等操作。
使用場景:
(1)編輯器中的撤銷和重做功能:雙向鏈表可以用于實現(xiàn)撤銷和重做功能,每次編輯操作都將其結(jié)果存儲為一個節(jié)點,并使用指針鏈接起來。通過雙向鏈表,可以方便地在撤銷和重做之間進行切換。
(2)瀏覽器的導航歷史:瀏覽器的導航歷史可以使用雙向鏈表來保存已訪問的頁面,每個頁面作為一個節(jié)點,并使用指針鏈接起來,以便進行前進和后退操作。
(3)實現(xiàn)LRU緩存替換算法:LRU緩存中,最近最少使用的數(shù)據(jù)被淘汰,可以使用雙向鏈表來維護緩存中的數(shù)據(jù),最近訪問的數(shù)據(jù)位于鏈表的頭部,最久未訪問的數(shù)據(jù)位于鏈表的尾部。
(4)實現(xiàn)雙向隊列:雙向鏈表可以用于實現(xiàn)雙向隊列(Dequeue),支持在隊列的兩端進行插入和刪除操作。
雙向鏈表提供了更多的靈活性和功能,特別是當需要在雙向遍歷、頻繁的插入和刪除操作等場景下使用。在許多常見的數(shù)據(jù)結(jié)構(gòu)和算法中都有廣泛的應用。
二、代碼實現(xiàn)
以下是使用C語言實現(xiàn)的完整雙向鏈表代碼,包含了鏈表的創(chuàng)建、增加、刪除、修改、排序和插入等功能。代碼中封裝了一套完整的子函數(shù),以方便使用。
#include <stdio.h>
#include <stdlib.h>
?
// 雙向鏈表節(jié)點結(jié)構(gòu)
typedef struct Node {
? ?int data; ? ? ? ? ? ? ?// 數(shù)據(jù)域
? ?struct Node* prev; ? ? // 指向前一個節(jié)點的指針
? ?struct Node* next; ? ? // 指向后一個節(jié)點的指針
} Node;
?
// 創(chuàng)建新節(jié)點
Node* createNode(int data) {
? ?Node* newNode = (Node*)malloc(sizeof(Node));
? ?if (newNode == NULL) {
? ? ? ?printf("Failed to allocate memory for new node\n");
? ? ? ?return NULL;
? }
? ?newNode->data = data;
? ?newNode->prev = NULL;
? ?newNode->next = NULL;
? ?return newNode;
}
?
// 在鏈表末尾添加節(jié)點
void append(Node** head, int data) {
? ?Node* newNode = createNode(data);
? ?if (newNode == NULL) {
? ? ? ?return;
? }
? ?if (*head == NULL) {
? ? ? ?*head = newNode;
? } else {
? ? ? ?Node* current = *head;
? ? ? ?while (current->next != NULL) { ? ?// 找到鏈表末尾
? ? ? ? ? ?current = current->next;
? ? ? }
? ? ? ?current->next = newNode;
? ? ? ?newNode->prev = current;
? }
}
?
// 在鏈表頭部添加節(jié)點
void prepend(Node** head, int data) {
? ?Node* newNode = createNode(data);
? ?if (newNode == NULL) {
? ? ? ?return;
? }
? ?if (*head == NULL) {
? ? ? ?*head = newNode;
? } else {
? ? ? ?newNode->next = *head;
? ? ? (*head)->prev = newNode;
? ? ? ?*head = newNode;
? }
}
?
// 在指定位置插入節(jié)點
void insert(Node** head, int data, int position) {
? ?if (position < 0) {
? ? ? ?printf("Invalid position\n");
? ? ? ?return;
? }
? ?if (position == 0) {
? ? ? ?prepend(head, data);
? ? ? ?return;
? }
? ?Node* newNode = createNode(data);
? ?if (newNode == NULL) {
? ? ? ?return;
? }
? ?Node* current = *head;
? ?int count = 0;
? ?while (count < (position - 1) && current != NULL) { ? ?// 找到插入位置前一個節(jié)點
? ? ? ?current = current->next;
? ? ? ?count++;
? }
? ?if (current == NULL) {
? ? ? ?printf("Invalid position\n");
? ? ? ?free(newNode);
? ? ? ?return;
? }
? ?newNode->next = current->next;
? ?newNode->prev = current;
? ?if (current->next != NULL) {
? ? ? ?current->next->prev = newNode;
? }
? ?current->next = newNode;
}
?
// 刪除指定位置的節(jié)點
void removeAt(Node** head, int position) {
? ?if (*head == NULL) {
? ? ? ?printf("List is empty\n");
? ? ? ?return;
? }
? ?if (position < 0) {
? ? ? ?printf("Invalid position\n");
? ? ? ?return;
? }
? ?Node* current = *head;
? ?int count = 0;
? ?if (position == 0) {
? ? ? ?*head = current->next;
? ? ? ?if (*head != NULL) {
? ? ? ? ? (*head)->prev = NULL;
? ? ? }
? ? ? ?free(current);
? ? ? ?return;
? }
? ?while (count < position && current != NULL) { ? ?// 找到要刪除的節(jié)點
? ? ? ?current = current->next;
? ? ? ?count++;
? }
? ?if (current == NULL) {
? ? ? ?printf("Invalid position\n");
? ? ? ?return;
? }
? ?current->prev->next = current->next;
? ?if (current->next != NULL) {
? ? ? ?current->next->prev = current->prev;
? }
? ?free(current);
}
?
// 修改指定位置的節(jié)點值
void modify(Node* head, int position, int data) {
? ?Node* current = head;
? ?int count = 0;
? ?while (count < position && current != NULL) { ? ?// 找到要修改的節(jié)點
? ? ? ?current = current->next;
? ? ? ?count++;
? }
? ?if (current == NULL) {
? ? ? ?printf("Invalid position\n");
? ? ? ?return;
? }
? ?current->data = data;
}
?
// 對鏈表進行排序
void sort(Node** head) {
? ?if (*head == NULL) {
? ? ? ?printf("List is empty\n");
? ? ? ?return;
? }
? ?Node* current = *head;
? ?Node* temp = NULL;
? ?int swapped;
? ?do {
? ? ? ?swapped = 0;
? ? ? ?current = *head;
? ? ? ?while (current->next != NULL) {
? ? ? ? ? ?if (current->data > current->next->data) {
? ? ? ? ? ? ? ?int tmp = current->data;
? ? ? ? ? ? ? ?current->data = current->next->data;
? ? ? ? ? ? ? ?current->next->data = tmp;
? ? ? ? ? ? ? ?swapped = 1;
? ? ? ? ? }
? ? ? ? ? ?current = current->next;
? ? ? }
? ? ? ?temp = current;
? } while (swapped);
}
?
// 打印鏈表
void printList(Node* head) {
? ?if (head == NULL) {
? ? ? ?printf("List is empty\n");
? ? ? ?return;
? }
? ?Node* current = head;
? ?while (current != NULL) {
? ? ? ?printf("%d ", current->data);
? ? ? ?current = current->next;
? }
? ?printf("\n");
}
?
// 釋放鏈表內(nèi)存
void freeList(Node** head) {
? ?if (*head == NULL) {
? ? ? ?return;
? }
? ?Node* current = *head;
? ?Node* next = NULL;
? ?while (current != NULL) {
? ? ? ?next = current->next;
? ? ? ?free(current);
? ? ? ?current = next;
? }
? ?*head = NULL;
}
?
int main() {
? ?Node* head = NULL;
? ?append(&head, 5);
? ?append(&head, 3);
? ?prepend(&head, 9);
? ?insert(&head, 7, 1);
? ?removeAt(&head, 2);
? ?modify(head, 0, 2);
? ?sort(&head);
? ?printList(head);
? ?freeList(&head);
? ?return 0;
}代碼里實現(xiàn)了創(chuàng)建雙向鏈表、在鏈表末尾添加節(jié)點、在鏈表頭部添加節(jié)點、在指定位置插入節(jié)點、刪除指定位置的節(jié)點、修改指定位置的節(jié)點值、對鏈表進行排序、打印鏈表及釋放鏈表內(nèi)存等功能。
三、思路講解
代碼里定義了一個雙向鏈表節(jié)點結(jié)構(gòu),包含數(shù)據(jù)域(data)、指向前一個節(jié)點的指針(prev)和指向后一個節(jié)點的指針(next)。
typedef struct Node {
? ?int data;
? ?struct Node* prev;
? ?struct Node* next;
} Node;(1)createNode函數(shù)用于創(chuàng)建新節(jié)點。分配內(nèi)存以存儲節(jié)點,并檢查內(nèi)存分配是否成功。設(shè)置節(jié)點的數(shù)據(jù)域為傳入的數(shù)據(jù),并將前一個節(jié)點和后一個節(jié)點的指針都設(shè)置為NULL。最后,返回新創(chuàng)建的節(jié)點的指針。
(2)append函數(shù)用于在鏈表末尾添加節(jié)點。首先調(diào)用createNode函數(shù)創(chuàng)建一個新節(jié)點。如果頭節(jié)點為空,則將新節(jié)點設(shè)置為頭節(jié)點。否則,遍歷鏈表直到找到最后一個節(jié)點,將新節(jié)點連接到最后一個節(jié)點的下一個位置,并設(shè)置新節(jié)點的prev指針指向最后一個節(jié)點。
(3)prepend函數(shù)用于在鏈表頭部添加節(jié)點。首先,調(diào)用createNode函數(shù)創(chuàng)建一個新節(jié)點。如果頭節(jié)點為空,則將新節(jié)點設(shè)置為頭節(jié)點。否則,將新節(jié)點的next指針指向當前的頭節(jié)點,將當前頭節(jié)點的prev指針指向新節(jié)點,然后將新節(jié)點設(shè)置為頭節(jié)點。
(4)insert函數(shù)用于在指定位置插入節(jié)點。首先,檢查插入位置是否合法。如果插入位置為0,則調(diào)用prepend函數(shù)在鏈表頭部插入節(jié)點。否則,調(diào)用createNode函數(shù)創(chuàng)建一個新節(jié)點,然后遍歷鏈表直到找到插入位置前一個節(jié)點,將新節(jié)點插入到這兩個節(jié)點之間,即將新節(jié)點的next指針指向前一個節(jié)點的next指針所指向的節(jié)點,將新節(jié)點的prev指針指向前一個節(jié)點,然后更新新節(jié)點兩側(cè)節(jié)點的指針。
(5)removeAt函數(shù)用于刪除指定位置的節(jié)點。首先,檢查鏈表是否為空。如果鏈表為空,則輸出相應的提示信息。如果要刪除的位置為0,即刪除頭節(jié)點,需要特殊處理,即將頭節(jié)點的下一個節(jié)點設(shè)置為新的頭節(jié)點,并將新的頭節(jié)點的prev指針設(shè)置為NULL。否則,遍歷鏈表直到找到要刪除的節(jié)點,將要刪除節(jié)點的前一個節(jié)點的next指針指向要刪除節(jié)點的下一個節(jié)點,然后更新兩側(cè)節(jié)點的指針。
(6)modify函數(shù)用于修改指定位置的節(jié)點值。首先,遍歷鏈表直到找到要修改的節(jié)點,然后將該節(jié)點的數(shù)據(jù)域設(shè)置為傳入的新數(shù)據(jù)。
(7)sort函數(shù)用于對鏈表進行排序。首先,檢查鏈表是否為空。如果鏈表為空,則輸出相應的提示信息。使用冒泡排序算法,重復遍歷鏈表并比較相鄰節(jié)點的值,如果前一個節(jié)點的值大于后一個節(jié)點的值,則交換它們的值。重復此過程,直到鏈表沒有發(fā)生交換為止。
(8)printList函數(shù)用于打印鏈表中的所有節(jié)點的值。首先,檢查鏈表是否為空。如果鏈表為空,則輸出相應的提示信息。遍歷鏈表的每個節(jié)點,并輸出節(jié)點中存儲的數(shù)據(jù)。
(9)freeList函數(shù)用于釋放鏈表的內(nèi)存。首先,檢查鏈表是否為空。如果鏈表為空,則直接返回。遍歷鏈表的每個節(jié)點,使用free函數(shù)釋放節(jié)點的內(nèi)存,并將節(jié)點指針設(shè)為NULL,最后將頭節(jié)點指針設(shè)為NULL。
以上就是C語言實例之雙向鏈表增刪改查的詳細內(nèi)容,更多關(guān)于C語言雙向鏈表的資料請關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
用C語言winform編寫滲透測試工具實現(xiàn)SQL注入功能
本篇文章主要介紹使用C#winform編寫滲透測試工具,實現(xiàn)SQL注入的功能。使用python編寫SQL注入腳本,基于get顯錯注入的方式進行數(shù)據(jù)庫的識別、獲取表名、獲取字段名,最終獲取用戶名和密碼;使用C#winform編寫windows客戶端軟件調(diào)用.py腳本,實現(xiàn)用戶名和密碼的獲取2021-08-08

