别说链表不重要(三):链表高频面试题、经典问题一篇搞定

链表高频面试题、经典问题

关于我

经典问题

在阅读了单链表原理双指针技技巧后,我们仍然在LeeCode上找出几个经典面试题,以此加强掌握。

一、反转链表

思路

我们接受到链表 [1, 2, 3, 4, 5]

  1. 首先,我们将head结点的下一个结点(即结点 2)移动到列表的头部pre
    在这里插入图片描述

  2. 然后,我们将黑色结点的下一个结点(即结点 3)移动到列表的头部:
    在这里插入图片描述

直至当前链表遍历到尾部。

在实际操作中,你需要:

  • 时刻保存pre节点,即前一个节点。
  • 时刻保存下一个节点,因为她要成为新的头节点

时间复杂度为 O(N)
空间复杂度为 O(1)

代码

var reverseList = function(head) {
    let cur = head //  当前节点
    let prev = null //  前置节点初始化(head节点无前置节点)
    while (cur) {
        let temp = cur.next // 记录当前节点的后置所有节点
        cur.next = prev // 记录上一个节点
        prev = cur // 每一步移动后的结果: 1->null | 2->1->null...
        cur = temp // 链表向后一位
    };
    return prev
};

二、移除链表元素

删除链表中等于给定值 val 的所有节点。

示例:

输入: 1->2->6->3->4->5->6, val = 6
输出: 1->2->3->4->5

思路

  • 创建一个哑节点来保存链表,以方比较头节点
  • 如果保存的值与给定的值相等,则跳过该节点,否则向后比较

代码

var removeElements = function(head, val) {
    if (head == null) return null;
    var temp = {
        val: -1,
        next: head
    }
    var search = temp;
    while (search.next) {
        if (search.next.val == val) {
            search.next = search.next.next;
        } else {
            search = search.next;
        }
    }
    return temp.next;
};

三、奇偶链表

给定一个单链表,把所有的奇数节点和偶数节点分别排在一起。请注意,这里的奇数节点和偶数节点指的是节点编号的奇偶性,而不是节点的值的奇偶性。

请尝试使用原地算法完成。你的算法的空间复杂度应为 O(1),时间复杂度应为 O(n),n 为节点总数。

示例 1:

输入: 1->2->3->4->5->NULL
输出: 1->3->5->2->4->NULL

示例 2:

输入: 2->1->3->5->6->4->7->NULL 
输出: 2->3->6->7->1->5->4->NULL

说明:

应当保持奇数节点和偶数节点的相对顺序。
链表的第一个节点视为奇数节点,第二个节点视为偶数节点,以此类推。

思路

  • 定义三个指针,分别记录奇数链表、偶数链表、偶数链表的head节点
  • 记录奇偶链表,最后将偶数链表接到奇数链表的尾端
    • 奇数链表节点 = 偶数链表最新节点的下一个
    • 偶数链表节点 = 奇数链表最新节点的下一个

代码

var oddEvenList = function(head) {
    if(!head) return ;
    var odd = head; // 奇数链表
    var even = head.next; // 偶数链表
    var evenHead = even // 偶数链表头
    while(even && even.next ){
        odd.next = even.next
        odd = odd.next
        even.next = odd.next
        even = even.next
    }
    odd.next = evenHead
    return head
};

四、回文链表

思路

  • 利用快慢指针确定链表的中间位置;
  • 将链表的前半部分进行反转,与链表的后半部分进行比对;

代码

var isPalindrome = function(head) {
    if(!head || !head.next) return true;
    var slow = head // 慢指针
    var fast = head // 快指针
    var prev = null // 保存上一个节点
    var temp = null // 保存下一个节点
    while(fast && fast.next){
        fast = fast.next.next // 快指针走2个节点

        temp = slow.next // 慢指针走过得链表直接反转
        slow.next = prev
        prev = slow

        slow = temp // 慢指针走1个节点
    }
    if(fast){ // 奇数个节点的链表
        slow = slow.next
    }
    while(slow){
        if(slow.val !== prev.val) return false;

        slow = slow.next
        prev = prev.next
    }
    return true
};

链表经典问题

  1. 你可以同时使用多个指针。

    • 有时,当你为链表问题设计算法时,可能需要同时跟踪多个结点。
    • 您应该记住需要跟踪哪些结点,并且可以自由地使用几个不同的结点指针来同时跟踪这些结点。
    • 如果你使用多个指针,最好为它们指定适当的名称,以防将来必须调试或检查代码。
  2. 在许多情况下,你需要跟踪当前结点的前一个结点。

    • 你无法追溯单链表中的前一个结点。
    • 因此,您不仅要存储当前结点,还要存储前一个结点。

我们将在下一篇文章中谈一谈双链表的基本原理,以及js的实战

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章