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)原题链接

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

相关推荐
桃酥40315 分钟前
20、移动语义有什么作用,原理是什么【中高频】
c++
闻缺陷则喜何志丹15 分钟前
【二分查找、滑动窗口】P10389 [蓝桥杯 2024 省 A] 成绩统计|普及+
c++·算法·蓝桥杯·二分查找·滑动窗口·洛谷·成绩
商bol451 小时前
算阶,jdk和idea的安装
数据结构·c++·算法
yyytucj1 小时前
C/C++通过SQLiteSDK增删改查
c语言·jvm·c++
冱洇3 小时前
168. Excel 表列名称
leetcode
float_六七4 小时前
二叉树三种遍历方式——前序、中序、后序(C++)
开发语言·c++·算法
邦之彦9 小时前
C++:四大强制类型转换
开发语言·c++·static_cast·const_cast·dynamic_cast·reinterpretcast
柿柿快乐10 小时前
C++入门基础
开发语言·c++
ks不知火12 小时前
【仿muduo库one thread one loop式并发服务器实现】
linux·c++
mit6.82412 小时前
[Lc(2)滑动窗口_1] 长度最小的数组 | 无重复字符的最长子串 | 最大连续1的个数 III | 将 x 减到 0 的最小操作数
数据结构·c++·算法·leetcode