单向链表及二级指针

8 篇文章 0 订阅

前言

链表中的二级指针问题经常会让人摸不着头脑,迷迷糊糊。经常搞不清楚什么时候用二级指针,什么时候用一级指针。本文咱们来简单讨论下这个问题。

代码

 接口.cpp

#pragma once
#include <assert.h>
#include <string>
#include <iostream>
using namespace std;

typedef struct LinkNode{
	string name;
	int age;
	LinkNode* next;
}MyNode;

// 打印链表
void printLinkList(MyNode* phead)
{
	cout << "----------------链表遍历打印----------------" << endl;
	if (phead == nullptr)
	{
		cout << "当前链表为空" << endl;
		return;
	}

	MyNode* ptr = phead;
	while (ptr)
	{
		cout << "当前节点值-" << "Name:" << ptr->name << " Age:" << ptr->age << endl;
		ptr = ptr->next;
	}
}

// 释放链表
void freeLinkList(MyNode** phead)
{
	cout << "----------------链表内存释放----------------" << endl;
	if (*phead == nullptr)
	{
		cout << "当前链表为空,跳过释放" << endl;
		return;
	}

	MyNode* pMov = nullptr;
	while (*phead !=nullptr)
	{
		pMov = *phead;
		*phead = pMov->next;
		free(pMov);
		pMov = nullptr;
	}
}

// 链表初始化
void initLinkListBad(MyNode* node, int age, string name)
{
	cout << "----------------初始化链表----------------" << endl;
	// node形参的修改不影响实参,此种初始化方法是错误的,实参还是nullptr
	node = new MyNode;
	node->age = age;
	node->name = name;
	node->next = nullptr;
}

// 链表初始化,需要修改头节点指针本身的值,则需要传递二级指针
void initLinkList(MyNode** pnode, int age, string name)
{
	cout << "----------------初始化链表----------------" << endl;
	// 修改头节点指针本身的值
	*pnode = new MyNode;

	(*pnode)->age = age;
	(*pnode)->name = name;
	(*pnode)->next = nullptr;
}

// 添加节点(尾部)
// pphead需要使用二级指针,入参需要传入指针的地址。如果使用一级指针,则相当于传值,并不会改变实际的值,只是修改了形参临时变量的值。
void addNode(MyNode** pphead, int age, string name)
{
	cout << "----------------链表节点添加----------------" << endl;
	MyNode* newNode = new MyNode;
	newNode->name = name;
	newNode->age = age;

	newNode->next = nullptr;

	// 如果在添加节点中初始化头节点,则参数需要传递二级指针,否则传递一级指针即可
	if (*pphead == nullptr)
	{
		*pphead = newNode;
	}
	else 
	{
		MyNode* tail = *pphead;
		// 寻找链表尾部
		while (tail->next != nullptr)
		{	
			tail = tail->next;
		}
		tail->next = newNode;
	}
}

void addNodeBad(MyNode* phead, int age, string name)
{
	cout << "----------------链表节点添加----------------" << endl;
	MyNode* newNode = new MyNode;
	newNode->name = name;
	newNode->age = age;

	newNode->next = nullptr;

	if (phead == nullptr)
	{
		phead = newNode;
	}
	else
	{
		MyNode* tail = phead;
		// 寻找链表尾部
		while (tail->next != nullptr)
		{
			tail = tail->next;
		}
		tail->next = newNode;
	}
}

// 删除节点(如果要删除的点为头节点,则需要传二级指针,否则头节点变为野指针)
bool delNode(MyNode** pphead, string name)
{
	cout << "----------------链表节点删除----------------" << endl;
	if (*pphead == nullptr)
	{
		cout << "链表为空,无法删除!" << endl;
		return false;
	}

	MyNode* pPre = *pphead;
	MyNode* pCur = nullptr;
	// 头节点是目标数据
	if (pPre->name == name)
	{
		pCur = pPre;
		*pphead = pCur->next;
		// 释放内存
		free(pCur);
	}
	else
	{
		// 当前的下下一个节点不是最后一个节点 而且 当前节点的下一个节点不是目标
		while (pPre->next->next != nullptr && pPre->next->name != name)
		{
			pPre = pPre->next;
		}

		if (pPre->next->name == name)
		{
			// 找到目标
			pCur = pPre->next;
			pPre->next = pCur->next;
			free(pCur);
		}
		else {
			// 未找见
			cout << "节点" << name << "未找见,无法删除" << endl;
		}
	}

	return true;
}

// 错误的示例,未使用二级指针,如果头节点为目标删除对象,则头指针变为野指针
bool delNodeBad(MyNode* pphead, string name)
{
	cout << "----------------链表节点删除----------------" << endl;
	if (pphead == nullptr)
	{
		cout << "链表为空,无法删除!" << endl;
		return false;
	}

	MyNode* pPre = pphead;
	MyNode* pCur = nullptr;
	// 头节点是目标数据
	if (pPre->name == name)
	{
		pCur = pPre;
		pphead = pCur->next;
		// 释放内存
		free(pCur);
	}
	else
	{
		// 当前的下下一个节点不是最后一个节点 而且 当前节点的下一个节点不是目标
		while (pPre->next->next != nullptr && pPre->next->name != name)
		{
			pPre = pPre->next;
		}

		if (pPre->next->name == name)
		{
			// 找到目标
			pCur = pPre->next;
			pPre->next = pCur->next;
			free(pCur);
		}
		else {
			// 未找见
			cout << "节点" << name << "未找见,无法删除" << endl;
		}	
	}

	return true;
}

// 修改节点
void modifyNode(MyNode* phead, string name, int newAge)
{
	cout << "----------------链表节点修改----------------" << endl;
	if (phead == nullptr)
	{
		cout << "链表为空,无法修改!" << endl;
		return;
	}

	MyNode* ptr = phead;

	// 找见或者查到最后一个元素就跳出循环
	while (ptr->name != name && ptr->next!=nullptr)
	{
		ptr = ptr->next;	
	}

	if (ptr->name == name)
	{
		// 找到目标
		ptr->age = newAge;
		cout << "节点" << name << "已找见,现在修改" << endl;
	}
	else
	{
		// 未找见
		cout << "节点" << name << "未找见,无法修改" << endl;
	}
}

// 查找指定节点
void findNode(MyNode* phead, string name, MyNode** dest)
{
	cout << "----------------链表节点查找----------------" << endl;
	if (phead == nullptr)
	{
		cout << "链表为空,未查找到目标!" << endl;
		return;
	}

	MyNode* ptr = phead;

	while (ptr->name != name && ptr->next != nullptr)
	{
		ptr = ptr->next;
	}

	if (ptr->name == name)
	{
		// 找到目标
		*dest = ptr;
		cout << "节点" << name << "已找见" << endl;
	}
	else
	{
		// 未找见
		cout << "节点" << name << "未找见" << endl;
	}
	
}

main.cpp

#include "linknode.h"

// 主函数
int main()
{
#if 1
    MyNode *headNode = nullptr;

    initLinkList(&headNode, 31, "mazhen");

    addNode(&headNode, 28, "meng");
    addNode(&headNode, 27, "xiaomeng");
    printLinkList(headNode);

    delNode(&headNode, "xiaomeng");
    delNode(&headNode, "mazhen");
    delNode(&headNode, "meng");

    cout << "删除节点后打印:" << endl;
    printLinkList(headNode);

    freeLinkList(&headNode);
    

    // 对比验证不使用二级指针的情况
    /*MyNode* headNodeTest = nullptr;
    initLinkListBad(headNodeTest, 28, "meng");
    printLinkList(headNodeTest);*/
#endif

    return 0;
}


讨论

1.应该传递二级指针还是一级指针?

总结:一般传递的指针参数为链表头节点的指针,如果对应的操作里需要修改头指针本身的值,则需要传递二级指针即头指针的地址或称头指针的指针。本质上需要理解最基础的传值和传地址或引用的概念。

a.如打印链表仅传递一级指针即可。如初始化头节点,则需要传递二级指针,因为初始化头节点这个操作需要为一级指针的值去赋值。

b.如添加节点的话,传递一级指针即可,因为例如尾插只需要根据头节点地址找到为最后一个节点,然后插入。哪怕传入一级指针,通过形参赋值也可以找到我们需要的东西。当然传递二级指针也是可以的,只不过没什么必要,如文中代码可能会在添加节点的代码addNode中初始化第一个节点,则使用二级指针能够兼容两种情况。

c.如删除指定节点的话,需要传递二级指针,这里有两种情况,如果需要删除的恰好是头节点,则您如果传递一级指针的话则实参头节点指针会变为野指针,使用二级指针则能避免此问题。如果需要删除的不是头节点,则使用一级指针即可,此时不会出现野指针问题。为了兼顾这两方面,删除节点时需要使用二级指针。

d.如清空链表则需要传递二级指针。试想传递一级指针的话因为传值问题导致出头节点外的其他节点均被释放,但头节点无法释放,形成野指针。如果传递二级指针,则不会存在该问题了。

其余操作分析方法类似,欢迎一起讨论。

相关截图:

 

 

 

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

我啥都会

如果觉得对您有帮助,打赏一下吧

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值