上一篇已经写过,线性表的存储结构有顺序存储结构和链式存储结构两种,前者称为
顺序表,后者称为
链表。
本篇文章是对链表的学习。
-------------------------------------------------------------------------------------------------------------------------
链表:在链表存储中,每个结点不仅包含所含元素的信息,还包含元素之间逻辑关系的信息。
顺序表与链表的比较:
(1)顺序表具有随机访问性,而链表不支持随机访问。(顺序表是连续存储的,只要知道开始的位置,就能找到任意一个位置的数,而链表中当前结点的位置是由其前驱结点中的地址信息所指示的,而不是由其相对于初始位置的偏移量来确定的)
(2)顺序表占用连续的存储空间,链表的结点可以散落在内存中的任意位置,不需要一次性划分所有结点所需的空间链表,支持存储空间的动态分配。
(3)链表中的每一个结点需要划出一部分空间来存储指向下一个结点位置的指针,因此链表中结点的存储空间利用率较顺序表稍微低一些。
--------------------------------------------------------------------------------------------------------------------------
单链表是长这个样子滴!
下面这个是单链表的结点结构图
---------------------------------------------------------------------------------------------------------------------------
还是直接上代码!
(1)单链表结点定义
typedef struct
{
int data;
struct LNode *next;
}LinkList;
(2)输入数据存入数组a
int n;int a[maxSize]; //这四个量要设置成全局变量,因为这两个量在多个函数中都要用到
LinkList *head; //头指针LinkList *L;
void InData( )
{
printf("请输入您要存入数据的个数:");
scanf_s("%d", &n);
if (n >= 0 && n < maxSize)
{
printf("请输入数据:");
for (int i = 0; i < n; i++)
{
scanf_s("%d", &a[i]);
}
printf("您所输入的数据为:");
for (int j = 0; j < n; j++)
{
printf("%d ", a[j]);
}
}
else
{
printf("不在区间内!请重新选定数值!");
}
(3)尾插法建立链表(有头结点)
int CreateList1(LinkList *L)
{
InData();
LinkList *s,*r; //s用来指向新申请的结点,r用来指向链表L的终端结点
L = (LinkList*)malloc(sizeof(LinkList)); //申请L的头结点空间
head = (LinkList*)malloc(sizeof(LinkList));
L->next = NULL; //初始化
r = L; //r指向头结点,此时的头结点就是终端结点
head = L; //非常重要!!!得到链表的头指针,以便在之后的函数中找到链表调用
for (int i = 0; i <n; i++)
{
s = (LinkList*)malloc(sizeof(LinkList)); //s指向新申请的结点
s->data = a[i];
r->next = s; //用r来接纳新结点
r = r->next; //r指向终端结点,以便于接纳下一个结点的到来
}
r->next = NULL; //数组a中所有的元素都已经装入链表L中,L的终端结点指针域设置为NULL,L就建立完成了
printf("\n链表中的数据为:");
LinkList *p = L->next;
for (int j = 0; j < n; j++)
{
printf("%d ",p->data);
p = p->next;
}
return L;
}
结果如图所示:
(4)头插法建立链表(有头结点)
void CreateList2(LinkList *L)
{
InData();
LinkList *s;
int i;
L = (LinkList*)malloc(sizeof(LinkList));
L->next = NULL;
head = (LinkList*)malloc(sizeof(LinkList));
head = L;
for (i = 0; i < n; i++)
{
s= (LinkList*)malloc(sizeof(LinkList));
s->data = a[i];
s->next = L->next; //s所指向的新节点的指针域指向L的开始结点
L->next = s; //头结点的指针域指向s结点,使得s成为新的开始结点
}
printf("\n链表中的数据为:");
LinkList *p = L->next;
for (int j = 0; j < n; j++)
{
printf("%d ", p->data);
p = p->next;
}
return L;
}
结果如下图所示:
(5)在指定位置后面插入链表
步骤1:先让S结点指向p之后的结点
步骤2:切断p和p后面的那个结点的关系
步骤3:让p结点的指针域指向S结点
int InsertNode()
{
LinkList *s,*r;
s = (LinkList*)malloc(sizeof(LinkList));
r = (LinkList*)malloc(sizeof(LinkList));
r = head; //r此时和头指针一样指向链表的头结点处
int x, e;
printf("\n请输入您想插入数据的位置:");
scanf_s("%d", &x);
if (x<0 || x>n)
{
printf("\n区间错误");
}
else
{
printf("\n请输入数据:");
scanf_s("%d", &e);
s->data = e;
}
for (int i = 0; i < x; i++)
head = head->next;
s->next = head->next; //插入最关键的两步
head->next = s;
n++; //链表长度增加
printf("\n数据为:");
for (int j = 0; j < n; j++)
{
r = r->next;
printf("%d ", r->data);
}
return L;
}
void main()
{
CreateList2(&L);
InsertNode();
system("pause");
}
结果如下图:
(6)删除指定位置的结点
步骤1:声明一个指针p指向链表头结点,向后遍历p=p->next,找到要删除的结点的位置
步骤2:要删除的结点q=p->next
步骤3:p->next=q->next
步骤4:释放内存 free(q)
int DeleteNode()
{
LinkList *q,*p;
int x;
q = (LinkList*)malloc(sizeof(LinkList));
p = (LinkList*)malloc(sizeof(LinkList));
p = head;
printf("\n请输入您想删除的结点的位置:");
scanf_s("%d", &x);
if (x<0 || x>n)
{
printf("不在区间内!");
}
for (int i = 0; i < x; i++)
{
head = head->next;
}
q = head->next;
head->next = q->next;
n--;
printf("\n数据为:");
for (int j = 0; j < n; j++)
{
p = p->next;
printf("%d ", p->data);
}
return L;
}
void main()
{
CreateList1(&L);
//InsertNode();
DeleteNode();
system("pause");
}