LeetCode 19. 删除链表的倒数第 N 个结点

19. 删除链表的倒数第 N 个结点

给你一个链表,删除链表的倒数第 n 个结点,并且返回链表的头结点。
在这里插入图片描述

此题目为链表题,拿出我们的杀手锏,链表解题经典三把斧:

  • 哑巴节点
  • 快慢指针

关于内存问题:由于Swift及OC均有ARC内存机制,因此删除的节点内容未主动释放,如在手动内存管理的情况下,需要释放被删除节点的内存占用。

方法一、计算链表长度

先求出链表长度L,再将链表从头移动到L-n+1的位置,删除其下一个节点。

时间复杂度:O(n),一次求长度n,极端情况下的一次遍历,2n->O(n)
空间复杂度:O(1)

Swift

//计算链表长度, 删除l-n+1的位置
func removeNthFromEnd(_ head: ListNode?, _ n: Int) -> ListNode? {
        let dummyNode = ListNode(0, head);
        let len = getLenOfListNode(head)
        
        var current:ListNode? = dummyNode
        for _ in 1..<len-n+1 {
            current = current?.next
        }
        
        current?.next = current?.next?.next
        //MRC Or ARC?被释放的节点内存需要处理吗?
        
        let ans = dummyNode.next
        return ans
    }
    
    func getLenOfListNode(_ listNode:ListNode?) -> Int {
        var len = 0
        var current = listNode
        while let cur = current {
            current = cur.next
            len += 1
        }
        return len
    }

OC

//计算链表长度, 删除l-n+1的位置
- (ListNodeOC *)removeNthFromEnd:(ListNodeOC *)head atReverseIdx:(NSInteger)n {
    //构造虚拟头节点
    ListNodeOC *dummyNode = [[ListNodeOC alloc] initWithVal:0 next:head];
    NSInteger len = [self lenOfListNode:head];

    ListNodeOC *cur = dummyNode;
    for (NSInteger i=1; i<len-n+1; i++) {
        cur = cur.next;
    }

    cur.next = cur.next.next;

    return dummyNode.next;
}

解法二、栈

先将链表入栈,再出栈n个元素后,栈顶部的元素就是我们需要删除的节点的前一个节点。

时间复杂度:O(n)
空间复杂度:O(n)

Swift

    //入栈后弹栈n次即可
	func removeNthFromEnd(_ head: ListNode?, _ n: Int) -> ListNode? {
        
        //构造影子节点
        let fickNode = ListNode(0)
        fickNode.next = head
        
        let stack = Stack<ListNode>()
        
        var cur:ListNode? = fickNode
        while let currentNode = cur {
            stack.push(currentNode)
            cur = currentNode.next
        }
        
        for _ in 0..<n {
            let _ = stack.pop()
        }
        
        if let preNode = stack.top() {
            preNode.next = preNode.next?.next
        }
        
        return fickNode.next
    }

OC

- (ListNodeOC *)removeNthFromEnd:(ListNodeOC *)head atReverseIdx:(NSInteger)n {

    ListNodeOC *dummyNode = [[ListNodeOC alloc] initWithVal:0 next:head];
    StackOC *stack = [[StackOC alloc] init];

    //先入栈
    ListNodeOC *cur = dummyNode;
    while (cur) {
        [stack push:cur];
        cur = cur.next;
    }

    //出栈n个元素
    for (NSInteger i=0; i<n; i++) {
        [stack pop];
    }

    //删除最后一个元素
    cur = [stack top];
    cur.next = cur.next.next;

    [stack cleanAll];

    return dummyNode.next;
}

双指针

创建哑巴节点,有两个指针均指向哑巴节点,首先移动第2个指针n次,此时第1、2个指针相距n个节点;然后同时移动1、2两个节点,直至第2个指针指向最后一个元素,此时的第1个指针指向的节点就是倒数第n个元素的前一个元素。

时间复杂度:O(n)
空间复杂度:O(1)

Swift

	func removeNthFromEnd(_ head: ListNode?, _ n: Int) -> ListNode? {
        //构造影子节点,为指针操作预留空间
        let dummyNode = ListNode(0, head)
        
        var first:ListNode? = dummyNode
        var second:ListNode? = dummyNode
        
        var current:ListNode? = dummyNode
        for _ in 0..<n {
            if let cur = current {
                second = cur.next
                current = second
            }
        }
        
        //同时移动两个指针,直至2到达结尾
        while let _ = current?.next {
            first = first?.next
            second = second?.next
            current = second
        }
        
        first?.next = first?.next?.next
        
        return dummyNode.next
    }

OC

- (ListNodeOC *)removeNthFromEnd:(ListNodeOC *)head atReverseIdx:(NSInteger)n {
    ListNodeOC *dummyNode = [[ListNodeOC alloc] initWithVal:0 next:head];
    
    ListNodeOC *firstNode = dummyNode;
    ListNodeOC *secondNode = dummyNode;
    
    //先移动指针,让两个指针差值为n
    for (NSInteger i=0; i<n; i++) {
        secondNode = secondNode.next;
    }
    
    //同时移动两个指针,当第二个指针指向最后一个元素的时候,第一个指针指向的正是倒数第n个元素
    while (secondNode.next) {
        firstNode = firstNode.next;
        secondNode = secondNode.next;
    }
    
    firstNode.next = firstNode.next.next;
    
    return dummyNode.next;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Jarlen John

谢谢你给我一杯咖啡的温暖

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值