《程序员代码面试指南》it名企算法与数据结构题目最优解(第二版)刷题笔记12

本文深入探讨了链表数据结构的多种操作方法,包括删除重复节点、移除指定值的节点、选择排序及特殊节点删除技巧。通过具体示例和代码实现,详细解析了每种方法的时间和空间复杂度。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

由于之前看了牛客网的数据结构和算法的课程知道了左神,现在找到了这本书当作入门书做做吧,虽然书的题解都是java实现的,但好在用c++实现难度不大

第二章 链表问题

第一题:删除无序单链表中值重复出现的节点
方法一:用哈希表,时间复杂度O(N),额外空间复杂度O(N)
方法二:类似选择排序的过程,时间复杂度O(N^2),额外空间复杂度O(1)

struct ListNode {
	int val;
	ListNode *next;
	ListNode(int x) : val(x), next(nullptr) {}
};

class Solution {
public:
	void removeRep2(ListNode* head) {
		ListNode* cur=head,*next=head->next,*pre=head;//不用先记录next,先删完再直接cur=cur->next
		while (cur)
		{
			while (next)
			{
				if (cur->val==next->val)
				{
					pre->next=next->next;
				} 
				else
				{
					pre=next;
				}
				next=next->next;
			}
			cur=cur->next;
		}
	}
};

题目二:在单链表中删除指定值的节点
方法一:利用栈或者其他容器收集节点的方法,时间复杂度O(N),额外空间复杂度O(N)
方法二:不用任何容器直接调整,时间复杂度O(N),额外空间复杂度O(1)

class Solution {
public:
	ListNode* removeVal(ListNode* head, int num) {
		ListNode*cur=head,*pre=head,*next=head->next;
		while(cur->val==num&&cur==head)
		{
			head=next;
			cur=head;
			next=next->next;
		}
		pre=head;
		cur=next;
		next=next->next;
		while (cur)
		{
			if (cur->val==num)
			{
				pre->next=next;
			}
			else{
				pre=cur;
			}
			cur=cur->next;
			next=next->next;
		}
		return head;
	}
}

第三题:单链表的选择排序

struct ListNode {
	int val;
	ListNode *next;
	ListNode(int x) : val(x), next(nullptr) {}
};
class Solution {
public:
	ListNode* getSmallestPreListNode(ListNode* head){
		ListNode* smallpre=nullptr,*pre=head,*cur=head->next,*small=head;//需要一个small来记录最小值
		while (cur)
		{
			if (cur->val<small->val)
			{
				smallpre=pre;
				small=cur;
			}
			pre=cur;
			cur=cur->next;
		}
		return smallpre;
	}
	ListNode* reverseKGroup(ListNode* head) {
		ListNode *tail=nullptr,*cur=head,*small=nullptr,*smallpre=nullptr;
		while (cur)
		{
			small=cur;//这样如果每一次遍历发现cur->val就是最小值,也就是getSmallestPreListNode返回nullptr,那么接下来的操作也可以顺利把cur->val放进有序链表中
			smallpre=getSmallestPreListNode(cur);
			if (smallpre!=nullptr)
			{
				small=smallpre->next;
				smallpre->next=smallpre->next->next;
			}
			cur=cur==small?cur->next:cur;
			//这个ifelse加tail=small完美将新的头结点的创建和尾巴的更新实现了
			if (tail==nullptr)
			{
				head=small;
			} 
			else
			{
				tail->next=small;
			}
			tail=small;
		}
		return head;
	}
}

第四题:一种怪异的节点删除方式
举例:链表1-2-3-null,只知道要删除节点2,而不知道头结点。
只需要把2节点变为3,然后删除3即可。
问题一:这样的删除方式无法删除最后一个节点,如果是节点3的话,我们能不能把节点3在内存上的区域变成nullptr呢?这样不就相当于让节点2的next指针指向了nullptr,起到节点3被删除的效果了吗?不可以。nullptr在系统中是一个特定的区域,如果想让节点2的next指针指向nullptr,必须找到节点2.

问题二:这种删除方式在本质上根本就不是删除了node节点,而是把node节点的值改变,然后删除node的下一个节点,在实际的工程中可能会带来很大问题。比如,工程上的一个节点可能代表很复杂的结构,节点值的复制相当复杂,或者可能改变节点值这个操作都是被禁止的;再如,工程上的一个节点代表提供服务的服务器,外界对每个节点都有很多依赖,比如,实例中删除节点2时,其实影响了节点3对外提供的服务。

struct ListNode {
	int val;
	ListNode *next;
	ListNode(int x) : val(x), next(nullptr) {}
};

class Solution {
public:
	int error(const char a[]) {
		std::cout << a;
	}
	void removeListNodeWired(ListNode* head) {
		if (head == nullptr)return;
		ListNode* next = head->next; 
		if (next == nullptr)
			throw error("can not remove last code");
		head->val = next->val;
		head->next = next->next;
	}
};
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值