链表相关算法复习三

135 阅读5分钟

文章简要概述

  • 本篇主要是复习链表相关的内容,部分算法题之前可能写过,这里进行回顾,进一步加深印象。刷题不是目的,目的是理解与掌握

  • 本文一共有4道题,主要介绍leetcode中分隔链表面试题 02.04. 分割链表第K个语法符号 剑指 Offer 10- I. 斐波那契数列的解题思路。

与链表相关算法

分隔链表

分隔链表 -- leetcode

题目大意:

给你一个头结点为 head 的单链表和一个整数 k ,请你设计一个算法将链表分隔为 k 个连续的部分。

每部分的长度应该尽可能的相等:任意两部分的长度差距不能超过 1 。这可能会导致有些部分为 null 。

这 k 个部分应该按照在链表中出现的顺序排列,并且排在前面的部分的长度应该大于或等于排在后面的长度。

返回一个由上述 k 部分组成的数组。

示例

split2-lc.jpeg

输入:head = [1,2,3,4,5,6,7,8,9,10], k = 3

输出:[[1,2,3,4],[5,6,7],[8,9,10]]

解释: 输入被分成了几个连续的部分,并且每部分的长度相差不超过 1 。前面部分的长度大于等于后面部分的长度。

解题思路:

  • 注意事项:链表数不够分隔时填充空数据,分隔链表后每个部分的长度不超过1
  • 先获取链表的长度l,计算l/k,得到分隔后链表的长度。如果l/k得到的不是整数,那么我们记录l%k的值remainder,让分隔的链表前remainder的部分多1个值
  • 然后遍历链表,按照上面的规则,切割链表。

代码:

/**
 * Definition for singly-linked list.
 * function ListNode(val, next) {
 *     this.val = (val===undefined ? 0 : val)
 *     this.next = (next===undefined ? null : next)
 * }
 */
/**
 * @param {ListNode} head
 * @param {number} k
 * @return {ListNode[]}
 */
var splitListToParts = function(head, k) {
  let l = 0;
  let node = head;
  while(node) {
      node = node.next;
      l++;
  }
  const res = new Array(k).fill(null);
  const remainder = l % k;
  const quantity = Math.floor(l/k);
  let cur = head;
  for(let i = 0; cur; i++) {
      res[i] = cur;
      const pageSize = quantity + (i < remainder ? 1 : 0)
      for(let j = 1; j < pageSize; j++) {
          cur = cur.next;
      }
      const next = cur.next;
      cur.next = null;
      cur = next;
  }
  return res;
};

面试题 02.04. 分割链表

面试题 02.04. 分割链表 -- leetcode

题目大意:

给你一个链表的头节点 head 和一个特定值 x ,请你对链表进行分隔,使得所有 小于 x 的节点都出现在 大于或等于 x 的节点之前。

你不需要 保留 每个分区中各节点的初始相对位置。

示例:

partition.jpeg

输入: head = [1,4,3,2,5,2], x = 3

输出:[1,2,2,4,3,5]

解题思路:

  • 设置两个链表,分别存链表中小于x的值small和大于等于x的值large
  • 然后将两个链表链接。

代码:

/**
 * Definition for singly-linked list.
 * function ListNode(val) {
 *     this.val = val;
 *     this.next = null;
 * }
 */
/**
 * @param {ListNode} head
 * @param {number} x
 * @return {ListNode}
 */
var partition = function(head, x) {
    let smallList = new ListNode(-1);
    const smallHead = smallList;
    let largeList = new ListNode(-1);
    const largeHead = largeList;
    while(head) {
        if (head.val < x) {
            smallList.next = head;
            smallList = smallList.next;
        } else {
            largeList.next = head;
            largeList = largeList.next
        }
        head = head.next;
    }
    largeList.next = null;
    smallList.next = largeHead.next;
    return smallHead.next;
};

第K个语法符号

第K个语法符号 -- leetcode

题目大意:

在第一行我们写上一个 `0`。接下来的每一行,将前一行中的`0`替换为`01``1`替换为`10`。

给定行数 `N` 和序数 `K`,返回第 `N` 行中第 `K`个字符。(`K`1开始)

示例:

输入: N = 1, K = 1 输出: 0

输入: N = 2, K = 1 输出: 0

输入: N = 2, K = 2 输出: 1

输入: N = 4, K = 5 输出: 1

解释:

第一行: 0

第二行: 01

第三行: 0110

第四行: 01101001

解题思路:

1641213862502.jpg

  • 可以分为两种情况
  • 如果K在前半段,所对应的值就是上一行的第K个值
  • 如果K在后半段,可以先算出K相对于后半段的位置,然后找出上一行这个位置的值,把值反过来

代码:

/**
 * @param {number} n
 * @param {number} k
 * @return {number}
 */
var kthGrammar = function(n, k) {
   if (n === 1) return 0;
   const l = Math.pow(2, n-1);
   if (k > l/2) {
       const val = kthGrammar(n-1, k-l/2);
       return val === 1 ? 0 : 1;
   }
    return kthGrammar(n-1, k);
};

剑指 Offer 10- I. 斐波那契数列

剑指 Offer 10- I. 斐波那契数列 -- leetcode

题目大意:

写一个函数,输入 n ,求斐波那契(Fibonacci)数列的第 n 项(即 F(N))。斐波那契数列的定义如下:

F(0) = 0,   F(1) = 1
F(N) = F(N - 1) + F(N - 2), 其中 N > 1.
斐波那契数列由 01 开始,之后的斐波那契数就是由之前的两数相加而得出。

答案需要取模 1e9+71000000007),如计算初始结果为:1000000008,请返回 1

示例:

输入: n = 2

输出: 1

解题思路:

  • 本题有动态规划的求解方法,留作后面动态规划练习时实现,感兴趣的可以看leetcode中的解答。 矩阵乘法案例 1641214635403.jpg

1641214283992.jpg

  • 只要我们能快速计算矩阵 M 的 n 次幂,就可以得到 F(n) 的值。
  • 如果直接求取 M^n,时间复杂度是 O(n),可以定义矩阵乘法,然后用快速幂算法来加速这里 M^n 的求取。

代码:

/**
 * @param {number} n
 * @return {number}
 */
var fib = function(n) {
    if (n < 2) {
        return n;
    }
    const q = [[1, 1], [1, 0]];
    const res = pow(q, n - 1);
    return res[0][0];
};

const pow = (a, n) => {
    let ret = [[1, 0], [0, 1]];
    while (n > 0) {
        if ((n & 1) === 1) {
            ret = multiply(ret, a);
        }
        n /=2;
        a = multiply(a, a);
    }
    return ret;
}

const multiply = (a, b) => {
    const c = new Array(2).fill(0).map(() => new Array(2).fill(0));
    for (let i = 0; i < 2; i++) {
        for (let j = 0; j < 2; j++) {
            c[i][j] = (BigInt(a[i][0]) * BigInt(b[0][j]) + BigInt(a[i][1]) * BigInt(b[1][j])) % BigInt(1000000007);
        }
    }
    return c;
}

结束语

数据结构与算法相关的练习题会持续输出,一起来学习,持续关注。当前是链表复习部分。后期还会有其他类型的数据结构,题目来源于leetcode。

往期文章:

链表相关算法复习                         数据结构与算法-栈一                         数据结构与算法-栈二

数据结构与算法-队列一                数据结构与算法-队列二                     数据结构与算法-链表一

数据结构与算法-链表二                数据结构与算法-链表三

有兴趣的可以一起来刷题,感谢点赞👍 , 关注!