1. 为什么要用到链表
数组作为存放同类数据的集合,给我们在程序设计时带来很多的方便,增加了灵活性。但数组也同样存在一些弊病。如数组的大小在定义时要事先规定,不能在程序中进行调整,这样一来,在程序设计中针对不同问题有时需要3
0个大小的数组,有时需要5
0个数组的大小,难于统一。我们只能够根据可能的最大需求来定义数组,常常会造成一定存储空间的浪费。
我们希望构造动态的数组,随时可以调整数组的大小,以满足不同问题的需要。链表就是我们需要的动态数组。它是在程序的执行过程中根据需要有数据存储就向系统要求申请存储空间,决不构成对存储区的浪费。
链表是一种复杂的数据结构,其数据之间的相互关系使链表分成三种:单链表、循环链表、双向链表,下面将逐一介绍。
2.单链表
链表在进行循环遍历时效率不高,但是插入和删除时优势明显。
单链表实际上是由节点(Node)组成的,一个链表拥有不定数量的节点。而向外暴露的只有一个头节点(Head),我们对链表的所有操作,都是直接或者间接地通过其头节点来进行的。节点(Node)是由一个需要储存的对象及对下一个节点的引用组成的。也就是说,节点拥有两个成员:储存的对象、对下一个节点的引用。其实应该用数据和地址代替前面的对象和引用的。
单链表的结构示意图(包括空的单链表):
那么大家可能清楚了,为什么说有了头节点就可以操作所有节点。因为有连续不断的引用嘛!那么在链表里的属性大概就只有两个了:头节点和节点数量。当然,根据需要,我们通常需要更多的属性。
下面用C语言简单写一个单链表,并完成初始化,创建链表与链表遍历、插入、删除等操作。
(1)链表的创建、输出步骤
单链表的创建过程有以下几步:
1 ) 定义链表的数据结构;
2
) 创建一个空表;
3 ) 利用malloc ( )函数向系统申请分配一个节点;
4 ) 将新节点的指针成员赋值为空。若是空表,将新节点连接到表头;若是非空表,将新节点接到表尾;
5 ) 判断一下是否有后续节点要接入链表,若有转到3 ),否则结束;
单链表的输出过程有以下几步:
1 ) 找到表头;
2 ) 若是非空表,输出节点的值成员,是空表则退出;
3 )
跟踪链表的增长,即找到下一个节点的地址;
4 ) 转到2 ) 。
3.代码
#include "stdafx.h"
#include "stdio.h"
#include
#include "string.h"
typedef int elemType;
typedef struct
Node{ elemType
element;
Node
*next;
}Node;
void initList(Node **pNode)
{
*pNode =
NULL;
printf("initList函数执行,初始化成功\n");
}
Node *createList(Node *pHead)
{
Node
*p1;
Node
*p2;
p1=p2=(Node
*)malloc(sizeof(Node)); //申请新节点
if(p1 ==
NULL || p2 ==NULL)
{
printf("内存分配失败\n");
exit(0);
}
memset(p1,0,sizeof(Node));
scanf("%d",&p1->element); //输入新节点
p1->next
=
NULL; //新节点的指针置为空
while(p1->element >
0) //输入的值大于0则继续,直到输入的值为负
{
if(pHead ==
NULL) //空表,接入表头
{
pHead = p1;
}
else {
p2->next =
p1; //非空表,接入表尾
}
p2 = p1;
p1=(Node
*)malloc(sizeof(Node)); //再重申请一个节点
if(p1 == NULL || p2 ==NULL)
{
printf("内存分配失败\n");
exit(0);
}