[算法]力扣刷题-初级算法 - 链表(二) [合并两个有序链表] [回文链表] [环形链表]

21 合并两个有序链表

将两个升序链表合并为一个新的 升序 链表并返回。新链表是通过拼接给定的两个链表的所有节点组成的。
示例 1:
输入:l1 = [1,2,4], l2 = [1,3,4]
输出:[1,1,2,3,4,4]
链接:https://leetcode-cn.com/problems/merge-two-sorted-lists

思路:

  1. 两个链表都存在时,遍历两个链表,并对比节点val,把较小的一个节点插到temp之后。
  2. 有一个链表不存在时跳出循环,然后把另一个链表的剩余部分全部插入到当前temp之后。
    注意:开头创建了一个假的链表头,让temp指针指向他,就不需要再对首个链表节点做特殊处理,牺牲了空间,换取了代码简洁性。
struct ListNode* mergeTwoLists(struct ListNode* list1, struct ListNode* list2)
{
    struct ListNode list_head = {0}, *list_temp = &list_head;

    while(list1 && list2)
    {
        if(list1->val > list2->val)
        {
            list_temp->next = list2;
            list2 = list2->next;
        }
        else
        {
            list_temp->next = list1;
            list1 = list1->next;
        }
        list_temp = list_temp->next;
    }

    if(list1)
    {
        list_temp->next = list1;
    }
    else
    {
        list_temp->next = list2;
    }
    return list_head.next;
}

在这里插入图片描述
其实有一个优化项是:假设list1一直小于list2,不需要把list1的每个节点指针都赋值给list_temp,只需要赋值最后一个小于list2的节点即可。


234 回文链表

给你一个单链表的头节点 head ,请你判断该链表是否为回文链表。如果是,返回 true ;否则,返回 false 。

示例 1:
输入:head = [1,2,2,1] 输出:true
示例 2:
输入:head = [1,2] 输出:false
提示:链表中节点数目在范围[1, 105] 内 0 <= Node.val <= 9
进阶:你能否用 O(n) 时间复杂度和 O(1) 空间复杂度解决此题
链接:https://leetcode-cn.com/problems/palindrome-linked-list

直接奔着进阶难度做:
O(n)时间复杂度和 O(1)空间复杂度

难点:

  1. 因为时间复杂度限制为O(n),所以不管是遍历还是递归,都不允许在到达某个节点后再去遍历链表,需要一次完成。
  2. 只遍历一遍的情况下,如何在遍历到第1个节点时,获取到第n个节点,遍历到第2个节点时,获取到第n-1个节点

思路:

  1. 还是通过递归来处理,从链表头递归到尾,逐层传递深度参数,再从最底部将整个链表的长度返回,这样在每层的处理中都可以判断自己是否是链表的后1/2,不会出现重复对比的情况
  2. 在第n个节点,获取到第1个节点的方法:递归函数中增加形参node_head,从第一层开始将链表头传递进来,再在递归函数逐层返回的过程中,返回node_head->next,实现了一次递归中同时从头尾两端向中间操作链表。
  3. 因为1、2中需要返回链表总长度和链表指针,因此创建了一个结构体Palindrome_t,用来同时传递这两个参数。
    时间复杂度和O(n)
    空间复杂度O(1)
struct Palindrome_t
{
    int number_all;
    struct ListNode* node;
};

struct Palindrome_t list_recursion(struct ListNode* node_curr, struct ListNode* node_head, int number)
{
    if(node_curr->next)
    {
        struct Palindrome_t palindrome_temp =  list_recursion(node_curr->next, node_head, number+1);
        if(palindrome_temp.node)
        {
            if(number > (palindrome_temp.number_all%2? palindrome_temp.number_all/2 : (palindrome_temp.number_all+1)/2) )
            {
                if(node_curr->val != palindrome_temp.node->val)
                {
                    return (struct Palindrome_t){0,0};
                }
                else
                {
                    return (struct Palindrome_t){palindrome_temp.number_all, palindrome_temp.node->next};
                }
            }
        }
        return palindrome_temp;
    }
    else
    {
        if(node_curr->val != node_head->val)
        {
            return (struct Palindrome_t){0,0};
        }
        else
        {
            return (struct Palindrome_t){number, node_head->next};
        }
    }
}

bool isPalindrome(struct ListNode* head)
{
    int data_map[10] = {0};
    if(list_recursion(head, head, 1).number_all)
    {
        return true;
    }
    return false;
}

在这里插入图片描述

141 环形链表

给你一个链表的头节点 head ,判断链表中是否有环。

如果链表中有某个节点,可以通过连续跟踪 next 指针再次到达,则链表中存在环。
为了表示给定链表中的环,评测系统内部使用整数 pos来表示链表尾连接到链表中的位置(索引从 0 开始)。
注意:pos 不作为参数进行传递 。仅仅是为了标识链表的实际情况。 如果链表中存在环 ,则返回 true 。 否则,返回 false 。

示例 1:
输入:head = [3,2,0,-4], pos = 1 输出:true 解释:链表中有一个环,其尾部连接到第二个节点。
示例 2:
输入:head = [1,2], pos = 0
输出:true
解释:链表中有一个环,其尾部连接到第一个节点。
示例 3:
输入:head = [1], pos = -1
输出:false
解释:链表中没有环。

提示:
链表中节点的数目范围是 [0, 104]-105 <= Node.val <= 105 pos 为 -1 或者链表中的一个 有效索引 。 进阶:你能用 O(1)(即,常量)内存解决此问题吗?
链接:https://leetcode-cn.com/problems/linked-list-cycle

思路1:
快慢指针

  1. 快指针一次走两个节点,慢指针一次走一个节点
  2. 如果链表中不存在环,则快指针早晚能检测到next为null
  3. 如果链表中存在换,则快指针会绕过环追上快指针
bool hasCycle(struct ListNode *head) 
{
    char i = 0;
    struct ListNode *fast_p = head, *slow_p = head;
    while(fast_p && fast_p->next)
    {
        fast_p = fast_p->next->next;
        slow_p = slow_p->next;
        
        if(slow_p == fast_p)
        {
            return true;
        }
    }
    return false;
}

在这里插入图片描述
思路2:
过河拆桥

  1. 因为题目中说明了val的范围:-10^5 <= Node.val <= 10^5,因此将所有遍历过的点的val修改为这个范围之外的一个值0x7fffffff。
  2. 检测到next为null,肯定没有环
  3. 检测到next->val==0x7fffffff,说明遍历到了之前遍历过的点,存在环。
#define NODE_MARK 0x7fffffff
bool hasCycle(struct ListNode *head) 
{
    if(!head)
    {
        return false;
    }
    while(head->next)
    {
        head->val = NODE_MARK;
        if(head->next->val == NODE_MARK)
        {
            return true;
        }
        head = head->next;
    }
    return false;
}

在这里插入图片描述

总体上我更倾向于使用快慢指针的方法,虽然快慢指针法在存在环时,遍历个数是大于链表中总节点数的,但第二种方法破坏了原有数据,换取一点的执行效率的提高实在不值得。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

___NULL___

你的鼓励将是我创作的最大动力

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

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

打赏作者

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

抵扣说明:

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

余额充值