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

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

相关推荐
XiaoLeisj13 分钟前
【递归,搜索与回溯算法 & 递归算法】递归算法入门详解:递归算法小专题
java·算法·leetcode·深度优先·推荐算法
闻缺陷则喜何志丹17 分钟前
【C++图论 DFS】1559. 二维网格图中探测环|1837
c++·算法·深度优先·力扣·图论··网格
Muisti35 分钟前
707. 设计链表 链表的知识复习
数据结构·链表
TANGLONG22241 分钟前
【初阶数据结构与算法】八大排序算法之选择排序(直接选择排序、堆排)
c语言·数据结构·c++·考研·算法·面试·排序算法
Aloha_up3 小时前
LeetCode hot100-78
算法·leetcode·职场和发展
维齐洛波奇特利(male)3 小时前
(后序遍历 简单)leetcode 101翻转二叉树
算法·leetcode·职场和发展
Y编程小白3 小时前
Leetcode经典题12--分发糖果
数据结构·算法·leetcode
戊子仲秋3 小时前
【LeetCode】每日一题 2024_12_15 数组大小减半(哈希、排序)
算法·leetcode·哈希算法
Y编程小白3 小时前
Leetcode经典题10--除自身以外数组的乘积
数据结构·算法·leetcode