概述
那么什么是链表呢?链表是一种物理存储单元上非连续、非顺序的存储结构,数据元素的逻辑顺序是通过链表中的指针链接次序实现的。链表由一系列结点组成,即链表中的每个元素,结点可以在运行时动态生成。每个结点均由两个部分所组成:一个是存储数据元素的数据域,另一个是存储相邻结点地址的指针域。相比于线性表顺序结构,链表比较方便插入和删除操作。
对于链表我们可以将其分为单链表、双向链表和循环链表等。首先我们先讲讲单链表。
所谓单链表,是指数据结点是单向排列的。一个单链表结点,其结构类型分为两部分:
1、数据域:用来存储本身数据
2、指针域:用来存储下一个结点地址或者说指向其直接后继的指针。
如下图所示:
注意:如果有图中的红色箭头部分,则变为了单向循环链表。
那什么又是双链表呢?双向链表其实是单链表的改进。当我们对单链表进行操作时,有时你要对某个结点的直接前驱进行操作时,又必须从表头开始查找。这是由单链表结点的结构所限制的。因为单链表每个结点只有一个存储直接后继结点地址的链域,那么能不能定义一个既有存储直接后继结点地址的链域,又有存储直接前驱结点地址的链域的这样一个双链域结点结构呢?这就是双向链表。
在双向链表中,结点除含有数据域外,还有两个链域,一个存储直接后继结点地址,一般称之为右链域;一个存储直接前驱结点地址,一般称之为左链域。
如下图所示:
注意:如果有图中的红色箭头部分,则变为了双向循环链表。
看完了上面的介绍之后,我想读者对于链表算是有了一个大致的了解。那么接下来我们的任务就是学习如何使用链表,我们从最简单的代码开始,教会读者来学习使用链表,首先我们来看一个单链表的创建。为了便于讲解,我在此就把所有的代码放到一个源文件中来执行了,当然读者在实际编写代码的过程中不管是为了维护还是阅读都应该使用头文件,而不要在一个源文件中一条龙似地写到底。
#include <stdio.h>
#include <malloc.h>
#include <string.h>
#include <stdlib.h>
#define N 3
#undef _EXAM_ASSERT_TEST_ //禁用
//#define _EXAM_ASSERT_TEST_ //启用
#ifdef _EXAM_ASSERT_TEST_ //启用断言测试
void assert_report( const char * file_name, const char * function_name, unsigned int line_no )
{
printf( "n[EXAM]Error Report file_name: %s, function_name: %s, line %un",
file_name, function_name, line_no );
abort();
}
#define ASSERT_REPORT( condition )
do{
if ( condition )
NULL;
else
assert_report( __FILE__, __func__, __LINE__ );
}while(0)
#else // 禁用断言测试
#define ASSERT_REPORT( condition ) NULL
#endif /* end of ASSERT */
typedef enum _SListReturn
{
SLIST_RETURN_OK
}SListReturn;
typedef struct node
{
char name[10];
int score;
struct node *link;
}stud;
stud * creat(int n)
{
stud *p,*h,*s;
int i;
if((h=(stud *)malloc(sizeof(stud)))==NULL)
{
printf("分配内存空间失败!");
exit(0);
}
h->name[0]='