文件:main.c
c
#include "linkedList.h"
int main(int argc, char *argv[]) {
// 创建头结点
NODE *head = NULL;
// 创建链表
if (llist_create(&head, 666) < 0)
{
perror("链表创建失败!");
return -1;
}
// 向链表插入数据
llist_addTail(&head, 777);
llist_addTail(&head, 888);
llist_addTail(&head, 999); // 666 777 888 999
llist_addHead(&head, 7777);
llist_addHead(&head, 8888);
llist_addHead(&head, 9999); // 9999 8888 7777 666 777 888 999
llist_insert(&head, 888, 1024); // 9999 8888 7777 666 777 1024 888 999
// 测试遍历数据
llist_showAll(head);
// 更新数据
llist_update(head, 999, 99999);
llist_showAll(head); // 9999 8888 7777 666 777 1024 888 99999
// 删除数据
llist_delete(&head, 777);
llist_showAll(head); // 9999 8888 7777 666 1024 888 99999
// 销毁链表
llist_destroy(&head);
return 0;
}
文件:linkedList
c
#include "linkedList.h"
/**
* @brief 创建一个节点(私有函数)
*
* @param head
* @param data
*
* @return
*/
static NODE* __node_create(NODE **head, DATA data)
{
// 创建一个新节点
NODE *p = (NODE*)malloc(sizeof(NODE));
// 校验是否创建成功
if (!p) return NULL;
// 初始化节点
p->data = data; // 将外部插入的数据存储到当前节点的data成员
p->next = NULL; // 节点默认指向NULL
return p;
}
/**
* @brief 链表的创建(没有指定头结点的前提下进行(本案例涉及的是无头节点))
* @param head 待操作链表,如果是改变链表结构,使用二级指针
* @param data 待插入数据
* @return 创建成功返回0,否则返回-1
*/
int llist_create(NODE **head, DATA data)
{
// 校验链表是否存在
if (*head != NULL) return -1;
// 创建一个新节点
NODE *p = (NODE*)malloc(sizeof(NODE));
// 校验是否创建成功
if (!p) return -1;
// 初始化节点
p->data = data; // 将外部插入的数据存储到当前节点的data成员
p->next = NULL; // 节点默认指向NULL
// 将创建的新节点作为链表的头节点
*head = p; // 就是让链表指针指向第一个节点
return 0;
}
/**
* @brief 向链表头部插入一个节点数据(头插法)
* @param head 待操作链表,如果是改变链表结构,使用二级指针
* @param data 待插入数据
* @return 创建成功返回0,否则返回-1
*/
int llist_addHead(NODE **head, DATA data)
{
// 创建一个新节点
NODE *p = (NODE*)malloc(sizeof(NODE));
// 校验是否创建成功
if (!p) return -1;
// 初始化节点
p->data = data; // 存放插入的数据
p->next = *head; // 让头指针指向新创建的节点p
// 将当前的新节点作为头节点(更新头指针)
*head = p;
return 0;
}
/**
* @brief 向链表尾部插入一个节点数据(尾插法)
* @param head 待操作链表,如果是改变链表结构,使用二级指针
* @param data 待插入数据
* @return 创建成功返回0,否则返回-1
*/
int llist_addTail(NODE **head, DATA data)
{
// 创建一个新节点
NODE *pNew = (NODE*)malloc(sizeof(NODE));
// 校验是否创建成功
if (!pNew) return -1;
// 初始化节点
pNew->data = data;
pNew->next = NULL;
// 接收链表
NODE *p = *head, *q = NULL;
// 如果*p不存在,说明这是一个空链表
if (!p)
{
*head = pNew; // 更新头指针,让头指针指向pNew
return 0;
}
// 如果是非空链表,使用循环查找尾结点
// 指针尾速法遍历链表
while (p)
{
q = p; // q记录p偏移之前的位置
p = p->next;// 指针偏移,得到尾结点
}
// 此时p指向NULL,q指向尾节点
// 让原链表的尾节点指向pNew
q->next = pNew;
return 0;
}
/**
* @brief 向链表指定位置插入一个节点数据(中间插法)
* @param head 待操作链表,如果是改变链表结构,使用二级指针
* @param pos 待插入位置数据
* @param data 待插入数据
* @return 创建成功返回0,否则返回-1
*/
int llist_insert(NODE **head, DATA pos, DATA data)
{
// 创建一个新节点
// NODE *pNew = __node_create(head,data);
NODE *pNew = (NODE*)malloc(sizeof(NODE));
// 校验是否创建成功
if (!pNew) return -1;
// 初始化节点
pNew->data = data;
pNew->next = NULL;
// 因为需要遍历链表得到前后两个位置,所以可以使用指针尾随法
NODE *p = *head, *q = NULL;
// 情景1:空链表
if (!p)
{
*head = pNew; // 更新头指针
return 0;
}
// 情景2:pos是头节点
if (memcmp(&(p->data), &pos, sizeof(DATA)) == 0)
{
// 头插法
pNew->next = *head;
*head = pNew; // 更新头指针
return 0;
}
// 情景3:pos是非头节点
while (p)
{
// 校验是否是pos
if (memcmp(&(p->data), &pos, sizeof(DATA)) == 0)
{
// 插入到目标节点pos前面
pNew->next = p; // p就是pos对应的节点
q->next = pNew; // q就是pos的上一个节点
return 0;
}
q = p;
p = p->next;
}
// 情景4:找不到pos的位置(使用尾插法,大家也可以选择不插入)
q->next = pNew;
return 0;
}
/**
* @brief 遍历链表数据
* @param head 待遍历的链表
*
* @return
*/
void llist_showAll(const NODE *head)
{
// 首先使用一个指针变量接受形参
const NODE *p = head;
while (p)
{
printf("%d\t", p->data);
p = p->next; // 类似与顺序表中的p++,实现相邻节点之间的指针偏移
}
printf("\n");
}
/**
* @brief 根据data返回其对应的节点
* @param head 待查找的链表
* @param data 待查找的节点数据(相同数据只匹配第一个)
*
* @return 成功返回节点指针,失败返回NULL
*/
NODE *llist_find(const NODE *head, DATA data)
{
const NODE *p = head;
while(p)
{
if (memcmp(&(p->data), &data, sizeof(DATA)) == 0)
{
return (NODE*)p;
}
p = p->next;
}
return NULL;
}
/**
* @brief 更新链表节点数据old为newdata
* @param head 待更新的链表
* @param old 待更新节点的数据
* @param newdata 节点需要更新的数据
*
* @return 修改成功返回0,否则返回-1
*/
int llist_update(const NODE *head, DATA old, DATA newdata)
{
NODE *p = NULL;
// 找到old对应的NODE
if (!(p = llist_find(head,old)))
{
return -1;
}
// 更新数据
p->data = newdata;
return 0;
}
/**
* @brief 删除链表中节点值为data的节点
* @param head 待删除节点链表
* @param data 待删除节点数据
*
* @return 删除成功返回0,否则返回-1
*/
int llist_delete(NODE **head, DATA data)
{
NODE *p = *head, *q = NULL;
// 情景1:空链表
if (!p) return -1;
// 情景2:头节点
if (memcmp(&(p->data), &data, sizeof(DATA)) == 0)
{
*head = p->next;
// 回收节点
free(p);
return 0;
}
// 情景3:非头节点(含尾节点)
while(p){
if (memcmp(&(p->data), &data, sizeof(DATA)) == 0)
{
q->next = p->next;// 让p的上一个节点的next指向p的下一个节点,实际上就是从链表中将p剔除
free(p);
return 0;
}
// 指针偏移
q = p;
p = p->next;
}
return -1;
}
/**
* @brief 销毁链表
* @param head 待销毁的链表
*
* @return
*/
void llist_destroy(NODE **head)
{
NODE *p = *head, *q = NULL;
while (p)
{
q = p;
p = p->next;
free(q);
}
// 重置头指针
*head = NULL;
}
文件:linkedList.h
c
#ifndef LINKEDLIST_H
#define LINKEDLIST_H
// 引用相关头文件
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
// 给数据设置一个别名,本次涉及的所有数据结构的存储数据的类型都是int,大家做项目的时候,可以替换成相应的结构体
typedef int DATA;
// 创建链表的节点结构体
typedef struct node
{
DATA data; // 数据域:存放节点数据
struct node *next; // 指针域:指向下一个同类型节点
} NODE;
/**
* @brief 链表的创建(没有指定头结点的前提下进行(本案例涉及的是无头节点))
* @param head 待操作链表,如果是改变链表结构,使用二级指针
* @param data 待插入数据
* @return 创建成功返回0,否则返回-1
*/
extern int llist_create(NODE **head, DATA data);
/**
* @brief 向链表头部插入一个节点数据(头插法)
* @param head 待操作链表,如果是改变链表结构,使用二级指针
* @param data 待插入数据
* @return 创建成功返回0,否则返回-1
*/
extern int llist_addHead(NODE **head, DATA data);
/**
* @brief 向链表尾部插入一个节点数据(尾插法)
* @param head 待操作链表,如果是改变链表结构,使用二级指针
* @param data 待插入数据
* @return 创建成功返回0,否则返回-1
*/
extern int llist_addTail(NODE **head, DATA data);
/**
* @brief 向链表指定位置插入一个节点数据(中间插法)
* @param head 待操作链表,如果是改变链表结构,使用二级指针
* @param pos 待插入位置数据
* @param data 待插入数据
* @return 创建成功返回0,否则返回-1
*/
extern int llist_insert(NODE **head, DATA pos, DATA data);
/**
* @brief 遍历链表数据
* @param head 待遍历的链表
*
* @return
*/
extern void llist_showAll(const NODE *head);
/**
* @brief 根据data返回其对应的节点
* @param head 待查找的链表
* @param data 待查找的节点数据(相同数据只匹配第一个)
*
* @return 成功返回节点指针,失败返回NULL
*/
extern NODE *llist_find(const NODE *head, DATA data);
/**
* @brief 更新链表节点数据old为newdata
* @param head 待更新的链表
* @param old 待更新节点的数据
* @param newdata 节点需要更新的数据
*
* @return 修改成功返回0,否则返回-1
*/
extern int llist_update(const NODE *head, DATA old, DATA newdata);
/**
* @brief 删除链表中节点值为data的节点
* @param head 待删除节点链表
* @param data 待删除节点数据
*
* @return 删除成功返回0,否则返回-1
*/
extern int llist_delete(NODE **head, DATA data);
/**
* @brief 销毁链表
* @param head 待销毁的链表
*
* @return
*/
extern void llist_destroy(NODE **head);
#endif