链表的定义:
链表是一种常见的重要的数据结构;它是动态地进行存储分配的一种结构。
链表示意图:
定义一个简单链表:
/*
File Name : linknode.h
定义链表结构体:UserInfo
*/
/*
链表结构体:
声明:age (年龄)
name (姓名)
next (下一个节点地址)
*/
struct UserInfo
{
//年龄
int age;
//姓名
char *name;
//存储下一个链表节点地址
struct UserInfo* next;
};
//简写链表结构体声明
typedef struct UserInfo USER;
一 简单链表的静态模式和动态模式:
代码实现:
#include<stdio.h>
#include<stdlib.h>
#include"linknode.h"
struct Info
{
int num;
char flag;
char* msg;
void* pNext;
};
//静态模式
void staticModel()
{
struct Info info1;
info1.num = 10;
info1.flag = 'Y';
info1.msg = "静态模式";
info1.pNext = &(info1.num);
printf("静态模式实现 num=%d, flag=%c , msg=%s , pNext=%p \n", info1.num, info1.flag, info1.msg,info1.pNext);
}
void dynamicModel()
{
struct Info* info1;
info1 = (struct Info*)malloc(sizeof(struct Info));//分配内存 malloc 返回值是 vold 空指针类型,必须进行强制转换
(*info1).num = 20; //等价形式 info1->num = 20; (*info1)加上 * 号就取出了结构体的内容,等价于一个结构体。
(*info1).flag = 'N'; //等价形式 info1->flag = 'N';
(*info1).msg = "动态模式";//等价形式 info1->msg = "动态模式";
(*info1).pNext = &((*info1).num); //取地址 ,等价于 info1->pNext = &(info1->num);
printf("静态模式实现 num=%d, flag=%c , msg=%s , pNext=%p \n", info1->num, info1->flag, info1->msg, info1->pNext);
}
void main()
{
printf("静态模式实现:\n");
staticModel();
printf("动态模式实现:\n");
dynamicModel();
system("pause");
}
输出:
在实际运用中一般都使用动态链表来存储数据
二 动态简单链表的 增删改查:
链表结构和函数声明:
/*
链表结构体和函数声明头文件 : linknode.h
*/
#include<stdio.h>
#include<stdlib.h>
/*
链表结构体:
声明:age (年龄)
name (姓名)
next (下一个节点地址)
*/
struct UserInfo
{
//年龄
int age;
//姓名
char *name;
//存储下一个链表节点地址
struct UserInfo* next;
};
//简写链表结构体声明
typedef struct UserInfo USER;
/*
显示链表所有元素
参数:
USER* pUser_head:头节点
返回:
无
*/
void showAll(USER* pUser_head);
/*
添加节点元素
参数:
USER** pUser_head:头节点
int iage:年龄
char *iname:姓名
返回:
无
*/
void add(USER** pUser_head, int iage, char* iname);
/*
链表逆转
参数:
USER* pUser_head :头节点指针
返回:
USER* pUser_head :头节点指针
*/
USER* reversed(USER* pUser_head);
/*
排序,根据链表数据结构 age(年龄)进行
参数:
USER* pUser_head :头节点指针地址
char flag :排序标志 '>'从大到小 ;'<' 从小到达
返回:
无
*/
void sort(USER* pUser_head, char flag);
/*
统计链表个数
参数:
USER* pUser_head :头节点指针地址
返回:
int :元素个数
*/
int count(USER* pUser_head);
/*
根据年龄检索链表元素
参数:
USER* pUser_head :头节点指针地址
int age:检索条件,年龄
返回:
USER* :元素节点指针
*/
USER* searchOfAge(USER* pUser_head, int age);
/*
根据姓名检索链表元素
参数:
USER* pUser_head :头节点指针地址
int age:检索条件,姓名
返回:
USER* :元素节点指针
*/
USER* searchOfName(USER* pUser_head, char * name);
/*
修改节点元素姓名
参数:
USER* pUser_head :头节点指针地址
char* oldname:旧名称
char* newname:新名称
返回(int):
1:修改成功
0:未找到元素
*/
int changeByName(USER* pUser_head, char* oldname, char* newname);
/*
根据年龄定位元素并元素前面插入新节点元素
参数:
USER* pUser_head :头节点指针地址
int age:年龄(定位)
int iage:新元素的年龄
char* iname:新元素的姓名
返回:
链表头指针
*/
USER* headInsert(USER* pUser_head,int age, int iage, char* iname);
/*
根据姓名定位元素并元素后插入新节点元素
参数:
USER* pUser_head :头节点指针地址
char* name:姓名(定位)
int iage:新元素的年龄
char* iname:新元素的姓名
返回:
链表头指针
*/
USER* backInsert(USER* pUser_head, char* name, int iage, char* iname);
/*
修改节点元素年龄
参数:
USER* pUser_head :头节点指针地址
int oldage:旧 年龄
int newage:新 年龄
返回(int):
1:修改成功
0:未找到元素
*/
int changeByAge(USER* pUser_head, int oldage, int newage);
/*
删除链表
参数:
USER* pUser_head :头节点指针地址
返回:
无
*/
void* freeAll(USER* pUser_head);
/*
根据年龄删除链表节点元素
参数:
USER* pUser_head :头节点指针地址
int age:年龄
返回:
USER* :头节点指针
*/
USER* deleteByAge(USER* pUser_head, int age);
/*
根据姓名删除链表节点元素
参数:
USER* pUser_head :头节点指针地址
char * name:姓名
返回:
USER* :头节点指针
*/
USER* deleteByName(USER* pUser_head, char* name);
/*
冒泡排序
参数:
USER* pUser_head :头节点指针地址
返回:
无
*/
void bubbleSort(USER* pUser_head);
/*
快速排序1(双冒泡)
参数:
USER* pUser_head :头节点指针地址
USER* pUser_back :尾节点(默认 NULL)
返回:
无
*/
void quickSort(USER* pUser_head, USER* pUser_back);
/*
快速排序2(双冒泡), 递归
参数:
USER* pUser_head :头节点指针地址
USER* pUser_back :尾节点(默认 NULL)
返回:
无
*/
void quickSort2(USER* begin, USER* end);
链表函数实现源文件:
/*
链表函数实现源文件:: linknode.c
*/
#include"linknode.h"
/*
显示链表所有元素
参数:
USER* pUser_head:头节点
返回:
无
*/
void showAll(USER* pUser_head)
{
if (pUser_head == NULL)
{
printf("链表元素为空!\n");
return;
}
else
{
while (pUser_head != NULL)
{
printf("\n 年龄= %d , 姓名= %s ", pUser_head->age, pUser_head->name);//输出当前节点
pUser_head = pUser_head->next; //指针不断向前循环
}
}
}
/*
添加节点元素
参数:
USER** pUser_head:头节点
int iage:年龄
char *iname:姓名
返回:
无
*/
void add(USER** pUser_head, int iage, char *iname)
{
if (*pUser_head == NULL)//如果头节点指针为空
{
USER* newNode = (USER*)malloc(sizeof(USER)); //分配空间
newNode->age = iage;
newNode->name = iname;
newNode->next = NULL;
*pUser_head = newNode;
}
else //如果头节点不为空,找到链表尾部插入数据
{
USER* p = *pUser_head;
while (p->next!=NULL) //结束条件 p->next!=NULL 不是 p!=NULL
{
p = p->next; //循环指向下以个节点,直到为空,说明到最后一个节点(链表最后节点指向为空)
}
USER* newNode = (USER*)malloc(sizeof(USER)); //分配空间
newNode->age = iage;
newNode->name = iname;
newNode->next = NULL;
p->next = newNode;//将节点连接到末尾
}
}
/*
链表逆转
参数:
USER* pUser_head :头节点指针
返回:
USER* pUser_head :头节点指针
*/
USER* reversed(USER* pUser_head)
{
USER* pU1, * pU2, * pU3; //定义三个辅助指针变量
pU1 = pU2 = pU3 = NULL;
if (pUser_head == NULL || pUser_head->next == NULL) //如果头节点为空,或者只有一个元素
{
return pUser_head;
}
pU1 = pUser_head;
pU2 = pUser_head->next;
while (pU2!=NULL) //pU2!=NULL 说明没有节点
{
pU3 = pU2->next;
pU2->next = pU1;//指向前一个节点
//指针向前移动,从第二个元素开始到最最后一个元素,全部指向它前面的节点
pU1 = pU2;
pU2 = pU3;
}
pUser_head->next = NULL; //代表链表结束,指向为空
pUser_head = pU1; //pU1 循环后指向最后一个节点
return pUser_head;
}
/*
排序,根据链表数据结构 age(年龄)进行
参数:
USER* pUser_head :头节点指针地址
char flag :排序标志 '>'从大到小 ;'<' 从小到达
返回:
无
*/
void sort(USER* pUser_head, char flag)
{
if (pUser_head == NULL || pUser_head->next == NULL)//如果链表为空,或只有一个元素
{
return;
}
if (flag == '>') //从大到小
{
for (USER* p1 = pUser_head; p1 != NULL; p1 = p1->next) //遍历链表所有节点
{
for (USER* p2 = pUser_head; p2 != NULL; p2 = p2->next)//遍历链表所有节点
{
if (p1->age < p2->age) //满足条件交换遍历
{
USER temp;
temp.age = p1->age;
temp.name = p1->name;
p1->age = p2->age;
p1->name = p2->name;
p2->age = temp.age;
p2->name = temp.name;
}
}
}
}
if (flag == '<')//从小到达
{
for (USER* p1 = pUser_head; p1 != NULL; p1 = p1->next) //遍历链表所有节点
{
for (USER* p2 = pUser_head; p2 != NULL; p2 = p2->next)//遍历链表所有节点
{
if (p1->age > p2->age) //满足条件交换遍历
{
USER temp;
temp.age = p1->age;
temp.name = p1->name;
p1->age = p2->age;
p1->name = p2->name;
p2->age = temp.age;
p2->name = temp.name;
}
}
}
}
}
/*
统计链表个数
参数:
USER* pUser_head :头节点指针地址
返回:
int :元素个数
*/
int count(USER* pUser_head)
{
int num = 0;
while (pUser_head!=NULL)
{
num++;
pUser_head = pUser_head->next;
}
return num;
}
/*
根据年龄检索链表元素
参数:
USER* pUser_head :头节点指针地址
int age:检索条件,年龄
返回:
USER* :元素节点指针
*/
USER* searchOfAge(USER* pUser_head, int age)
{
USER* node = NULL;
if (pUser_head != NULL)
{
while (pUser_head!=NULL)
{
if (age == pUser_head->age)
{
node = pUser_head;
break;
}
pUser_head = pUser_head->next;//指针向前
}
}
return node;
}
/*
根据姓名检索链表元素
参数:
USER* pUser_head :头节点指针地址
int age:检索条件,姓名
返回:
USER* :元素节点指针
*/
USER* searchOfName(USER* pUser_head, char* name)
{
USER* node = NULL;
if (pUser_head != NULL)
{
while (pUser_head != NULL)
{
if (name == pUser_head->name)
{
node = pUser_head;
break;
}
pUser_head = pUser_head->next;//指针向前
}
}
return node;
}
/*
修改节点元素姓名
参数:
USER* pUser_head :头节点指针地址
char* oldname:旧名称
char* newname:新名称
返回(int):
1:修改成功
0:未找到元素
*/
int changeByName(USER* pUser_head, char* oldname, char* newname)
{
if (pUser_head == NULL)
{
return 0;
}
USER *p= searchOfName(pUser_head, oldname);
if (p == NULL)
{
return 0;
}
p->name = newname;
return 1;
}
/*
修改节点元素年龄
参数:
USER* pUser_head :头节点指针地址
int oldage:旧 年龄
int newage:新 年龄
返回(int):
1:修改成功
0:未找到元素
*/
int changeByAge(USER* pUser_head, int oldage, int newage)
{
if (pUser_head == NULL)
{
return 0;
}
USER* p = searchOfAge(pUser_head, oldage);
if (p == NULL)
{
return 0;
}
p->age = newage;
return 1;
}
/*
根据年龄定位元素并元素 “前” 面插入新节点元素
参数:
USER* pUser_head :头节点指针地址
int age:年龄(定位)
int iage:新元素的年龄
char* iname:新元素的姓名
返回:
链表头指针
*/
USER* headInsert(USER* pUser_head, int age, int iage, char* iname)
{
USER* p1, * p2;
p1 = p2 = NULL;
p1 = pUser_head;
while (p1 != NULL)
{
if (p1->age == age)//找到指定节点元素
{
break; //结束循环
}
else
{
p2 = p1;//记录当前节点的前一个节点
p1 = p1->next; //当前节点指针向前移动
}
}
if (p1 == pUser_head) //如果头节点前面插入节点
{
USER* pnode = (USER*)malloc(sizeof(USER)); //分配存储空间
pnode->age = iage;
pnode->name = iname;
pnode->next = pUser_head; //新节点链接指向原来第一个节点
pUser_head = pnode;//改变头节点指针指向新节点
}
else //其他节点满足要求
{
USER* pnode = (USER*)malloc(sizeof(USER)); //分配存储空间
pnode->age = iage;
pnode->name = iname;
pnode->next = p1; //新节点链接 指向满足要求的节点
p2->next = pnode; //p2 存储了当前满足要求的前一个元素节点,p1 存储了满足要求的节点, 所以,p2 的链接指向新节点
}
return pUser_head;
}
/*
根据姓名定位元素并元素 “后” 插入新节点元素
参数:
USER* pUser_head :头节点指针地址
char* name:姓名(定位)
int iage:新元素的年龄
char* iname:新元素的姓名
返回:
链表头指针
*/
USER* backInsert(USER* pUser_head, char* name, int iage, char* iname)
{
USER* p1, *p2;
p1=p2= NULL;
p1 = pUser_head;
while (p1 != NULL)
{
if (p1->name == name)//找到指定节点元素
{
break; //结束循环
}
else
{
p1 = p1->next; //当前节点指针向前移动
}
}
if (p1->next==NULL)//最后一个节点
{
USER* pnode = (USER*)malloc(sizeof(USER)); //分配存储空间
pnode->age = iage;
pnode->name = iname;
pnode->next = NULL;//链接为空,它是最后一个节点
p1->next = pnode; //原来最后节点连接指向新节点
}
else//其他节点
{
p2 = p1->next; //存储 满足条件节点的下一个节点
USER* pnode = (USER*)malloc(sizeof(USER)); //分配存储空间
pnode->age = iage;
pnode->name = iname;
pnode->next = p2; //指向满足条件节点的下一个节点
p1->next = pnode; //满足条件节点的链接指向新节点
}
return pUser_head; //头节点没有改变,返回值可有可无。
}
/*
删除链表
参数:
USER* pUser_head :头节点指针地址
返回:
指针地址为 NULL ,调用时让头指针接受返回值
*/
void* freeAll(USER* pUser_head)
{
USER* p1, * p2;
p1 = p2 = NULL;
p1 = pUser_head;
while (p1->next!=NULL)
{
p2 = p1->next; //指向头节点(p1)的下一个节点
p1->next = p2->next; //头节点指向 p2 的下一个节点 ,此时链表跳过了 p2(中间节点)
free(p2); //释放 p2
}
free(p1); //释放p1 (头节点)
return NULL;
}
/*
根据年龄删除链表节点元素
参数:
USER* pUser_head :头节点指针地址
int age:年龄
返回:
USER* :头节点指针
*/
USER* deleteByAge(USER* pUser_head, int age)
{
USER* p1, * p2;
p1 = p2 = NULL;
p1 = pUser_head;
while (p1!=NULL)
{
if (p1->age == age)
{
break; //结束循环
}
else
{
p2 = p1;//记录当前节点的前一个节点
p1 = p1->next; //指针向前移动
}
}
if (p1 == pUser_head) //如果头节点满足要求
{
pUser_head = p1->next;//改变头节点指针指向第二个节点
free(p1);
}
else //其他节点满足要求
{
p2->next = p1->next; //p2 存储了当前满足要求的前一个元素节点,p1 存储了满足要求的节点, 所以,跳过 p1 节点
free(p1); //释放
}
return pUser_head;
}
/*
根据姓名删除链表节点元素
参数:
USER* pUser_head :头节点指针地址
char * name:姓名
返回:
USER* :头节点指针
*/
USER* deleteByName(USER* pUser_head, char* name)
{
USER* p1, * p2;
p1 = p2 = NULL;
p1 = pUser_head;
while (p1 != NULL)
{
if (p1->name == name)
{
break; //结束循环
}
else
{
p2 = p1;//记录当前节点的前一个节点
p1 = p1->next; //指针向前移动
}
}
if (p1 == pUser_head) //如果头节点满足要求
{
pUser_head = p1->next;//改变头节点指针指向第二个节点
free(p1);
}
else //其他节点满足要求
{
p2->next = p1->next; //p2 存储了当前满足要求的前一个元素节点,p1 存储了满足要求的节点, 所以,跳过 p1 节点
free(p1); //释放
}
return pUser_head;
}
/*
快速分区
参数:
USER* begin :头节点指针地址
USER* end :尾节点
返回:
USER* 指针
*/
USER* partition(USER* begin, USER* end)
{
int key = begin->age; //第一个数据为区分基准数
//创建双指针
USER* p1 = begin; //第一个节点
USER* p2 = begin->next;//下一个节点
while (p2!=end)
{
if (p2->age < key)
{
p1 = p1->next;//循环下一个节点
//交换符合条件的数据
int temp_age = p1->age;
char* temp_name = p1->name;
p1->age = p2->age;
p1->name = p2->name;
p2->age = temp_age;
p2->name = temp_name;
}
p2 = p2->next; //第二指针不断前进
}
//交互最后节点
int temp_age = p1->age;
char* temp_name = p1->name;
p1->age = begin->age;
p1->name = begin->name;
begin->age = temp_age;
begin->name = temp_name;
return p1;
}
/*
快速排序1(双冒泡), 递归
参数:
USER* pUser_head :头节点指针地址
USER* pUser_back :尾节点(默认 NULL)
返回:
无
*/
void quickSort(USER* pUser_head, USER* pUser_back)
{
if (pUser_head == NULL || pUser_head->next == NULL || pUser_head == pUser_back)
{
return;
}
USER* mid = partition(pUser_head, pUser_back);
quickSort(pUser_head, mid);
quickSort(mid->next, pUser_back);
}
/*
快速排序2(双冒泡,指针循环), 递归
参数:
USER* begin :头节点指针地址
USER* end :尾节点(默认 NULL)
返回:
无
*/
void quickSort2(USER* begin, USER* end)
{
if (begin == NULL || begin->next == NULL || begin == end)
{
return;
}
int key = begin->age; //第一个数据为区分基准数
//创建双指针
USER* p1 = begin; //第一个节点
for (USER* p2 = begin->next; p2 != NULL; p2 = p2->next)
{
if (p2->age < key)
{
p1 = p1->next;//指针向前
//交换符合条件的数据
int temp_age = p1->age;
char* temp_name = p1->name;
p1->age = p2->age;
p1->name = p2->name;
p2->age = temp_age;
p2->name = temp_name;
}
}
//交互最后节点
int temp_age = p1->age;
char* temp_name = p1->name;
p1->age = begin->age;
p1->name = begin->name;
begin->age = temp_age;
begin->name = temp_name;
quickSort2(begin, p1);
quickSort2(p1->next, end);
}
主函数调用:
#include<stdio.h>
#include<stdlib.h>
#include"linknode.h"
void main()
{
struct UserInfo* user1 = NULL;
add(&user1, 16, "张三");
add(&user1, 22, "李四");
add(&user1, 28, "王五");
add(&user1, 19, "赵六");
add(&user1, 13, "田七");
showAll(user1);
printf("\n ----------------- \n");
printf("链表逆转后: \n");
user1 = reversed(user1); //逆转链表
showAll(user1);
printf("\n ----------------- \n");
printf("链表从大到小排序后: \n");
sort(user1, '<');
showAll(user1);
printf("\n ----------------- \n");
printf("链表元素个数:%d \n", count(user1));
printf("\n ----------------- \n");
printf("分别根据年龄和姓名检索元素: \n");
USER* u1 = searchOfAge(user1, 19);
if (u1 != NULL)
{
printf("检索年龄为19的节点元素 姓名为:%s ,地址为: %p\n", u1->name, u1);
}
USER* u2 = searchOfName(user1, "田七");
if (u2 != NULL)
{
printf("检索姓名是 田七 的节点元素 年龄为:%d ,地址为: %p\n", u2->age, u2);
}
printf("\n ----------------- \n");
printf("插入元素节点: \n");
user1 = headInsert(user1, 13, 24, "Jom");
user1 = backInsert(user1, "Jom", 25, "Lily");
showAll(user1);
printf("\n ----------------- \n");
printf("分别根据年龄和姓名修改元素: \n");
int n1= changeByName(user1, "赵六", "ZhaoLiu");
if (n1 == 1)
{
printf("根据姓名修改元素成功! \n");
}
else
{
printf("根据姓名修改元素失败! \n");
}
int n2 = changeByAge(user1, 13, 130);
if (n2 == 1)
{
printf("根据年龄修改元素成功! \n");
}
else
{
printf("根据年龄修改元素失败! \n");
}
showAll(user1);
printf("\n ----------------- \n");
printf("根据年龄或姓名删除链表元素:\n");
user1 = deleteByAge(user1, 28);
showAll(user1);
system("pause");
}
输出结果:
链表逆转示意图:
执行步骤:
NO 1:
NO 2:
NO 3:
链表合并:
#include<stdio.h>
#include<stdlib.h>
#include"linknode.h"
void main()
{
struct UserInfo* user1 = NULL;
struct UserInfo* user2 = NULL;
add(&user1, 11, "张三");
add(&user1, 13, "李四");
add(&user1, 15, "王五");
add(&user1, 17, "赵六");
add(&user1, 19, "田七");
add(&user1, 20, "嘎嘎");
add(&user2, 10, "ZhangSan");
add(&user2, 12, "LiSi");
add(&user2, 14, "WangWu");
add(&user2, 16, "ZhaoLiu");
add(&user2, 18, "TianQi");
showAll(user1);
printf("\n ----------------- \n");
showAll(user2);
printf("\n --------链表合并--------- \n");
USER* node = NULL; //从小到大插入合并
//定义两个 临时指针
USER* p1 = user1;
USER* p2 = user2;
while (p1!=NULL||p2!=NULL)
{
if (p1 != NULL && p2 != NULL)
{
if (p1->age < p2->age)
{
add(&node, p1->age, p1->name);
//指针向前
p1 = p1->next;
}
else
{
add(&node, p2->age, p2->name);
p2 = p2->next;
}
}
else
{
while (p1!=NULL)
{
add(&node, p1->age, p1->name);
//指针向前
p1 = p1->next;
}
while (p2 != NULL)
{
add(&node, p2->age, p2->name);
//指针向前
p2 = p2->next;
}
break;
}
}
showAll(node);
printf("\n ----------------- \n");
system("pause");
}