LeetCode 热题 100_删除链表的倒数第 N 个结点(29_19_中等_C++)(单链表;双指针)(new_delete)

LeetCode 热题 100_删除链表的倒数第 N 个结点(29_19)

题目描述:

给你一个链表,删除链表的倒数第 n 个结点,并且返回链表的头结点。

输入输出样例:

示例 1:

输入 :head = 1,2,3,4,5, n = 2
输出:1,2,3,5

示例 2:
输入 :head = 1, n = 1
输出:\[\]

示例 3:
输入 :head = 1,2, n = 1
输出:1

提示:

链表中结点的数目为 sz

1 <= sz <= 30

0 <= Node.val <= 100

1 <= n <= sz

题解:

解题思路:

思路一(计算链表长度):

1、先求出链表的长度,再定位其前驱结点将对应结点删除 。(注意头结点的删除和其他结点不同,因没有前驱结点。)

2、复杂度分析:

① 时间复杂度:O(L),其中 L 是链表的长度。

② 空间复杂度:O(1)。

思路二(双指针):

1、使用两个指针,控制距离为n+1。当后边的指针遍历到NULL时,前边的指针正好指向要删除结点的前驱。(为了统一各个结点的删除操作,这里我们对链表添加一个新头结点(dummyHead),体会和方法一中的不同)。

2、具体思路如下:

① 添加一个新头结点(dummyHead)。

② 先将一个指针tail从链表头结点(dummyHead)开始移动n+1步。再令结点pre指向头节点(dummyHead)。

③ 这时tail和pre同时移动,当tail到达尾结点时,pre正好是倒数第 n 个结点的前去结点。

3、复杂度分析

① 时间复杂度:O(L),其中 L 是链表的长度。

② 空间复杂度:O(1)。

代码实现

代码实现(思路一(计算链表长度)):
cpp 复制代码
ListNode* removeNthFromEnd1(ListNode* head, int n) {
	ListNode *p=head;
	int len=0;
	//统计链表长度 
	while(p!=nullptr){
		++len;
		p=p->next;
	}
	//删除结点为头结点的情况 
	if(len==n){
		p=head;
		head=head->next;
		delete p;
		return head;
	}
	//删除结点不是头结点的情况,先找到前驱结点
	p=head;
	for(int i=1;i<=len-n-1;++i){
		p=p->next;	
	}
	//删除结点 
	ListNode *tmp=p->next;
	p->next=p->next->next;
	//注意将结点释放
	delete tmp;
    return head;
}
代码实现(思路二(双指针)):
cpp 复制代码
ListNode* removeNthFromEnd2(ListNode* head, int n) {
	//为了方便统一删除操作,这里我们对链表添加一个新头结点(dummyHead)
	ListNode *dummyHead=new ListNode(0,head); 
	//pre记录删除结点的前驱结点,tail用于控制距离 
	ListNode *pre=dummyHead,*tail=dummyHead;
	//tail拉开n+1的距离 
	while(n+1){
		tail=tail->next;
		--n;
	} 
	//tail和pre保持n+1个距离移动 
	while(tail!=nullptr){
		tail=tail->next;
		pre=pre->next;
	}
	//删除结点(用tail来存储需删除的结点节省内存空间) 
	tail=pre->next;
	pre->next=pre->next->next;
	head=dummyHead->next;
	delete dummyHead;
	//注意返回头节点之后的结点 
	return head;
}
以思路二为例进行调试:
cpp 复制代码
#include<iostream> 
#include<vector>
using namespace std;

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

//尾插法创建单链表 
ListNode *createList(vector<int> arr){
	ListNode *head=nullptr,*tail=nullptr;
	for(const auto &val:arr){
		if(head==nullptr){
			tail=head=new ListNode(val);
		}else{
			tail->next=new ListNode(val);
			tail=tail->next;
		}
	}
	return head;
}

//方法二
ListNode* removeNthFromEnd2(ListNode* head, int n) {
	//为了方便统一删除操作,这里我们对链表添加一个新头结点(dummyHead)
	ListNode *dummyHead=new ListNode(0,head); 
	//pre记录删除结点的前驱结点,tail用于控制距离 
	ListNode *pre=dummyHead,*tail=dummyHead;
	//tail拉开n+1的距离 
	while(n+1){
		tail=tail->next;
		--n;
	} 
	//tail和pre保持n+1个距离移动 
	while(tail!=nullptr){
		tail=tail->next;
		pre=pre->next;
	}
	//删除结点(用tail来存储需删除的结点节省内存空间) 
	tail=pre->next;
	pre->next=pre->next->next;
	head=dummyHead->next;
	delete dummyHead;
	//注意返回头节点之后的结点 
	return head;
}

int main(){
	vector<int> a={1,2,3,4,5};
	int n=2;
	//创建单链表
	ListNode *head=createList(a);
	//删除链表的倒数第 N 个结点
	ListNode *ans=removeNthFromEnd2(head,n);
	//输出删除后的单链表
	while(ans!=nullptr){
		cout<<ans->val<<"->";
		ans=ans->next;
	}
	cout<<"null"; 
	return 0;
}
部分代码解读
cpp 复制代码
delete dummyHead,tail; 

通过 new 动态分配的内存,如果你不显式地 delete,该节点的内存将不会被回收,导致内存泄漏。

LeetCode 热题 100_删除链表的倒数第 N 个结点(29_19)原题链接

欢迎大家和我沟通交流(✿◠‿◠)

相关推荐
FlightYe29 分钟前
音视频修炼之容器与协议(一):MP4和MOV的box剖析
android·linux·c++·ffmpeg·音视频
音草1 小时前
AVL 树详解:从概念到 C++ 实现
c++
凉茶钱1 小时前
list:哨兵节点,const_iterator 和 const iterator的区别与迭代器失效
数据结构·c++·list
沫璃染墨1 小时前
从零入门计算机网络系列(六):TCP套接字编程实战——从listen监听到客户端与服务器通信
linux·服务器·c++·tcp/ip·计算机网络·tcp
努力实干2 小时前
C++ 面向对象与工程架构
开发语言·c++·架构
代钦塔拉2 小时前
【C++/Qt 进阶】设计模式系列(6):桥接模式——把“两个独立变化的维度“拆开
c++·qt·设计模式
青少儿编程课堂2 小时前
蓝桥杯·全国青少年信息素养大赛·电子学会Python/C/C++考级:任意进制互转与大数多进制加法解析
c++·python·算法·bfs·信息学竞赛
Shadow(⊙o⊙)2 小时前
仿muduo实现高并发服务器——EPOLL设置与移除Poller.hpp管理Channel
运维·服务器·c++
闻缺陷则喜何志丹2 小时前
【C++特性及类库】多线程
开发语言·c++·多线程·语言特性·系统库