单链表的应用

本次使用C++
删除相比于插入稍微简单一点
首先设单链表长为NodeNum
假设要删除第i个结点,只需要遍历到第i个,并删除这个结点,即可。
需要使用的函数free()
头文件:malloc.h或stdlib.h
作用:释放malloc(或calloc、realloc)函数给指针变量分配的内存空间。
注意:使用后该指针变量一定要重新指向NULL,防止野指针出现,有效规避错误操作。
本次因使用C++ 因此需要一个无需这些头文件
原理:先建立一个单链表,再对单链表进行遍历,让一个结构体指针指向对应要删除的结点,对其进行释放。

#include<iostream>
using namespace std;
struct node{
	int num;
	node* next;
};

首先是声明一个结构体

node* head, * now, * p;

再试对创建链表函数需要的变量声明
该变量最好声明为全局变量,因为是单向链表,所以后期想要让指针返回到头指针,全局变量比较方便

node* create()
{
	int Node;
	int NodeNum;
	head = new node;
	now = head;
	cout << "Please input NodeNum:";
	cin >> NodeNum;
	for (int i = 0; i < NodeNum; i++)
	{
		cout << "Please input number:";
		cin >> Node;
		p = new node;
		p->num = Node;
		now->next = p;
		now = p;

	}
	now->next = NULL;

	return head;
}

这里是最简单的单链表的建立
很常规

接下来是删除链表对应结点的函数

void Delete(node* head, int n)//删除第n个结点
{
	node* front, * N;
	front = head;
	N = head;
	for (int i = 0; i < n - 1; i++)//刚好在第n - 1个结点
		front = front->next;
	for (int i = 0; i < n ; i++)//刚好在第n 个结点
		N = N->next;
	
	//删除第n个结点
	front->next = N->next;
	free(N);
	N = NULL;

}

front是指向对应结点的前趋,
N指向对应结点

	front = head;
	N = head;

首先都让这两个指针指向头结点

	for (int i = 0; i < n - 1; i++)//刚好在第n - 1个结点
		front = front->next;
	for (int i = 0; i < n ; i++)//刚好在第n 个结点
		N = N->next;

接下来,用for循环分别将两个指针指向对应结点的前 和 对应结点

	front->next = N->next;

让front指针跳过对应结点,连接对应结点的下一个结点

	free(N);

然后释放对应结点
同时

N = NULL;

使用后该指针变量一定要重新指向NULL,防止野指针出现,有效规避错误操作。

为了有效的确定已经删除了这个结点
我们写一个求长函数,来求出这个链表的长度

int len(node* head)
{
	int n=0;
	node* p;
	p = head;
	while (p != NULL)
	{
		n++;
		p = p->next;
	}
	return n;
}

简单的循环遍历这个链表
n来记录这个链表的长度
返回n的值

最后的主函数

int main()
{
	
	int DeleteNodeNum;
	node* po;
	po = create();
	cout << "现在链表的实际长度" << len(head) << endl;
	po = po->next;
	while (po != NULL)
	{
		cout << "Out num:" << po->num << endl;
		po = po->next;
	}

	po = head;
	cout << "请输入要删除的结点数:";
	cin >> DeleteNodeNum;
	Delete(po, DeleteNodeNum);

	po = po->next;
	while (po != NULL)
	{
		cout << "Out num:" << po->num << endl;
		po = po->next;
	}
	cout << "现在链表的实际长度" << len(head);
	return 0;
}

主函数只是有效的验证链表的建立 以及告诉我们链表是否删除
以及现在链表的长度 没有什么技术含量

以上是对单向链表的删除

接下来是对单链表的插入
几乎所有都是相同的
不同只有一个插入函数

void Insert(node* head, int n)//n结点之后
{
	node* heq,* heh;
	node* p;
	heq = head;
	heh = head;
	int Node;

	p = new node;
	cout << "请输入要插入的数据:";
	cin >> Node;
	p->num = Node;
	for (int i = 0; i < n; i++)
	{
		heq = heq->next;//循环结束刚好在n结点
	}
	for (int i = 0; i <= n; i++)
	{
		heh = heh->next;//循环结束刚好在n结点后
	}
	heq->next = p;
	p->next = heh;

}

解析变量:

node* heq,* heh;

一个是n结点前的指针
另一个是指向n结点的指针

	heq->next = p;
	p->next = heh;

该语句连接了p指向的结点,p的前后继指向heh指向的结点
这样就完成了对链表的插入
为了确保插入的成功
可以利用求长函数len();来确定链表的长度

以上是对链表的插入

接下来是建立一个简单的双向链表
双向链表的好处就是知道一个结点就可以知道他的前趋和后继
但是这种事以空间换时间的方法
换句话说就是占用了你的空间来使时间上更快

struct node {
	int num;
	node* pre, * next;
};
node* head, * now, * p;
node* create()
{
	int Node;
	int NodeNum;
	head = new node;
	now = head;
	cout << "Please input NodeNum:";
	cin >> NodeNum;
	for (int i = 0; i < NodeNum; i++)
	{
		p = new node;
		cout << "Please input number:";
		cin >> Node;
		p->num = Node;
		p->pre = now;
		now->next = p;
		now = p;
	}
	now->next = NULL;
	return head;
}

看到代码就应该知道,与单向链表几乎差不多。
就是指针域多了一个,一个指向前趋 另一个指向后继
还有代码唯一不同的就是:

		p->pre = now;
		now->next = p;
		now = p;

建立时候让p的前趋指向now

此处我犯了一个错误
就是我把 p->pre = now;写成了now->pre=now
我以为在now跳到下一个结点之前,让他的前趋指向他自己
写的时候没有问题
但是在遍历的时候实际上他的前趋指向自己所在的结点
因为now的后继存的是其他指针的地址 这个指针指向的是一个结点 但是如果我这样写 now的前趋指向是自己的结点
就像是一个等差数列公差为1
一个从0开始
一个从1开始
即使后面差不多 但是每个数都相差了一
好比现在这个语句now的前趋指向now
到了指针的最后now的后继是NULL
那么now的前趋就是now 因此说明了前面的每一个结点的前趋都只是指向了自己,没有达到连接的目的

接下来是主函数:

int main()
{
	node* po;
	po = create();

		po = po->next;
	while (po != NULL)
	{
		cout << "Out num:" << po->num << endl;
		po = po->next;
	}
	cout << endl << endl;
	//因为此时的po是NULL 所以连数据域都没有 
	//cout << po->pre->num;
	po = head->next;
	po = po->next;
	cout << "当前:" << po->num;
	cout << "前驱:" << po->pre->num;
	cout << "后驱:" << po->next->num;

	return 0;
}

主函数也犯了一个错误
就是我注释的地方
因为循环让此时的po是空指针
因为空指针更本连数据域都没有,更没有什么前趋和后继
所以这样肯定是错误的

	cout << "当前:" << po->num;
	cout << "前驱:" << po->pre->num;
	cout << "后驱:" << po->next->num;

这个代码是为了验证这个链表是不是双向链表
就是有了这个代码我才发现前面建立链表时的错误

以上就是双向链表的建立
双向链表可以更好的进行插入 因为知道了某一结点 就知道了前趋和后继

当然双向链表只是比单向链表多了一个指针域
所以插入 删除 和 求长几乎都是一样的 所以没有多大的必要在这上面做文章
其次 还有一种循环链表 只是在最后一个结点的后继指向了头结点而已 所以近期我应该马上就会再写一片博客
以上的所有代码 均是在没有看书 求助等情况下写出 如有雷同纯属巧合

本次博客是在我失眠的情况下写的 从早上四五点左右写的
有些语言可能表达不当 请见谅

然后就是我们的镇店之图:
在这里插入图片描述

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值