赞
踩
目录
单链表(Singly linked list)是一种常见的数据结构,它由一个结点(Node)的链表构成,每个结点包含两个域:数据域和指针域。数据域用于存储结点的数据,指针域用于存储下一个结点的地址。
单链表中的第一个结点被称为头结点(Head),它用来存放链表的基地址。头结点中的指针域指向链表的第一个结点,最后一个结点的指针域指向空地址,表示链表结束。
下面是一个单链表结点的定义:
- typedef int DataType;
-
- typedef struct ListNode
- {
- DataType data; // 数据域,存储结点的数据
- struct ListNode* next; // 指针域,指向下一个结点
- }ListNode;
值得注意的是,单链表是一种动态数据结构,可以在运行时动态添加或删除结点,也可以在任何位置插入或删除结点,因为每个结点的指针域储存的是下一个结点的地址,所以链表的结构是相对灵活的,有着较强的扩展性。
链表是动态数据结构:链表的长度可以根据实际需要进行动态调整,可以在运行时添加或删除结点。
链表的插入和删除操作高效:在链表中插入或删除结点的操作时间复杂度为O(1),只需要修改指针的指向即可,而不需要像数组那样需要移动其他元素。
链表对内存的利用率较低:链表中的结点是通过指针连接的,需要额外的内存空间来存储指针。
链表的访问操作相对较慢:由于链表中的结点不是连续存储的,因此无法像数组一样通过下标直接访问元素,需要从头结点开始遍历链表,直到找到目标结点。
链表的结构灵活:可以在链表中间插入或删除结点,只需修改指针的指向即可。而数组的长度是固定的,插入或删除元素需要移动其他元素。
链表适用于频繁插入和删除的场景:由于链表插入和删除的时间复杂度为O(1),相比数组,链表更适合频繁插入和删除结点的情况。
需要注意的是,链表的访问操作时间复杂度为O(n),其中n是链表的长度。因此,在需要频繁根据索引访问元素的情况下,数组可能更加合适。而链表适用于需要动态调整长度、频繁插入和删除元素的场景。
单链表的初始化即构造一个仅包含头结点的空单链表。其过程是首先申请一个结点并让指针 head指向该结点,然后将它的指针域赋为空(NULL),最后返回头指针 head。
首先我们先了解下什么是头节点以及为什么要设置头节点:
方便操作空链表:初始时,链表是空的,没有任何结点。如果没有头结点,链表的第一个结点就是空链表。通过设置头结点,即使链表为空,也有了一个起始的位置,可以方便地进行插入和删除操作。
简化插入和删除操作:有了头结点,插入和删除操作可以统一处理。无论在链表的头部、中间还是尾部插入或删除结点,操作逻辑都是一致的。通过将头结点作为起点,可以避免处理边界情况的复杂性。
方便获取链表长度:头结点为链表提供了一个便捷的起点,从头结点开始遍历链表,可以方便地计算链表的长度。
方便遍历操作:通过头结点,从链表的第一个结点开始遍历整个链表,可以避免操作和管理指针时的边界情况。
注意:头结点不存储实际的数据,仅作为链表的起点,其下一个结点指向链表的第一个有效节点。因此,在使用链表时,需要特别注意头结点的存在,并在涉及到插入、删除和遍历操作时进行处理。
由此可见,设置头节点可以简化部分操作。当然不设置头节点也是可以的,但是博主建议大家在学习单链表的初期尽量使用带头节点的单链表,等掌握熟练后自己再尝试不包含头节点的单链表。
- // 初始化单链表
- ListNode* InitList()
- {
- // 创建头结点,并进行内存分配
- ListNode* head = (ListNode*)malloc(sizeof(ListNode));
-
- // 检查内存分配是否成功
- if (head == NULL)
- {
- perror("malloc fail");
- exit(-1);
- }
-
- // 将头结点的指针域置空
- head->next = NULL;
-
- return head;
- }
链表与顺序表不同,它是一种动态管理的存储结构,链表中的每个结点占用的存储空间不是预先分配,而是运行时系统根据需求生成的。因此,建立在初始化链表后,建立线性链表从空表开始,每读入有效的数据则申请一个结点s,并将读取的数据存放到新结点s的数据域中,然后将新结点插入到当前链表头结点head之后,直到循环结束为止。
注意:代码中head为二级指针,即链表头结点的地址。
*head
是一个解引用操作,意味着它获取了 head
指针所指向的值。在这种情况下,head
是一个指向链表头结点指针的指针,通过 *head
可以获取到链表头结点指针的值。
通过 *head
可以访问并修改链表的头指针。在 AddListHead
函数中,(*head)->next
用于获取头结点的下一个节点的指针,而 (*head)->next = newnode
则是将新节点的地址赋值给头结点的下一个节点指针,从而实现在链表头部插入新节点的操作。
- void CreateListHead(ListNode** head)
- {
- assert(head && *head);
- int i, n, val;
-
- // 获取要增加的节点数
- printf("需要增加多少个节点:");
- scanf("%d", &n);
-
- // 循环增加新节点
- for (i = 0; i < n; i++)
- {
- // 获取用户输入的节点值
- printf("请输入第 %d 个节点的数据:", i + 1);
- scanf("%d", &val);
-
- // 创建新节点,并进行内存分配检查
- ListNode* newnode = (ListNode*)malloc(sizeof(ListNode));
-
- // 检查内存分配是否成功
- if (newnode == NULL)
- {
- perror("malloc fail");
- exit(-1);
- }
-
- // 将节点值存储在新节点中
- newnode->data = val;
-
- // 将新节点插入到链表头部:将新节点的下一个节点指向当前头节点的下一个节点
- // 然后将新节点设为头节点的下一个节点
- newnode->next = (*head)->next;
- (*head)->next = newnode;
- }
- }
头插法建立链表虽然算法简单易理解,但生成的链表中结点的次序和原输入的次序相反。而尾插法建立链表可实现次序的一致,该算法依旧从空表开始,但需增加一个尾指针last,使其指向当前链表的尾结点。其过程是:每读入有效的数据则申请一个结点s,并将读取的数据存放到新结点s的数据域中,将s的尾指针设为空指针(NULL),然后将新结点插入到当前链表尾部(last 指针所指的结点后面),直到循环结束为止。
- void CreateListLast(ListNode** head)
- {
- assert(head && *head);
-
- int i, n, val;
-
- // 获取要增加的节点数
- printf("需要增加多少个节点:");
- scanf("%d", &n);
-
- // 创建临时指针并指向链表的头节点
- ListNode* temp = *head;
-
- // 移动到链表的最后一个节点
- while (temp->next)
- {
- temp = temp->next;
- }
-
- // 循环增加新节点
- for (i = 0; i < n; i++)
- {
- // 获取用户输入的节点值
- printf("请输入第 %d 个节点的数据:", i + 1);
- scanf("%d", &val);
-
- // 创建新节点,并进行内存分配检查
- ListNode* newnode = (ListNode*)malloc(sizeof(ListNode));
-
- // 检查内存分配是否成功
- if (newnode == NULL)
- {
- perror("malloc fail");
- exit(-1);
- }
-
- // 将节点值存储在新节点中
- newnode->data = val;
- newnode->next = NULL;
-
- // 将新节点连接到链表的最后一个节点,并更新最后一个节点为新节点
- temp->next = newnode;
- temp = newnode;
- }
- }
注意:头节点不计入链表长度。
- int LengthList(ListNode* head)
- {
- // 检查头结点是否为空
- assert(head);
-
- // 初始化指针p,指向链表的第一个节点
- ListNode* p = head->next;
-
- // 初始化计数器count为0
- int count = 0;
-
- // 使用循环遍历链表,计算节点数量
- while (p != NULL)
- {
- // 每遍历到一个节点,计数器加1
- count++;
-
- // 将指针p指向下一个节点
- p = p->next;
- }
-
- // 返回链表节点的数量
- return count;
- }
说明:本函数中仅对单链表进行遍历,未对链表内容进行改变,所以函数形参为一级指针即可,即头指针。
- // 按值查找
- void Locate(ListNode* head, DataType x)
- {
- int j = 1;
- ListNode* p;
- p = head->next;
-
- while (p != NULL)
- {
- if (p->data == x)
- {
- // 在表中找到值为x的结点
- printf("在表的第%d位找到值为%d的结点!", j, x);
- return;
- }
- p = p->next;
- j++;
- }
-
- // 在链表中未找到值为x的结点
- printf("未找到值为%d的结点!", x);
- }
- // 按序号查找
- void SearchList(ListNode* head, int i)
- {
- int j = 0;
- ListNode* p = head->next;
-
- while (p != NULL && j < i)
- {
- p = p->next;
- j++;
- }
-
- if (j == i)
- {
- // 在第i位上找到结点
- printf("在第%d位上的元素值为%d!", i, p->data);
- }
- else
- {
- // 位置错误,链表中没有该位置
- printf("位置错误,链表中没有该位置!");
- }
- }
算法思路:
- // 链表插入节点函数
- void InsertNode(ListNode** head, int i, int value)
- {
- // 创建新节点
- ListNode* newNode = (ListNode*)malloc(sizeof(ListNode));
- newNode->data = value;
- newNode->next = NULL;
-
- // 如果链表为空
- if (*head == NULL)
- {
- newNode->next = *head;
- *head = newNode;
- printf("插入成功\n");
- return;
- }
-
- // 找到插入位置的前一个节点
- int count = 0;
- ListNode* cur = *head;
- while (cur->next != NULL && count < i - 1)
- {
- cur = cur->next;
- count++;
- }
-
- // 如果插入位置超过链表长度,插入失败
- if (cur == NULL)
- {
- free(newNode);
- printf("插入失败\n");
- return;
- }
-
- // 插入新节点
- newNode->next = cur->next;
- cur->next = newNode;
-
- printf("插入成功\n");
- return;
- }
- void InsertNodeBeforeValue(ListNode** head, int x, int value)
- {
- // 创建新节点
- ListNode* newNode = (ListNode*)malloc(sizeof(ListNode));
- newNode->data = value;
- newNode->next = NULL;
-
- // 如果链表为空
- if (*head == NULL)
- {
- newNode->next = *head;
- *head = newNode;
- printf("插入成功\n");
- return;
- }
-
- // 找到要插入节点的前一个节点
- ListNode* prev = NULL;
- ListNode* cur = *head;
- while (cur != NULL)
- {
- // 如果找到要插入节点的值
- if (cur->data == x)
- {
- // 插入新节点
- newNode->next = cur;
- if (prev == NULL)
- {
- *head = newNode;
- }
- else
- {
- prev->next = newNode;
- }
- printf("插入成功\n");
- return;
- }
-
- prev = cur;
- cur = cur->next;
- }
-
- // 未找到要插入节点的值,插入失败
- free(newNode);
- printf("插入失败\n");
- }
- void DeleteNodeAtPosition(ListNode** head, int index) {
- // 链表为空或者索引无效,无法进行删除操作
- if (*head == NULL || index <= 0) {
- printf("删除失败\n");
- return;
- }
-
- ListNode* cur = *head;
- int count = 0;
-
- // 找到待删除节点的前一个节点
- while (count < index - 1 && cur->next != NULL) {
- cur = cur->next;
- count++;
- }
-
- // 如果索引超出链表范围,删除失败
- if (count != index - 1 || cur->next == NULL) {
- printf("删除失败\n");
- return;
- }
-
- // 删除节点
- ListNode* temp = cur->next;
- cur->next = cur->next->next;
- free(temp);
-
- printf("删除成功\n");
- return;
- }
- void DeleteNodeByValue(ListNode** head, int x)
- {
- assert(head && *head); // 确保指针不为空
-
- ListNode* cur = *head; // 当前节点指针
- while (cur->next != NULL) // 遍历链表
- {
- if (cur->next->data == x) // 找到要删除的节点
- {
- ListNode* temp = cur->next; // 临时保存要删除的节点
- // 修改指针,将当前节点的下一个节点指向要删除节点的下一个节点
- cur->next = cur->next->next;
- free(temp); // 释放要删除节点的内存
- }
- else // 当前节点不是要删除的节点
- {
- cur = cur->next; // 移动到下一个节点
- }
- }
- if (cur->next == NULL)
- {
- printf("删除成功\n");
- return;
- }
- printf("删除失败\n");
- }
- // 打印链表数据
- void PrintList(ListNode* head)
- {
- if (head->next == NULL)
- {
- printf("链表为空\n");
- return;
- }
-
- ListNode* cur = head->next;
- while (cur != NULL)
- {
- printf("%d ", cur->data);
- cur = cur->next;
- }
- printf("\n");
- }
- void ListDestory(ListNode* head)
- {
- assert(head);
- ListNode* cur = head;
- while (cur != NULL)
- {
- head = cur->next;
- free(cur);
- cur = head;
- }
- }
- #include<stdio.h>
- #include<stdlib.h>
- #include<assert.h>
-
- typedef int DataType;
-
- typedef struct ListNode
- {
- DataType data;
- struct ListNode* next;
- }ListNode;
-
- ListNode* InitList();
-
- void CreateListHead(ListNode** head);
- void CreateListLast(ListNode** head);
-
-
- int LengthList(ListNode* head);
- void Locate(ListNode* head, DataType x);
- void SearchList(ListNode* head, int i);
- void InsertNode(ListNode** head, int i, int value);
- void InsertNodeBeforeValue(ListNode** head, int x, int value);
- void DeleteNodeByValue(ListNode** head, int x);
- void DeleteNodeAtPosition(ListNode** head, int index);
- void PrintList(ListNode* head);
- void ListDestory(ListNode* head);
-
- //初始化
- ListNode* InitList()
- {
- ListNode* head = (ListNode*)malloc(sizeof(ListNode));
- if (head == NULL)
- {
- perror("malloc fail");
- exit(-1);
- }
- head->next = NULL;
- return head;
- }
-
- void CreateListHead(ListNode** head)
- {
- assert(head && *head);
- int i, n, val;
-
- // 获取要增加的节点数
- printf("需要增加多少个节点:");
- scanf("%d", &n);
-
- // 循环增加新节点
- for (i = 0; i < n; i++)
- {
- // 获取用户输入的节点值
- printf("请输入第 %d 个节点的数据:", i + 1);
- scanf("%d", &val);
-
- // 创建新节点,并进行内存分配检查
- ListNode* newnode = (ListNode*)malloc(sizeof(ListNode));
-
- // 检查内存分配是否成功
- if (newnode == NULL)
- {
- perror("malloc fail");
- exit(-1);
- }
-
- // 将节点值存储在新节点中
- newnode->data = val;
-
- // 将新节点插入到链表头部:将新节点的下一个节点指向当前头节点的下一个节点
- // 然后将新节点设为头节点的下一个节点
- newnode->next = (*head)->next;
- (*head)->next = newnode;
- }
- }
-
- void CreateListLast(ListNode** head)
- {
- assert(head && *head);
-
- int i, n, val;
-
- // 获取要增加的节点数
- printf("需要增加多少个节点:");
- scanf("%d", &n);
-
- // 创建临时指针并指向链表的头节点
- ListNode* temp = *head;
-
- // 移动到链表的最后一个节点
- while (temp->next)
- {
- temp = temp->next;
- }
-
- // 循环增加新节点
- for (i = 0; i < n; i++)
- {
- // 获取用户输入的节点值
- printf("请输入第 %d 个节点的数据:", i + 1);
- scanf("%d", &val);
-
- // 创建新节点,并进行内存分配检查
- ListNode* newnode = (ListNode*)malloc(sizeof(ListNode));
-
- // 检查内存分配是否成功
- if (newnode == NULL)
- {
- perror("malloc fail");
- exit(-1);
- }
-
- // 将节点值存储在新节点中
- newnode->data = val;
- newnode->next = NULL;
-
- // 将新节点连接到链表的最后一个节点,并更新最后一个节点为新节点
- temp->next = newnode;
- temp = newnode;
- }
- }
-
- int LengthList(ListNode* head)
- {
- // 检查头结点是否为空
- assert(head);
-
- // 初始化指针p,指向链表的第一个节点
- ListNode* p = head->next;
-
- // 初始化计数器count为0
- int count = 0;
-
- // 使用循环遍历链表,计算节点数量
- while (p != NULL)
- {
- // 每遍历到一个节点,计数器加1
- count++;
-
- // 将指针p指向下一个节点
- p = p->next;
- }
-
- // 返回链表节点的数量
- return count;
- }
-
- // 按值查找
- void Locate(ListNode* head, DataType x)
- {
- int j = 1;
- ListNode* p;
- p = head->next;
-
- while (p != NULL)
- {
- if (p->data == x)
- {
- // 在表中找到值为x的结点
- printf("在表的第%d位找到值为%d的结点!", j, x);
- return;
- }
- p = p->next;
- j++;
- }
-
- // 在链表中未找到值为x的结点
- printf("未找到值为%d的结点!", x);
- }
-
- // 按序号查找
- void SearchList(ListNode* head, int i)
- {
- int j = 0;
- ListNode* p = head->next;
-
- while (p != NULL && j < i)
- {
- p = p->next;
- j++;
- }
-
- if (j == i)
- {
- // 在第i位上找到结点
- printf("在第%d位上的元素值为%d!", i, p->data);
- }
- else
- {
- // 位置错误,链表中没有该位置
- printf("位置错误,链表中没有该位置!");
- }
- }
-
- // 链表插入节点函数
- void InsertNode(ListNode** head, int i, int value)
- {
- // 创建新节点
- ListNode* newNode = (ListNode*)malloc(sizeof(ListNode));
- newNode->data = value;
- newNode->next = NULL;
-
- // 如果链表为空或者插入位置是第一个位置
- if (*head == NULL)
- {
- newNode->next = *head;
- *head = newNode;
- printf("插入成功\n");
- return;
- }
-
- // 找到插入位置的前一个节点
- int count = 0;
- ListNode* cur = *head;
- while (cur->next != NULL && count < i - 1)
- {
- cur = cur->next;
- count++;
- }
-
- // 如果插入位置超过链表长度,插入失败
- if (cur == NULL)
- {
- free(newNode);
- printf("插入失败\n");
- return;
- }
-
- // 插入新节点
- newNode->next = cur->next;
- cur->next = newNode;
-
- printf("插入成功\n");
- return;
- }
-
- // 打印链表数据
- void PrintList(ListNode* head)
- {
- if (head->next == NULL)
- {
- printf("链表为空\n");
- return;
- }
-
- ListNode* cur = head->next;
- while (cur != NULL)
- {
- printf("%d ", cur->data);
- cur = cur->next;
- }
- printf("\n");
- }
-
- void InsertNodeBeforeValue(ListNode** head, int x, int value)
- {
- // 创建新节点
- ListNode* newNode = (ListNode*)malloc(sizeof(ListNode));
- newNode->data = value;
- newNode->next = NULL;
-
- // 如果链表为空
- if (*head == NULL)
- {
- newNode->next = *head;
- *head = newNode;
- printf("插入成功\n");
- return;
- }
-
- // 找到要插入节点的前一个节点
- ListNode* prev = NULL;
- ListNode* cur = *head;
- while (cur != NULL)
- {
- // 如果找到要插入节点的值
- if (cur->data == x)
- {
- // 插入新节点
- newNode->next = cur;
- if (prev == NULL)
- {
- *head = newNode;
- }
- else
- {
- prev->next = newNode;
- }
- printf("插入成功\n");
- return;
- }
-
- prev = cur;
- cur = cur->next;
- }
-
- // 未找到要插入节点的值,插入失败
- free(newNode);
- printf("插入失败\n");
- }
-
- void DeleteNodeAtPosition(ListNode** head, int index) {
- // 链表为空或者索引无效,无法进行删除操作
- if (*head == NULL || index <= 0) {
- printf("删除失败\n");
- return;
- }
-
-
- ListNode* cur = *head;
- int count = 0;
-
- // 找到待删除节点的前一个节点
- while (count < index - 1 && cur->next != NULL) {
- cur = cur->next;
- count++;
- }
-
- // 如果索引超出链表范围,删除失败
- if (count != index - 1 || cur->next == NULL) {
- printf("删除失败\n");
- return;
- }
-
- // 删除节点
- ListNode* temp = cur->next;
- cur->next = cur->next->next;
- free(temp);
-
- printf("删除成功\n");
- return;
- }
-
- void DeleteNodeByValue(ListNode** head, int x)
- {
- assert(head && *head); // 确保指针不为空
-
- ListNode* cur = *head; // 当前节点指针
- while (cur->next != NULL) // 遍历链表
- {
- if (cur->next->data == x) // 找到要删除的节点
- {
- ListNode* temp = cur->next; // 临时保存要删除的节点
- // 修改指针,将当前节点的下一个节点指向要删除节点的下一个节点
- cur->next = cur->next->next;
- free(temp); // 释放要删除节点的内存
- }
- else // 当前节点不是要删除的节点
- {
- cur = cur->next; // 移动到下一个节点
- }
- }
- if (cur->next == NULL)
- {
- printf("删除成功\n");
- return;
- }
- printf("删除失败\n");
- }
-
- void ListDestory(ListNode* head)
- {
- assert(head);
- ListNode* cur = head;
- while (cur != NULL)
- {
- head = cur->next;
- free(cur);
- cur = head;
- }
- }
-
- int main()
- {
- ListNode* head = InitList();
- //CreateListLast(&head);
- CreateListHead(&head);
- PrintList(head);
- InsertNode(&head, 2, 11);
- PrintList(head);
- DeleteNodeAtPosition(&head, 3);
- PrintList(head);
- DeleteNodeByValue(&head, 3);
- PrintList(head);
- return 0;
- }
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。