链表的简单实现和为什么要传二级指针

        因为顺序表的内存连续存放,导致存储数据和删除挪动数据时都需要耗费很大的力气,

        而链表因为不是物理上连续存储的就可以充分利用内存空间,并简化了删除增加方面的操作。简单的来说,链表就是根据地址这条链,将元素串联到一起。

例如:每个struct结构体中有两个成员,一个是要存放数据的类型,一个是结构体指针类型。

        如图所示,每个日字型的格子都是一个struct结构体,且链表的最后的结构体中的指针为空,表示链表结束。

那么如何实现一个链表呢,假设一开始只有一个结构体指针(链表为空):

        我们可能会想到先开辟一个struct结构体的内存:malloc(sizeof(struct xxx));然后让最开始的struct的指针指向这个结构体的地址,这是一个正确的想法,接下来我们来实现一下:

#include<stdio.h>
#include<stdlib.h>
typedef int StructType;
typedef struct stu{
	StructType number;
	struct stu* pointer;
}stu;

void SeqListPushBack2(stu* plist, StructType num) {
	stu* phead = plist;
	if (phead == NULL) {
		stu* newhead = malloc(sizeof(stu));
		phead = newhead;
		newhead->number = num;
		newhead->pointer = NULL;
	}
	else {
		while (phead -> pointer != NULL) {
			phead = phead->pointer;
		}
		stu* newhead = malloc(sizeof(stu));
		phead->pointer = newhead;
		newhead->number = num;
		newhead->pointer = NULL;
	}	
}
int main(){
    stu* plist = NULL;
    SeqListPushBack2(plist, 5);
    return 0;
}

        我写了一个尾插函数,用于从链表尾部插入一个元素,因为指向链表的指针为空,链表也为空,所以会进入尾插函数的if语句部分,但是奇怪的是,当程序运行,plist链表并没有发生改变(可以写一个打印链表的函数,一会我也会给),当我们调试发现了问题:

        可以看到,执行完了if语句,phead确实指向链表的第一个结构体元素,第一个结构体元素也确实建立起来,接收的数字5的传参,但是当我们结束运行完这个函数,plist并没有指向刚才malloc的空间,plist也没有改变:

        这是因为plist是传值调用,但是乍一看,这不是传的指针吗?我下面举个例子帮助理解:

        仔细看上面的代码,第一个可能很好理解,函数传的是形参,所以 a 拷贝了 b 的值,a 被赋值但是最后出函数销毁,所以 b 的值没有变,第二个例子同样是这样:a指针 拷贝了 b指针 的值,被赋为空指针但是出函数被销毁,所以 b指针 的值仍不受影响。

        那么如何避免出现这样的情况,就要传地址调用,要改变b的值就要传b的地址(一级指针),要改变 b指针 的值就要传b指针的地址(二级指针):

接下来就要修改我们的尾插函数,即传二级指针才能修改一级指针(plist)的值:

#include<stdio.h>
#include<stdlib.h>
typedef int StructType;
typedef struct stu{
	StructType number;
	struct stu* pointer;
}stu;

void SeqListPushBack1(stu** pointer, StructType num) {
	if (*pointer == NULL) {
		stu* newhead = malloc(sizeof(stu));
		*pointer = newhead;
		newhead->number = num;
		newhead->pointer = NULL;
	}
	else {
		stu* tile = *pointer;
		while (tile->pointer != NULL) {
			tile = tile->pointer;
		}
		stu* newhead = malloc(sizeof(stu));
		tile->pointer = newhead;
		newhead->number = num;
		newhead->pointer = NULL;
	}
}
int main(){
    stu* plist = NULL;
    SeqListPushBack1(plist, 5);
    return 0;
}

我们还可以写一个打印出链表值的函数:对于打印函数来说,传一二级指针都可以,因为不涉及到改变指针值的问题,所以下面两个函数都可以打印。

void SeqListPrintf(stu* phead) {
	stu* pos = phead;
	while (pos != NULL) {
		printf("%d ", pos->number);
		pos = pos->pointer;
	}
	printf("NULL");
}
void SeqListPrint(stu** phead) {
	stu* pos = *phead;
	while (pos!=NULL)
	{
		printf("%d ", pos->number);
		pos = pos->pointer;
	}
	printf("NULL");
}

        有人可能又糊涂了,怎么这里又可以使用一级指针了呢,因为传值是对实参的拷贝,相当于复制了一份链表,拷贝也可以打印,传地址是直接找到结构体元素,直接打印原有的链表。

最后附上打印的结果,这一部分要多进行调试,了解链表建立的过程。

又忘记给malloc开辟的空间进行释放了,还要写一个函数给链表进行空间释放,否则会内存泄漏。

这就是文章的全部内容了,希望对你有所帮助,如有错误欢迎讨论。 

  • 24
    点赞
  • 12
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值