LeetCode每日一题

​ 今天的每日一题是2807. 在链表中插入最大公约数,难度标的中等,不过我认为应该是简单题

题干

给你一个链表的头 head ,每个结点包含一个整数值。

在相邻结点之间,请你插入一个新的结点,结点值为这两个相邻结点值的 最大公约数

请你返回插入之后的链表。

两个数的 最大公约数 是可以被两个数字整除的最大正整数。

示例 1:

img

输入:head = [18,6,10,3]
输出:[18,6,6,2,10,1,3]
解释:第一幅图是一开始的链表,第二幅图是插入新结点后的图(蓝色结点为新插入结点)。

  • 18 和 6 的最大公约数为 6 ,插入第一和第二个结点之间。
  • 6 和 10 的最大公约数为 2 ,插入第二和第三个结点之间。
  • 10 和 3 的最大公约数为 1 ,插入第三和第四个结点之间。
    所有相邻结点之间都插入完毕,返回链表。

示例 2:

img

输入:head = [7]
输出:[7]
解释:第一幅图是一开始的链表,第二幅图是插入新结点后的图(蓝色结点为新插入结点)。
没有相邻结点,所以返回初始链表。

提示:

  • 链表中结点数目在 [1, 5000] 之间。
  • 1 <= Node.val <= 1000

思路分析

​ 这道题很直观,明显是考差欧几里得算法(辗转相除法)gcd(a,b) = gcd(b,a mod b)原理为:两个整数的最大公约数等于其中较小的数和两数相除余数的最大公约数。

​ 明白了考察的知识点,这道题就非常简单了,直接遍历链表,在每两个节点直接插入这两个节点的值的最大公约数gcd(node1.val, node2.val)即可,计算gcd的方法可以是 Math.gcd或者自定义方法。

​ 有一点需要注意,只有节点数大于1时才需要插入,否则直接返回。

代码

class Solution {
public:
int gcd(int a, int b){
return b == 0 ? a : gcd(b, a % b);
}

ListNode* insertGreatestCommonDivisors(ListNode* head) {
if(!head || !head->next) return head;

ListNode *pre = head, *ne = head->next;
while(ne){
ListNode *node = new ListNode(gcd(pre->val, ne->val), ne);
pre->next = node;
pre = ne;
ne = ne->next;
}
return head;
}
};
class Solution {
public int gcd(int a, int b){
return b == 0 ? a : gcd(b, a % b);
}

public ListNode insertGreatestCommonDivisors(ListNode head) {
if (head == null || head.next == null) return head;

ListNode pre = head, ne = head.next;
while(ne != null){
int g = gcd(pre.val, ne.val);
ListNode newNode = new ListNode(g, ne);
pre.next = newNode;
pre = ne;
ne = ne.next;
}
return head;
}
}
class Solution:
def insertGreatestCommonDivisors(self, head: Optional[ListNode]) -> Optional[ListNode]:
def gcd(a, b):
return a if b == 0 else gcd(b, a % b)

if not head or not head.next: return head

pre, ne = head, head.next
while ne:
node = ListNode(gcd(pre.val, ne.val), ne)
pre.next = node
pre = ne
ne = ne.next
return head

复杂度分析

  • 时间复杂度:O(nlog⁡M),其中n为链表长度,M是节点最大可能的值,每次计算要O(logM)的时间。
  • 空间复杂度:O(1)。