线性表是一种最基本、最简单的数据结构,数据元素之间仅具有单一的前驱和后继关系,它提供了简单而强大的方法来处理和操作数据集合。
线性表的数据元素具有抽象(即不确定)的数据类型,在实际问题中,数据元素的抽象类
型将被具体的数据类型所取代。(常见整形和结构体)
线性表的存储结构有两种常见的实现方式:顺序存储和链式存储。
(1)顺序存储结构:
顺序存储结构使用一段连续的内存空间来存储线性表中的元素。在内存中,线性表的元素按照其在序列中的位置依次存放。其中,首元素存放在起始地址,后续元素紧接着存放在连续的内存单元中。
(2)链式存储结构:
链式存储结构通过节点之间的指针连接来存储线性表中的元素。每个节点包含两部分信息:数据域用于存储元素值,指针域用于指向下一个节点的地址。
优点:
缺点:
总结:
顺序存储结构适用于频繁访问和随机访问元素的场景,而链式存储结构适用于动态大小和频繁插入、删除操作的场景。
线性表作为一种基本的数据结构,广泛应用于各个领域,包括但不限于以下几个方面:
链表是一种常见且重要的数据结构,用于组织和存储数据。相比于顺序存储结构,链表通过节点之间的指针连接实现元素的存储和访问。本节将详细介绍链表的概念、特点、常见的类型以及操作和应用场景。
(1)概念与特点:
(2)常见类型:
(3)常见操作:
(4)应用场景:
链表作为一种灵活的数据结构,广泛应用于各个领域,特别适用于以下几个方面:
单链表(singly linked list)是用一组任意的存储单元存放线性表的元素,这组存储单元可以连续也可以不连续,甚至可以零散分布在内存中的任意位置。
为了能正确表示元素之间的逻辑关系,每个存储单元在存储数据元素的同时,还必须存储其后继元素所在的地址信息,即指针,这两部分组成了数据元素的存储映像,称为结点(node)。
单链表正是通过每个结点的指针域将线性表的数据元素按其逻辑次序链接在一起的,由于每个结点只有一个指针域,故称为单链表。
单链表中每个结点的存储地址存放在其前驱结点的 指针域中,而第一个元素无前驱,所以设头指针(head pointer)指向第一个元素所在结点(称为开始结点),整个单链表的存取必须从头指针开始进行,因而头指针具有标识一个单链表的作用;由于最后一个元素无后继,故最后一个元素所在结点(称为终端结点)的指针域为空,即NULL
(图中用“∧”表示),也称尾标志(tail mark)。
上面的表述中:元素从第一个节点开始存储,看起来没什么问题。但是当链表为空、在最前面插入和删除元素、遍历链表时会更加麻烦。所以,通常在第一个元素的节点前面添加一个头结点。(在后面的介绍中,可以想象没有头结点应该如何操作)
细介绍单链表的各种操作(大概的操作,没有细分是否带头结点,详见代码部分):
(1)创建链表:
创建一个空的单链表需要初始化一个头指针,并将头指针指向空值。
(2)插入操作:
(3)删除操作:
(4)查找操作:
修改操作:
根据指定位置或节点,将节点的数据域修改为新的值。
获取链表长度:
从头节点开始遍历链表,累计节点的个数,直到遍历到链表末尾。
遍历链表:
从头节点开始依次遍历每个节点,可以输出节点的值或进行其他操作。
需要注意的是,在进行插入和删除操作时,要确保链表的指针关系正确,避免出现内存泄漏或指针丢失的情况。
单链表的操作灵活性较高,尤其适用于频繁插入和删除操作的场景。然而,由于无法直接访问前一个节点,某些操作可能需要从头节点开始遍历整个链表,因此在使用时需注意操作的效率。
带头结点:创建一个链表即创建一个头结点。
不带头结点:创建链表即创建一个节点类型的空指针。
位置:从第一个数据节点开始计算,第一个数据节点位置为0。
长度:数据节点的个数。
下面的代码也可以进一步完善,比如判断malloc内存申请是否成功;当数据有重复时,可以返回所有匹配的元素的位置.
注意处理好节点之间的链接关系就好了。
此外,链表节点使用malloc申请内存,在所有操作完成后,记得释放内存,遍历链表,free即可(我只在第一个代码里面写了,读者注意一下)。
// 释放整个链表内存
void freeList(Node* head) {
Node* curr = head->next;
while (curr) {
Node* temp = curr;
curr = curr->next;
free(temp);
}
free(head);
}
// 带头结点的单链表
// 头指针指向头结点,头结点不存放数据
// 计算位置是从第一个数据节点开始计算,0即为第一个数据节点
#include
#include
// 定义链表节点
typedef struct Node {
int data;
struct Node* next;
} Node;
// 创建链表
// 即创建一个头结点,head指针指向头结点,头结点指针为NULL
// 如果没有头结点,那么创建一个链表就至少有一个数据
Node* createList() {
Node* head = (Node*)malloc(sizeof(Node));
head->next = NULL;
return head;
}
// 在指定位置插入节点(前面)
void insertNode(Node* head, int position, int data) {
Node* newNode = (Node*)malloc(sizeof(Node));
newNode->data = data;
Node* curr = head;
int count = 0;
while (curr && count < position) {
curr = curr->next;
count++;
}
if (curr) {
newNode->next = curr->next; // 将新节指针指向插入位置后面的节点
curr->next = newNode; // 插入位置的节点指针执行刚刚插入的新节点
} else {
printf("Invalid position.\n");
free(newNode);
}
}
// 删除指定位置的节点
void deleteNode(Node* head, int position) {
Node* prev = head; // 删除位置前一个节点
Node* curr = head->next; // 删除位置的节点
int count = 0;
while (curr && count < position) {
prev = curr;
curr = curr->next;
count++;
}
if (curr) {
prev->next = curr->next; // 将删除位置前一个节点指向删除位置后一个节点
free(curr); // 释放删除位置的节点
} else {
printf("Invalid position.\n");
}
}
// 根据值查找节点的位置
// 假设没有重复元素,否则只能返回第一个匹配到的,不过也可以进行修改
int findPositionByValue(Node* head, int value) {
Node* curr = head->next; // 从第一个数据节点开始找,位置为0
int position = 0;
while (curr) {
if (curr->data == value) {
return position;
}
curr = curr->next;
position++;
}
return -1; // 值不存在
}
// 根据位置查找节点的值
int findValueByPosition(Node* head, int position) {
Node* curr = head->next;
int count = 0;
while (curr && count < position) {
curr = curr->next;
count++;
}
if (curr) {
return curr->data;
} else {
printf("Invalid position.\n");
return -1; // 位置无效
}
}
// 根据位置修改节点的值
void modifyValueByPosition(Node* head, int position, int newValue) {
Node* curr = head->next;
int count = 0;
while (curr && count < position) {
curr = curr->next;
count++;
}
if (curr) {
curr->data = newValue;
} else {
printf("Invalid position.\n");
}
}
// 获取链表长度(即数据节点的个数)
int getLength(Node* head) {
Node* curr = head->next;
int length = 0;
while (curr) {
curr = curr->next;
length++;
}
return length;
}
// 遍历链表
void traverseList(Node* head) {
Node* curr = head->next;
while (curr)
{
printf("%d ", curr->data);
curr = curr->next;
}
printf("\n");
}
// 释放整个链表内存
void freeList(Node* head) {
Node* curr = head->next;
while (curr) {
Node* temp = curr;
curr = curr->next;
free(temp);
}
free(head);
}
// 测试链表操作
int main() {
Node* myList = createList(); // 创建链表,即创建一个头结点
insertNode(myList, 0, 1); //在开头插入1
insertNode(myList, 1, 2);
insertNode(myList, 2, 3);
insertNode(myList, 1, 666);
printf("List: ");
traverseList(myList); // 遍历打印
printf("Length: %d\n", getLength(myList)); // 求链表长度(数据节点个数)
printf("Position of value 3: %d\n", findPositionByValue(myList, 3)); // 按位置查找
printf("Value at position 1: %d\n", findValueByPosition(myList, 1)); // 按值查找
modifyValueByPosition(myList, 1, 888); // 按位置修改
printf("List after modification: ");
traverseList(myList);
deleteNode(myList, 2); // 按位置删除
printf("List after deletion: ");
traverseList(myList);
freeList(myList); // 释放链表内存
return 0;
}
// 不带头结点的链表
#include
#include
// 定义链表节点
typedef struct Node {
int data;
struct Node* next;
} Node;
// 创建链表(是Node*类型)
Node* createList() {
return NULL;
}
// 在链表头部插入节点
Node* insertNode(Node* head, int data) {
Node* newNode = (Node*)malloc(sizeof(Node));
newNode->data = data;
newNode->next = head;
return newNode;
}
// 在指定位置插入节点
// 这个函数也可以实现头插
Node* insertNodeAtPosition(Node* head, int position, int data) {
Node* newNode = (Node*)malloc(sizeof(Node));
newNode->data = data;
newNode->next = NULL;
if (position == 0) {
newNode->next = head;
return newNode;
}
Node* curr = head;
int count = 0;
while (curr && count < position - 1) {
curr = curr->next;
count++;
}
if (curr && count == position - 1) {
newNode->next = curr->next;
curr->next = newNode;
} else {
printf("Invalid position.\n");
free(newNode);
}
return head;
}
// 删除指定位置的节点
Node* deleteNode(Node* head, int position) {
if (position == 0) {
Node* temp = head;
head = head->next;
free(temp);
return head;
}
Node* prev = head;
Node* curr = head->next;
int count = 0;
while (curr && count < position - 1) {
prev = curr;
curr = curr->next;
count++;
}
if (curr && count == position - 1) {
prev->next = curr->next;
free(curr);
} else {
printf("Invalid position.\n");
}
return head;
}
// 根据值查找节点的位置
int findPositionByValue(Node* head, int value) {
Node* curr = head;
int position = 0;
while (curr) {
if (curr->data == value) {
return position;
}
curr = curr->next;
position++;
}
return -1; // 值不存在
}
// 根据位置查找节点的值
int findValueByPosition(Node* head, int position) {
Node* curr = head;
int count = 0;
while (curr && count < position) {
curr = curr->next;
count++;
}
if (curr && count == position) {
return curr->data;
} else {
printf("Invalid position.\n");
return -1; // 位置无效
}
}
// 根据位置修改节点的值
void modifyValueByPosition(Node* head, int position, int newValue) {
Node* curr = head;
int count = 0;
while (curr && count < position) {
curr = curr->next;
count++;
}
if (curr && count == position) {
curr->data = newValue;
} else {
printf("Invalid position.\n");
}
}
// 获取链表长度
int getLength(Node* head) {
Node* curr = head;
int length = 0;
while (curr) {
curr = curr->next;
length++;
}
return length;
}
// 遍历链表
void traverseList(Node* head) {
Node* curr = head;
while (curr) {
printf("%d ", curr->data);
curr = curr->next;
}
printf("\n");
}
// 释放链表内存
void freeList(Node* head) {
Node* curr = head;
while (curr) {
Node* temp = curr;
curr = curr->next;
free(temp);
}
}
// 测试链表操作
int main() {
Node* myList = createList();
myList = insertNode(myList, 1); // 头插
myList = insertNode(myList, 2);
myList = insertNode(myList, 3);
myList=insertNodeAtPosition(myList, 1, 666);
myList=insertNodeAtPosition(myList, 0, 888); // 也可以实现头
printf("List: ");
traverseList(myList);
printf("Length: %d\n", getLength(myList));
printf("Position of value 2: %d\n", findPositionByValue(myList, 2));
printf("Value at position 1: %d\n", findValueByPosition(myList, 1));
modifyValueByPosition(myList, 1, 4);
printf("List after modification: ");
traverseList(myList);
myList = deleteNode(myList, 2);
printf("List after deletion: ");
traverseList(myList);
freeList(myList); // 释放链表内存
return 0;
}
双向链表(Doubly Linked List)是一种常见的数据结构,它允许在链表中的节点之间进行双向遍历。相比于单向链表,双向链表在每个节点中保存了两个指针,分别指向前一个节点和后一个节点。这使得在双向链表中进行插入、删除和遍历操作更加高效灵活。
双向链表由一系列节点组成,每个节点包含两个重要的部分:数据和指针。数据部分用于存储节点所需的信息,而指针部分用于指向前一个节点和后一个节点。在双向链表中,第一个节点被称为头节点,最后一个节点被称为尾节点。头节点的前指针为空,尾节点的后指针为空。
与单链表相比,双向链表的主要优势是在插入、删除和遍历方面具有更高的效率和更大的灵活性。下面是与单链表相比的一些区别和优势:
双向遍历:双向链表可以从任意一个节点开始,沿着前指针或后指针进行遍历,而单链表只能从头节点开始单向遍历。这使得双向链表在需要从后往前遍历、查找或操作链表的情况下更加方便。
插入和删除操作效率更高:在双向链表中,插入和删除节点只需要修改相邻节点的指针,而在单链表中,为了插入或删除一个节点,需要找到前一个节点并修改其后指针。这使得双向链表的插入和删除操作更高效,特别是在已知节点位置的情况下,其时间复杂度为O(1)
,而单链表则需要O(n)的时间复杂度。
反转链表更方便:由于双向链表中每个节点都存储了前一个节点和后一个节点的指针,因此在双向链表中进行链表反转操作更加方便。只需交换每个节点的前指针和后指针即可实现链表的反转。而在单链表中,为了反转链表,需要使用额外的指针或递归操作,使得实现更加复杂。
更多的灵活性:双向链表的每个节点都存储了前一个节点和后一个节点的指针,这使得在插入、删除和修改节点时更加灵活。例如,在双向链表中删除一个节点时,只需修改前后节点的指针即可,而在单链表中删除一个节点则需要找到前一个节点并修改其后指针。双向链表的这种灵活性可以使得操作更加简单和高效。
然而,与单链表相比,双向链表需要额外的内存开销来存储前指针,这增加了内存的使用量。另外,双向链表的实现可能稍微复杂一些,因为需要确保前指针和后指针的正确性。因此,在具体应用中,需要根据实际需求和性能要求来选择使用单链表还是双向链表。
双向链表在许多情况下都有广泛的应用,以下是一些双向链表的常见用处:
高效的插入和删除:相比于数组,双向链表的插入和删除操作更加高效。在双向链表中,插入和删除一个节点只需要修改相邻节点的指针,而不需要像数组那样进行元素的搬移。这使得双向链表在需要频繁插入和删除操作的场景中非常有用。
双向遍历:双向链表允许从任意一个节点开始,沿着前指针或后指针进行遍历。这使得在某些情况下,从前往后和从后往前遍历链表都很方便。例如,用于实现浏览器的前进和后退功能时,可以使用双向链表存储浏览历史记录,用户可以通过双向遍历来导航。
缓存实现:双向链表常用于实现缓存。缓存是一种用于加速数据访问的机制,常见的缓存算法如LRU
(Least Recently Used)依赖于双向链表的特性。LRU缓存使用双向链表来维护最近使用的数据项顺序,当缓存满时,删除链表尾部的数据项,将新的数据项插入链表头部。这样可以快速定位到最近使用的数据项,并在O(1)的时间复杂度内进行插入和删除操作。
实现其他数据结构:双向链表是其他复杂数据结构的基础。例如,双向链表常用于实现栈(双向链表头部作为栈顶)、队列(双向链表头部作为队列头)、哈希表中的链表桶等。在这些数据结构中,双向链表的灵活性和高效性使得它成为理想的选择。
图形算法:在图形算法中,双向链表可用于存储图的邻接表。每个节点表示一个图的顶点,通过前指针和后指针与其他相邻的顶点建立连接。这样可以方便地访问一个顶点的邻居顶点,从而实现图的遍历和其他图形算法。
(1)插入操作:
(2)删除操作:
(3)查找操作:
(4) 反转链表:
(5) 获取链表长度:
(6)遍历操作:
// 带头结点的双向老板
#include
#include
// 定义双向链表节点结构
typedef struct Node {
int data;
struct Node* prev;
struct Node* next;
} Node;
// 初始化双向链表,即创建头结点
void initializeList(Node** head) {
*head = (Node*)malloc(sizeof(Node));
(*head)->data = 0;
(*head)->prev = NULL;
(*head)->next = NULL;
}
// 在链表头部插入节点
void insertAtHead(Node* head, int value) {
Node* newNode = (Node*)malloc(sizeof(Node));
newNode->data = value;
newNode->prev = head;
newNode->next = head->next;
if (head->next != NULL) {
head->next->prev = newNode;
}
head->next = newNode;
}
// 在链表尾部插入节点
void insertAtTail(Node* head, int value) {
Node* newNode = (Node*)malloc(sizeof(Node));
newNode->data = value;
newNode->prev = NULL;
newNode->next = NULL;
Node* current = head;
while (current->next != NULL) {
current = current->next;
}
current->next = newNode;
newNode->prev = current;
}
// 在指定位置插入节点
void insertAtPosition(Node* head, int value, int position) {
Node* newNode = (Node*)malloc(sizeof(Node));
newNode->data = value;
newNode->prev = NULL;
newNode->next = NULL;
Node* current = head;
int currentPosition = 0;
while (current != NULL && currentPosition < position) {
current = current->next;
currentPosition++;
}
if (current == NULL) {
printf("Invalid position!\n");
free(newNode);
return;
}
newNode->prev = current->prev;
newNode->next = current;
current->prev->next = newNode;
current->prev = newNode;
}
// 删除头节点
void deleteAtHead(Node* head) {
if (head->next == NULL) {
printf("List is empty!\n");
return;
}
Node* toDelete = head->next;
head->next = toDelete->next;
if (toDelete->next != NULL) {
toDelete->next->prev = head;
}
free(toDelete);
}
// 删除尾节点
void deleteAtTail(Node* head) {
if (head->next == NULL) {
printf("List is empty!\n");
return;
}
Node* current = head;
while (current->next != NULL) {
current = current->next;
}
current->prev->next = NULL;
free(current);
}
// 删除指定位置的节点
void deleteAtPosition(Node* head, int position) {
if (head->next == NULL) {
printf("List is empty!\n");
return;
}
Node* current = head;
int currentPosition = 0;
while (current->next != NULL && currentPosition < position) {
current = current->next;
currentPosition++;
}
if (current->next == NULL) {
printf("Invalid position!\n");
return;
}
Node* toDelete = current->next;
current->next = toDelete->next;
if (toDelete->next != NULL) {
toDelete->next->prev = current;
}
free(toDelete);
}
// 打印链表
void printList(Node* head) {
Node* current = head->next;
while (current != NULL) {
printf("%d ", current->data);
current = current->next;
}
printf("\n");
}
int main() {
Node* head;
initializeList(&head);
insertAtHead(head, 2);
insertAtHead(head, 1);
insertAtTail(head, 3);
insertAtPosition(head, 4, 2);
printf("List: ");
printList(head);
deleteAtHead(head);
deleteAtTail(head);
deleteAtPosition(head, 1);
printf("List after deletion: ");
printList(head);
return 0;
}
// 不带头结点的双向链表
#include
#include
// 定义双向链表节点结构
typedef struct Node {
int data;
struct Node* prev;
struct Node* next;
} Node;
// 初始化双向链表
Node* initializeList() {
return NULL;
}
// 在链表头部插入节点
Node* insertAtHead(Node* head, int value) {
Node* newNode = (Node*)malloc(sizeof(Node));
newNode->data = value;
newNode->prev = NULL;
newNode->next = head;
if (head != NULL) {
head->prev = newNode;
}
return newNode;
}
// 在链表尾部插入节点
Node* insertAtTail(Node* head, int value) {
Node* newNode = (Node*)malloc(sizeof(Node));
newNode->data = value;
newNode->prev = NULL;
newNode->next = NULL;
if (head == NULL) {
return newNode;
}
Node* current = head;
while (current->next != NULL) {
current = current->next;
}
current->next = newNode;
newNode->prev = current;
return head;
}
// 在指定位置插入节点
Node* insertAtPosition(Node* head, int value, int position) {
Node* newNode = (Node*)malloc(sizeof(Node));
newNode->data = value;
newNode->prev = NULL;
newNode->next = NULL;
if (position <= 0) {
newNode->next = head;
if (head != NULL) {
head->prev = newNode;
}
return newNode;
}
Node* current = head;
int currentPosition = 0;
while (current != NULL && currentPosition < position) {
current = current->next;
currentPosition++;
}
if (current == NULL) {
printf("Invalid position!\n");
free(newNode);
return head;
}
newNode->prev = current->prev;
newNode->next = current;
if (current->prev != NULL) {
current->prev->next = newNode;
}
current->prev = newNode;
if (currentPosition == 0) {
return newNode;
}
return head;
}
// 删除头节点
Node* deleteAtHead(Node* head) {
if (head == NULL) {
printf("List is empty!\n");
return NULL;
}
Node* toDelete = head;
head = head->next;
if (head != NULL) {
head->prev = NULL;
}
free(toDelete);
return head;
}
// 删除尾节点
Node* deleteAtTail(Node* head) {
if (head == NULL) {
printf("List is empty!\n");
return NULL;
}
if (head->next == NULL) {
free(head);
return NULL;
}
Node* current = head;
while (current->next != NULL) {
current = current->next;
}
current->prev->next = NULL;
free(current);
return head;
}
// 删除指定位置的节点
Node* deleteAtPosition(Node* head, int position) {
if (head == NULL) {
printf("List is empty!\n");
return NULL;
}
if (position <= 0) {
Node* toDelete = head;
head = head->next;
if (head != NULL) {
head->prev = NULL;
}
free(toDelete);
return head;
}
Node* current = head;
int currentPosition = 0;
while (current != NULL && currentPosition < position) {
current = current->next;
currentPosition++;
}
if (current == NULL) {
printf("Invalid position!\n");
return head;
}
current->prev->next = current->next;
if (current->next != NULL) {
current->next->prev = current->prev;
}
free(current);
return head;
}
// 打印链表
void printList(Node* head) {
Node* current = head;
while (current != NULL) {
printf("%d ", current->data);
current = current->next;
}
printf("\n");
}
int main() {
Node* head = initializeList();
head = insertAtHead(head, 2);
head = insertAtHead(head, 1);
head = insertAtTail(head, 3);
head = insertAtPosition(head, 4, 2);
printf("List: ");
printList(head);
head = deleteAtHead(head);
head = deleteAtTail(head);
head = deleteAtPosition(head, 1);
printf("List after deletion: ");
printList(head);
return 0;
}
循环链表是一种特殊类型的链表,与常规链表不同之处在于,循环链表的尾节点指向头节点,形成一个闭环。这意味着循环链表中的每个节点都有一个指针指向下一个节点,并且最后一个节点指向第一个节点。这种特性使得循环链表可以像常规链表一样遍历和访问节点,同时也可以更方便地实现循环操作。
循环链表可以分为两种类型:单向循环链表和双向循环链表。单向循环链表中的节点只包含一个指向下一个节点的指针,而双向循环链表中的节点则包含一个指向前一个节点和一个指向下一个节点的指针。
单向循环链表:
在用头指针指示的循环单链表中,找到开始结点的时间是 O(1),然而要找到终端结点,则需从头指针开始遍历整个链表,其时间是 O(n)。在很多实际问题中,操作是在表的首或尾两端进行的,此时头指针指示的循环单链表就显得不够方便。如果改用指向终端结点的尾指针(rearpointer)来指示循环单链表,则查找开始结点和终端结点都很方便,它们的存储地址分别是(rear -> next)-> next和 rear,显然,时间都是O(1)。因此,实际应用中多采用尾指针指示的循环单链表。
双向循环链表:
循环链表的特点和优势:
循环链表的基本操作包括以下几种:
循环链表由于其特殊的闭环结构,可以在许多场景下提供便利和高效的解决方案。以下是一些常见的应用场景:
缓冲区:循环链表可以用于实现缓冲区(buffer),其中数据按照循环方式存储和访问。例如,在音频或视频流处理中,可以使用循环链表来存储连续的数据块,以实现循环缓冲区的功能。
线程调度:在操作系统中,循环链表可以用于实现进程或线程的调度算法。每个进程或线程可以表示为链表中的一个节点,调度算法可以根据一定的策略在各个节点之间进行切换。
约瑟夫问题:约瑟夫问题是一个经典的数学问题,涉及到一组人围成一个圆圈报数,每报到特定数目的人就被淘汰,直到只剩下最后一个人。循环链表可以用于解决约瑟夫问题,通过不断删除节点来模拟淘汰过程。
循环队列:循环队列是一种基于循环链表的数据结构,用于解决队列元素的循环利用问题。它在一端插入元素,在另一端删除元素,并且可以循环利用队列空间,提高存储效率。
轮播图:在网页设计中,循环链表可以用于实现轮播图效果,其中每个图片或广告可以表示为链表中的一个节点,通过不断循环遍历链表来实现图片的轮播展示。
密码学:在密码学中,循环链表可以用于实现一些加密算法,例如循环移位密码(Caesar Cipher)或循环替换密码(Polyalphabetic Cipher)等。
这只是循环链表应用的一小部分示例,实际上,在许多需要循环或环形结构的场景中,循环链表都可以提供简洁、高效的解决方案。
单向循环链表、双向循环链表,各自又分为带不带头结点2种。
#include
#include
// 定义链表节点结构体
typedef struct Node {
int data;
struct Node* next;
} Node;
// 初始化链表,创建头节点
Node* initializeList() {
Node* head = (Node*)malloc(sizeof(Node));
head->next = head; // 头节点指向自身形成循环
return head;
}
// 在链表尾部插入节点
void insertAtTail(Node* head, int value) {
Node* newNode = (Node*)malloc(sizeof(Node));
newNode->data = value;
newNode->next = head->next; // 新节点指向头节点
head->next = newNode; // 头节点指向新节点
head = newNode; // 更新头节点
}
// 在指定位置插入节点
void insertAtPosition(Node* head, int value, int position) {
Node* newNode = (Node*)malloc(sizeof(Node));
newNode->data = value;
Node* current = head;
int currentPosition = 0;
while (current->next != head && currentPosition < position - 1) {
current = current->next;
currentPosition++;
}
newNode->next = current->next;
current->next = newNode;
}
// 删除指定位置的节点
void deleteAtPosition(Node* head, int position) {
Node* current = head;
Node* toDelete = NULL;
int currentPosition = 0;
while (current->next != head && currentPosition < position - 1) {
current = current->next;
currentPosition++;
}
toDelete = current->next;
current->next = toDelete->next;
free(toDelete);
}
// 打印链表
void printList(Node* head) {
Node* current = head->next;
while (current != head) {
printf("%d ", current->data);
current = current->next;
}
printf("\n");
}
int main() {
Node* head = initializeList();
insertAtTail(head, 1);
insertAtTail(head, 2);
insertAtTail(head, 3);
insertAtPosition(head, 4, 2);
printf("List: ");
printList(head);
deleteAtPosition(head, 1);
printf("List after deletion: ");
printList(head);
return 0;
}
#include
#include
// 定义链表节点结构体
typedef struct Node {
int data;
struct Node* next;
} Node;
// 初始化链表,创建第一个节点
Node* initializeList(int value) {
Node* head = (Node*)malloc(sizeof(Node));
head->data = value;
head->next = head; // 第一个节点指向自身形成循环
return head;
}
// 在链表尾部插入节点
void insertAtTail(Node* head, int value) {
Node* newNode = (Node*)malloc(sizeof(Node));
newNode->data = value;
newNode->next = head; // 新节点指向头节点
Node* current = head;
while (current->next != head) {
current = current->next;
}
current->next = newNode; // 将新节点连接到尾节点之后
}
// 在指定位置插入节点
void insertAtPosition(Node* head, int value, int position) {
Node* newNode = (Node*)malloc(sizeof(Node));
newNode->data = value;
Node* current = head;
int currentPosition = 0;
while (current->next != head && currentPosition < position - 1) {
current = current->next;
currentPosition++;
}
newNode->next = current->next;
current->next = newNode;
}
// 删除指定位置的节点
void deleteAtPosition(Node* head, int position) {
Node* current = head;
Node* toDelete = NULL;
int currentPosition = 0;
while (current->next != head && currentPosition < position - 1) {
current = current->next;
currentPosition++;
}
toDelete = current->next;
current->next = toDelete->next;
free(toDelete);
}
// 打印链表
void printList(Node* head) {
Node* current = head;
do {
printf("%d ", current->data);
current = current->next;
} while (current != head);
printf("\n");
}
int main() {
Node* head = initializeList(1);
insertAtTail(head, 2);
insertAtTail(head, 3);
insertAtPosition(head, 4, 2);
printf("List: ");
printList(head);
deleteAtPosition(head, 1);
printf("List after deletion: ");
printList(head);
return 0;
}
#include
#include
// 定义链表节点结构体
typedef struct Node {
int data;
struct Node* prev;
struct Node* next;
} Node;
// 初始化链表,创建头节点
Node* initializeList() {
Node* head = (Node*)malloc(sizeof(Node));
head->prev = head;
head->next = head; // 头节点的前后指针都指向自身形成循环
return head;
}
// 在链表尾部插入节点
void insertAtTail(Node* head, int value) {
Node* newNode = (Node*)malloc(sizeof(Node));
newNode->data = value;
newNode->prev = head->prev;
newNode->next = head;
head->prev->next = newNode; // 新节点插入到尾节点之后
head->prev = newNode; // 更新尾节点
}
// 在指定位置插入节点
void insertAtPosition(Node* head, int value, int position) {
Node* newNode = (Node*)malloc(sizeof(Node));
newNode->data = value;
Node* current = head;
int currentPosition = 0;
while (current->next != head && currentPosition < position) {
current = current->next;
currentPosition++;
}
newNode->prev = current;
newNode->next = current->next;
current->next->prev = newNode;
current->next = newNode;
}
// 删除指定位置的节点
void deleteAtPosition(Node* head, int position) {
Node* current = head;
Node* toDelete = NULL;
int currentPosition = 0;
while (current->next != head && currentPosition < position) {
current = current->next;
currentPosition++;
}
toDelete = current->next;
current->next = toDelete->next;
toDelete->next->prev = current;
free(toDelete);
}
// 打印链表
void printList(Node* head) {
Node* current = head->next;
while (current != head) {
printf("%d ", current->data);
current = current->next;
}
printf("\n");
}
int main() {
Node* head = initializeList();
insertAtTail(head, 1);
insertAtTail(head, 2);
insertAtTail(head, 3);
insertAtPosition(head, 4, 2);
printf("List: ");
printList(head);
deleteAtPosition(head, 1);
printf("List after deletion: ");
printList(head);
return 0;
}
#include
#include
typedef struct Node {
int data;
struct Node* prev;
struct Node* next;
} Node;
Node* initializeList(int value) {
Node* head = (Node*)malloc(sizeof(Node));
head->data = value;
head->prev = head;
head->next = head;
return head;
}
void insertAtTail(Node* head, int value) {
Node* newNode = (Node*)malloc(sizeof(Node));
newNode->data = value;
newNode->prev = head->prev;
newNode->next = head;
head->prev->next = newNode;
head->prev = newNode;
}
void insertAtPosition(Node* head, int value, int position) {
Node* newNode = (Node*)malloc(sizeof(Node));
newNode->data = value;
Node* current = head->next;
int currentPosition = 0;
while (current != head && currentPosition < position) {
current = current->next;
currentPosition++;
}
newNode->next = current;
newNode->prev = current->prev;
current->prev->next = newNode;
current->prev = newNode;
}
void deleteAtPosition(Node* head, int position) {
Node* current = head->next;
int currentPosition = 0;
while (current != head && currentPosition < position) {
current = current->next;
currentPosition++;
}
if (current != head) {
current->prev->next = current->next;
current->next->prev = current->prev;
free(current);
}
}
void printList(Node* head) {
Node* current = head->next;
while (current != head) {
printf("%d ", current->data);
current = current->next;
}
printf("\n");
}
int main() {
Node* head = initializeList(1);
printf("初始化OK");
insertAtTail(head, 2);
insertAtTail(head, 3);
insertAtPosition(head, 4, 2);
printf("List: ");
printList(head);
deleteAtPosition(head, 1);
printf("List after deletion: ");
printList(head);
return 0;
}
要反转单链表,可以使用迭代或递归的方法。
下面是使用迭代方法反转单链表的示例代码(带不带头结点,自己稍微处理一下):
Node* reverseList(Node* head) {
Node* prev = NULL;
Node* current = head;
Node* next = NULL;
while (current != NULL) {
next = current->next;
current->next = prev;
prev = current;
current = next;
}
return prev; // prev指向原链表的最后一个节点,现在是反转后链表的头节点
}
下面是使用递归方法反转单链表的示例代码:
Node* reverseList(Node* head) {
if (head == NULL || head->next == NULL) {
return head;
}
Node* newHead = reverseList(head->next);
head->next->next = head;
head->next = NULL;
return newHead;
}
要反转双向链表,可以使用类似的迭代或递归的方法。
下面是使用迭代方法反转双向链表的示例代码:
Node* reverseList(Node* head) {
Node* prev = NULL;
Node* current = head;
Node* next = NULL;
while (current != NULL) {
next = current->next;
current->next = prev;
current->prev = next;
prev = current;
current = next;
}
return prev; // prev指向原链表的最后一个节点,现在是反转后链表的头节点
}
下面是使用递归方法反转双向链表的示例代码:
Node* reverseList(Node* head) {
if (head == NULL || head->next == NULL) {
return head;
}
Node* newHead = reverseList(head->next);
head->next->prev = head->next->next;
head->next->next = head;
head->prev = head->next;
head->next = NULL;
return newHead;
}
通过迭代或递归方法,可以有效地反转双向链表。根据具体情况选择合适的方法进行实现。
循环链表一般不需要反转。
应用:
- 数据检索;
- 数据展示;
- 数据去重;
- 数据统计;
要对单向链表进行排序,常见的方法是使用排序算法,如冒泡排序、插入排序、选择排序、归并排序或快速排序。这些排序算法可以通过对链表节点之间的值进行比较和交换来实现排序。
以下是使用归并排序对单向链表进行排序的示例代码:
// 定义链表节点结构体
typedef struct Node {
int data;
struct Node* next;
} Node;
// 获取链表的中间节点
Node* getMiddle(Node* head) {
if (head == NULL)
return head;
Node* slow = head;
Node* fast = head;
while (fast->next != NULL && fast->next->next != NULL) {
slow = slow->next;
fast = fast->next->next;
}
return slow;
}
// 合并两个有序链表
Node* merge(Node* head1, Node* head2) {
if (head1 == NULL)
return head2;
if (head2 == NULL)
return head1;
Node* mergedHead = NULL;
if (head1->data <= head2->data) {
mergedHead = head1;
mergedHead->next = merge(head1->next, head2);
} else {
mergedHead = head2;
mergedHead->next = merge(head1, head2->next);
}
return mergedHead;
}
// 归并排序
Node* mergeSort(Node* head) {
if (head == NULL || head->next == NULL)
return head;
Node* middle = getMiddle(head);
Node* nextToMiddle = middle->next;
middle->next = NULL;
Node* left = mergeSort(head);
Node* right = mergeSort(nextToMiddle);
return merge(left, right);
}
使用归并排序时,首先通过 getMiddle
函数找到链表的中间节点,然后将链表分为两部分。接下来递归地对两部分链表进行排序,然后使用 merge
函数合并排序后的链表。最后返回排序后的链表。
请注意,在使用归并排序时,可能需要使用递归方法,因此要注意对于大型链表可能导致栈溢出。在实际使用中,可以根据链表的长度和具体需求选择适当的排序算法。
约瑟夫环(Josephus problem)是一个经典的数学问题,它得名于古代历史学家和数学家约瑟夫斯(Flavius Josephus)。问题的描述如下:
有 n 个人(编号为 1 到 n)围成一个圆圈,从某个人开始顺时针报数,报到 m 的人出列,然后从下一个人重新开始报数,直到所有人都出列。求最后剩下的人的编号。
例如,假设有 7 个人围成一个圆圈,报数到 3 的人出列,那么出列的顺序是 3、6、2、7、5、1,最后剩下的人的编号是 4。
约瑟夫环问题可以使用递归或数学推导来解决。通过推导可以得到一个递推公式:
f ( n , m ) = ( f ( n − 1 , m ) + m ) f(n, m) = (f(n-1, m) + m) % n f(n,m)=(f(n−1,m)+m)
其中 f(n, m) 表示 n 个人报数到 m 时最后剩下的人的编号。
通过递归或循环计算,可以解决约瑟夫环问题。这个问题在计算机科学和数学领域具有一定的研究和应用价值,同时也是一个有趣的智力游戏和谜题。
typedef struct Node {
int data;
struct Node* next;
} Node;
Node* createCircularList(int n) {
Node* head = NULL;
Node* tail = NULL;
for (int i = 1; i <= n; i++) {
Node* newNode = (Node*)malloc(sizeof(Node));
newNode->data = i;
newNode->next = NULL;
if (head == NULL) {
head = newNode;
tail = newNode;
} else {
tail->next = newNode;
tail = newNode;
}
}
tail->next = head; // 将最后一个节点的 next 指针指向头节点,形成循环
return head;
}
int josephusCircle(Node* head, int m) {
Node* current = head;
Node* prev = NULL;
while (current->next != current) {
// 报数 m-1 次,找到要出列的节点
for (int i = 0; i < m - 1; i++) {
prev = current;
current = current->next;
}
// 删除当前节点
prev->next = current->next;
Node* toDelete = current;
current = prev->next;
free(toDelete);
}
return current->data; // 返回最后剩下的节点的编号
}
int main() {
int n = 7; // 人数
int m = 3; // 报数值
Node* head = createCircularList(n);
int lastPerson = josephusCircle(head, m);
printf("The last person remaining is %d\n", lastPerson);
return 0;
}
块状链表(Block-linked list),用于高效地管理大量小对象的分配和释放。它的设计目标是减少内存碎片化,并提高分配和释放操作的性能。
在传统的链表数据结构中,每个节点都包含一个指向下一个节点的指针。而在块状链表中,节点被组织成固定大小的块,每个块包含多个节点。每个块都有一个指向下一个块的指针,形成了一个链表结构。块状链表通常使用位图或其他方法来跟踪节点的使用状态。
简单来说就是这样:
typedef struct Block {
Node nodes[BLOCK_SIZE];
struct Block* next;
} Block;
块状链表的主要优点是减少了指针的数量,从而节省了内存空间。由于块中包含多个节点,节点之间的空间利用率更高,减少了内存碎片化的问题。此外,由于块内节点的连续存储,对于分配和释放操作,可以更高效地操作整个块,而不是单个节点,提高了性能。
块状链表在许多应用中都有广泛的使用,特别是在嵌入式系统、操作系统内存管理以及高性能计算等领域。它们通常用于管理大量的小对象,例如文件系统中的磁盘块、内存管理中的页表等。
需要注意的是,块状链表的设计和实现相对复杂,需要处理块的分配、释放、合并等操作。这些操作的具体实现可能因不同的应用和要求而有所不同。
#include
#include
#define BLOCK_SIZE 4 // 每个块中节点的数量
// 定义节点结构体
typedef struct Node {
int data;
struct Node* next;
} Node;
// 定义块结构体
typedef struct Block {
Node nodes[BLOCK_SIZE];
struct Block* next;
} Block;
// 初始化块
Block* initializeBlock() {
Block* block = (Block*)malloc(sizeof(Block));
block->next = NULL;
return block;
}
// 从块中分配一个节点
Node* allocateNode(Block* block) {
for (int i = 0; i < BLOCK_SIZE; i++) {
if (block->nodes[i].next == NULL) {
return &(block->nodes[i]);
}
}
return NULL; // 没有可用的节点
}
// 释放节点到块中
void deallocateNode(Block* block, Node* node) {
node->next = NULL;
}
// 向链表中插入节点
void insertNode(Block** head, int value) {
Block* currentBlock = *head;
Node* newNode = NULL;
while (currentBlock != NULL) {
newNode = allocateNode(currentBlock);
if (newNode != NULL) {
break;
}
currentBlock = currentBlock->next;
}
if (newNode == NULL) {
// 没有可用的节点,需要分配新的块
Block* newBlock = initializeBlock();
newNode = allocateNode(newBlock);
// 将新的块插入链表
newBlock->next = *head;
*head = newBlock;
}
newNode->data = value;
}
// 打印链表
void printList(Block* head) {
Block* currentBlock = head;
Node* currentNode = NULL;
while (currentBlock != NULL) {
for (int i = 0; i < BLOCK_SIZE; i++) {
currentNode = &(currentBlock->nodes[i]);
if (currentNode == NULL) {
break;
}
printf("%d ", currentNode->data);
}
currentBlock = currentBlock->next;
}
printf("\n");
}
// 释放链表内存
void freeList(Block* head) {
Block* currentBlock = head;
Block* nextBlock = NULL;
Node* currentNode = NULL;
while (currentBlock != NULL) {
nextBlock = currentBlock->next;
for (int i = 0; i < BLOCK_SIZE; i++) {
currentNode = &(currentBlock->nodes[i]);
currentNode->next = NULL;
}
free(currentBlock);
currentBlock = nextBlock;
}
}
int main() {
Block* head = initializeBlock();
insertNode(&head, 1);
insertNode(&head, 2);
insertNode(&head, 3);
insertNode(&head, 4);
insertNode(&head, 5);
printf("List: ");
printList(head);
freeList(head);
return 0;
}
把 永 远 爱 你 写 进 诗 的 结 尾 ~