前面已经对单链表做了一些解释。链表在进行循环遍历时效率不高,但是插入和删除时优势明显。
单链表实际上是由节点(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函数执行,初始化成功 "); } /* 2.创建线性表,此函数输入负数终止读取数据*/ Node *creatList(Node *pHead) { Node *p1; Node *p2; p1=p2=(Node *)malloc(sizeof(Node)); //申请新节点 if(p1 == NULL || p2 ==NULL) { printf("内存分配失败 "); 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("内存分配失败 "); exit(0); } memset(p1,0,sizeof(Node)); scanf("%d",&p1->element); p1->next = NULL; } printf("creatList函数执行,链表创建成功 "); return pHead; //返回链表的头指针 } /* 3.打印链表,链表的遍历*/ void printList(Node *pHead) { if(NULL == pHead) //链表为空 { printf("PrintList函数执行,链表为空 "); } else { while(NULL != pHead) { printf("%d ",pHead->element); pHead = pHead->next; } printf(" "); } } int main() { Node *pList = NULL; int length = 0; ElemType posElem; initList(&pList); //链表初始化 printList(pList); //遍历链表,打印链表 printf("请为链表输入节点值,输入负数退出 "); pList=creatList(pList); //创建链表 printList(pList); }
链表是由不定数量的节点连接(通过相互之间的引用)起来的,由于这种关系,在链表里我们只定义了头节点和节点数量。节点是由存储的对象及对下一个“节点”的引用封装而成。