数据结构之线性表的链式存储结构 C语言版

前言

在学习单链表的过程中,发现了很多问题:
1.结构体的有关问题
2.链表的表示问题
在这篇文章中,刚开始将会说明这两个问题,然后再讨论单链表的有关操作。

1.结构体

不论是顺序表还是链表,都用到了结构体。接下来通过本篇中的程序来说明结构体

typedef struct Node
{
	ElemType data;//数据域
	struct Node* next;//存放后继节点的指针域
}Node;
typedef struct Node* LinkList;//定义了新类型:结构指针Linklist来代表struct Node*

在这个结构体定义中,第一句的”typedef struct Node”语句中的”Node“”是不可以省略的,而在顺序表的定义中却是可以省略的,之所以不能省略,是因为链表的结构体中”truct Node* next;”语句用到了自身的类型;而且语句“struct Node”为一个结构类型,和int,char 都是类型说明符

在这块代码中,语句“typedef struct Node* LinkList;”,struct Node是一个指针类型,与int 或者char* 是差不多的,这句话用typedef把他用Linklist替换了,为了下面写代码节省时间

2.链表的表示

链表中所说的节点其实就是结构体,所谓链表,就是许多个结构体连接在一起,这就组成了链表。
在链表操作的函数中,有使用了LinkList* L,这一形参,这其实是定义了一个指向指针的指针,在操作的时候(*L)就相当于解引用,为(*L)->next;。而如果形参定义为LinkList L,就直接用L->next;

3.内容布局

此篇文章中用到了动态内存申请+模块化设计的方法,最重要的一个模块是创建一个新结点。不管在初始化整表,添加,删除等操作都用到了这个模块,这篇文章涉及到的操作有:
1.插入链表
1.1.头插法
1.2.尾插法
1.3.在任意位置插入
2.删除链表
2.1整表删除
2.1在任意位置删除
3.显示输出链表
4.获取元素
对代码的一些解释带附在代码中

4.操作的一些图解

图片描述
图片描述
图片描述

代码如下

//单链表:结构体变量与结构体通过指针连接到一起
/*动态内存申请+模块化设计
1.创建链表,创建一个表头表示整个链表,链表就是一个结构体变量
2.创建节点,形成新的节点
3.插入节点,删除节点
5.遍历*/
#include <stdio.h>
#include <stdlib.h>

#define OK 1
#define ERROR 0
typedef int Status;
typedef int ElemType;

typedef struct Node
{
	ElemType data;//数据域
	struct Node* next;//存放后继节点的指针域
}Node;
typedef struct Node* LinkList;//定义了新类型:结构指针Linklist来代表struct Node*



LinkList creatNode(int data);//创建新结点(插入,得先有新的,才让你插
LinkList creatList();//创建链表(头结点)
Status listHeadInsert(LinkList List);//头插入
Status listTailInsert(LinkList List);//尾插入
Status listDisplay(LinkList head);//遍历显示链表
Status listInsert(LinkList *L,int i,ElemType e);//插入
Status listDelete(LinkList *L,int i,ElemType *e);//删除
Status clearList(LinkList* L);//清空链表
Status getElem(LinkList L,int i,ElemType *e);//查找元素

int main()
{
	LinkList List = creatList();//创建一个链表(头结点)
	Status i;
	ElemType e;
	int n=0,j=0,x=0;
	while(n!=7)
	{
		printf("												  \n");
		printf("**************************************************\n");
		printf("*1.尾插*2.头插*3.插入*4.删除*5.清空*6.查找*\n");
		scanf("%d",&n);
		switch(n)
		{
		case 1:
			listTailInsert(List);
			printf("添加数据后的链表:\n");
			listDisplay(List);
			break;
		case 2:
			listHeadInsert(List);
			printf("添加数据后的链表:\n");
			listDisplay(List);
			break;
		case 3:
			printf("请选择在第几个元素前插入:\n");
			scanf("%d",&j);
			printf("请输入插入的数据:\n");
			scanf("%d",&x);
			i = listInsert(&List,j,x);
			if(i==ERROR)
				printf("插入失败\n");
			else
				printf("插入成功\n");
			printf("操作后的顺序表:\n");
			listDisplay(List);
			break;
		case 4:
			printf("请选择删除第几个元素:\n");
			scanf("%d",&j);
			i = listDelete(&List,j,&e);
			if(i==ERROR)
				printf("删除失败\n");
			else
				printf("删除成功\n");
			printf("操作后的顺序表:\n");
			listDisplay(List);
			break;
		case 5:
			i = clearList(&List);
			if(i==ERROR)
				printf("格式化失败\n");
			else
				printf("格式化成功\n");
			break;
		case 6:
			printf("请选择查找第几个元素:\n");
			scanf("%d",&x);
			i = getElem(List,x,&e);
			if(i==ERROR)
				printf("查找失败\n");
			else
				printf("查找成功,第%d个元素的值为:%d\n",x,e);
			break;
		}
	}
}

LinkList creatNode(int data)//创建新结点(插入,得先有新的,才让你插
{
	LinkList newNode = (LinkList)malloc(sizeof(Node));//生成新结点
	newNode->data = data;
	newNode->next = NULL;
	return newNode;
}

LinkList creatList()//创建链表(头结点)
{
	LinkList headNode = (LinkList)malloc(sizeof(Node));//headNode为一个结构体变量
	headNode->next;
	return headNode;
}

/*	1.声明一结构指针newNode,和计数器变量i,数据x
	2.newNode指向一生成的新结点
	3.给newNode->data赋值,再模块中完成
	4.将newNode插入到头结点与前一新结点之间	*/
Status listHeadInsert(LinkList List)//插入节点(头插入)
{
	LinkList newNode;//声明一指针p,作用为生成新结点
	int i,x=0;
	printf("请输入要添加的数(头插):\n");
	for(i=0;x!=-1;i++)
	{
		scanf("%d",&x);
		if(x!=-1)
		{
			newNode = creatNode(x);//生成新结点
			newNode->next = List->next;
			List->next = newNode;//插入到表头
		}
	}
		return OK;
}

Status listTailInsert(LinkList List)//插入节点(尾插入)
{
	LinkList newNode,tailNode=List;//声明一指针newNode,作用为生成新结点,tailNode指向尾部的节点
	int i,x=0;
	printf("请输入要添加的数(尾插):\n");
	for(i=0;x!=-1;i++)
	{
		scanf("%d",&x);
		if(x!=-1)
		{
			newNode = creatNode(x);//生成新结点
			tailNode->next = newNode;
			tailNode = newNode;//插入到表头
		}
	}
	tailNode->next = NULL;
	return OK;
}

Status listDisplay(LinkList head)//head其实是头结点
{
	LinkList pMove;//声明一指针p
	pMove = head->next;//让p指向链表L的第一个节点
	while(pMove)//打印的位置不能为空
	{
		printf("%d ",pMove->data);
		pMove = pMove->next;//让pMove指向下一个节点
	}
	printf("\n");
	return OK;
}

Status listInsert(LinkList *L,int i,ElemType e)
{
	int j = 1;//j为计数器
	LinkList newNode,sNode;
	newNode = *L;
	while(newNode&&j<i)
	{
		newNode = newNode->next;
		++j;
	}
	if(!newNode||j>i)
		return ERROR;//第i个结点不存在
	sNode = creatNode(e);
	sNode->next = newNode->next;
	newNode->next = sNode;
	return OK;
}

Status listDelete(LinkList *L,int i,ElemType *e)
{
	int j = 1;//j为计数器
	LinkList nNode,sNode;
	nNode = *L;
	while(nNode->next&&j<i)
	{
		nNode = nNode->next;
		++j;
	}
	if(!nNode||j>i)
		return ERROR;//第i个结点不存在
	sNode = nNode->next;
	nNode->next = sNode->next;
	*e = nNode->data;
	free(sNode);
	return OK;
}

Status clearList(LinkList* L)//清空列表
{
	LinkList p,q;
	p = (*L)->next;
	while(p)//没到表尾
	{
		q = p->next;
		free(p);
		p = q;
	}
	(*L)->next = NULL;
	return OK;
}

Status getElem(LinkList L,int i,ElemType *e)
{
	int j = 1;
	LinkList p;
	p = L->next;
	while(p&&j<i)
	{
		p = p->next;
		++j;
	}
	if(!p||j>i)
		return ERROR;
	*e = p->data;
	return OK;
}

运行结果

运行结果

后记

上述代码把链表的各种操作柔和到一个方程里面,以致于代码有些庞大,但是在实际操作中还可以适当加减操作

以上就是单链表的表示和各种操作,喜欢的多多支持哦~

发布了12 篇原创文章 · 获赞 10 · 访问量 1145

猜你喜欢

转载自blog.csdn.net/bsqetuo/article/details/94656185