数据结构--单链表的实现

之前我们了解了链表中的知识,现在我们进行实现,以C语言为例。
1.我们都需要实现的功能

  • 头文件Linklist.c
    我们在头文件里声明要实现的函数
#pragma once


typedef int DataType;
// 节点的定义
typedef struct Linknode{
	DataType data;
	node* next;//因为next指针是指向下一个结点的,因此要定义成结构体的指针类型
}node;

//链表里面的方法
node* Linklist_node(DataType data);
void LinklistInit(node** head);//初始化
void Linklist_pushback(node** head, DataType data);//尾插
void Linklist_popback(node** head);//尾删
void Linklist_pushfront(node** head, DataType data);//头插
void Linklist_popfront(node** head);//头删
void Linklist_insert(node* pos,DataType data);//pos位置插入
void Linklist_Erase(node* pos);//pos位置删除
int Linklist_Size(node* head);//计算链表大小
node* Linklist_find(node* head, DataType data);//查找data
void Linklist_Destory(node** head);//链表的销毁
void Linklist_print(node* head);//输出链表

接下来,我们分别来实现不同功能。

  • 尾插

在进行尾插时,我们要注意的时此时链表是否为空。
当链表为空时:在这里插入图片描述
当链表不为空时:在这里插入图片描述

//想要通过对形参修改达到外部实参的目的,在传参时就传递实参的地址
//在函数操作中,如果需要修改head的指向,不许传递二级指针
//初始化函数
void LinklistInit(node** head){
	assert(head);
	*head = NULL;
}
//添加新结点函数
node* Linklist_node(DataType data){//构造一个新结点
	node* _node = (node*)malloc(sizeof(node));//链表上的结点我们一般用malloc从堆上进行申请
	if (NULL == _node){//证明在这里我们申请空间失败
		assert(0);
		return NULL;
	}
	//申请成功后,给节点的值域进行赋值
	_node->data = data;
	_node->next = NULL;
	return _node;
}
//尾插函数
void Linklist_pushback(node** head, DataType data){
	assert(head);
	if (NULL == *head){//空链表
		*head = Linklist_node(data);
	}
	else{//非空链表
		//1.找到最后一个结点;2.插入
		node* cer = *head;
		while (cer->next){
			cer = cer->next;
		}
		cer->data = Linklist_node(data);//cer指向一个新结点
		cer->next = NULL;
	}
}

在这里,我们要理解一个问题,链表不存在和空链表的区别:
空链表是一种有效链表,知识链表中没有有效结点,head=NULL;
链表不存在是指连head都没有,如这种操作:Linklist_pushback(NULL,0)这是种非法操作。

  • 尾删

在进行尾删是,我们也要注意链表的有效结点个数。
空链表:在这里插入图片描述
非空链表:在这里插入图片描述
在这里插入图片描述
/

/尾删函数
void Linklist_popback(node** head){
	assert(head);
	if (NULL == *head) return;
	else if (NULL == (*head)->next){
		//链表中只有一个结点
		free(*head);//对链表进行释放,释放完后成为空链表
		*head = NULL;
	}
	else{
		node* cer = *head;
		node* prev = NULL;
		while (cer->next){
			prev = cer;//保存前一个结点
			cer = cer->next;
		}
		free(cer);
		prev->next = NULL;
	}
}
  • 头插

头插时我们也可以分为两种情况。
空链表:
在这里插入图片描述

非空链表:在非空链表中,有两种插入方式,但是那一只才可以实现呢?
在这里插入图片描述
我们在这里就考虑,可知,第二种情况时不可行的,因为如果先将插入的数据给了head,那么后面的节点就无法找到。因此,我们应该使用第一种插入的方法。

//链表的头插
void Linklist_pushfront(node** head, DataType data){
	assert(head);
	node* newnode = Linklist_node(data);
	newnode->next = *head;//这里时新加的结点始终指向原链表,这样就不会丢失原链表
	*head = newnode;
}
  • 头删

在进行头删时,我们考虑三种情况:空链表时,只有一个结点时,多个节点时;这三种情况。
在这里插入图片描述

 //头删
void Linklist_popfront(node** head){
	assert(head);
	if (NULL == *head) return;
	else if ((*head)->next == NULL){
		free(*head);
		*head = NULL;
	}
	else{
		node* delnode = *head;//先标记要删除的结点
		*head = delnode->next;
		free(delnode);
	}
}
  • 任意位置插入

在任意位置进行插入,我们就必须的考虑插入的确切位置,以及位置插入的合法性。比如说,我们现在拿到一个pos=3,data=0;将他插入链表中,且当他插入时,是插入3位置之前,还是3位置之后?如果要插在3之前该如何插入?
因为现在接口已经给出,所以没有办法插入3之前,若要插入则可以进行数据的交换。
在这里插入图片描述

//在pos位置进行插入
void Linklist_insert(node* pos, DataType data){
	node* newnode = NULL;
	if (NULL == pos) return;

	else{
		newnode = Linklist_node(data);
		newnode->next = pos->next;
		pos->next = newnode;

	}
}
  • 任意位置删除
  //pos位置的删除
void Linklist_Erase(node* pos){
	//只能删除pos之后的结点
	node* prev = NULL;
	if (NULL == pos || NULL == pos->next) return;
	else
	{
		prev = pos->next;
		pos ->next= prev->next;
		free(prev);
		prev = NULL;
		//pos->next=pos->next->next;
		//free(pos);
	}
}
  • 计算大小

//计算链表的大小
int Linklist_Size(node* head){
	int count = 0;
	node* cer = head;
	while (cer){
		cer = cer->next;
		count++;
	}
	return count;
}

  • 查找data
node* Linklist_find(node* head, DataType data){
	node* cer = head;
	while (cer){
		if (cer->data == data){
			return cer;
		}
    	cer = cer->next;
	}
	return NULL;
}
  • 链表销毁
//链表的销毁,采用头删的方法进行销毁
void Linklist_Destory(node** head){
	assert(head);
	node* delnode = NULL;
	while (NULL != *head){
		delnode = *head;
		*head = delnode->next;
		free(delnode);
	}
}
  • 打印链表
//辅助方法,输出链表
void Linklist_print(node* head){
	node*cer = head;
	while (cer){
		printf("%d--->", cer->data);
		cer = cer->next;
	}
	printf("NULL");
	printf("\n");
}
  • 最终代码及其实现:
#include"Linklist.h"
#include<stdio.h>
#include<assert.h>
#include<malloc.h>



//想要通过对形参修改达到外部实参的目的,在传参时就传递实参的地址
//在函数操作中,如果需要修改head的指向,不许传递二级指针
void LinklistInit(node** head){
	assert(head);
	*head = NULL;
}
node* Linklist_node(DataType data){//构造一个新结点
	node* _node = (node*)malloc(sizeof(node));//链表上的结点我们一般用malloc从堆上进行申请
	if (NULL == _node){//证明在这里我们申请空间失败
		assert(0);
		return NULL;
	}
	//申请成功后,给节点的值域进行赋值
	_node->data = data;
	_node->next = NULL;
	return _node;
}
void Linklist_pushback(node** head, DataType data){
	assert(head);
	if (NULL == *head){//空链表
		*head = Linklist_node(data);
	}
	else{//非空链表
		//1.找到最后一个结点;2.插入
		node* cer = *head;
		while (cer->next){
			cer = cer->next;
		}
		cer->next = Linklist_node(data);//cer指向一个新结点
	}
}
//尾删函数
void Linklist_popback(node** head){
	assert(head);
	if (NULL == *head) return;
	else if (NULL == (*head)->next){
		//链表中只有一个结点
		free(*head);//对链表进行释放,释放完后成为空链表
		*head = NULL;
	}
	else{
		node* cer = *head;
		node* prev = NULL;
		while (cer->next){
			prev = cer;//保存前一个结点
			cer = cer->next;
		}
		free(cer);
		prev->next = NULL;
	}
}

//链表的头插
void Linklist_pushfront(node** head, DataType data){
	assert(head);
	node* newnode = Linklist_node(data);
	newnode->next = *head;//这里时新加的结点始终指向原链表,这样就不会丢失原链表
	*head = newnode;
}
//头删
void Linklist_popfront(node** head){
	assert(head);
	if (NULL == *head) return;
	else if ((*head)->next == NULL){
		free(*head);
		*head = NULL;
	}
	else{
		node* delnode = *head;//先标记要删除的结点
		*head = delnode->next;
		free(delnode);
	}
}

//在pos位置进行插入
void Linklist_insert(node* pos, DataType data){
	node* newnode = NULL;
	if (NULL == pos) return;

	else{
		newnode = Linklist_node(data);
		newnode->next = pos->next;
		pos->next = newnode;

	}
}

//pos位置的删除
void Linklist_Erase(node* pos){
	//只能删除pos之后的结点
	node* prev = NULL;
	if (NULL == pos || NULL == pos->next) return;
	else
	{
		prev = pos->next;
		pos ->next= prev->next;
		free(prev);
		prev = NULL;
		//pos->next=pos->next->next;
		//free(pos);
	}
}

//计算链表的大小
int Linklist_Size(node* head){
	int count = 0;
	node* cer = head;
	while (cer){
		cer = cer->next;
		count++;
	}
	return count;
}

node* Linklist_find(node* head, DataType data){
	node* cer = head;
	while (cer){
		if (cer->data == data){
			return cer;
		}
    	cer = cer->next;
	}
	return NULL;
}

//链表的销毁,采用头删的方法进行销毁
void Linklist_Destory(node** head){
	assert(head);
	node* delnode = NULL;
	while (NULL != *head){
		delnode = *head;
		*head = delnode->next;
		free(delnode);
	}
}
//辅助方法,输出链表
void Linklist_print(node* head){
	node*cer = head;
	while (cer){
		printf("%d--->", cer->data);
		cer = cer->next;
	}
	printf("NULL");
	printf("\n");
}
void main(){
	node* head = NULL;
	
	LinklistInit(&head);
	Linklist_pushback(&head,1);
	Linklist_pushfront(&head, 0);
	Linklist_pushback(&head, 2);
	Linklist_pushback(&head, 3);
	Linklist_pushback(&head, 4);
	Linklist_pushback(&head, 5);
	Linklist_print(head);
	Linklist_popback(&head);
	Linklist_print(head);
	Linklist_popfront(&head);
	Linklist_print(head);
	Linklist_insert(Linklist_find(head,2), 5);
	Linklist_print(head);
	Linklist_Erase(head);
	Linklist_print(head);
	int x = Linklist_Size(head);
	printf("size=%d\n", x);
	Linklist_Destory(&head);
	Linklist_print(head);
}

运行截图:在这里插入图片描述
傻作者实现了无头结点单链表的功能,如果有什么错误,欢迎在评论区指正嗷。

  • 3
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值