数据结构链表力扣例题AC(2)——代码以及思路记录

本文详细解读了链表反转、查找倒数第k个节点以及链表分割的两种常见算法,包括使用迭代和快慢指针的方法,以及如何处理边界条件和链表结构的操作。
摘要由CSDN通过智能技术生成

206. 反转链表

给你单链表的头节点 head ,请你反转链表,并返回反转后的链表。

AC方法1

struct ListNode* reverseList(struct ListNode* head) {
    if(head == NULL)
        return NULL;

    struct ListNode* n1, *n2, *n3;
    n1 = NULL;
    n2 = head;
    n3 = head->next;

    while(n2)
    {
        n2->next = n1;

        n1 = n2;
        n2 = n3;
        if(n3)
        {
            n3 = n3->next;
        }
        
    }
    return n1;
}

代码思路

观察示例,1->2->3->4->5 ==>> 5->4->3->2->1

要将箭头的方向都旋转,假设从左向右改变,第一步2->1,用两个指针 n1 和 n2 分别指向1和2,使 n2 指向 n1 就可以实现了,但是此时由于 2 之前保存的 3 的地址,现在保存了 1 的地址,3 的地址丢失了,因此在实现变换前,需要先将交换的两个数字后面一位的地址也保存下来,预防这种情况的发生。因为链表的末尾是指向NULL的,因此第一个也需要变为指向NULL,所以第一步将三个指针设置为 n1 = NULL, n2 = head,n3 = head->next。考虑结束条件,如果是 n3 指向空的话,此时 n2 刚指向最后一个元素,最后一个元素依然指向NULL,因此结束条件设置为 n2 指向空更合适。改变链表方向时,将 n2 指向 n1 (n2->next = n1,改变了箭头方向),再将 n1 、n2 和 n3 向前移动(n1 = n2;n2 = n3),但是这里要考虑 n3 向前移动的特殊情况,因为结束条件是 n2 指向空(说明箭头方向都已经完成变换),但是 n2 移动到NULL之前,n3 会先指向NULL,当 n2 指向 NULL的时候,n3也跟着移动就会发生错误(NULL->next是绝对错误的),因此加一个判断,当 n3 已经为空的时候就不移动了。此时原来的最后一个元素是第一个元素,也就是该链表的地址,n1 指向他,返回 n1 地址即可。

AC方法2

struct ListNode* cur, *newhead;
    cur = head;
    newhead = NULL;

    while(cur)
    {
        struct ListNode* next = cur->next;
        cur->next = newhead;
        newhead = cur;
        cur = next;
    }
    return newhead;

代码思路

联想一下栈的原理,进栈和出栈是不是就顺序相反,因此可以创建一个新的空链表,从原链表第一个节点开始加入新链表,因此需要将头结点的下一个节点存储(因为头结点会保存新链表的节点地址),创建cur指向头结点,新链表由于是空,指向NULL;接着开始向新链表添加,先创建一个新指针保存cur的下一个节点(next = cur->next),然后将原链表第一个移动到新链表里面进行头插(cur->next = newhead,这一步就是相当于头插),然后更新了新链表的头结点后,对应的指向头结点的指针newhead移动指向新的头结点(newhead = cur;)cur回到原链表,指向next保存的新节点。由于cur是添加到新链表以后再回到原链表,因此当cur指向NULL的时候就可以停止了,因此作为while循环条件,最后返回新链表的头结点地址就是变换了顺序的链表的地址了。

链表中倒数第k个结点

输入一个链表,输出该链表中倒数第k个结点。

AC

struct ListNode* FindKthToTail(struct ListNode* pListHead, int k ) {
    // write code here

    struct ListNode* fast = pListHead;
    struct ListNode* slow = pListHead;

    for(int i = 0; i < k; i++)
    {
        if(fast == NULL)
        {
            return NULL;
        }
        fast = fast->next;
    }
    while(fast)
    {
        fast = fast->next;
        slow = slow->next;
    }
    return slow;
}

代码思路

这道题用快慢指针的方法就非常妙,先简单说说一般思路:先创建一个指针将链表遍历一遍得出链表长度,再用链表长度减去 k 得出正数是第几个,然后输出正数的节点。

但如果使用快慢指针:fast 指针和 slow 指针最开始都指向头结点,先让 fast 指针走 k 个节点,此时 fast 指针和 slow 指针间就有 k 个节点距离,接着让 fast 和 slow 指针同时向前走,这个过程中两个指针之间始终保持着 k 个距离,因此当 fast 指针走到最后的时候,slow 指针正好处于倒数第 k 个。不过有特殊情况需要考虑,就是如果 k 大于了链表节点数,结果是输出空指针,因此在 fast 移动的时候就去检验他会不会等于空,如果 fast 等于空了直接 return NULL,因为k大于链表节点数,那么 i 还没有大于等于 k ,fast 就已经变为 NULL 了。其次还有 k=0 的情况,这时候进不去 for 循环,但是可以进去 while 循环,也就是快慢指针同时走了,fast=NULL 的时候 slow 也为 NULL 了,最后返回 slow 也就是返回空了。

这道题只写快慢指针的方法,有兴趣的实现第一种简单思想的,以练算法为主就不暴力了

CM11 链表分割

AC

ListNode* partition(ListNode* pHead, int x) {
        struct ListNode* head1, *head2, *tail1, *tail2;
        head1 = tail1 = (struct ListNode*)malloc(sizeof(struct ListNode));
        head2 = tail2 = (struct ListNode*)malloc(sizeof(struct ListNode));
        struct ListNode* cur = pHead;

        while(cur)
        {
            if(cur->val < x)
            {
                tail1->next = cur;
                tail1 = tail1->next;
            }
            else{
            tail2->next = cur; 
            tail2 = tail2->next;
            }
            cur = cur->next;

        }
        tail1->next = head2->next;
        tail2->next = NULL; //这一步不写的话会形成一个环
        pHead = head1->next;
        free(head1);
        free(head2);

        return pHead;
    }

代码思路

这段代码思路就是新建了两个链表,然后遍历一遍原链表,小于x的加入到第一个新的链表中,大于的加到另一个里边,然后把两个新链表合并起来,也就是让小于x的那个链表的尾节点指向另一个新建链表的头结点,并且大于x的那个链表的尾节点置空,返回新链表的地址。置空的原因是因为,如果不这么操作可能会形成一个环,这种情况会发生在当原链表的尾节点不是大于x的时候,那么尾节点的前一个若大于x,被分到大于x的链表的时候,他的next还未改变,仍然指向尾节点,而尾节点因为比x小而分到了小于x的那个新建链表中,因此形成了一个环。

  • 19
    点赞
  • 16
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

Outlier_9

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

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

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

打赏作者

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

抵扣说明:

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

余额充值