链表的创建,插入,删除代码理解

对与链表来说,它与数组都是数据存储的一种结构,但是数组往往更好理解,因为数组可以通过下标去访问,对于数据的查找可以说是更加的方便,也更好让人理解,况且数组的顺序和电脑上的物理顺序也是相同的,我们可以通过下标的改变从而对直接对数组中的数据进行访问,但是数组也有自己的局限性,如数组需要声明大小,从而从电脑中申请一段合适的空间,而且数组下标移动空间是固定的,取决与声明的数组类型,所以当我们要对数组中的元素进行增加或者删除的话,往往是通过遍历的方法去不断的进行数据存储与数据覆盖的方法去进行操作,这里的时间复杂度往往很高,所以我们选择用链表去处理一些需要频繁增加或者删减的数据。链表与数组不同,它更注重的是逻辑顺序而不是空间的物理顺序,所以当我们碰见链表时往往要有严密的逻辑思维,以及他的以地址查找的方式对于初学者可能会有些抽象,但是他在某些情况下的优秀表现使得我们必须了解它,并且合理的运用它。

链表的缺点

不可否认的是,链表也有缺点,例如在数据的定向查找方面不如数组,链表想要访问其中一个元素,必须从首节点挨个遍历,才可以访问到这个元素,而且链表的数据利用率不如数组,数组中的存储的数据就是我们要的数据,但是链表中我们必须存储下一个节点的地址,这些数据并不是我们要利用的数据,而是链表这个数据结构所带来的额外数据。


 

这是一道在C语言网上的经典例题,本文将为绕着这篇文章讲解链表的创捷,删除,插入,以及对特定位置的链表元素的获取。

节点的构造

在链表中,最基本的单元就是节点,我们把各个节点相连就可以得到一条完整的链表,而节点一般分为两块区域,数据域与指针域,我们通过数据域存放我们要利用的数据,而用指针域存放下一个节点的地址(双重链表即同时记录上一个元素的地址)

struct MyLinkList
{
	int data;
	MyLinkList* next;
};

非常简单的一个结构体,但却是链表结构的基本单元

链表的创建(头插法)

在链表的构建中,我们往往会制造一个头节点,这样更加方便删除节点等功能的实现,链表与数组不同,数组的空间是固定大小的,但是链表则不同,他在每一次的循环中创造新的空间去存储数据,而循环的次数我们可以控制,所以链表的数据个数是动态的,这也是链表的优点之一。

​
MyLinkList* CreateList(int n,MyLinkList *head)
{
	head = new MyLinkList;//头节点
	head->next = NULL;
	for (int i = 0;i < n;i++)
	{
		MyLinkList* newnode = new MyLinkList;
		cin >> newnode->data;
		newnode->next = head->next;
		head->next = newnode;
	}
	return head;
}

​

对于上述代码,我们首先传入在主函数中声明的头指针与要输入的数据个数,再以头指针指向地址申请空间作为头节点,同时将head->next指向空,之后进入循环,有多少个数据就建造多少个数据节点,再把他们相连,在进入循环后便是链表构建的核心,我们需要创建一个新节点,这个节点的主要作用是确立链表的逻辑顺序,因为是单链表,所以我们要确立下一个节点的地址,而新节点便代表了“下一个节点”的概念,而head节点代表了链表中的第一个节点。这里的概念有点抽象,所以我将会围绕三层核心代码向大家画图解释。 

这样不断的插入新节点,最后从head遍历可以逆序输出,符合我们的题目要求 

在这段代码中,创建完链表返回头指针,再把头指针传入到其他函数中

链表的遍历(输出)

相比于链表的创建,链表的遍历无疑更好理解,我们只需要创造一个遍历指针,让这个指针指向第一个数据节点,也就是头节点的head->next,然后进入while循环,直到遍历完整个链表(链表中只有最后一个元素的next指向NULL。这样在遍历链表的同时输出每一个节点中数据域中存储的值。这样子看还是比较直观的。

void ShowList(MyLinkList* head)
{
	MyLinkList* visit;
	visit = head->next;
	if (visit == NULL)
	{
		cout << "Link list is empty";
	}
	while (visit != NULL)
	{
		cout << visit->data << " ";
		visit = visit->next;
	}
	cout << endl;
}

链表中节点的删除

链表的删除相对与数组删除有很大优势,相信你已经发现,链表节点没有整体的概念,与一个链表节点有关的只有他的上一个节点与下一个节点,由于是单链表,所以我们只需要考虑上一个节点的next,那么这个时候我们删除链表中节点的思路就比较清楚了,关键就是在删除节点时,我们要找到这个节点的同时也要知道上一个节点,并把上个节点的next指向被删除节点的下一个节点的位置,而下一个节点的位置数据我们是存储在被删除的节点的next中,所以这里的关键就是用被删除的节点的next中的地址覆盖掉上一个节点中next值。这样我们的被删除节点就脱离了链表。

特别注意的是,我们一定要考虑内存的占用与释放,这里我们选用了perserve指针记录被删除节点的位置,而在链表删除结束后,我们再通过perserve指针去查找节点,最后释放掉这段空间的内存。

在下面的代码中,我们为了使得防止visit->next为NULL而使得visit->next->next无法访问的情况出现,我们选用visit->next遍历

void DeleteList(MyLinkList* head,int n)
{
	int dn;
	cin >> dn;
	int num = 0;
	MyLinkList* visit, * preserve;
	visit = head;
	if (visit->next==NULL)
	{
		cout << "delete fail"<<endl;
		return;
	}
	preserve = NULL;
	while (visit->next != NULL)
	{
		num++;
		if (num == dn)
		{
			preserve = visit->next;
			visit->next = visit->next->next;
			break;
		}
		visit = visit->next;
	}
	if (preserve != NULL)
	{
		delete preserve;
	}
	cout << "delete OK" << endl;
}

同样的,我将画图方便大家的理解

链表的插入

链表的插入思路其实与链表的删除的思路异曲同工,关键是找到插入节点的前一个节点,同时把新节点的next与前一个节点next进行调整

void InsertList(MyLinkList* head)
{
	MyLinkList* visit, * s;
	visit = head;
	int j = 0;//记录遍历几次,同时监视是否应该插入
	int location;//插入的位置
	cin >> location;
	int insert;//插入的数值
	cin >> insert;
	while (visit && j < location - 1)//只要检测到插入位置后跳出循环
	{
		visit = visit->next;
		++j;
	}
	if (!visit || j > location - 1)//若遍历完所有节点未找到插入地点直接返回
	{
		cout << "insert fail" << endl;
		return;
	}
	s = new MyLinkList;
	s->data = insert;//赋值
	s->next = visit->next;//新节点插入链表
	visit->next = s;
	cout << "insert OK";
	cout << endl;
}

图片较好理解

链表的数据定向获取 

这部分较较简单,直接上代码

void GetList(MyLinkList* head)
{
	int getn;//获取第几个节点的数据
	cin >> getn;
	MyLinkList* visit = head->next;
	int num = 0;//记录器,监视到第几个节点
	while (visit != NULL)
	{
		num++;
		if (num == getn)
		{
			cout << visit->data;
			break;
		}
		visit = visit->next;
	}
}

到此为止,本人对链表的初步见解结束。

纯新手,对数据结构的学习总结,不喜勿喷。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值