(一)单链表图文解析

<mark>线性表链式存储结构的特点</mark>

结点 包括两个域:其中存储数据元素信息的域称为数据域;存储直接后继存储位置的域称为指针域。
数据域: 用一组任意的存储单位存储线性表的数据元素(该组存储单元的地址可以是连续的,也可以是不连续的)。
指针域: 为了表示每个数据元素ai与其直接后继数据元素a(i+1)之间的逻辑关系,除了存储其本身的信息之外,还需要一个指示其直接后继的信息(即直接后继的存储位置)。

<mark>图示</mark>

<mark>用通俗简单的话来讲:</mark>

单链表就像过去靠挂钩连接的火车,一节车厢代表着一个结构体,车厢里面装着的东西代表着数据域,车厢的挂钩代表着指针域,车厢可以装载货物或者人或者其他的都是数据,而每一节车厢可以依靠挂钩(指针)连接着其他车厢,火车车头就是单链表的表头,火车车尾就是单链表的表尾。

(二)单链表代码解析

(1)单链表的基本操作

1.1 创建单链表

typedef char ElemType;	//定义ElemType的数据类型尾char字符类型
typedef struct LNode	//定义结构体LNode
{
	ElemType data;		//数据域,存放数据
	struct LNode *next;	//指针域,存放指针
}LinkList;				//结点

1.2 初始化单链表

void InitList(LinkList *&L)		//初始化单链表,即单链表初始只有一个没有数据且指针为空的结点
{
	L = (LinkList *)malloc(sizeof(LinkList));//为结点分配空间
	L->next = NULL;		//指针所指为空
}

1.3 销毁单链表

void DestroyList(LinkList *&L)//销毁单链表
{
	LinkList *q;
	if (!L)		//首先判断单链表是否存在
		printf("单链表不存在\n");
	else	
	{
		while (L)		//若存在,进入循环,直到单链表L为空
		{
			q = L->next;	//q为单链表的下一个结点
			free(L);		//释放头结点
			L = q;			//转到下一个结点进入下一次循环进行释放
		}
	}
}

1.4 清空单链表

void ClearList(LinkList *&L)		//与销毁单链表操作类似
{
	LinkList *p = L->next, *q;		//但不释放头结点,因为清空后还需要继续使用单链表
	if (!p)
		printf("单链表已空\n");
	else
	{
		while (p)
		{
			q = p->next;
			free(p);
			p = q;
		}
		L->next = NULL;			//最后只剩下头结点指向空,若还需要用可继续在头结点后增加结点
	}
}

1.5 单链表表长

void ListLength(LinkList *L)
{
	LinkList *p = L;
	int i = 0;
	while (p->next != NULL) //遍历结点
	{
		i++;			//每遍历到一个结点通过i来记录长度
		p = p->next;	//移动位置到下一个结点
	}
	printf("链表长为%d\n", i); //当尾节点指向为空NULL,退出循环输出所计数i的值
}

1.6 遍历单链表

void DispList(LinkList *L)
{
	LinkList *p = L->next;
	if (!p) 	//若头结点的next为空结点,则该单链表为空
		printf("链表为空");
	while (p != NULL)		//遍历
	{
		printf("%c ",p->data);	//每遍历到一个结点,就输出该结点的数据元素
		p = p->next;		//指向下一个结点
	}
	printf("\n");
}

1.7 单链表插入结点

void ListInsert(LinkList *&L)
{
	ElemType e;    
	int i,j = 0;
	LinkList *p = L, *s;
	printf("请输入插入结点的位置和元素值:");
	scanf("%d %c", &i, &e);		//输入插入结点的位置和数据元素
	while (j < i - 1 && p != NULL)	//查找所要插入的位置的前一个结点,且确保插入位置合法(即存在该位置)
	{
		j++;
		p = p->next;
	}
	if (p == NULL)			//若p为空,即一直遍历位置的到表尾结点也未找到所输入的位置
		printf("未查询到该位置信息\n");
	else		//在确保位置信息正确后
	{
		s = (LinkList *)malloc(sizeof(LinkList));	//创建一个用于插入的新结点
		s->data = e;		//将输入的数据赋值给新结点的数据域
		s->next = p->next;	//将插入结点的前一个结点p的下一个结点赋值给新结点的指针域
		p->next = s;		//最后将新结点接在插入结点前的结点p的后面,完成插入
		printf("插入成功!\n");
	}

}

1.8 判断单链表是否为空

void ListEmpty(LinkList *L)   //直接判断头结点的下一个结点是否为空
{
	if (L->next == NULL)
		printf("单链表为空\n");
	else
		printf("单链表非空\n");
}

1.9 查找单链表某数据的位置

void LocateElem(LinkList *L)
{
	ElemType e;
	int i = 1;
	LinkList *p = L->next;
	printf("请输入查询的元素值:");
	rewind(stdin);		//在使用scanf()函数之前先清空键盘缓存区,防止回车键的干扰跳过输入函数
	scanf("%c",&e);		//输入查询的元素值
	while (p != NULL&&p->data!=e)	//遍历单链表并判断每个结点的数据域元素是否与查找元素相等
	{
		p = p->next;	//转到下一个结点 
		i++;			//通过i来记录结点位置
	}
	if (p == NULL)		//若p为空则未查询到输入元素位置
		printf("未查询到该元素位置\n");
	else			//若查询到元素值,则输入元素位置
		printf("该元素位置为:%d\n", i);	
}

1.10 取单链表第i个结点的数据

void GetElem(LinkList *L)
{
	int i,j = 0;
	LinkList *p = L;
	printf("请输入需要查找的位置:");
	scanf("%d", &i);	//输入需要查找的位置
	while (j < i&&p != NULL)	//遍历单链表查找位置
	{
		j++;
		p = p->next;
	}
	if (p == NULL)		//若p为空,则未查询到该位置的元素
		printf("未查询到该位置的元素\n");
	else			//若不为空,即查询到该结点位置,输出该结点数据元素
		printf("该位置的元素为:%c\n",p->data);
}

1.11 删除单链表的某个结点

void ListDelete(LinkList *&L, ElemType &e)
{
	int i,j = 0;
	LinkList *p = L, *q;
	printf("请输入需要删除的结点位置:");
	scanf("%d", &i);	//输入需要删除的结点位置
	while (j < i - 1 && p != NULL)	//遍历单链表,查询所输入位置的前一个结点,且确保输入的位置合法
	{
		j++;
		p = p->next;
	}
	if (p == NULL)	//未查询到该位置的前一个结点,即所查询位置的前一个结点为空
		printf("未查询到该位置信息\n");
	else		//若查询到该位置的前一个结点
	{
		q = p->next;
		if (q == NULL)	//但查询位置的所在结点为空
			printf("该结点为空\n");	//即所查询位置的结点为空
		else	//若查询结点不为空,即查询位置结点和其前一个结点都存在
		{
			e = q->data;	//将该结点数据域赋值给e
			p->next = q->next;	//将删除结点的下一个结点赋值给删除结点的前一个结点指针域,即将删除结点的下一个结点交给前一个结点的指针next
			free(q); //最后释放删除结点
		}
		printf("删除成功!\n");
	}

}

(2)单链表源代码及测试

2.1 源代码

#include <stdio.h>
#include<malloc.h>

typedef char ElemType;

typedef struct LNode
{
	ElemType data;
	struct LNode *next;
}LinkList;

void InitList(LinkList *&L)
{
	L = (LinkList *)malloc(sizeof(LinkList));
	L->next = NULL;
}

void DestroyList(LinkList *&L)//销毁单链表
{
	LinkList *q;
	if (!L)
		printf("单链表不存在\n");
	else
	{
		while (L)
		{
			q = L->next;
			free(L);
			L = q;
		}
	}
}

void ClearList(LinkList *&L)
{
	LinkList *p = L->next, *q;
	if (!p)
		printf("单链表已空\n");
	else
	{
		while (p)
		{
			q = p->next;
			free(p);
			p = q;
		}
		L->next = NULL;
	}
}

void ListEmpty(LinkList *L)
{
	if (L->next == NULL)
		printf("单链表为空\n");
	else
		printf("单链表非空\n");
}

void ListLength(LinkList *L)
{
	LinkList *p = L;
	int i = 0;
	while (p->next != NULL)
	{
		i++;
		p = p->next;
	}
	printf("链表长为%d\n", i);
}

void DispList(LinkList *L)
{
	LinkList *p = L->next;
	if (!p)
		printf("链表为空");
	while (p != NULL)
	{
		printf("%c ",p->data);
		p = p->next;
	}
	printf("\n");
}

void GetElem(LinkList *L)
{
	int i,j = 0;
	LinkList *p = L;
	printf("请输入需要查找的位置:");
	scanf("%d", &i);	
	while (j < i&&p != NULL)
	{
		j++;
		p = p->next;
	}
	if (p == NULL)
		printf("未查询到该位置的元素\n");
	else
		printf("该位置的元素为:%c\n",p->data);
}

void LocateElem(LinkList *L)
{
	ElemType e;
	int i = 1;
	LinkList *p = L->next;
	printf("请输入查询的元素值:");
	rewind(stdin);
	scanf("%c",&e);	
	while (p != NULL&&p->data!=e)
	{
		p = p->next;
		i++;
	}
	if (p == NULL)
		printf("未查询到该元素位置\n");
	else
		printf("该元素位置为:%d", i);	
}


void ListInsert(LinkList *&L)
{
	ElemType e;
	int i,j = 0;
	LinkList *p = L, *s;
	printf("请输入插入结点的位置和元素值:");
	scanf("%d %c", &i, &e);
	while (j < i - 1 && p != NULL)
	{
		j++;
		p = p->next;
	}
	if (p == NULL)
		printf("未查询到该位置信息\n");
	else
	{
		s = (LinkList *)malloc(sizeof(LinkList));
		s->data = e;
		s->next = p->next;
		p->next = s;
		printf("插入成功!\n");
	}

}

void ListDelete(LinkList *&L, ElemType &e)
{
	int i,j = 0;
	LinkList *p = L, *q;
	printf("请输入需要删除的结点位置:");
	scanf("%d", &i);
	while (j < i - 1 && p != NULL)
	{
		j++;
		p = p->next;
	}
	if (p == NULL)
		printf("未查询到该位置信息\n");
	else
	{
		q = p->next;
		if (q == NULL)
			printf("该结点为空\n");
		else
		{
			e = q->data;
			p->next = q->next;
			free(q);
		}
		printf("删除成功!\n");
	}

}

void main()
{
	int k;
	ElemType e;
	LinkList *h;
	InitList(h);
	printf("单链表已初始化\n");
	while (1)
	{
		printf("0:退出 1:插入 2:删除 3:查询位置 4:查询元素 5:表长 6:遍历单链表 7:清空 8:销毁 9:表空\n请选择:");
		scanf("%d", &k);
		switch (k)
		{
		case 1:
			ListInsert(h);
			break;
		case 2:
			ListDelete(h, e);
			break;
		case 3:
			LocateElem(h);
			break;
		case 4:
			GetElem(h);
			break;
		case 5:
			ListLength(h);
			break;
		case 6:
			DispList(h);
			break;
		case 7:
			ClearList(h);
			break;
		case 8:
			DestroyList(h);
			break;
		case 9:
			ListEmpty(h);
			break;
		}
		if (k == 0)
			break;
	}
}

2.2 测试结果

测试环境 : Windows 10
编译软件 : Visual Stdio 2015