C语言-单向不循环链表的基本操作(增、删、改、查)
一、前言
单向不循环链表作为链表中最简单的存在,比较适合才接触链表时进行学习,以下所有的代码都有详细注释以及可以在visual stdio中跑通。
二、详细代码
1、定义链表的节点以及包含用到头文件
#include<stdio.h>
#include<stdlib.h>
typedef struct Node
{
int data;
struct Node* next;
} Node;
2、创建新节点
Node* create_node(int val)
{
Node* new_node = (Node*)malloc(sizeof(Node));
if (!new_node)
{
printf("malloc error!");
return NULL;
}
new_node->data = val;
new_node->next = NULL;
return new_node;
}
3、打印链表
void list_print(Node* head)
{
Node* cur = head;
printf("List: ");
while (cur)
{
printf("%d -> ", cur->data);
cur = cur->next;
}
printf("NULL\n");
}
4、头插
void insert_head(Node** head, int val)
{
Node* new_node = create_node(val);
new_node->next = *head;
*head = new_node;
}
5、尾插
void insert_tail(Node** head, int val)
{
Node* new_node = create_node(val);
/* 空链表 */
if (*head == NULL)
{
*head = new_node;
return;
}
/* 非空链表 */
Node* tail = *head;
while (tail->next)
tail = tail->next;
tail->next = new_node;
}
6、任意位置插入,从0开始;返回0成功,返回-1失败
int insert_list(Node** head, int pos, int val)
{
/* 位置小于0,直接返回失败 */
if (pos < 0) return -1;
/* 位置为0,相当于头插 */
if (pos == 0)
{
insert_head(head, val);
return 0;
}
/* 找到插入位置的前驱节点 */
Node* cur = *head;
for (int i = 0; cur && i < pos - 1; i++)
cur = cur->next;
/* 处理pos过大以及空链表的情况 */
if (!cur) return -1;
Node* new_node = create_node(val);
new_node->next = cur->next;
cur->next = new_node;
return 0;
}
7、删除第一个值为val的节点,返回0成功,返回-1失败
int list_delete_val(Node** head, int val)
{
if (*head == NULL) return -1;
/* 处理头节点 */
if ((*head)->data == val)
{
Node* temp = *head;
*head = (*head)->next;
free(temp);
return 0;
}
/* 处理非头节点 */
Node* cur = *head, * prev = NULL;
while (cur && cur->data != val)
{
prev = cur;
cur = cur->next;
}
/* 没找到 */
if (cur == NULL) return -1;
prev->next = cur->next;
free(cur);
return 0;
}
8、删除指定位置节点,pos从0开始,返回0成功,返回-1失败
int list_delete_pos(Node** head, int pos)
{
/* 排除链表为空或者位置错误情况 */
if (*head == NULL || pos < 0) return -1;
/* 处理头节点 */
if (pos == 0)
{
Node* temp = *head;
*head = (*head)->next; // 此处已保证 *head 非 NULL
free(temp);
return 0;
}
/* 处理非头节点 */
Node* cur = *head, * prev = NULL;
for (int i = 0; cur && i < pos; i++)
{
prev = cur;
cur = cur->next;
}
/* pos越界 */
if (cur == NULL) return -1;
prev->next = cur->next;
free(cur);
return 0;
}
9、把查找的第一个值替换为新的值,返回0成功,返回-1失败
int list_replace_val(Node* head, int oldval, int newval)
{
Node* cur = head;
while (cur && cur->data != oldval)
cur = cur->next;
if (cur == NULL) return -1;
cur->data = newval;
return 0;
}
10、把当前位置的值替换为新的值,返回0成功,返回-1失败
int list_find_pos(Node* head, int pos, int val)
{
Node* cur = head;
for (int i = 0; cur && i < pos; i++)
cur = cur->next;
if (cur == NULL) return -1;
cur->data = val;
return 0;
}
11、销毁整个链表
void list_destroy(Node** head)
{
Node* cur = *head;
while (cur)
{
Node* next = cur->next;
free(cur);
cur = next;
}
*head = NULL;
}
12、测试函数
int main()
{
Node* head = NULL;
/* 增 */
insert_head(&head, 10);
insert_head(&head, 20);
insert_tail(&head, 50);
insert_list(&head, 1, 15);
list_print(head);
/* 改 */
list_replace_val(head, 20, 60);
list_find_pos(head, 1, 1);
list_print(head);
/* 删 */
list_delete_val(&head, 15);
list_delete_pos(&head, 1);
list_print(head);
/* 销毁 */
list_destroy(&head);
return 0;
}
三、额外补充
为什么有的函数用了二重指针,有的函数没有用二重指针
因为使用了二重指针的函数都有可能操作到“链表头”,使用了二重指针就可以找到“链表头”的真正地址,否则调用者手里的head就永远停留在旧地址
浙公网安备 33010602011771号