题目描述(中等难度)
题目描述:给定一个链表,将倒数第 n
个结点删除。
解法一
删除一个结点,无非是遍历链表找到那个结点前边的结点,然后改变下指向就好了。但由于它是链表,它的长度我们并不知道,我们得先遍历一遍得到它的长度,之后用长度减去 n 就是要删除的结点的位置,然后遍历到结点的前一个位置就好了。
Java
class ListNode {
int val;
ListNode next;
ListNode(int val) {
this.val = val;
}
}
public class removeNthFromEnd {
public static ListNode removeNthFromEnd(ListNode head, int n) {
int len = 0;
ListNode h = head;
while (h != null) {
h = h.next;
len++;
}
//长度等于 1 ,再删除一个结点就为 null 了
if (len == 1) return null;
int rm_node_index = len - n;
//如果删除的是头结点
if (rm_node_index == 0) return head.next;
//找到被删除结点的前一个结点
h = head;
for (int i = 0; i < rm_node_index - 1; i++) {
h = h.next;
}
//改变指向
h.next = h.next.next;
return head;
}
public static void main(String[] args) {
int n=2;
ListNode l1 = new ListNode(1);
ListNode p = l1;
p.next = new ListNode(2);
p = p.next;
p.next = new ListNode(3);
p = p.next;
p.next = new ListNode(4);
p=p.next;
p.next=new ListNode(5);
ListNode ans=removeNthFromEnd(l1,n);
while(ans !=null) {
System.out.println(ans.val);
ans=ans.next;
}
}
}
时间复杂度:假设链表长度是 L
,那么就第一个循环是 L
次,第二个循环是 L - n
次,总共 2L - n
次,所以时间复杂度就是 O(L)
。
空间复杂度:O(1)
。
Python
# Definition for singly-linked list.
# class ListNode(object):
# def __init__(self, val=0, next=None):
# self.val = val
# self.next = next
class Solution(object):
def removeNthFromEnd(self, head, n):
length = 0
h = head
while(h!=None):
h=h.next
length +=1
if(length == 1):
return None
rm_node_index=length-n
if rm_node_index == 0:
return head.next
h=head
for i in range(rm_node_index-1):
h=h.next
h.next=h.next.next
return head
解法二 遍历一次链表
上边我们遍历链表进行了两次,我们如何只遍历一次呢。
想象一下,两个人进行 100m 赛跑,假设他们的速度相同。开始的时候,第一个人就在第二个人前边 10m ,这样当第一个人跑到终点的时候,第二个人相距第一个人依旧是 10m ,也就是离终点 10m。
对比于链表,我们设定两个指针,先让第一个指针遍历 n
步,然后再让它俩同时开始遍历,这样的话,当第一个指针到头的时候,第二个指针就离第一个指针有 n
的距离,所以第二个指针的位置就刚好是倒数第 n
个结点。
Java
class Solution {
public ListNode removeNthFromEnd(ListNode head, int n) {
ListNode dummy = new ListNode(0);
dummy.next=head;
ListNode first = dummy;
ListNode second = dummy;
for(int i=1;i<=n+1;i++){
first=first.next;
}
while(first!=null){
first=first.next;
second=second.next;
}
second.next=second.next.next;
return dummy.next;
}
}
时间复杂度:第一个指针从 0 到 n ,然后「第一个指针再从 n 到结束」和「第二个指针从 0 到倒数第 n 个结点的位置」同时进行。
而解法一无非是先从 0 到 结束,然后从 0 到倒数第 n 个结点的位置。
所以其实它们语句执行的次数其实是一样的,从 0 到倒数第 n 个结点的位置都被遍历了 2 次,所以总共也是 2L - n 次。只不过这个解法把解法一的两次循环合并了一下,使得第二个指针看起来是顺便遍历,想法很 nice。
所以本质上,它们其实是一样的,时间复杂度依旧是 O(n)。
空间复杂度:O(1)。
Python
class Solution(object):
def removeNthFromEnd(self, head, n):
dummy=ListNode(0)
dummy.next=head
first = dummy
second = dummy
for i in range(1,n+2):
first=first.next
while(first!=None):
first=first.next
second=second.next
second.next=second.next.next
return dummy.next