第五期——LeetCode 876链表的中间节点 面试题02.02返回倒数第k个节点 206反转链表 nowcoderOR36 链表的回文结构

题目1:链表的中间节点

查看原题目请点击这里~

给你单链表的头节点 head ,请你找出并返回链表的中间节点。

如果有两个中间节点,则返回第二个中间节点。

/**

 * Definition for singly-linked list.

 * struct ListNode {

 *     int val;

 *     struct ListNode *next;

 * };

 */

struct ListNode* middleNode(struct ListNode* head) {

}

思路1

对于这个题目,我们想到的第一个思路就是先遍历链表得到链表的节点总数num,然后找到num/2个节点

代码1(C语言版)

/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     struct ListNode *next;
 * };
 */
struct ListNode* middleNode(struct ListNode* head) {
    struct ListNode* cur=head;
    int n=0;
    while(cur)
    {
        n++;
        cur=cur->next;
    }
    int i=0;
    for(i=1;i<=n/2+1;i++)
    {
        if(i==1)
        {
            cur=head;
        }
        else
        cur=cur->next;
    }
    return cur;
}

但是这种办法要遍历两遍链表,虽然代码实现起来比较简单,但是程序的效率不高,于是我们思考能否只遍历一遍链表,就可以得到链表的中间节点?

思路2

我们可以建立两个指针变量,第一个指针每次向前走两步,第二个指针每次向前走一步,于是第一个指针变量指向的节点是链表的第2n个节点,而第二个指针变量指向的节点是链表的第n个节点。我们将这两个指针形象地称为快慢指针。

当快指针指向链表的最后一个节点时,慢指针恰好指向链表中间的节点

代码2(C语言版)

/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     struct ListNode *next;
 * };
 */
struct ListNode* middleNode(struct ListNode* head) {
    struct ListNode* fast=head;
    struct ListNode* slow=head;
    while(fast && fast->next)
    {
        fast=fast->next->next;
        slow=slow->next;
    }

    return slow;
}

题目2:返回倒数第k个节点

查看原题目请点击这里~

实现一种算法,找出单向链表中倒数第 k 个节点。返回该节点的值。

/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     struct ListNode *next;
 * };
 */
int kthToLast(struct ListNode* head, int k) {

}

示例:

输入: 1->2->3->4->5 和 k = 2
输出: 4

如果想要得到链表的倒数第k个节点,我们可以先遍历依次链表,直至找到链表的最后一个节点,同时得到链表的长度,有个链表的长度我们就可以再次遍历链表得到倒数第k个节点。

思路

思考一下,我们上面介绍了快慢指针的方法帮助我们在时间复杂度为O(1)就得到了链表的中间节点,那如何是否可以利用快慢指针的方法在时间复杂度O(1)就得到指针的倒数第k个节点呢?

也就是说,当fast指针指向链表最后一个节点的时候,slow指针恰好指向链表的倒数第k个节点,所以,fast指针比slow指针快了k步

于是,在最开始的时候我们让fast先走k步,然后fast和slow再一次走一步,当fast偏好指向链表最后一个节点的时候,slow指针指向的节点就是链表的倒数第k个节点

代码(C语言版)

/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     struct ListNode *next;
 * };
 */
int kthToLast(struct ListNode* head, int k) {
    struct ListNode* slow=head;
    struct ListNode* fast=head;
    while(k-1)
    {
        fast=fast->next;
        k--;
    }
    while(fast->next)
    {
        fast=fast->next;
        slow=slow->next;
    }
    return slow->val;
}

题目3:反转链表

查看原题目点击这里~

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

/**

 * Definition for singly-linked list.

 * struct ListNode {

 *     int val;

 *     struct ListNode *next;

 * };

 */

struct ListNode* reverseList(struct ListNode* head) {

}

思路

  • 我们首先会想到,建立两个指针,先让第二个节点的next指向第一个节点,然后n2=n2->next指向第三个节点再改变第三个节点的指向,但由于此时n2已经指向了n1,所以无法通过n2=n2->next找到第三个节点,所以我们发现这里需要三个指针

建立三个指针变量n1,n2,n3,先让n2->next指向n1实现反转,然后让n1=n2,n2=n3,n3=n3=n3->next就可以实现迭代

其次我们要注意的是循环的初始条件和结束条件

  • 当链表反转后,原链表的第一个节点应该是新链表的尾节点,所以再反转的第一步,应该让n2等于原链表的第一个节点,让第一个链表的next指向NULL
  • 进入循环的条件是n2不为NULL
  • 循环结束的条件应该是当n2为空时,n2已经将链表的所有的节点都反转,但是值得注意的是,n3比n2先一步为NULL,所以应该加上条件判断

代码(C语言版)

/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     struct ListNode *next;
 * };
 */
struct ListNode* reverseList(struct ListNode* head) {
    if(head==NULL)
    {
        return NULL;
    }
    struct ListNode* n1=NULL;
    struct ListNode* n2=head;
    struct ListNode* n3=head->next;
    while(n2)
    {
        //反转
        n2->next=n1;
        //迭代
        n1=n2;
        n2=n3;
        if(n3)
            n3=n3->next;
    }
    return n1;

}

题目4:链表的回文结构

查看原题目点击这里~

对于一个链表,请设计一个时间复杂度为O(n),额外空间复杂度为O(1)的算法,判断其是否为回文结构。

给定一个链表的头指针A,请返回一个bool值,代表其是否为回文结构。保证链表长度小于等于900。

/*

struct ListNode {

    int val;

    struct ListNode *next;

    ListNode(int x) : val(x), next(NULL) {}

};*/

class PalindromeList {

public:

    bool chkPalindrome(ListNode* A) {

        // write code here

    }

};

测试样例:

1->2->3->2->1
返回:true

思路

对于一个回文结构的链表来说,它的前半段和后半段是对称的,那么我们就会想到将后半部分的链表反转,然后跟前一半的链表进行比较看是否相等。

那该如何得到后一半的链表呢?

我们题目一就已经回答了如何找到链表的中间节点,题目三回答了如何将链表反转,有了这两道题的基础,我们很快就可以解答这道题。

于是我们的代码一共分为三步:

第一步得到链表中间的的节点,将这个节点作为新链表的头节点

第二步将新链表反转

第三步将新链表和原链表的前半部分进行比较,如果相等,返回true

代码(C语言版)

/*
struct ListNode {
    int val;
    struct ListNode *next;
    ListNode(int x) : val(x), next(NULL) {}
};*/
struct ListNode* reverseList(struct ListNode* head) {
    if (head == NULL) {
        return NULL;
    }
    struct ListNode* n1 = NULL;
    struct ListNode* n2 = head;
    struct ListNode* n3 = head->next;
    while (n2) {
        //反转
        n2->next = n1;
        //迭代
        n1 = n2;
        n2 = n3;
        if (n3)
            n3 = n3->next;
    }
    return n1;

}
struct ListNode* middleNode(struct ListNode* head) {
    struct ListNode* fast = head;
    struct ListNode* slow = head;
    while (fast && fast->next) {
        fast = fast->next->next;
        slow = slow->next;
    }

    return slow;
}
class PalindromeList {
  public:
    bool chkPalindrome(ListNode* A) {
        struct ListNode* n1=A;
        struct ListNode* mid=middleNode(A);
        struct ListNode* n2=reverseList(mid);
        while(n1&&n2)
        {
            if(n1->val!=n2->val)
            {
                return false;
            }
            else 
            {
                n1=n1->next;
                n2=n2->next;
            }
            
        }
        return true;
    }

};

(持续更新,下期见)

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值