前面已经对单链表做了一些解释。链表在进行循环遍历时效率不高,但是插入和删除时优势明显。
单链表实际上是由节点(Node)组成的,一个链表拥有不定数量的节点。而向外暴露的只有一个头节点(Head),我们对链表的所有操作,都是直接或者间接地通过其头节点来进行的。节点(Node)是由一个需要储存的对象及对下一个节点的引用组成的。也就是说,节点拥有两个成员:储存的对象、对下一个节点的引用。其实应该用数据和地址代替前面的对象和引用的。
单链表的结构示意图(包括空的单链表):
那么大家可能清楚了,为什么说有了头节点就可以操作所有节点。因为有连续不断的引用嘛!那么在链表里的属性大概就只有两个了:头节点和节点数量。当然,根据需要,我们通常需要更多的属性。
下面用C语言简单写一个单链表,并完成初始化,创建链表与链表遍历。
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
typedef int ElemType; /* ElemType类型根据实际情况而定,这里假设为int */
/* 定义单链表结点类型 */
typedef struct Node{
ElemType element;
struct Node *next;
}Node;
/* 1.初始化线性表,即置单链表的表头指针为空 */
void initList(Node **pNode)
{
*pNode = NULL;
printf("initList函数执行,初始化成功\n");
}
/* 2.创建线性表,此函数输入负数终止读取数据*/
Node *creatList(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);
}
memset(p1,0,sizeof(Node));
scanf("%d",&p1->element);
p1->next = NULL;
}
printf("creatList函数执行,链表创建成功\n");
return pHead; //返回链表的头指针
}
/* 3.打印链表,链表的遍历*/
void printList(Node *pHead)
{
if(NULL == pHead) //链表为空
{
printf("PrintList函数执行,链表为空\n");
}
else
{
while(NULL != pHead)
{
printf("%d ",pHead->element);
pHead = pHead->next;
}
printf("\n");
}
}
int main()
{
Node *pList = NULL;
int length = 0;
ElemType posElem;
initList(&pList); //链表初始化
printList(pList); //遍历链表,打印链表
printf("请为链表输入节点值,输入负数退出 \n");
pList=creatList(pList); //创建链表
printList(pList);
}
链表是由不定数量的节点连接(通过相互之间的引用)起来的,由于这种关系,在链表里我们只定义了头节点和节点数量。节点是由存储的对象及对下一个“节点”的引用封装而成。