C++之链表

1. 教程上的概念在这里插入图片描述在这里插入图片描述

在这里插入图片描述在这里插入图片描述
教材来源:殷人昆 数据结构:用面向对象方法与C++语言描述 (第二版)

2. 博客参考

C++链表学习笔记
https://blog.csdn.net/u011411195/article/details/51150102

关于链表中头指针和头结点的理解:
https://blog.csdn.net/weixin_41413441/article/details/79063738

C++ 链表:
https://blog.csdn.net/shuwenting/article/details/81479520

链表节点的增加和删除:
https://blog.csdn.net/happyhuidi/article/details/82685948

链表的定义:

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

注意:一般算法中点的都是结点
教材中写的就是结点而非节点。

节点呢,被认为是一个实体,有处理能力,比如,网络上的一台计算机;而结点则只是一个交叉点,像“结绳记事”,打个结,做个标记,仅此而已,还有就是,要记住:一般算法中点的都是结点,不要写错成节点,我发现剑指Offer上就写成节点了,这种写法是错误的

概念总结

1. 头指针
(1)链表的名字就是其头指针,当没有头结点时,头指针所指的data就是第一个结点所存的数
(2)头指针是链表中必须存在的

(2)头指针中存储的是第一个节点的地址,若链表中存在头结点,那么头指针存储的是头结点的地址,若头结点不存在,则存储的是首元结点的地址

2. 头结点

(1)头结点不是必须存在的

(2)头结点中可以不存储data,也可以存储如链表长度等

3. 结点

结点由两部分组成,一部分存储的为值即Value,另外一部分存储了一个指针,指针存储的是下一个节点的地址

下图为带有头结点的示意图:
在这里插入图片描述
下图为没有头结点的单链表的示意图:
在这里插入图片描述
深刻理解:带头结点和不带头结点的区别 使用头结点的优势
https://blog.csdn.net/qq_24118527/article/details/81317410

2. 在循环遍历链表时要定义一个临时的指针

该临时指针用于接收头指针的地址


    ListNode* pNode = *pHead;

为什么? 要这么做,应该找个头指针我们可能后面还要用,如果直接用头指针参与循环遍历,则头指针的地址会被覆盖,最后找不到了。导致我们后期无法使用头指针用来遍历输出链表。
参考博客:https://blog.csdn.net/liu16659/article/details/70184399

3. 删除链表中值为value的结点

首先明确一点,是下面哪种情况。

1.删除第一个含有value的结点
2.删除所有含有值为value的结点

下面是这两种情况的实现

1. 删除第一个含有value的结点

void RemoveNode(ListNode** pHead,int value)
{
	if(pHead == nullptr || *pHead == nullptr)
		return;
	//定义一个ListNode指针,用于释放要删除的结点
	ListNode* pToBeDeleted = nullptr;  
	if((*pHead)->m_nValue == value)
	{
		pToBeDeleted = *pHead;
		*pHead = *pHead->m_pNext;
	}
	else
	{
		// 定义一个临时的指针,用于遍历链表以找到要删除的结点(如果存在的话)的前一个结点
		ListNode* pNode = *pHead;
		
		//&&具有短路的功能
		while(pNode->m_pNext != nullptr && pNode->m_pNext->m_nValue != value)
			pNode = pNode->m_pNext;
		
		//pNode->m_pNext != nullptr还得加上,不然万一上面的while是因为pNode->m_pNext == nullptr而终止(表明没有找到value),若不加上,则pNode->m_pNext->m_nValue == value这句就会空指针异常。
		//为啥需要这个if判断,怕万一没找到带有value值的结点while以pNode->m_pNext == nullptr结束的。
		if(pNode->m_pNext != nullptr && pNode->m_pNext->m_nValue == value)
		{
			pToBeDeleted = pNode->m_pNext;
			pNode->m_pNext = pNode->m_pNext->m_pNext;
		}
	}
	if(pToBeDeleted != nullptr)
	{
		delete pToBeDeleted;
		pToBeDeleted = nullptr;
	}
}

我们要删除节点A,必须找到结点A的前一个结点,然后对其后继结点进行值判断,所以循环里用的是pNode->m_pNext->m_nValue,这里的pNode指的得是当前结点,但进行值判断的是其后继结点。while()循环中pNode->m_pNext != nullptr是用了判别当前结点是否到达末尾。
&&具有短路的功能,即如果第一个表达式为false,则不再计算第二个表达式,例如,对于pNode->m_pNext != nullptr && pNode->m_pNext->m_nValue != value表达式,当pNode->m_pNext 为nullptr时,后面的表达式不会执行,所以不会出现NullPointerException

另外
虽然我们必须得找到要删除的结点A的前一个结点,但是就只对当前结点的值(value)进行判别也是可以的,感觉还更容易理解一些。代码如下:

node *del(node *head, int num)
{
	node *p1, *p2;
	p2 = new node;
	p1 = head;
 
	while (num != p1->data && p1->next != NULL)
	{
		p2 = p1;  //保留当前结点p1的前一个结点
		p1 = p1->next;// p1和p2位置: p2->p1		
	}
 	
 	//判断是否p1的值是我们寻找的。若不是,就证明上面的while结束是因为遍历到末尾了,也没找到值为data的结点。
	if (num == p1->data)  
	{
		if (p1 == head)// 删除头结点
		{
			head = p1->next;
			delete p1;
			p1 = nullptr;
		}
		else
		{
			p2->next = p1->next;
			delete p1;
			p1 = nullptr;
		}
	}
	else
	{
		cout << num << " could not been found in the current single linker!" << endl;
	}
	return head;
}

p2的作用是保留当前结点p1的前一个结点。

2.删除所有含有值为value的结点

void deleteSpecialData(Node* &L,int x) {	
	Node* r;
	r = L;
	while(r->next!=NULL){//当没有到达链表尾部的时候,继续循环 
		if(r->next->data == x){
			Node* q;//申请一个指针q 
			q=r->next;//保存L->next的指针位置 
			r->next = r->next->next;//改变链表的指针 
			delete(q);//释放节点空间q 
		} 
		else
		r= r->next;//往下循环 
	}
}
  • 2
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
你可以使用C++来输入链表和输出链表。以下是一个简单的示例代码: ```cpp #include <iostream> // 链表节点的定义 struct ListNode { int val; ListNode* next; ListNode(int x) : val(x), next(nullptr) {} }; // 输入链表 ListNode* inputLinkedList() { int n; std::cout << "输入链表节点个数: "; std::cin >> n; ListNode* head = nullptr; ListNode* curr = nullptr; std::cout << "输入链表节点值: "; for (int i = 0; i < n; i++) { int val; std::cin >> val; ListNode* newNode = new ListNode(val); if (head == nullptr) { head = newNode; curr = newNode; } else { curr->next = newNode; curr = curr->next; } } return head; } // 输出链表 void outputLinkedList(ListNode* head) { std::cout << "链表节点值: "; while (head != nullptr) { std::cout << head->val << " "; head = head->next; } std::cout << std::endl; } int main() { ListNode* head = inputLinkedList(); outputLinkedList(head); return 0; } ``` 在这个示例中,我们首先定义了一个名为 `ListNode` 的结构体,用于表示链表的节点。然后,我们编写了两个函数 `inputLinkedList` 和 `outputLinkedList` 分别用于输入和输出链表。 在 `inputLinkedList` 函数中,我们首先要求用户输入链表节点的个数。然后,我们使用一个循环来读取每个节点的值,并将其插入链表中。最后,函数返回链表的头节点。 在 `outputLinkedList` 函数中,我们遍历链表并输出每个节点的值。 在 `main` 函数中,我们首先调用 `inputLinkedList` 函数来输入链表,然后调用 `outputLinkedList` 函数来输出链表的节点值。 你可以根据自己的需要修改代码,并根据输入和输出格式进行相应的调整。希望对你有帮助!

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值