19

面试:删除链表的节点

 4 years ago
source link: https://segmentfault.com/a/1190000022105978
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.

给定单向链表的头指针和一个要删除的节点的值,定义一个函数删除该节点。

返回删除后的链表的头节点。

注意:此题对比原题有改动

示例 1:

输入: head = [4,5,1,9], val = 5

输出: [4,1,9]

解释: 给定你链表中值为 5 的第二个节点,那么在调用了你的函数之后,该链表应变为 4 -> 1 -> 9.

示例 2:

输入: head = [4,5,1,9], val = 1

输出: [4,5,9]

解释: 给定你链表中值为 1 的第三个节点,那么在调用了你的函数之后,该链表应变为 4 -> 5 -> 9.

说明:

题目保证链表中节点的值互不相同

若使用 C 或 C++ 语言,你不需要 free 或 delete 被删除的节点

这题的难度定义为简单,但我觉得如果对于链表题目并不熟练的人来说,想到题解还是有一点难度的。

我一开始以为很简单,因为知道链表的删除操作是怎样的,关键点在于获取需要删除的节点的前一个节点,剩余的问题就迎刃而解了,然而做起来还是有点费脑子的。

接下来就跟大家说一下我的题解及解题思路。

特此说明,我只想到了第一种解法,其他两种是理解大神们的题解

题解一:假头指针+双指针

/**
 * Definition for singly-linked list.
 * type ListNode struct {
 *     Val int
 *     Next *ListNode
 * }
 */
func deleteNode(head *ListNode, val int) *ListNode {
    if head == nil {
        return head
    }
    
    newHead := &ListNode{0,head}
    pre, cur := newHead, head
    for cur != nil {
        if cur.Val == val {
            pre.Next = cur.Next
            break
        }
        pre = cur
        cur = cur.Next
    }

    return newHead.Next
}

思路:核心是要先能想到假头指针,最后直接返回假头指针的下一个节点即可,这样头和尾就搞定了。

然后就是中间步骤,这里我想到的是用双指针,用 pre 指针记录上一个节点,用 cur 指针记录当前节点,如果比较后值相等,就可以用 pre 指针指向当前指针的下一个指针,完成删除操作。

这里分析一下复杂度:

时间复杂度:O(N),因为最好的情况是头指针就是该值,直接返回下一个节点,最坏的情况则是最后一个节点才是目标值,则需要一直遍历到尾部,因此平均时间复杂度是O(N)

空间复杂度:O(1),可以看到我们只是额外申请了假头指针和两个指针。

题解二:假头指针+单指针

/**
 * Definition for singly-linked list.
 * type ListNode struct {
 *     Val int
 *     Next *ListNode
 * }
 */
func deleteNode(head *ListNode, val int) *ListNode {
    if head.Val == val {
        return head.Next
    }

    newHead := &ListNode{0, head}
    for head != nil && head.Next != nil {
        if head.Next.Val == val {
            head.Next = head.Next.Next
        }
        head = head.Next
    }
    return newHead.Next
}

思路:假头指针我就不说了,用 head 单指针来代替双指针,需要注意的是,这样的写法,是需要先判断 head 的值是否等于目标值,才可以走下面的步骤,相信大家理解了第一个题解,也能看懂这一题解吧?

这个题解还有一点就是没有第一个题解清晰,需要用 Next.Next 进行赋值迭代,对链表不熟悉的朋友可以画一下图理解一下。

题解三:单指针(你没看错,据说是 Linux 大佬的解法)

/**
 * Definition for singly-linked list.
 * type ListNode struct {
 *     Val int
 *     Next *ListNode
 * }
 */
func deleteNode(head *ListNode, val int) *ListNode {
    var indirect **ListNode = &head
    for ; *indirect != nil; indirect = &((*indirect).Next) {
        if ((*indirect).Val == val) {
            *indirect = (*indirect).Next
            break
        }    
    }
    return head
}

思路:实际上就是对于指针地址的理解了,想清楚是这一块,其实理解这个解法不难,关键是想到这个解法,以及想到了不要写错,才是真正的厉害,反正我是膜拜了一波~

看完并理解思路后,整个人都凌乱了,越看越觉得牛逼!

三个题解的结果如下(不明白为啥数据是一样的):

naymeeB.png!web

不说了我去买瓶营养快线补补(营养快线快打钱hhhhh)

NBBji2F.jpg!web


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK