13_链表
链表
链表的概述
数组和链表的优缺点
静态数组: int arr[5]; 必须事先确定元素个数, 过多浪费, 过小溢出, 删除插入效率低
动态数组: 不需要知道元素个数, 在使用中动态申请, 删除插入数据效率低
数组优点: 遍历元素方便
链表: 不需要事先知道数据的个数, 在使用中动态申请, 插入删除不需要移动数据
链表概述
链表由一个个节点组成, 节点没有名字, 每个节点从堆区动态申请, 节点间物理空间是非连续的, 但是每个节点通过指针域, 保存下一个节点的位置, 达到逻辑上的连续
静态链表
设计链表节点
struct stu
{
//数据域
int num;
char name[32];
//指针域
struct stu *next;
};
int main(int argc, char const *argv[])
{
struct stu node1 = {101, "lucy", NULL};
struct stu node2 = {102, "bob", NULL};
struct stu node3 = {103, "tom", NULL};
struct stu node4 = {104, "tak", NULL};
struct stu node5 = {105, "fub", NULL};
//定义链表头
struct stu *head = &node1;
node1.next = &node2;
node2.next = &node3;
node3.next = &node4;
node4.next = &node5;
//遍历
struct stu *pb = head;
while (pb != NULL)
{
//访问数据
printf("%d %s\n", pb -> num, pb -> name);
pb = pb->next;
}
return 0;
}
学生管理系统
结构体起别名
//第一种
typedef struct Data1
{
int a;
char b;
} data1, *D_POINTER; //类型名, 指针类型名
int main(int argc, char const *argv[])
{
data1 d1 = {100, 'e'};
D_POINTER p = &d1;
return 0;
}
//第二种
struct Data2
{
int a;
char b;
};
int main(int argc, char const *argv[])
{
typedef struct Data2 data2;
data2 d2;
return 0;
}
工程的main函数的设计
#include<stdio.h>
#include<string.h>
#include"link.h"
STU *head = NULL; //一定要初始化为NULL
void help()
{
printf("********************************\n");
printf("*help:帮助信息 *\n");
printf("*insert:插入链表节点 *\n");
printf("*print:遍历链表节点 *\n");
printf("*search:查询链表某个节点 *\n");
printf("*delete:删除链表某个节点 *\n");
printf("*free:释放整个链表 *\n");
printf("*quit:退出程序 *\n");
printf("********************************\n");
}
int main(int argc, char const *argv[])
{
help();
while (1)
{
char cmd[128] = "";
printf("请输入操作命令:");
scanf("%s", cmd);
if (strcmp(cmd, "help") == 0)
{
help();
}
else if (strcmp(cmd, "insert") == 0)
{
printf("请输入需要插入的学生信息num name score: ");
STU tmp;
scanf("%d %s %f", &tmp.num, &tmp.name, &tmp.score);
//将tmp插入到链表中
head = insert_link(head, tmp);
}
else if (strcmp(cmd, "print") == 0)
{
printf("---------链表遍历----------\n");
}
else if (strcmp(cmd, "search") == 0)
{
printf("---------链表查询----------\n");
}
else if (strcmp(cmd, "delete") == 0)
{
printf("---------删除链表指定节点----------\n");
}
else if (strcmp(cmd, "free") == 0)
{
printf("---------释放链表----------\n");
}
else if (strcmp(cmd, "quit") == 0)
{
printf("---------退出程序----------\n");
break;
}
}
return 0;
}
链表插入节点值---头部之前插入
如果链表不存在
#include<stdio.h>
#include"link.h"
#include<stdlib.h>
//头部之前插入
STU* insert_link(STU *head, STU tmp)
{
//为待插入的数据申请空间
STU *pi = (STU *)calloc(1, sizeof(STU));
if (pi == NULL)
{
perror("calloc");
exit(-1); //结束进程
}
//将tmp数据赋值到*pi
*pi = tmp;
pi->next = NULL;
//判断链表是否存在
if (head == NULL) //不存在
{
head = pi;
}
else //存在
{
pi->next = head;
head = pi;
}
return head;
}
尾部插入
//尾部插入
STU* insert_link(STU *head, STU tmp)
{
//为待插入的数据申请空间
STU *pi = (STU *)calloc(1, sizeof(STU));
if (pi == NULL)
{
perror("calloc");
exit(-1); //结束进程
}
//将tmp数据赋值到*pi
*pi = tmp;
pi->next = NULL;
//判断链表是否存在
if (head == NULL) //不存在
{
head = pi;
}
else //存在
{
STU *pb = head;
while (pb->next != NULL)
{
pb = pb->next;
}
pb->next = pi;
}
return head;
}
有序 插入
// 有序插入
STU *insert_link(STU *head, STU tmp)
{
// 为待插入的数据申请空间
STU *pi = (STU *)calloc(1, sizeof(STU));
if (pi == NULL)
{
perror("calloc");
exit(-1); // 结束进程
}
// 将tmp数据赋值到*pi
*pi = tmp;
pi->next = NULL;
// 判断链表是否存在
if (head == NULL) // 不存在
{
head = pi;
}
else // 存在
{
// 寻找插入点位置
STU *pb = head, *pf = head;
while ((pi->num > pb->num) && (pb->next != NULL))
{
pf = pb;
pb = pb->next;
}
if (pi->num > pb->num) // 尾部插入
{
pb->next = pi;
}
else // 头部或中部插入
{
if (head == pb) // 头部之前插入
{
pi->next = head;
head = pi;
}
else // 中部插入
{
pf->next = pi;
pi->next = pb;
}
}
}
return head;
}
遍历
// 遍历
void printf_link(STU *head)
{
// 判断链表是否存在
if (head == NULL)
{
printf("link not exits\n");
return;
}
else
{
STU *pb = head;
while (pb != NULL)
{
printf("%d %s %f\n", pb->num, pb->name, pb->score);
pb = pb->next;
}
}
}
姓名查询
//姓名查询
STU *search_link(STU *head, char *name)
{
// 判断链表是否存在
if (NULL == head)
{
printf("link not exits\n");
return NULL;
}
else // 链表存在
{
STU *pb = head;
while ((strcmp(pb->name, name) != 0) && (pb->next != NULL))
{
pb = pb->next;
}
if (strcmp(pb->name, name) == 0) // 找到
{
return pb;
}
return NULL;
}
}
删除指定节点
STU *delete_link(STU *head, int num) //删除节点
{
// 判断链表是否存在
if (NULL == head)
{
printf("链表为空\n");
return head;
}
// 查找删除点
STU *pb = head, *pf = head;
while ((pb->num != num) && (pb->next != NULL))
{
pf = pb;
pb = pb->next;
}
if (pb->num == num) // 找到
{
if (pb == head) // 是头节点
{
head = head->next;
}
else //是中间或尾节点
{
pf->next = pb->next;
}
free(pb);
printf("已成功删除num = %d的节点\n", num);
}
else // 未找到
{
printf("没找到该节点\n");
}
return head;
}
释放链表
STU* free_link(STU *head) //释放链表
{
//判断链表是否存在
if (head == NULL)
{
printf("link not exits\n");
}
else
{
STU *pb = head;
while (pb != NULL)
{
head = pb->next;
free(pb);
pb = head;
}
printf("释放链表成功\n");
}
return NULL;
}
反转链表
STU *reverse_link(STU *head) // 反转链表
{
// 判断链表是否存在
if (NULL == head)
{
printf("link not exits\n");
}
else
{
STU *pb = head->next, *pf = head;
pf->next = NULL;
while (pb != NULL)
{
head = pb;
pb = pb->next;
head->next = pf;
pf = head;
}
printf("链表反转成功!!");
}
return head;
}
链表排序
void sort_link(STU *head) // 排序
{
if (NULL == head)
{
printf("link is not exits\n");
}
else
{
STU *p_i = head, *p_j, *min, temp;
for (; p_i->next != NULL; p_i = p_i->next)
{
min = p_i;
for (p_j = p_i->next; p_j != NULL; p_j = p_j->next)
{
if (p_j->num < p_i->num)
{
min = p_j;
}
}
if (min != p_i)
{
temp = *min;
*min = *p_i;
*p_i = temp;
temp.next = min->next;
min->next = p_i->next;
p_i->next = temp.next;
}
}
printf("排序成功!\n");
}
return head;
}
双向链表
typedef struct stu
{
//数据域
int num;
char name[32];
//指针域
struct stu *pre;
struct stu *next;
}STU;
尾插法
// 尾插法
void insert_link(STU **p_head, STU tmp)
{
STU *head = *p_head;
// 为插入的节点申请空间
STU *pi = (STU *)calloc(1, sizeof(STU));
*pi = tmp;
pi->next = NULL;
pi->pre = NULL;
// 判断链表是否为空
if (NULL == head)
{
head = pi;
pi->next = pi;
pi->pre = pi;
}
else
{
head->pre->next = pi;
pi->pre = head->pre;
head->pre = pi;
pi->next = head;
}
// 更新外部的head
*p_head = head;
}
双向遍历
// 双向遍历
void print_link(STU *head)
{
if (NULL == head)
{
printf("The linked list does not exist.");
}
else
{
STU *pn = head;
STU *pr = head->pre;
while (1)
{
if (pn == pr) //链表节点为奇数个
{
printf("%d %s\n", pn->num, pn->name);
break;
}
else if(pn->next == pr) //链表节点为偶数个
{
printf("%d %s\n", pn->num, pn->name);
printf("%d %s\n", pr->num, pr->name);
break;
}
printf("%d %s\n", pn->num, pn->name);
printf("%d %s\n", pr->num, pr->name);
pn = pn->next;
pr = pr->pre;
}
}
}
双向查询
// 双向查询
STU *search_link(STU *head)
{
if (NULL == head)
{
printf("The linked list does not exist.");
}
else
{
printf("请输入你要查询的学号: ");
int num = 0;
scanf("%d", &num);
STU *ph = head;
STU *pt = head->pre;
while ((ph->num != num) && (pt->num != num) && (ph->next != pt) && (pt != ph))
{
ph = ph->next;
pt = pt->pre;
}
if (ph->num == num)
{
return ph;
}
else if (pt->num == num)
{
return pt;
}
else
{
printf("没查到\n");
}
}
return NULL;
}
双向删除
// 删除
void delete_link(STU **p_head, int num)
{
STU *head = *p_head;
if (NULL == head)
{
printf("link does not exits!");
}
else
{
STU *top = head;
STU *tail = head->pre;
while ((top->num != num) && (tail->num != num) && (top->next != tail) && (tail != top))
{
top = top->next;
tail = tail->pre;
}
if (top->num == num)
{
if (top == head)
{
head = head->next;
}
top->pre->next = top->next;
top->next->pre = top->pre;
free(top);
}
else if (tail->num == num)
{
tail->pre->next = tail->next;
tail->next->pre = tail->pre;
free(tail);
}
else
{
printf("没找到num, 删除失败!\n");
}
*p_head = head;
}
}
释放双向链表
// 释放整个链表
void free_link(STU **p_head)
{
STU *head = *p_head;
if (NULL == head)
{
printf("link does not exits.\n");
}
else
{
STU *pn = head;
do
{
head = head->next;
printf("释放%d\n", pn->num);
free(pn);
pn = head;
} while (pn != *p_head);
*p_head = NULL;
printf("链表释放成功!\n");
}
}
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】凌霞软件回馈社区,博客园 & 1Panel & Halo 联合会员上线
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】博客园社区专享云产品让利特惠,阿里云新客6.5折上折
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 在鹅厂做java开发是什么体验
· 百万级群聊的设计实践
· WPF到Web的无缝过渡:英雄联盟客户端的OpenSilver迁移实战
· 永远不要相信用户的输入:从 SQL 注入攻防看输入验证的重要性
· 浏览器原生「磁吸」效果!Anchor Positioning 锚点定位神器解析