LeetCode 第367题:有效的完全平方数

题目描述

给定一个正整数 num,判断它是否是一个完全平方数。即是否存在一个整数 x,使得 x 2 = num x^2 = \text{num} x2=num。

解法思路

我们可以采用以下几种常见的方法来判断是否为完全平方数:

  1. 二分查找法 :通过二分查找找到一个整数 x,使得 x 2 = num x^2 = \text{num} x2=num,若找到了就返回 true
  2. 牛顿法:通过迭代方式逼近平方根。
  3. 数学方法:不断减去奇数,直到剩下的数为零。

在这篇文章中,我们将使用 二分查找法 来实现解决方案,因为它具有较好的时间复杂度和简洁性。

二分查找法

由于 x 2 x^2 x2 是一个单调递增的函数,我们可以通过二分查找来找到 x,使得 x 2 = num x^2 = \text{num} x2=num。如果在查找过程中我们发现 x 2 = num x^2 = \text{num} x2=num,那么 num 就是完全平方数。如果没有找到,则 num 不是完全平方数。

C语言代码实现

c 复制代码
#include <stdio.h>
#include <stdbool.h>

/**
 * 判断一个数是否是完全平方数
 * @param num: 要判断的整数
 * @return: 如果 num 是完全平方数,则返回 true,否则返回 false
 */
bool isPerfectSquare(int num) {
    // 特殊情况:0 和 1 是完全平方数
    if (num == 0 || num == 1) {
        return true;
    }

    // 设置二分查找的左右边界
    int left = 1, right = num / 2;
    
    // 使用二分查找
    while (left <= right) {
        int mid = left + (right - left) / 2;
        int mid_square = mid * mid;

        // 判断 mid 的平方是否等于 num
        if (mid_square == num) {
            return true;  // 如果找到了完全平方数,返回 true
        } 
        // 如果 mid 的平方小于 num,说明平方根在右半部分
        else if (mid_square < num) {
            left = mid + 1;
        } 
        // 如果 mid 的平方大于 num,说明平方根在左半部分
        else {
            right = mid - 1;
        }
    }

    // 如果没有找到完全平方数,返回 false
    return false;
}

int main() {
    // 测试用例
    int test_cases[] = {16, 14, 25, 30, 1, 0, 144};
    int num_tests = sizeof(test_cases) / sizeof(test_cases[0]);

    for (int i = 0; i < num_tests; i++) {
        int num = test_cases[i];
        if (isPerfectSquare(num)) {
            printf("%d is a perfect square.\n", num);
        } else {
            printf("%d is not a perfect square.\n", num);
        }
    }

    return 0;
}

逐行解释代码

isPerfectSquare 函数
c 复制代码
bool isPerfectSquare(int num) {
    // 特殊情况:0 和 1 是完全平方数
    if (num == 0 || num == 1) {
        return true;
    }
  • 这部分代码处理了特殊情况:01 都是完全平方数。因此如果 num01,直接返回 true
c 复制代码
    // 设置二分查找的左右边界
    int left = 1, right = num / 2;
  • 设置二分查找的边界:
    • 左边界 left = 1,因为 01 已经被单独处理过。
    • 右边界 right = num / 2,因为 num 的平方根最大值是 num / 2。例如,对于 num = 16,平方根最大值是 4
c 复制代码
    // 使用二分查找
    while (left <= right) {
        int mid = left + (right - left) / 2;
        int mid_square = mid * mid;
  • 进入二分查找循环,在每次循环中计算中间值 mid,并计算其平方 mid_square = mid * mid
    • 使用 left + (right - left) / 2 来计算中间值,可以避免直接计算 (left + right) / 2 可能导致的溢出。
c 复制代码
        // 判断 mid 的平方是否等于 num
        if (mid_square == num) {
            return true;  // 如果找到了完全平方数,返回 true
        } 
  • 如果中间值 mid 的平方等于 num,那么 num 是完全平方数,直接返回 true
c 复制代码
        // 如果 mid 的平方小于 num,说明平方根在右半部分
        else if (mid_square < num) {
            left = mid + 1;
        } 
        // 如果 mid 的平方大于 num,说明平方根在左半部分
        else {
            right = mid - 1;
        }
    }
  • 如果 mid_square 小于 num,说明平方根应该在右半部分,因此将 left 移动到 mid + 1
  • 如果 mid_square 大于 num,说明平方根应该在左半部分,因此将 right 移动到 mid - 1
c 复制代码
    // 如果没有找到完全平方数,返回 false
    return false;
}
  • 如果在二分查找中没有找到平方根,则返回 false,说明 num 不是完全平方数。
main 函数
c 复制代码
int main() {
    // 测试用例
    int test_cases[] = {16, 14, 25, 30, 1, 0, 144};
    int num_tests = sizeof(test_cases) / sizeof(test_cases[0]);

    for (int i = 0; i < num_tests; i++) {
        int num = test_cases[i];
        if (isPerfectSquare(num)) {
            printf("%d is a perfect square.\n", num);
        } else {
            printf("%d is not a perfect square.\n", num);
        }
    }

    return 0;
}
  • main 函数中,我们创建了多个测试用例,包括完美的平方数和非完美的平方数。
  • 通过 isPerfectSquare 函数判断每个测试用例,并输出相应的结果。

复杂度分析

  • 时间复杂度 :二分查找的时间复杂度是 O(log n),其中 nnum 的大小。每次我们将搜索范围缩小一半,直到找到目标。

  • 空间复杂度 :O(1),仅使用了常数空间来保存 left, right, 和 mid 等变量。

输出示例

复制代码
16 is a perfect square.
14 is not a perfect square.
25 is a perfect square.
30 is not a perfect square.
1 is a perfect square.
0 is a perfect square.
144 is a perfect square.

总结

通过二分查找法,我们能够高效地判断一个数是否是完全平方数。该方法时间复杂度为 O(log n),适用于较大的整数输入。希望本篇文章能够帮助你掌握这道题目的解决方案!如果你有任何问题或改进意见,欢迎在评论区留言。

相关推荐
不当菜鸡的程序媛9 分钟前
Flow Matching|什么是“预测速度场 vt=ε−x”?
人工智能·算法·机器学习
sali-tec38 分钟前
C# 基于halcon的视觉工作流-章58-输出点云图
开发语言·人工智能·算法·计算机视觉·c#
_OP_CHEN39 分钟前
算法基础篇:(四)基础算法之前缀和
c++·算法·前缀和·蓝桥杯·acm·icpc·算法竞赛
_OP_CHEN43 分钟前
算法基础篇:(五)基础算法之差分——以“空间”换“时间”
c++·算法·acm·icpc·算法竞赛·差分算法·差分与前缀和
DuHz1 小时前
霍夫变换和基于时频脊线的汽车FMCW雷达干扰抑制——论文阅读
论文阅读·物联网·算法·汽车·信息与通信·毫米波雷达
秋风&萧瑟1 小时前
【C++】智能指针介绍
java·c++·算法
QiZhang | UESTC1 小时前
JAVA算法练习题day67
java·python·学习·算法·leetcode
多则惑少则明1 小时前
高频面试八股文用法篇(十五)如何高效操作字符串?
面试·职场和发展
陌路201 小时前
S15 排序算法--归并排序
数据结构·算法·排序算法
智者知已应修善业1 小时前
【c# 想一句话把 List<List<string>>的元素合并成List<string>】2023-2-9
经验分享·笔记·算法·c#·list