C语言数据结构单链表

单链表的的特点:用一组任意的储存单元存储线性表的数据元素(这组存储单元可以是连续的,也可以是不连续的)。为了表示每个数据元素与其直接后续数据元素的逻辑关系,对于每个数据元素来说,除了存储其本身的信息外,还需存储一个指示其直接后继的信息,这两部分信息便称为结点。

整个链表的存取必须从头指针开始进行,头指针表示链表的第一个结点,同时由于最后一个元素没有直接后继,则单链表中最后一个的节点为空(NULL)。

类型命名

为了方便我们写代码,我们先将类型名重命名。

typedef int lala;

单链表的存储结构

typedef struct SListNode//链表结构
{
	lala data;
	struct SListNode* next;//指向下一个节点的指针
}sn;

data为存储结点本身的信息,next为指向下一个结点的指针。

链表的打印

void print(sn* ps)//打印链表,接受链表的地址
{
	sn* pa = ps;
	while (pa)
	{
		printf("%d->", pa->data);
		pa = pa->next;
	}
	printf("NULL");
}

为了方便我们调试,先写一个链表的打印函数。

插入数据

sn* buy(lala x)//插入数据
{
	sn* pa = malloc(sizeof(sn));
	if (pa == NULL)
	{
		printf("NULL");
		exit(1);
	}
	pa->next = NULL;
	pa->data = x;
	return pa;
}

我们先为要插入的数据创建一个结点,之后再写出尾插和头插函数。

尾插法

void backru(sn** ps, lala x)//尾部插入,创建二级指针是要接受一级结构体指针的地址
{
	assert(ps);
	sn* pa = buy(x);
	if (*ps == NULL)//*ps为指向第一个节点的指针,而这种情况表示链表只有一个节点
	{
		*ps = pa;
	}
	else//链表有多个节点的情况
	{
		sn* pb=*ps;
		
		while (pb->next)
		{
			pb = pb->next;
		}
		pb->next = pa;
	}
}

这里我们尾插函数是要修改指针里面的内容,所以我们要使用二级指针来接收一级指针的地址,

这里尾插有两种情况,一种是链表内只有一个结点,第二种是链表内有多个结点,所以在尾插函数内要分两种情况讨论。

头插法

void frontru(sn** pa, lala x)//头部插入
{
	assert(pa);
	sn* pb = buy(x);
	pb->next = *pa;
	*pa = pb;
}

头插法相对比较简单,首先我们要断言pa指针是否为空,然后生成一个结点,并将这个结点插入头部。

尾删法

void backshan(sn** pa)//尾删
{
	assert(pa && *pa);
	if ((*pa)->next == NULL)//链表只有一个节点
	{
		free(*pa);
		*pa = NULL;
	}
	else//链表有多个节点
	{
		sn* a;
		sn* b=*pa;
		a = *pa;
		while (a->next)
		{
			b = a;
			a = a->next;
		}
		free(a);
		a = NULL;
		b->next = NULL;
	}
}

 尾删法和尾插法一样也分两种情况讨论,链表有一个或多个结点,并且在尾删法的时候我们要进行断言,排除链表为空删除空间这种错误情况。

头删法

void toushan(sn** ps)//头删
{
	assert(ps && *ps);
	sn* new = (*ps)->next;
	free(*ps);
	*ps = new;//将第二个节点的地址给头节点
}

在进行头删的时候我们也要进行断言指针,然后创建一个新的结构体指针,释放它所指向的空间并让他指向下一个位置。

查找

sn* find(sn* ps,lala x)//查找
{
	assert(ps);
	sn* new = ps;
	while (new)
	{
		if (new->data == x)
		{
			return new;
		}
		new = new->next;
	}
	return NULL;
}

可通过查找函数的返回值来判断是否找到数据。

在指定位置之前插入信息

void insert(sn** ps, sn* pos, lala x)//在指定位置之前插入信息
{
	assert(ps && *ps);
	assert(pos);
	sn* new = buy(x);
	if (*ps == pos)//链表只有一个节点
	{
		frontru(ps, x);
	}
	else//链表有多个节点
	{
		sn* pa = *ps;
		while (pa->next != pos)
		{
			pa = pa->next;//找到pos前的一个位置
		}
		//找到位置了
		pa->next = new;
		new->next = pos;
	}

}

我们首先要断言ps和*ps以及pos位置,排除这些位置为空的情况,我们的pos位置可以通过find函数去寻找,如果链表只有一个结点,我们可以直接去使用头插法,如果有多个结点,我们就要去遍历整个链表。

在指定位置之后插入结点

void backint(sn* pos, lala x)//在指定位置之后插入信息
{
	assert(pos);
	sn* new = buy(x);
	new->next = pos->next;
	pos->next = new;
}

在指定位置之后插入结点,我们不需要头结点,我们只需要将pos的next指针传给新节点的next,意思是新节点的next指向后继结点,然后再将新节点的地址赋予pos的next。

删除pos结点


void shan(sn** pa, sn* pos)//删除pos节点
{
	sn* new = *pa;
	if (pos == *pa)
	{
		toushan(pa);
	}
	else
	{
		while (new->next != pos)
		{
			new = new->next;
		}
		new->next = pos->next;
		free(pos);
		pos = NULL;
    }
}

删除pos结点也分为两种情况,链表只有一个或多个结点。有多个结点时通过遍历链表来找到pos的位置并将其释放,只有一个结点使用头删便可以删除。

删除pos之后的结点

void after(sn* pos)//删除pos之后节点
{
	sn* new = NULL;
	new= pos->next;
	pos->next = new->next;
	free(new);
	new = NULL;
}

删除pos之后的结点我们只需通过pos的next来找到pos之后的结点,并将其释放就行了。

单链表的销毁

void delete(sn** ps)
{
	sn* del = *ps;
	sn* next = NULL;
	while (del)
	{
		next = (*ps)->next;
		free(del);
		*ps = next;
	}
	*ps == NULL;
}

我们只需通过遍历链表并不断的释放该链表的结点,最后将头节点置空。

源码

至此单链表的基础结构以全部完成,以下是源码。

#include<stdio.h>
#include<stdlib.h>
#include<assert.h>
typedef int lala;

typedef struct SListNode//链表结构
{
	lala data;
	struct SListNode* next;//指向下一个节点的指针
}sn;

void print(sn* ps)//打印链表,接受链表的地址
{
	sn* pa = ps;
	while (pa)
	{
		printf("%d->", pa->data);
		pa = pa->next;
	}
	printf("NULL");
}

sn* buy(lala x)//插入数据
{
	sn* pa = malloc(sizeof(sn));
	if (pa == NULL)
	{
		printf("NULL");
		exit(1);
	}
	pa->next = NULL;
	pa->data = x;
	return pa;
}
void backru(sn** ps, lala x)//尾部插入,创建二级指针是要接受一级结构体指针的地址
{
	assert(ps);
	sn* pa = buy(x);
	if (*ps == NULL)//*ps为指向第一个节点的指针,而这种情况表示链表只有一个节点
	{
		*ps = pa;
	}
	else//链表有多个节点的情况
	{
		sn* pb=*ps;
		
		while (pb->next)
		{
			pb = pb->next;
		}
		pb->next = pa;
	}
}

void frontru(sn** pa, lala x)//头部插入
{
	assert(pa);
	sn* pb = buy(x);
	pb->next = *pa;
	*pa = pb;
}

void backshan(sn** pa)//尾删
{
	assert(pa && *pa);
	if ((*pa)->next == NULL)//链表只有一个节点
	{
		free(*pa);
		*pa = NULL;
	}
	else//链表有多个节点
	{
		sn* a;
		sn* b=*pa;
		a = *pa;
		while (a->next)
		{
			b = a;
			a = a->next;
		}
		free(a);
		a = NULL;
		b->next = NULL;
	}
}

void toushan(sn** ps)//头删
{
	assert(ps && *ps);
	sn* new = (*ps)->next;
	free(*ps);
	*ps = new;//将第二个节点的地址给头节点
}

sn* find(sn* ps,lala x)//查找
{
	assert(ps);
	sn* new = ps;
	while (new)
	{
		if (new->data == x)
		{
			return new;
		}
		new = new->next;
	}
	return NULL;
}

void insert(sn** ps, sn* pos, lala x)//在指定位置之前插入信息
{
	assert(ps && *ps);
	assert(pos);
	sn* new = buy(x);
	if (*ps == pos)//链表只有一个节点
	{
		frontru(ps, x);
	}
	else//链表有多个节点
	{
		sn* pa = *ps;
		while (pa->next != pos)
		{
			pa = pa->next;//找到pos前的一个位置
		}
		//找到位置了
		pa->next = new;
		new->next = pos;
	}

}

void backint(sn* pos, lala x)//在指定位置之后插入信息
{
	assert(pos);
	sn* new = buy(x);
	new->next = pos->next;
	pos->next = new;
}

void shan(sn** pa, sn* pos)//删除pos节点
{
	sn* new = *pa;
	if (pos == *pa)
	{
		toushan(pa);
	}
	else
	{
		while (new->next != pos)
		{
			new = new->next;
		}
		new->next = pos->next;
		free(pos);
		pos = NULL;
    }
}

void after(sn* pos)//删除pos之后节点
{
	sn* new = NULL;
	new= pos->next;
	pos->next = new->next;
	free(new);
	new = NULL;
}

void delete(sn** ps)
{
	sn* del = *ps;
	sn* next = NULL;
	while (del)
	{
		next = (*ps)->next;
		free(del);
		*ps = next;
	}
	*ps == NULL;
}

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值