力扣刷题笔记(七)

来水一波吧,这两天有事儿都没写这一部分的,有点难受。

前两题都很基本,第三题还好啦。

这篇主要是关于链表的,所以我先放一个链表的基础篇,今天整理的。

回顾通用链表(亲测代码示例)

接下来如正题:

第一题 删除指定节点

请编写一个函数,使其可以删除某个链表中给定的(非末尾)节点,你将只被给定要求被删除的节点。

现有一个链表 – head = [4,5,1,9]

示例 1:

输入: head = [4,5,1,9], node = 5
输出: [4,1,9]
解释: 给定你链表中值为 5 的第二个节点,那么在调用了你的函数之后,该链表应变为 4 -> 1 -> 9.
示例 2:

扫描二维码关注公众号,回复: 11183536 查看本文章

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

说明:

链表至少包含两个节点。
链表中所有节点的值都是唯一的。
给定的节点为非末尾节点并且一定是链表中的一个有效节点。
不要从你的函数中返回任何结果。

来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/delete-node-in-a-linked-list
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。


第二题 删除倒数第n个节点

给定一个链表,删除链表的倒数第 n 个节点,并且返回链表的头结点。

示例:

给定一个链表: 1->2->3->4->5, 和 n = 2.

当删除了倒数第二个节点后,链表变为 1->2->3->5.
说明:

给定的 n 保证是有效的。

进阶:

你能尝试使用一趟扫描实现吗?

来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/remove-nth-node-from-end-of-list
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。


第三题 翻转链表

反转一个单链表。

示例:

输入: 1->2->3->4->5->NULL
输出: 5->4->3->2->1->NULL
进阶:
你可以迭代或递归地反转链表。你能否用两种方法解决这道题?

来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/reverse-linked-list
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。

我的题解(1)

滥竽充数一下

void deleteNode(ListNode* node) {
        // write your code here
        if(node==NULL) return;
        if(node->next==NULL)
        {
            node=NULL;
            return;
        }
        node->val=node->next->val;
        node->next=node->next->next;
    }

我的题解(2)

这题难度居然能排在中等。。。
还是滥竽充数一下。

ListNode* removeNthFromEnd(ListNode* head, int n) {
       if(!head) return NULL;
        ListNode* new_head = new ListNode(0);
        new_head->next = head;
        ListNode* slow = new_head, *fast = new_head;
        for(int i = 0;i<n;++i){  //fast先移动n
            fast = fast->next;
        }
        if(!fast) return new_head->next;
        while(fast->next){      //一起移动
            fast = fast->next;
            slow = slow->next;
        }
        slow->next = slow->next->next;
        return new_head->next;
    }

我的题解(3)

这题有点意思,重点讲这题吧

一开始我的想法是这样的:先遍历一遍,将链表中的值都取出来,然后逆向插回去,不过这样要遍历两遍,于是我就想一遍实现。

我的想法是这样的:头向后遍历,每次将当前遍历到的尾部节点提取出来,放到最前面,实现逆转乾坤。
但是,想法总是好的。

先看我的第一个实验:

struct ListNode {
    int val;
    ListNode* next;
    ListNode(int x) : val(x), next(NULL) {}
};	//通用
//ListNode* reverseList(ListNode* head)
//{
//    ListNode* node_temp;
//    ListNode* new_head;
//
//    node_temp = head;
//    //遍历一个节点,就把它拿下来放到头去
//    while (head->next != NULL)
//    {
//        //先考虑只又两个节点的情况 
//        head = head->next;
//        new_head = head;
//        new_head->next = node_temp;
//        node_temp = new_head;
//    }
//    return new_head;
//}

我也不多说,为什么失败,要是一眼看出来的话,容我呼你一声大神。反正这失败我是无语了。


再看第二次,算了我还是说一下上面那个。
我想原地逆置,然后一不小心,成环了。。。。。。。

于是我就换了个思路,用两条链表,一个原链表,一个链栈。
然而,想法还是好的。

//ListNode* reverseList(ListNode* head) 
//{
//    ListNode* node_temp = new ListNode(NULL);
//    ListNode* new_head = new ListNode(NULL);   //链栈的头
//    
//    //遍历一个节点,就把它拿下来放到头去
//    while (head != NULL)
//    {
//        node_temp = head;   //先将节点取出
//        //先考虑只又两个节点的情况 
//        head = head->next;  //这个不放这里又成环了
//        
//        node_temp->next = new_head;
//        new_head= node_temp;
//    }
//    return new_head;
//}

哎,要是一眼没看出来哪里出问题了,那咱俩水平半斤八两。

这时候,我的好兄弟一直喊我用头插法,我不以为然,让他给我发过来瞅瞅。


在我看之前,我好像发现了什么。于是我就把上面的代码稍微调了一下。。。
成功了。。。

ListNode* reverseList(ListNode* head) 
{
    ListNode* node_temp = NULL; //这里设为NULL
    ListNode* new_head = NULL;   //链栈的头
    
    //遍历一个节点,就把它拿下来放到头去
    while (head != NULL)
    {
        node_temp = head;   //先将节点取出
        //先考虑只又两个节点的情况 
        head = head->next;  //这个不放这里又成环了
        
        node_temp->next = new_head;
        new_head= node_temp;
    }
    return new_head;
}

ListNode* reverseList(ListNode* head) 
    {
        ListNode* node_temp = NULL; //这里设为NULL
        ListNode* new_head = NULL;   //链栈的头

        //遍历一个节点,就把它拿下来放到头去
        while (head != NULL)
        {
            node_temp = head;   //先将节点取出
            //先考虑只又两个节点的情况 
            head = head->next;  //这个不放这里又成环了

            node_temp->next = new_head;
            new_head= node_temp;
        }
        return new_head;
    }

成功就算了,我回头一看他的“头插法”,我都懵了,怎么一模一样。。。。。


官方题解(3)

假设存在链表 1 → 2 → 3 → Ø,我们想要把它改成 Ø ← 1 ← 2 ← 3。

在遍历列表时,将当前节点的 next 指针改为指向前一个元素。由于节点没有引用其上一个节点,因此必须事先存储其前一个元素。在更改引用之前,还需要另一个指针来存储下一个节点。不要忘记在最后返回新的头引用!

public ListNode reverseList(ListNode head) {
    ListNode prev = null;
    ListNode curr = head;
    while (curr != null) {
        ListNode nextTemp = curr.next;
        curr.next = prev;
        prev = curr;
        curr = nextTemp;
    }
    return prev;
}

复杂度分析

时间复杂度:O(n),假设 n 是列表的长度,时间复杂度是 O(n)。
空间复杂度:O(1)。

作者:LeetCode
链接:https://leetcode-cn.com/problems/reverse-linked-list/solution/fan-zhuan-lian-biao-by-leetcode/
来源:力扣(LeetCode) 著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

方法二:递归
递归版本稍微复杂一些,其关键在于反向工作。假设列表的其余部分已经被反转,现在我该如何反转它前面的部分?

假设列表为:

在这里插入图片描述

public ListNode reverseList(ListNode head) {
    if (head == null || head.next == null) return head;
    ListNode p = reverseList(head.next);
    head.next.next = head;
    head.next = null;
    return p;
}

复杂度分析

时间复杂度:O(n)O(n),假设 nn 是列表的长度,那么时间复杂度为 O(n)O(n)。
空间复杂度:O(n)O(n),由于使用递归,将会使用隐式栈空间。递归深度可能会达到 nn 层。

作者:LeetCode
链接:https://leetcode-cn.com/problems/reverse-linked-list/solution/fan-zhuan-lian-biao-by-leetcode/
来源:力扣(LeetCode) 著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

原创文章 153 获赞 719 访问量 3万+

猜你喜欢

转载自blog.csdn.net/qq_43762191/article/details/105847580