数据结构(链表篇)入门

一、数据结构概述:

概念:计算机存储,管理,组织数据的方式方法;

常用的数据结构类型:

1.根据数据的逻辑结构(数据关系)划为:

集合:   数据元素之间除了"同属于某一个组织"外,再无其他关系。

线性数据结构:   数据之间"一对一"关系,简单理解,每一个元素具有唯一的前驱和后继;

例子:一维数组

非线性数据结构:   每一个元素不具有唯一的前驱和后继;

例子:二维数组

2.根据数据元素的存储方式划为:

顺序结构:各个元素存储在连续的内存空间中       

链式结构:各个元素存储在不连续的内存空间中  

索引结构:元素在存储时,不仅仅存储元素数据,还要为数据建立索引表标识元素的地址;

哈希结构:元素在存储时,为元素设置关键字(key),在访问元素时,根据关键字访问元素。

常见的数据结构:

链表,树型结构,栈,图,映射,散列表...

二、数据结构的设计实现:

引入问题:根据之前所学的知识内容,我们对同类型的多个数据可以利用数组来存储管理,对不同类型的多个数据

可以利用结构体来存储的,但不论是数组还是结构体,数据存储都是在连续的内存空间中,那么如果此时内存中不存在连续的内存空间用于存储多个数据,此时的数据该如何存储?

解决方案:链表

1.链表的应用场景

链表是一种动态的数据结构,比较适合于事先无法获知待存存储的数据的数据数量,可能需要随时增加数据;

比较始合于对数据的操作是频繁的增加和删除的情况。

2.单向链表的设计实现

 1)单向链表的组成:

头指针  (指向头结点的指针变量)

 节点/结点

   数据域

   指针域

typedef int data_t;
typedef struct linklist
{
	data_t data;
	struct linklist* next;
}linklist_t;

  2)链表的相关操作算法(增,删,改,查)  注:链表的操作是在堆上进行的

2.1链表创建:

    第一步:首先建立头文件linklist.h

#ifndef __LINKLIST_H  //预处理用于条件编译 避免头文件反复包含
#define __LINKLIST_H

typedef int data_t;
typedef struct linklist
{
	data_t data;
	struct linklist* next;
}linklist_t;

int list_create(linklist_t** head, data_t data);
#endif

  第二步:创建建立链表函数 linklist.c

#include "linklist.h"
#include<stdio.h>
#include<stdlib.h>

//创建节点  输出型参数返回地址(指针)
int list_create(linklist_t** head,data_t data)  //一级指针的时候此时head还是值传递 所以需要用二级指针对head指针的地址进行传递
{
	linklist_t *p = (linklist_t*)malloc(sizeof(linklist_t)); //申请一个节点空间
	if (p == NULL)
		return -1;

	p->data = data;
	p -> next = NULL;

	*head = p;  //通过对头指针解引用
	return p;
}

  第三步:创建main.c函数进行相关操作

#include "linklist.h"  //不能多次重复包含 会报错
#include<stdio.h>

int main()
{
	linklist_t* head = NULL;
	list_create(&head,321); //接收指针的地址

	printf("%d\n", head->data);

	return 0;
}

2.2增加数据(数据可插入头部,中间,尾部)

  ①头插法:

//在头结点插入元素(头插法)
int list_addhead(linklist_t** head, data_t data)
{
	linklist_t* p = (linklist_t*)malloc(sizeof(linklist_t));
	
	if (p == NULL)
		return -1;
	
	p->data = data;
	p->next = *head;  //将要插入数据节点的后驱指向头结点的地址

	*head = p;  //通过对头指针解引用

	return 0;
}

  ②尾插法:

//在链表尾部插入数据(尾插法)
int list_addtail(linklist_t** head, data_t data)
{
	linklist_t* pnew = (linklist_t*)malloc(sizeof(linklist_t));

	if (pnew == NULL)
		return -1;
	pnew->data = data;
	pnew->next = NULL;


	linklist_t* p = *head, * q = NULL; //利用快慢指针  要对head解引用
	if (p == NULL)
	{
		*head = pnew; //p为空,则head链表是空链表,所以将要插入的节点就为头节点
		return 0;
	}
	//遍历链表
	while (p->next)
	{
		p = p->next;
	}
	p->next = pnew;
	return 0;
}

  ③中间插法:

//中间插法

int list_insert(linklist_t** head, data_t newdata, data_t olddata)
{
	linklist_t* pnew = (linklist_t*)malloc(sizeof(linklist_t));
	if (pnew == NULL)
		return -1;
	pnew->data = newdata;
	pnew->next = NULL;

	linklist_t* p = *head, * q = NULL;
	if (p == NULL)             //链表为空,用新数据创建链表
	{
		*head = pnew;
		return 0;
	}
	if (memcmp(&p->data, &olddata, sizeof(data_t)) == 0) //插入的位置为头节点位置
	{
		pnew->next = p;
		*head = pnew;
		return 0;
	}
	while (p)
	{
		if (memcmp(&p->data, &olddata, sizeof(data_t)) == 0) //插入到链表中的某节点位置
		{
			if (p->next == NULL)
			{
				p->next = pnew;
				pnew->next = NULL;
				return 0;
			}
			else
			{
				q = p->next;
				p->next = pnew;
				pnew->next = q;
				return 0;
			}
		}
		q = p;  //使用快慢指针
		p = p->next;
	}
	//未找到插入位置,将新节点尾插到链表中
	q->next = pnew;
	pnew->next = NULL;

	return 0;
}

 2.3 删除数据  

//删除节点
int list_delete(linklist_t** head, data_t data)
{
	linklist_t* p = *head,*q = NULL;
	if (p == NULL)
		return -1;
	if (memcmp(&p->data, &data, sizeof(data_t)) == 0) //删除的是头节点
	{
		if (p->next == NULL) //头节点是唯一的节点
		{
			free(p);
			return 0;
		}
		else //头节点不是唯一的节点
		{
			*head = p->next;
			free(p);
			return 0;
		}
		
	}
	while (p)  //进入循环后表示删除的不是头节点
	{
		if (memcmp(&p->data, &data, sizeof(data_t)) == 0) //数据找到
		{
			
			q->next = p->next;
			free(p);
			return 0;
		}
		q = p;
		p = p->next;
	}
	return -1;
}

 2.4修改数据

//修改数据
int list_updata(linklist_t** head, data_t olddata, data_t newdata)
{
	linklist_t* p = *head;
	if (p == NULL)
		return -1;
	while (p)
	{
		if (memcmp(&p->data, &olddata, sizeof(data_t)) == 0)
		{
			p->data = newdata;
			return 0;
		}
		p = p->next;
	}
	return -1;
}

 2.5查找数据

//查找数据
linklist_t* list_find(linklist_t* head, data_t data)
{
	linklist_t* p = head;
	if (p == NULL)
		return -1;
	while (p)
	{
		if (memcmp(&p->data, &data, sizeof(data_t)) == 0)
		{
			printf("找到了%d\n", data);
			return 0;
		}
		p = p->next;
	}
	return -1;

}

 2.6遍历链表

//遍历链表
int list_showall(linklist_t* head)  //不涉及对头指针地址的修改,所以用一级指针即可
{
	linklist_t* p = head;
	if (p == NULL)
		return -1;
	while (p)
	{
		printf("%d ", p->data);
		p = p->next;
	}
	printf("\n");
	return 0;
}

 2.7回收链表

//回收链表
int list_free(linklist_t** head)
{
	linklist_t* p = *head, * q = NULL;
	while (p)
	{
		q = p;
		p = p->next;
		free(q);
	}
	*head = NULL;
	return 0;
}

 附完整代码:

linklist.h

#ifndef __LINKLIST_H  //预处理用于条件编译 避免头文件反复包含
#define __LINKLIST_H

//双向链表
typedef int data_t;
typedef struct linklist
{
	data_t data;
	struct linklist* next;

}linklist_t;

int list_create(linklist_t** head, data_t data);
int list_addhead(linklist_t** head, data_t data);
int list_insert(linklist_t** head, data_t newdata, data_t olddata);
int list_addtail(linklist_t** head, data_t data);
int list_updata(linklist_t** head, data_t olddata, data_t newdata);
linklist_t* list_find(linklist_t* head, data_t data);
int list_delete(linklist_t** head, data_t data);
int list_showall(linklist_t* head);
int list_free(linklist_t** head);
#endif

linklist.c

#include "linklist.h"
#include<stdio.h>
#include<stdlib.h>

//创建节点  输出型参数返回地址(指针)
int list_create(linklist_t** head, data_t data)  //一级指针的时候此时head还是值传递 所以需要用二级指针对head指针的地址进行传递
{
	linklist_t* p = (linklist_t*)malloc(sizeof(linklist_t)); //申请一个节点空间
	if (p == NULL)
		return -1;

	p->data = data;
	p->next = NULL;

	*head = p;  //通过对头指针解引用
	return p;
}

//在头结点插入元素(头插法)
int list_addhead(linklist_t** head, data_t data)
{
	linklist_t* p = (linklist_t*)malloc(sizeof(linklist_t));

	if (p == NULL)
		return -1;

	p->data = data;
	p->next = *head;  //将要插入数据节点的后驱指向头结点的地址

	*head = p;

	return 0;
}

//中间插法

int list_insert(linklist_t** head, data_t newdata, data_t olddata)
{
	linklist_t* pnew = (linklist_t*)malloc(sizeof(linklist_t));
	if (pnew == NULL)
		return -1;
	pnew->data = newdata;
	pnew->next = NULL;

	linklist_t* p = *head, * q = NULL;
	if (p == NULL)             //链表为空,用新数据创建链表
	{
		*head = pnew;
		return 0;
	}
	if (memcmp(&p->data, &olddata, sizeof(data_t)) == 0) //插入的位置为头节点位置
	{
		pnew->next = p;
		*head = pnew;
		return 0;
	}
	while (p)
	{
		if (memcmp(&p->data, &olddata, sizeof(data_t)) == 0) //插入到链表中的某节点位置
		{
			if (p->next == NULL)
			{
				p->next = pnew;
				pnew->next = NULL;
				return 0;
			}
			else
			{
				q = p->next;
				p->next = pnew;
				pnew->next = q;
				return 0;
			}
		}
		q = p;  //使用快慢指针
		p = p->next;
	}
	//未找到插入位置,将新节点尾插到链表中
	q->next = pnew;
	pnew->next = NULL;

	return 0;
}


//在链表尾部插入数据(尾插法)
int list_addtail(linklist_t** head, data_t data)
{
	linklist_t* pnew = (linklist_t*)malloc(sizeof(linklist_t));

	if (pnew == NULL)
		return -1;
	pnew->data = data;
	pnew->next = NULL;


	linklist_t* p = *head, * q = NULL; //利用快慢指针  要对head解引用
	if (p == NULL)
	{
		*head = pnew; //p为空,则head链表是空链表,所以将要插入的节点就为头节点
		return 0;
	}
	//遍历链表
	while (p->next)
	{
		p = p->next;
	}
	p->next = pnew;
	return 0;
}

//修改数据
int list_updata(linklist_t** head, data_t olddata, data_t newdata)
{
	linklist_t* p = *head;
	if (p == NULL)
		return -1;
	while (p)
	{
		if (memcmp(&p->data, &olddata, sizeof(data_t)) == 0)
		{
			p->data = newdata;
			return 0;
		}
		p = p->next;
	}
	return -1;
}

//查找数据
linklist_t* list_find(linklist_t* head, data_t data)
{
	linklist_t* p = head;
	if (p == NULL)
		return -1;
	while (p)
	{
		if (memcmp(&p->data, &data, sizeof(data_t)) == 0)
		{
			printf("找到了%d\n", data);
			return 0;
		}
		p = p->next;
	}
	return -1;

}

//删除节点
int list_delete(linklist_t** head, data_t data)
{
	linklist_t* p = *head,*q = NULL;
	if (p == NULL)
		return -1;
	if (memcmp(&p->data, &data, sizeof(data_t)) == 0) //删除的是头节点
	{
		if (p->next == NULL) //头节点是唯一的节点
		{
			free(p);
			return 0;
		}
		else //头节点不是唯一的节点
		{
			*head = p->next;
			free(p);
			return 0;
		}
		
	}
	while (p)  //进入循环后表示删除的不是头节点
	{
		if (memcmp(&p->data, &data, sizeof(data_t)) == 0) //数据找到
		{
			
			q->next = p->next;
			free(p);
			return 0;
		}
		q = p;
		p = p->next;
	}
	return -1;
}

//回收链表
int list_free(linklist_t** head)
{
	linklist_t* p = *head, * q = NULL;
	while (p)
	{
		q = p;
		p = p->next;
		free(q);
	}
	*head = NULL;
	return 0;
}

//遍历链表
int list_showall(linklist_t* head)  //不涉及对头指针地址的修改,所以用一级指针即可
{
	linklist_t* p = head;
	if (p == NULL)
		return -1;
	while (p)
	{
		printf("%d ", p->data);
		p = p->next;
	}
	printf("\n");
	return 0;
}

main.c

#pragma warning(disable:4996)
#include "linklist.h"  //不能多次重复包含 会报错
#include<stdio.h>

int main()
{
	linklist_t* head = NULL;
	list_create(&head, 321); //接收指针的地址
	list_addhead(&head, 666);//头插法
	//中间插法
	list_addtail(&head, 222);  //尾插法
	list_addtail(&head, 888);
	list_addtail(&head, 999);
	list_showall(head); //遍历链表

	list_find(head, 666);
	/*while (1)
	{
		data_t data;
		printf("请输入要删除的数据(-1退出):\n");
		scanf("%d", &data);
		if (data == -1)
			break;
		if (-1 == list_delete(&head, data))
		{
			printf("删除的数据不存在,请重试...\n");
			continue;
		}
		list_showall(head); //遍历链表
	}
	*/
	
	while (1)
	{
#if 0  //预处理条件编译
		data_t data;
		printf("请输入要删除的数据(-1退出):\n");
		scanf("%d", &data);
		if (data == -1)
			break;
		if (-1 == list_delete(&head, data))
		{
			printf("删除的数据不存在,请重试...\n");
			continue;
		}
		list_showall(head); //遍历链表
#else
		data_t olddata, newdata;
		printf("请输入要在那个数据前插入(-1退出):\n");
		scanf("%d", &olddata);
		if (olddata == -1)
			break;
		printf("请输入插入的数据(-1退出):\n");
		scanf("%d", &newdata);
		if (-1 == list_insert(&head, newdata, olddata))
		{
			printf("数据插入失败,请重试...\n");
			continue;
		}
		list_showall(head); //遍历链表
#endif
	}
	
	list_free(&head); //回收链表

	return 0;
}

  • 4
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值