x的平方根-69
cpp
class Solution {
public:
int mySqrt(int x) {//利用二分法找x的平方根
//定义三个整形的变量,left为查找范围的左边界,right为右边界,ans用来准确的保存满足条件的结果
int left = 0,right = x,ans = -1;
//循环条件为左边界小于等于有边界,如果左边界大于右边界说明范围内的整数已经全部查找完毕
while(left<=right){
//mid中间值用于每次循环缩小查找范围,用来找到目标值
int mid = (left+right)/2;
//如果mid的平方大于x,说明当前的数大了,目标值在当前mid的左边,所以有边界置为mid-1,缩小查找范围
if((long long)mid*mid>x)right = mid-1;
/*如果mid的平方小于等于x,说明当前的数小了,或者就是目标值,因为要找的是不超过x平方根的最大整数,
所以当mid满足条件时就更新ans为mid,然后置左边界为mid+1,继续寻找更大的可能值*/
//注意:如果不开long long,两个int想乘可能会超出int所能表示的范围
else if((long long)mid*mid<=x){
ans = mid;
left = mid+1;
}
}
//最后返回找到的结果
return ans;
}
};
每日问题
如何使用函数模板和类模板
1.函数模板的使用
定义函数模板:
函数模板的定义以template关键字开头,后面跟着模板参数列表,通常是typename(或clss,它们在这种情况下是等价的)加上一个参数名。例如,定义一个简单的交换函数模板:
cpp
template<typename T>
void swap(T& a,T& b){
T temp = a;
a = b;
b = temp;
}
这个函数模板可以交换两个相同类型的变量的值,T是一个模板参数,它可以代表任意类型,只要该类型支持赋值操作。
调用函数模板:
显示指定模板参数类型:可以在函数名后用尖括号<>显式指定模板参数类型。例如,如果有两个int类型的变量x和y,可以这样调用:
cpp
int x = 5,y = 10;
swap<int>(x,y);
这会告诉编译器使用int类型来实例化swap函数模板,生成一个专门用于交换int类型变量的函数。
隐式推导模板参数类型:
如果编译器可以从函数调用的实参类型推导出模板参数类型,就可以省略显式指定。例如,对于上面的swap函数,还可以这样调用:
cpp
double m = 3.14, n = 2.71;
swap(m,n);
在这里,编译器根据m和n是double类型,自动推导出T为double,并生成一个用于交换double类型变量的函数。
2.模板类的使用
定义类模板:
类模板的定义也是以template关键字开头,后面跟着模板参数列表。例如,定义一个简单的动态数组类模板:
cpp
template<typename T>
class DynamicArray{
private:
T* data;
int size;
int capacity;
public:
DynamicArray(int initialCapacity = 10);
~DynamicArray();
void push_back(T element);
T& operator[](int index);
//其他成员函数的定义
}
这个类模板DynamicArray可以用于创建存储不同类型元素的动态数组,T是模板参数,代表数组中元素的类型。类模板中的成员函数也需要在类外定义时,要带上模板参数列表,例如:
cpp
template<typename T>
DynamicArray<T>::DynamicArray(int initialCapacity):size(0),capacity(initialCapacity){
data = new T[capacity];
}
使用类模板:
显式指定模板参数类型:和函数模板类似,可以在类名后用尖括号<>显示指定模板参数类型来创建对象。例如,创建一个存储int类型元素的动态数组对象:
cpp
DynamicArray<int> intArray(5);
intArray.push_back(10);
这会实例化一个DynamicArray类模板,其中T被指定为Int,用于创建一个可以存储int类型元素的动态数组对象。
模板参数类型推导(部分情况):
在某些情况下,编译器可以根据初始化表达式来推导模板参数类型。例如,如果有一个函数模板返回一个类模板对象,并且可以从函数参数类型推导出类模板的参数类型,就可以省略显式指定。不过这种情况相对复杂,且不是所有编译器都支持所有可能的推导场景。
模板的优点是什么?
1.代码复用性高
函数模板方面:
函数模板允许编写通用的函数代码,可以处理多种不同类型的参数。例如,有一个用于比较两个值大小的函数模板,它可以用于比较整数、浮点数、字符等各种类型数据。例如:
cpp
template<typename T>
T max(T a,T b){
return (a>b)?a:b;
}
这个函数模板可以用于int、double、char等多种类型。如果没有函数模板,就需要为每种类型分别编写一个max函数,如maxInt、maxDouble、maxChar等。函数模板大大减少了代码的重复编写,提高了代码的复用性。
类模板方面:
类模板使得可以创建通用类结构,能够适应不同的数据类型。比如,一个简单的动态数组类模板,可以用来创建存储不同类型元素的数组。像这样的类模板定义:
cpp
template<typename T>
class DynamicArray{
//类的成员变量和成员函数定义
};
可以通过实例化来创建存储int类型的数组(DynamicArray)、存储double类型的数组(DynamicArray)等,避免为每种类型都编写一个独立的数组类。
2.增强代码的通用性和灵活性
适应多种数据类型:
模板可以轻松地处理各种数据类型,包括基本数据类型和自定义数据类型。在编写模板时,不需要预先知道具体的类型,只要该类型满足模板中使用的操作要求(如对于比较函数模板,要求类型支持比较运算符)。例如,对于一个排序函数模板,它可以对整数数组,浮点数数组或者包含自定义结构体的数组进行排序,只要为自定义结构体定义了合适的比较规则。
参数化代码逻辑:
通过模板参数,不仅可以指定类型,还可以指定一些非类型参数(如整数、指针等)来控制代码的逻辑。以一个固定大小的栈类模板为例,模板参数可以包括栈中元素的类型和栈的最大容量。这样就可以根据具体需求灵活地创建不同大小和存储不同类型元素的栈。
3.易于维护和更新
由于模板代码是集中编写的通用代码,当需要对功能进行修改或优化时,只需要在模板定义处进行更改,而不是在每个使用该功能的具体函数或类中修改。例如,如果发现排序函数模板的排序算法有缺陷,只需要在模板函数的算法部分进行修改,所有使用该排序模板的不同类型数据的排序应用都会收益。这使得代码的维护和更新更加容易,降低了代码出错的风险。