力扣 876. 链表的中间结点

目录

第一站 LeetCode 新手村

前言

876. 链表的中间结点

题目描述

解题思路

代码

C++

Python3

知识拓展

总结

题目来源


第一站 LeetCode 新手村


前言

最近玩OJ赛,发现对算法的理解还需要更加扎实,code能力还可以进一步提升,所以做这样一个算法的系列文章,用于记录学习心得,交流经验,更好地进步和成长。


876. 链表的中间结点

题目描述

给定一个头结点为 head 的非空单链表,返回链表的中间结点。

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

示例1

输入:[1,2,3,4,5]
输出:此列表中的结点 3 (序列化形式:[3,4,5])
返回的结点值为 3 。 (测评系统对该结点序列化表述是 [3,4,5])。
注意,我们返回了一个 ListNode 类型的对象 ans,这样:
ans.val = 3, ans.next.val = 4, ans.next.next.val = 5, 以及 ans.next.next.next = NULL.

示例 2

输入:[1,2,3,4,5,6]
输出:此列表中的结点 4 (序列化形式:[4,5,6])
由于该列表有两个中间结点,值分别为 3 和 4,我们返回第二个结点。

提示

  • 给定链表的结点数介于 1 和 100 之间。

解题思路

预知

LeetCode是核心代码模式,所以只需要考虑核心算法,输入由系统自动完成,最后的输出以return返回;

思路

单指针(时间复杂度O(N),空间复杂度O(1))

统计链表的长度,并找到其中间结点,返回中间结点即可,链表默认返回中间结点之后的所有元素;

目前没有在C++中找到可以直接用于统计链表长度的方法,所以需要遍历链表,并从头结点指向下一个结点,依次类推,直到下一个结点为空;

双指针(时间复杂度O(N),空间复杂度O(1))

不需要统计链表长度,两个指针均指向初始结点,一个指针1步长为1,指针2步长为2,当指针2指向尾结点时,指针1刚好指向中间结点;

该方法可以通过C++中的指向下一个结点实现,具体原理实现可参考代码注释;

代码

C++

单指针(时间复杂度O(N),空间复杂度O(1))

/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     ListNode *next;

//声明链表的三种方式
 *     ListNode() : val(0), next(nullptr) {}
 *     ListNode(int x) : val(x), next(nullptr) {}
 *     ListNode(int x, ListNode *next) : val(x), next(next) {}
 * };
 */
class Solution {
public:
    ListNode* middleNode(ListNode* head) {
        //统计链表的长度
        int n =0;
        ListNode* A = head;
        while(A != nullptr){
            n++;
            A = A -> next;
        }

        int k = 0;
        A = head;
        while(k next;
        }
        return A;     //中间结点出现,返回该元素以及其后的所有元素
    }
};

双指针(时间复杂度O(N),空间复杂度O(1))

/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     ListNode *next;
 *     ListNode() : val(0), next(nullptr) {}
 *     ListNode(int x) : val(x), next(nullptr) {}
 *     ListNode(int x, ListNode *next) : val(x), next(next) {}
 * };
 */
class Solution {
public:
    ListNode* middleNode(ListNode* head) {
        ListNode* slow = head;    //慢指针
        ListNode* fast = head;    //快指针
        while(fast != nullptr and fast->next != nullptr){  //注意不知要判断fast不为空,如果fast的下一个元素为空则fast已经是尾元素,已经达到查找的目的,停止查找
            slow = slow -> next;       //步长为1
            fast = fast->next->next;   //步长为2
        }
        return slow;    //返回慢指针,及其之后的所有元素,由于列表是指向性,所以可以理解为slow为开始然后,以一列的方式输出,
    }
};

Python3

# Definition for singly-linked list.
# class ListNode:
#     def __init__(self, val=0, next=None):
#         self.val = val
#         self.next = next

class Solution:
    def middleNode(self, head: ListNode) -> ListNode:
        slow = fast = head
        while fast and fast.next:
            slow = slow.next
            fast = fast.next.next
        return slow

知识拓展

链表与数组的优缺点

数组在查找和占用的内存空间上优于链表,数组具有按地址索引访问的特性,而链表若想访问某个结点只能从头结点开始,所以数组查找较快;

在占用空间上,数组不需要存储元素上一个结点和下一个结点的位置信息,所以更加节省空间;

链表在删除和修改以及空间的灵活度上优于数组,链表在删除或增加元素时,只需要修改存储在内存中的结点信息即可,将指针指向新加入的元素的位置,或者指向被删除元素的下一个结点的位置,但数组若删除或插入一个元素,则该位置后的所有元素均需向前或向后移动,操作影响面较广;

同时数组的存储模式要求必须将数组存储在内存的连续区域,这给数组的灵活度造成了限制,当连续区域内存空间小于要存储的数组大小时,就会发生报错,不得不寻找更大的连续的内存空间;而链表则允许以散列的方式存储在内存当中,指针会指向其存储的位置,将该链表的各元素连接起来,对内存空间的利用率较高,同时使链表的灵活度大大提高;

所以,在日常的使用过程中,一般面对数据改动较小且查找频繁的问题时往往采用数组来减少查找时间;在增删操作较多的情况下则使用链表,以减少数组元素在空间移动上所产生的时间消耗。


总结

以上就是今天要讲的内容,本文仅仅简单讲解了《链表的中间结点》这一题目,对快慢指针的内容进行了解,还对链表与数组的优缺点进行了讨论;

题目来源

来源:力扣(LeetCode)
链接:https://leetcode.cn/problems/middle-of-the-linked-list

你可能感兴趣的:(算法,leetcode,链表,算法)