线性表(LinkList)的链式表示和实现----线性结构

线性表的链式表示即通过链表的形式实现线性结构。

我们可以用C语言可以定义一个结构体,结构体可以分为两部分,一部分存储想要进行保存的数据,另一部分用来定义一个指向该结构体的指针。此指针指向下一个这样的结构体,下个结构体的指针又可以指向下下一个结构体…

如此往复,结构体直接互相连接,从而可以实现链表的形式。

对于上一次的以数组存储的以顺序形式实现的线性结构,逻辑地址相邻,物理地址也相邻,具有方便随机存取查看不便插入删除(都要前移元素或后挪)

这次的以链表形式实现的线性结构,逻辑地址相邻,物理地址不一定相邻,具有方便插入删除(无需向前后移动数据),不便随机存取查看(需要进行遍历)

链表形式线性表的所有算法思想操作如下

结构体链表定义

#include<stdio.h>
#include<stdlib.h>
#define TRUE 1
#define FALSE 0
#define OK 1
#define ERROR 0
#define INFEASIBLE -1
#define OVERFLOW -2

//定义结构体Student,作为链表的数据 
struct Student{
    
    
	int id;
	char *name;
	int age;
};

//定义链表结构体(此结构体也可单独作为链表结点) 
typedef struct LNode{
    
    
	Student student;		//数据Student 
	LNode *next;			//下一结点LNode 
}LNode, *LinkList;			//LinkList即为链表的定义形式

所有函数声明


int initLinkList(LinkList &list);									//初始化链表 
void printLinkList(LinkList &list);									//打印链表列表 
int getStudentList(LinkList list, int i, Student &student);			//获取链表中某一元素 
int insertLinkList(LinkList &list, int i, Student student);			//为链表的指定位置插入元素 
int deleteLinkList(LinkList &list, int i, Student &student);		//删除链表指定位置的元素 
int mergeLinkList(LinkList &list_a, LinkList &list_b, LinkList &list_c);		//合并有序链表list_a、list_b为新有序单链表list_c 


初始化链表

//链表的初始化操作 
int initLinkList(LinkList &list)
{
    
    
	list = (LNode *)malloc(sizeof(LNode));		//开辟头结点指针 
	list->next = NULL;							//头结点的后继结点初始化为NULL 
	return OK;				//返回成功信息 
}

获取链表某一元素

//数据结构 算法2.8---->获取线性表中的某个元素 
int getStudentList(LinkList list, int i, Student &student)
{
    
    
	if(i < 1) return ERROR;			//若序号小于1表示获取元素失败,返回错误信息 
	int j = 1;						//定义标识位j并设置为1 
	LNode *p = list;				//定义p指针指向链表的头结点 
		
	//循环找出链表中的第i个结点,令p指向它 
	while(j <= i)
	{
    
    
		p = p->next;				//指针后挪,遍历下一结点 
		if(p==NULL) return ERROR;		//若结点已为NULL,返回错误信息 
		j++;							//计数器累加 
	}
	
	student = p->student;			//将该结点的信息赋值返回 
		
	return OK;						//返回操作成功信息 
}

链表插入操作

//数据结构 算法2.9---->指定位置插入一个元素 
int insertLinkList(LinkList &list, int i, Student student)
{
    
    
	int j = 1;
	if(i < 1) return ERROR;			//若插入位置小于1,返回ERROR失败信息,插入失败 
	LNode *p = list, *node;			//定义p指针指向头节点,定义node结点作为插入结点 
	
	//通过循环找到待插入结点的前一个结点,即i-1个结点 
	for(j = 1; j < i && (p!=NULL); j++)
	{
    
    
		p = p->next;	
	}
	 
	if(p == NULL && j >= i) return ERROR;		//若前一个结点为NULL或者插入位置超出范围表示插入失败,返回失败信息 
	
	node = (LNode *)malloc(sizeof(LNode));		//开辟一个新的结点内存信息 
	node->student = student;					//保存插入元素的值 
	node->next = p->next;					//将目前第i个位置的结点作为node结点的后继结点 
	p->next = node;							//再将第i-1个结点的后继结点作为node结点,即插入第i个位置成功 
	
	return OK;								//返回插入成功信息 
}

链表删除操作

//数据结构 算法2.10---->指定位置删除一个元素,并返回该元素的值 
int deleteLinkList(LinkList &list, int i, Student &student)
{
    
    
	int j = 1;					//定义统计次数标识位j并初始化为1 
	if(i < 1) return ERROR;			//如果删除位置小于1,则返回删除失败 
	LNode *p = list, *q;			//定义p指针最先指向链表头指针
	
	//循环遍历找到链表的第i-1个位置,准备进行插入 
	for(j = 1; j < i && (p->next!=NULL); j++)
	{
    
    
		p = p->next;
	}
	
	//如果第i个位置为null或者删除位置已超出,返回删除失败信息 
	if(p->next == NULL || j >= i) return ERROR;
	
	student = p->next->student;			//将待删除位置的结点信息赋值为student,待传回操作 
	q = p->next;						//保存删除结点的位置信息 
	p->next = p->next->next;			//直接将待删除结点的前一个结点的后继结点 连接到 待删除结点的后继节点(相当于删除) 
	free(q);							//释放删除结点的地址信息数据 

	return OK;							//返回删除成功 
}

合并两个有序单链表操作

//数据结构 算法2.12---->合并两个有序单链表 
int mergeLinkList(LinkList &list_a, LinkList &list_b, LinkList &list_c)
{
    
    
	LNode *pa = list_a->next, *pb = list_b->next, *pc;		//定义pa、pb、pc三个指针, pa、pb分别指向链表list_a, list_b 
	initLinkList(list_c);					//初始化链表c 
	pc = list_c;							//pc指向链表list_c
	
	//若链表pa,pb都未合并完毕,重复循环插入 
	while(pa != NULL && pb != NULL)
	{
    
    
		if(pa->student.id <= pb->student.id)			//按照升序进行有序合并,若pa指向结点学生的序号小于pb 
		{
    
    
			pc->next = pa;								//则把pa指向结点添加到pc指向链表之后 
			pc = pa;									//pc移动到后一个结点(即pa),准备添加下一个结点 
			pa = pa->next;								//pa添加过之后,指针也向后进行移动 
		}
		else								//反之亦然 
		{
    
    
			pc->next = pb;
			pc = pa;
			pb = pb->next;
		}
	}
	
	if(pa != NULL) pc->next = pa;			//若最终pa指向链表未添加完毕,直接将pa剩余链表接到pc链表后 
	if(pb != NULL) pc->next = pb;			//若最终pb指向链表未添加完毕,直接将pb剩余链表接到pc链表后 
	
	return OK;					//返回成功信息 
}

打印单链表操作

//打印所有结点 
void printLinkList(LinkList &list)
{
    
    
	int i = 0;				//设置i作为记录结点个数 
	LNode *p = list->next;	
	while(p!=NULL)			//循环输出所有结点 
	{
    
    
		printf("id = %d.\tname = %s.\tage = %d.\n", p->student.id, p->student.name, p->student.age);		//输出结点信息 
		p = p->next;		//p指针后挪至下一结点信息 
		i++;
	}
	printf("输出链表列表完毕,共%d条数据.\n", i);
}

合并代码+测试
#include<stdio.h>
#include<stdlib.h>
#define TRUE 1
#define FALSE 0
#define OK 1
#define ERROR 0
#define INFEASIBLE -1
#define OVERFLOW -2

//定义结构体Student,作为链表的数据 
struct Student{
    
    
	int id;
	char *name;
	int age;
};

//定义链表结构体(此结构体也可单独作为链表结点) 
typedef struct LNode{
    
    
	Student student;		//数据Student 
	LNode *next;			//下一结点LNode 
}LNode, *LinkList;


int initLinkList(LinkList &list);									//初始化链表 
void printLinkList(LinkList &list);									//打印链表列表 
int getStudentList(LinkList list, int i, Student &student);			//获取链表中某一元素 
int insertLinkList(LinkList &list, int i, Student student);			//为链表的指定位置插入元素 
int deleteLinkList(LinkList &list, int i, Student &student);		//删除链表指定位置的元素 
int mergeLinkList(LinkList &list_a, LinkList &list_b, LinkList &list_c);		//合并有序链表list_a、list_b为新有序单链表list_c 

int main()
{
    
    
	LinkList list;
	initLinkList(list);
	LNode *q = list, *p = list;
	for(int i = 0; i < 10; i++)
	{
    
    
		p = (LNode *)malloc(sizeof(LNode));
		
		p->student.id = i;
		p->student.name = {
    
    "Hello World"};
		p->student.age = i+20; 
		
		q->next = p;
		q = p;
	}
	q->next = NULL;
	printLinkList(list);
	
	while(1)
	{
    
    
		int i = 0;
		Student student;
		printf("请输入您想要查找的元素:\n");
		scanf("%d", &i);
		if(getStudentList(list, i , student))
		{
    
    
			printf("查找成功!\n");
			printLinkList(list);
		} 
		else printf("查找失败!\n");
	}
	return 0;
}


//链表的初始化操作 
int initLinkList(LinkList &list)
{
    
    
	list = (LNode *)malloc(sizeof(LNode));		//开辟头结点指针 
	list->next = NULL;							//头结点的后继结点初始化为NULL 
	return OK;				//返回成功信息 
}


//数据结构 算法2.8---->获取线性表中的某个元素 
int getStudentList(LinkList list, int i, Student &student)
{
    
    
	if(i < 1) return ERROR;			//若序号小于1表示获取元素失败,返回错误信息 
	int j = 1;						//定义标识位j并设置为1 
	LNode *p = list;				//定义p指针指向链表的头结点 
		
	//循环找出链表中的第i个结点,令p指向它 
	while(j <= i)
	{
    
    
		p = p->next;				//指针后挪,遍历下一结点 
		if(p==NULL) return ERROR;		//若结点已为NULL,返回错误信息 
		j++;							//计数器累加 
	}
	
	student = p->student;			//将该结点的信息赋值返回 
		
	return OK;						//返回操作成功信息 
}


//数据结构 算法2.9---->指定位置插入一个元素 
int insertLinkList(LinkList &list, int i, Student student)
{
    
    
	int j = 1;
	if(i < 1) return ERROR;			//若插入位置小于1,返回ERROR失败信息,插入失败 
	LNode *p = list, *node;			//定义p指针指向头节点,定义node结点作为插入结点 
	
	//通过循环找到待插入结点的前一个结点,即i-1个结点 
	for(j = 1; j < i && (p!=NULL); j++)
	{
    
    
		p = p->next;	
	}
	 
	if(p == NULL && j >= i) return ERROR;		//若前一个结点为NULL或者插入位置超出范围表示插入失败,返回失败信息 
	
	node = (LNode *)malloc(sizeof(LNode));		//开辟一个新的结点内存信息 
	node->student = student;					//保存插入元素的值 
	node->next = p->next;					//将目前第i个位置的结点作为node结点的后继结点 
	p->next = node;							//再将第i-1个结点的后继结点作为node结点,即插入第i个位置成功 
	
	return OK;								//返回插入成功信息 
}


//数据结构 算法2.10---->指定位置删除一个元素,并返回该元素的值 
int deleteLinkList(LinkList &list, int i, Student &student)
{
    
    
	int j = 1;					//定义统计次数标识位j并初始化为1 
	if(i < 1) return ERROR;			//如果删除位置小于1,则返回删除失败 
	LNode *p = list, *q;			//定义p指针最先指向链表头指针
	
	//循环遍历找到链表的第i-1个位置,准备进行插入 
	for(j = 1; j < i && (p->next!=NULL); j++)
	{
    
    
		p = p->next;
	}
	
	//如果第i个位置为null或者删除位置已超出,返回删除失败信息 
	if(p->next == NULL || j >= i) return ERROR;
	
	student = p->next->student;			//将待删除位置的结点信息赋值为student,待传回操作 
	q = p->next;						//保存删除结点的位置信息 
	p->next = p->next->next;			//直接将待删除结点的前一个结点的后继结点 连接到 待删除结点的后继节点(相当于删除) 
	free(q);							//释放删除结点的地址信息数据 

	return OK;							//返回删除成功 
}


//数据结构 算法2.12---->合并两个有序单链表 
int mergeLinkList(LinkList &list_a, LinkList &list_b, LinkList &list_c)
{
    
    
	LNode *pa = list_a->next, *pb = list_b->next, *pc;		//定义pa、pb、pc三个指针, pa、pb分别指向链表list_a, list_b 
	initLinkList(list_c);					//初始化链表c 
	pc = list_c;							//pc指向链表list_c
	
	//若链表pa,pb都未合并完毕,重复循环插入 
	while(pa != NULL && pb != NULL)
	{
    
    
		if(pa->student.id <= pb->student.id)			//按照升序进行有序合并,若pa指向结点学生的序号小于pb 
		{
    
    
			pc->next = pa;								//则把pa指向结点添加到pc指向链表之后 
			pc = pa;									//pc移动到后一个结点(即pa),准备添加下一个结点 
			pa = pa->next;								//pa添加过之后,指针也向后进行移动 
		}
		else								//反之亦然 
		{
    
    
			pc->next = pb;
			pc = pa;
			pb = pb->next;
		}
	}
	
	if(pa != NULL) pc->next = pa;			//若最终pa指向链表未添加完毕,直接将pa剩余链表接到pc链表后 
	if(pb != NULL) pc->next = pb;			//若最终pb指向链表未添加完毕,直接将pb剩余链表接到pc链表后 
	
	return OK;					//返回成功信息 
}


//打印所有结点 
void printLinkList(LinkList &list)
{
    
    
	int i = 0;				//设置i作为记录结点个数 
	LNode *p = list->next;	
	while(p!=NULL)			//循环输出所有结点 
	{
    
    
		printf("id = %d.\tname = %s.\tage = %d.\n", p->student.id, p->student.name, p->student.age);		//输出结点信息 
		p = p->next;		//p指针后挪至下一结点信息 
		i++;
	}
	printf("输出链表列表完毕,共%d条数据.\n", i);
}

小结

总之,链表在插入和删除元素时较顺序存储较为方便,采用动态形式进行结点的扩充,对空间利用率也更高,但需要从头结点依次遍历查找,略有不便,当我们需要保存链表的长度信息时,也可以将其它信息保存到头结点中去,链表的形式还有很多,如循环链表、双向链表等,后续再慢慢更新上来!

加油,奥里给~

猜你喜欢

转载自blog.csdn.net/weixin_43479947/article/details/113098907