【数据结构】单向不带头结点 非循环链表的 增,删,查,改 的实现

#include <iostream>

using namespace std;

int main()
{
	/*结点一般是在堆上申请空间的,申请的空间的地址在堆区上是“随机分配”的,
	即空间的地址是随机的,
	这也是为什么每一个结点的地址不一定相互连续*/

	typedef int SListType;
	typedef struct SListNode
	{
		SListType data;
		struct SListNode *next;//这里必须要用struct,而不能用 typedef,因为这是在函数体内部

	}SListNode;

	//以下实现的均为:单向不带头不循环链表

	//创建新结点:
	SListNode* BuySListNode(SListType x)//注意该函数的参数是单链表当中的数据域
	{
		SListNode *node = (SListNode *)malloc(sizeof(SListNode));
		node->data = x;
		node->next = NULL;

		return node;
	}

	//链表的尾插:
	void SListPushBack(SListNode **pplist, SListType x)
	{
		SListNode *newnode = BuySListNode(x);//这里直接传入实际参数即可,无需连同类型一并传入

		if (*pplist == NULL)
		{
			*pplist = newnode;//传二级指针的目的便是为了空链表的地址能够 真正的 指向 newnode
		}
		else
		{
			SListNode *tmp = *pplist;//目的是对传入的参数不作修改

			while (tmp->next != NULL)//这里不要忘掉在pplist的后面加上->next,因为null永远在尾结点的下一个位置
			{
				tmp = tmp->next;
			}

			tmp->next = newnode;//将新结点链接到原有链表之后

			/*pplist->data = x;  //这二者的实现已经在创建新结点的函数中被实现过,无需重复实现
			pplist->next = NULL;*/
		}		
	}

	//链表的头插:
	void SListPushFront(SListNode **pplist, SListType x)
	{
		SListNode *newnode = BuySListNode(x);
		
		/*if (*pplist == NULL)//不同于链表的尾插,链表的头插无需对传入的链表是否为空链表进行判断
		{
			*pplist = newnode;
		}
		else
		{
			SListNode *tmp = *pplist;
			*pplist = newnode;
			*pplist->next = tmp;
		}*/

		newnode->next = *pplist;//这里的 newnode 即为该链表的头结点指针
		*pplist = newnode;
	}

	//链表的尾删:
	void SListPopBack(SListNode **pplist)
	{
		SListNode *cur = *pplist,*tmp = NULL;

		if (*pplist == NULL)//这里如果不分情况讨论的话,则会对内存空间造成二次free,从而出错
		{
			cout << "该链表为空,无需进行尾删操作" << endl;
			return;
		}
		else if ((*pplist)->next == NULL)//注意这里存在的优先级问题,-> 的优先级大于 *
		{
			/*这里讨论传入的链表长度为1的原因是:
			在下面的链表长度 > 1 的代码实现中,程序不会进入while循环,
			所以tmp的值为随机值,
			就会引发报错*/
			free(*pplist) = NULL;
			*pplist = NULL;
		}
		else
		{
			while (cur->next != NULL)
			{
				tmp = cur;
				cur = cur->next;
			}

			tmp->next = NULL;
			free(cur);
			cur = NULL;
		}

		//链表的头删:
		void SListNodePopFront(SListNode **pplist)
		{
			SListNode *tmp = (*pplist)->next;//不要忽略了这里 * 与 -> 的优先级
			free(*pplist);
			*pplist = tmp;
		}

		//查找链表的结点:
		SListNode *SListFind(SListNode *pplist, SListType x)
		{
			SListNode *cur = *pplist;
			while (cur->next != NULL)
			{
				if (cur->data == x)
				{
					return cur;
				}
				cur = cur->next;
			}

			return NULL; //不要忽略了在链表中没有找到的情况
		}

		//对链表中的其中一个结点进行后插
		void SListInsertAfter(SListNode *pos, SListType x)//因为有pos的地址,所以这里无需传入整个链表的地址
		{
			//法一:
			/*SListNode *tmp = pos->next;
			pos->next = BuySListNode(x);
			pos->next->next = tmp;*/

			//法二:就要注意下面代码的先后顺序问题
			SListNode *newnode = BuySListNode(x);
		    newnode->next = pos->next;
		    pos->next = newnode;			
		}

		//对链表中的其中一个结点进行前插:
		/*注意:只要用到BuySLListNode()函数的地方,就得创建一个指针变量去存放这个新结点的地址*/
		void SListPushFront(SlistNode **pplist,SListNode *pos, SListType x)//此时就要传入整个链表的地址,因为这是前插,需要对链表进行遍历
		{
			
			SlistNode *newnode = BuySListNode(x);

			if (pos == *pplist)
			{
				//此时相当于头插
				SListPushFront(pos, x);
			}
			else
			{
				SListNode *cur = *pplist, *tmp = NULL;

				while (cur != pos) //因为有 while 循环条件的限制,所以要对传入链表的长度分情况讨论
				{
					tmp = cur;
					cur = cur->next;
				}
				
				tmp->next = newnode;
				newnode->next = pos;
			}
			
		}

		//对某结点进行后删
		void SListPopBack(SListNode **pplist , SListNode *pos)
		{
			if (pos->next == NULL)//此时无需进行删除,因为这是给定结点的后删
			{
				return;
			}
			else
			{
				pos->next = pos->next->next;
				free(pos->next);
			}
		}

		//销毁链表(也需要逐个去释放结点)
		void SListDestroy(SListNode **pplist)
		{
			SListNode *cur = *pplist;
			while (cur != NULL)
			{
				SListNode *tmp = cur->next;//使用一个临时变量去存储下一个结点的地址

				free(cur);
				cur = tmp;
			}

			*pplist = NULL;
		}
		
	}
}

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值