数据结构——单链表

一.单链表的定义

链表是由一个个结点,通过指针串联起来的,一个结点是一个结构体,结构体中包括数据域和指针域。数据域中存储数据,指针域存储的是下一个结点的地址。

    1.不带头结点的单链表

无论带头还是不带头,都应该先创建一个指针,这个指针可以指向一个结构体(结点)。

先定义一个结构体:

typedef struct s {
	int data;
	struct s* next;
}s;

这个结构体中有int类型的数据域 ,和指向下一个相同类型结构体的指针

什么叫不带头结点,就是初始化时只创建了一个指针,但是这个指针并没有指向一个结构体,而是指向NULL。

如下图所示:

 相应的代码:

void init(s* ps) //ps为创建的结构体指针
{
	ps = NULL;
}

2.带头结点的单链表

带头结点的单链表会比不带头结点的单链表有很多的好处

首先,什么叫带头结点的单链表,和不带头结点的区别是创建了一个新的结构体指针,这个指针不再指向NULL。而是指向一个结构体,而这个结构体里面的一个指针指向NULL.

如图所示:

 相应的代码如下:

    s* ps;
	
	ps = (s*)malloc(sizeof(s));
	ps->next = NULL;

二.单链表的插入,删除

1.前插操作

先输入的排在后面,而后输入的排在前面

如图所示:

 从头指针往后看,先创建的排在后面,而后创建的排在前面。

代码实现如下:

int main()
{
	s* p;   //先创建一个指针
	p = (s*)malloc(sizeof(s));
	p->next = NULL;//创建一个带头结点的单链表
	s* s1;  //创建一个新指针,指向一个新的结构体
	int x = 10;//要输入到一个结点的数据,可以是任何数字
	s1 = (s*)malloc(sizeof(s));  
	s1->data = x;
	s1->next = p->next;
	p->next = s1;//注意的地方
	return 0;
}

如图:

 这里需要注意的是上面代码中两个指针改变的语句。上下两句顺序不可改变。

首先应用新创建的结构体里面的指针指向头指针指向的内容

然后再覆盖头指针只得地方,改为s1指针指向的地方,

这样新创建的结点就串在这个单链表中了

2.后插操作

从头指针往后看,先插入的排在前面,后插入的排在后面。

如图所示:

 实现代码如下:

int main()
{
	s* p;
	p = (s*)malloc(sizeof(s));//和前插法不同,这里一开始并没有让头结点的指针指向NULL
	s* p1;
	s* end = p;//指向最后一个结点位置
	int x = 10;
	p1 = (s*)malloc(sizeof(s));
	p1->data = x;
	end->next = p1;//让最后一个结点的指针指向要再后面传进来的结点
	end = p1;// p1 = end; //end指向新串进来的最后一个结点
	end->next = NULL;//在插入完之后,最后才让最后一个结点里面的指针指向NULL
	return 0;
}

如图:

 3.在已有的一串单链表中后插一个结点

假设,现在获取了一串单链表中的一个结点,并知道指向这个结点的一个指针

那么我们就可以进行操作了

首先如图所示:

 代码实现:

int main()
{
	s* p;//表示获取的一个指针,我们这里假设就是头指针了
	p = (s*)malloc(sizeof(s));
	p->next = NULL;
	s* s1;
	int x = 10;//要插入的数据
	s1 = (s*)malloc(sizeof(s));
	s1->data = x;
	s1->next = p->next;
	p->next = s1;
	return 0;
}

 4.在已有的一串单链表中前插一个结点

这里我们其中有两种方法

第一种就是从头开始,然后往后查找,找到指定的结点,在前面插入一个新的结点

这种方法的时间复杂度是O(n)。

第二种方法是在这个结点后面插入一个新的结点,然后进行数据的交换,这样就可以实现前插操作

代码实现:

int main()
{
	s* p;//假设这是我们获取的一个结点,要在这个结点前插入一个结点
	p = (s*)malloc(sizeof(s));
	s* s1;//创建的一个新的结点指针
	s1 = (s*)malloc(sizeof(s));//新的结点
	int x = 10;//要前插的数据
	s1->next = p->next;
	p->next = s1;//先实现后插操作
	s1->data = p->data;//前一个结点的数据赋值到后面
	p->data = x;//前面结点的数据被新的数据覆盖
	return 0;
}

5.删除第i个结点的内容

首先,要删除第i个结点的内容,我们要找到第i-1个结点。因为我们要修改第i-1个结点的指针,指向第i+1个结点,然后把第i个结点的内容释放掉。

如图所示:

代码如下:

int main()
{
	s* p;//
	s* p1 = p;//用来标记查找到哪个节点了
	int j = 0;//当前p指向的是第几个结点
	int i = 5;//假设我们要删除第5个结点的内容
	while (p1 != NULL && j < i - 1)//目的是找到第i-1个结点
	{
		p1 = p1->next;
		j++;
	}
	if (p1 == NULL)
	{
		printf("i的数值不合法");
		return 0;
	}
	s* shanchu = p1->next;
	p->next = shanchu->next;//第i-1个结点指针指向第i+1个结点
	free(shanchu);//释放掉这个结点
	return 0;
}

这里我只是按照思路写的代码,并不能运行,因为我还没有初始化一个单链表

值得思考的是,这里我新建了一个shanchu这个指针,指向要删除的结点,为什么要新建一个,而不是直接用p1->next来进行操作

因为p1->next在过程中会发生改变。最后如果释放掉p1->next的值,会错误释放,因此需要特地来新建一个指针,指向要删除的结点。

三.查找结点

1.指定要查找第几个结点

代码如下:

int main()
{
	s* p;//为头指针
	s* p1 = p;//用来移动指向
	int j = 0;//用来标记第几个结点
	//假设要查找第i个结点
	int i = 5;//我们随便设一个数字
	while (p1 != NULL && j < i) //思考为什么是j<i
	{
		p = p->next;
		j++;
	}
	return 0;
}

2.根据结点的数据内容查找结点

和上面的代码类似,时间复杂度都是O(n)

代码如下:

int main()
{
	s* p;//p为头指针
	s* p1 = p;//用来移动,防止头指针因移动导致找不到头结点
	int j = 0;//用来标记找到了第几个结点
	//假设要查找的这个结点的数据为5
	int i = 5;
	while (p != NULL && p->data != i)
	{
		p = p->next;
		j++;
	}
	return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值