【数组OJ题】有效的完全平方数

 OJ链接:367. 有效的完全平方数 - 力扣(LeetCode)

引言:

当我们面对这道题目时,首先要理解什么是完全平方数。完全平方数是指一个数能够被某个整数的平方表示,也就是说,它可以写成两个相同的因数相乘。解决这个问题的关键在于如何判断一个给定的正整数是否是完全平方数,而且题目要求不能使用内置库函数如 sqrt。那我们比较简单的方法呢就是暴力求解,从头开始遍历一遍就能知道是不是完全平方数了,要是这样做呢,这道题目就失去了锻炼的意义了。这里我们有两种思路,第一种呢使用二分查找的思想,第二种使用一些数学上的技巧。

思路一:二分查找

bool isPerfectSquare(int num) 
{
    int left = 1;  // 这里我让它从1开始是因为题目说了n是正整数,不过从0开始也是可以的
    int right = num;
    int mid;
    while (left <= right)
    {
        mid = left + (right -left) / 2; // 防止溢出
        if ((long)mid * mid < num)  // 这里强制类型转换成long类型,防止溢出
            left = mid + 1;
        else if ((long)mid * mid > num)
            right = mid - 1;
        else   
            return true;
    }
    return false;
}

我们要找的那个平方根就在0到num的范围里,我们使用更新中间值的方法来每次缩小一半的查找方法,如果中间值的平方比该数字大,那就说明要找的中间值应该是在左半边区间的,这时候我们让 right 的值变成 mid - 1,因为接下来查找的区间肯定不包括 mid 了,然后就能更新 mid 的值,继续比较,直到找到平方根了就返回,或者说当 left 比 right 大了,这时候就找不到了,也返回。

如果说不想去担心溢出问题的话,可以用下面这一种写法,思路也是二分查找,只是变成了用除法来判断。这样就不用担心溢出问题。

bool isPerfectSquare(int num) 
{
    int left = 1;
    int right = num;
    int mid;
    while (left <= right)
    {
        mid = left + (right - left) / 2;
        if (num / mid == mid && num % mid == 0)
            return true;
        else if (num / mid < mid)
        {
            right = mid - 1;
        }
        else
            left = mid + 1;
    }

    return false;
}

注意这里不能在 num / mid == mid 的时候就直接返回 true,因为整数的除法是直接截断小数点部分的,一个数可能在数学层面上是2.5,但是在这里它就是2,所以我们多加一个取模的值为0的判断,这样就杜绝了一些数来滥竽充数了。
 

这种二分查找的思路,它的时间复杂度是:O(logN)

思路二:数学性质

bool isPerfectSquare(int num) 
{
    int n = 0;
    while (num > 0)
    {
        num -= (2 * n + 1);
        n++;
    }
    if (num == 0)
        return true;
        
    return false;
}

完全平方数是一系列奇数之和。例如:1=1, 4=1+3, 9=1+3+5, 16=1+3+5+7 …
这样的话,我们可以遍历奇数,不断让 num 减去这些奇数,看最终是否能够减到0。如果刚好减到0就说明是完全平方数,不然就不是了。

这种思路就比较巧妙。它的时间复杂度是:O(√n) 

总结:

在解决这个问题的过程中,我们通过二分查找数学性质两种不同的方法来判断一个正整数是否是完全平方数。这两种方法各有优劣,选择哪一种取决于具体的应用场景和个人的偏好。通过理解这道题目,我们可以更深入地了解二分查找的应用和数学性质在算法中的作用。希望本篇博客能够帮助你更好地理解并解决这个问题。

  • 7
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 4
    评论
数组是一种非常常见的数据结构,它是相同数据类型的元素集合。在大多数编程语言中,数组都是一种基本的数据结构类型,因此对于编程来说,数组的处理方法也非常重要。以下是一些数组对象的处理方法: 1. 声明数组对象:在声明数组对象时,需要指定数组的数据类型和大小。例如,在C++中声明一个包含10个整数的数组可以使用以下语法:int array[10]; 2. 初始化数组对象:初始化是指给数组的每个元素赋初值,可以在声明时或之后进行初始化。例如,在C++中可以使用以下语法进行初始化:int array = {1, 2, 3, 4, 5}; 3. 访问数组元素:通过数组名和下标可以访问数组中的元素。例如,在C++中访问数组中的第3个元素可以使用以下语法:array。 4. 数组遍历:遍历数组是指按照顺序访问数组中的每个元素,可以使用for循环来实现。例如,在C++中可以使用以下语法来遍历数组:for(int i=0; i<5; i++) {cout << array[i] << endl;} 5. 数组排序:排序是指将数组中的元素按照一定的规则进行排列,可以使用内置函数或手动实现排序算法来完成。例如,在C++中可以使用sort函数进行排序:sort(array, array+5); 6. 数组查找:查找是指在数组中查找指定元素,可以使用线性查找或二分查找等算法来完成。例如,在C++中可以使用内置函数find来查找指定元素:int index = find(array, array+5, 3) - array;
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值