1.定义
相比较于顺序表,单链表在内存中存储数据时是不连续的,可以说是一种见缝插针的方式。因为存储时是不连续存储,因此在每一个节点都需要一个指向下一个节点的指针,如此才能把每一个节点串起来,形成一个链表。
2.基本操作
1.初始化链表
2.创建链表(头插法或尾插法)
3.插入节点(前插法或后插法)
4.删除节点
5.查找节点(按序号查找或按值查找)
6.遍历链表
7.释放链表
3.Show Code
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#pragma warning(disable:4996) //用于屏蔽4996的错误
#define ElemType int
typedef struct LinkList
{
ElemType data; //数据
struct LinkList *next; //指向下一个节点的指针
}LinkList;
LinkList *L = NULL; //用L作为全局变量,在下面的函数中就不用再传参
//初始化链表
void InitList()
{
//L = new LinkList;//分配内存
L = (LinkList *)malloc(sizeof(LinkList));//分配内存
L->next = NULL;
printf("初始化成功!\n");
}
//头插法创建链表
void CreateList_Head()
{
LinkList *node;
ElemType data;
int i = 1;
printf("第%d个数据:", i);
scanf("%d", &data);
while (data != 999) //输入999完成链表创建
{
node = (LinkList *)malloc(sizeof(LinkList));
node->data = data;
node->next = L->next;
L->next = node;
i++;
printf("第%d个数据:", i);
scanf("%d", &data);
}
printf("创建完成\n");
}
//尾插法创建链表
void CreateList_Tail()
{
LinkList *p ,*tail;
tail = L;
ElemType data;
ElemType i = 1;
printf("第%d个数据:", i);
scanf("%d", &data);
while (data != 999)
{
p = (LinkList *)malloc(sizeof(LinkList));
p->data = data;
p->next = NULL;
tail->next = p;
tail = p;
i++;
printf("第%d个数据:", i);
scanf("%d", &data);
}
printf("创建完成\n");
}
//插入新结点--前插法
void ListInsert_Pre()
{
LinkList *p = L;
LinkList *newNode; //要插入的新节点
ElemType pos,elem;
printf("请输入要插入的位置:");
scanf("%d", &pos);
printf("请输入要插入的数据:");
scanf("%d", &elem);
//为新节点分配空间
//newLinkList = new LinkList;
newNode = (LinkList *)malloc(sizeof(LinkList));
newNode->data = elem;
//判断插入位置是否合法
if (pos == 0)
return;
//寻找pos-1的位置
ElemType j = 0;
while (p && j < pos - 1)
{
p = p->next;
j++;
}
if (!p || j > pos - 1)
return;
newNode->next = p->next;
p->next = newNode;
}
//插入节点-后插法
void ListInsert_back()
{
LinkList *p = L;
LinkList *newNode;
ElemType pos, elem;
printf("请输入要插入的位置:");
scanf("%d", &pos);
printf("请输入要插入的数据:");
scanf("%d", &elem);
//为新节点分配空间
newNode = (LinkList *)malloc(sizeof(LinkList));
newNode->data = elem;
//寻找第pos个节点
ElemType j = 0;
while (p && j < pos)
{
p = p->next;
j++;
}
if (!p || j > pos)
return;
newNode->next = p->next;
p->next = newNode;
}
//删除某个结点
void ListDel()
{
LinkList *p = L;
LinkList *q;//用于记录被删除的节点
ElemType pos;
ElemType elem;
printf("请输入要删除的位置:");
scanf("%d", &pos);
//寻找第pos-1个结点
ElemType j = 0;
while (p && j < pos - 1)
{
p = p->next;
j++;
}
if (!p || j > pos - 1)
return;
q = p->next;
elem = q->data;
p->next = q->next;
q->next = NULL;
printf("被删除的元素为:%d\n", elem);
free(q);
}
//遍历链表并输出表中元素
void ListTraverse()
{
if (L == NULL)
{
printf("链表为空!\n");
return;
}
LinkList *p;
p = L->next;
printf("表中元素:\n");
while (p)
{
printf("%d\t", p->data);
p = p->next;
}
printf("\n");
}
//查找--按值查找
void ListSearch_Value()
{
LinkList *p;
p = L->next;
ElemType elem;
printf("请出入要查找的值:");
scanf("%d", &elem);
ElemType j = 1;
while (p && p->data != elem)
{
p = p->next;
j++;
}
if (!p)
printf("链表中没有该元素!\n");
else
printf("该元素在链表中的位置为:%d\n", j);
}
//查找--按序号查找
void ListSearch_Num()
{
LinkList *p;
p = L->next;
ElemType pos;
printf("请出入要查找的序号:");
scanf("%d", &pos);
ElemType j = 1;
while (p && j < pos)
{
p = p->next;
j++;
}
if (!p || j > pos)
{
printf("链表中没有该元素!\n");
return;
}
else
printf("链表中第%d个元素为:%d\n",pos, p->data);
}
void ListDestroy()
{
LinkList *p = L;
LinkList *q = NULL;
while (p)
{
q = p; //保存当前结点
p = p->next;//让当前结点指向下一个结点
free(q); //释放当前结点
}
free(p);
L = NULL;
if (L = NULL)
printf("释放成功!\n");
}
void Menu()
{
printf("\t\t\t\t***************************************************\n");
printf("\t\t\t\t1.初始化\t\t\t\t2.创建链表\n");
printf("\t\t\t\t3.插入节点\t\t\t\t4.删除节点\n");
printf("\t\t\t\t5.查找节点\t\t\t\t6.遍历链表\n");
printf("\t\t\t\t7.释放链表\n");
printf("\t\t\t\t***************************************************\n");
}
//主函数测试
int main()
{
while (1)
{
Menu();
ElemType choice;
printf("请选择菜单:");
scanf("%d", &choice);
switch (choice)
{
case 1:
{
InitList();
break;
}
case 2:
{
printf("请选择创建方式(1.头插法 2.尾插法):");
ElemType num;
scanf("%d", &num);
if (num == 1)
{
CreateList_Head();
break;
}
if (num == 2)
{
CreateList_Tail();
break;
}
}
case 3:
{
printf("请选择插入方式(1.前插法 2.尾插法):");
ElemType num;
scanf("%d", &num);
if (num == 1)
{
ListInsert_Pre();
break;
}
if (num == 2)
{
ListInsert_back();
break;
}
}
case 4:
{
ListDel();
break;
}
case 5:
{
printf("请出入查找方式(1.按值查找 2.按序号查找):");
ElemType num;
scanf("%d", &num);
if (num == 1)
{
ListSearch_Value();
break;
}
if (num == 2)
{
ListSearch_Num();
break;
}
}
case 6:
{
ListTraverse();
break;
}
case 7:
{
ListDestroy();
break;
}
default:
break;
}
}
}
4.运行结果
5.总结
单链表和顺序表都属于线性表,因此二者都具备线性表的特性,表中元素的数据类型都相同,每个元素占有相同大小的存储空间。
从上一篇的顺序表和这一篇的单链表中,对比可以看出,在查找数据和修改数据时,顺序表不需要移动数据,直接根据下标就可以查找对应的数据对其进行更新。而单链表中无论是查找还是更新,都必须从头开始遍历;但是对于从增加和删除,顺序表每次都要移动数据,而对于单链表来说,只需要修改指针的指向即可。
因此,总结来说,当对数据查找和修改操作比较频繁时,我们可以优先考虑顺序表,而对数据增加删除操作比较频繁时,可以优先考虑单链表。