C++常用数学函数
cpp
#include<iostream>
#include<cmath>
using namespace std;
int main() {
double x = 4.0, y = 2.0;
// 平方根
std::cout << "sqrt(" << x << ") = " << sqrt(x) << std::endl; //sqrt(4) = 2
// 幂函数
std::cout << "pow(" << x << ", " << y << ") = " << pow(x, y) << std::endl;
//pow(4, 2) = 16
// 指数函数 (e 的 x 次幂)
std::cout << "exp(" << x << ") = " << exp(x) << std::endl; //exp(4) = 54.5982
// 以 2 为底的对数
std::cout << "log2(" << x << ") = " << log2(x) << std::endl;
// 自然对数
std::cout << "log(" << x << ") = " << log(x) << std::endl; //log(4) = 1.38629
// 常用对数(以10为底)
std::cout << "log10(100) = " << log10(100) << std::endl; //log10(100) = 2
double num = 3.7;
// 向上取整 4
std::cout << "ceil(" << num << ") = " << ceil(num) << std::endl;
// 向下取整 3
std::cout << "floor(" << num << ") = " << floor(num) << std::endl;
// 四舍五入 4
std::cout << "round(" << num << ") = " << round(num) << std::endl;
// 向零取整(就是去掉小数部分) 3
std::cout << "trunc(" << num << ") = " << trunc(num) << std::endl;
double angle = 45.0; // 角度
const double M_PI = 3.14159265358979323846;
// M_PI 在某些编译环境中可能未定义。这是因为 M_PI 不是 C++ 标准的一部分
double radian = angle * M_PI / 180.0; // 转换为弧度
std::cout << "sin(" << angle << "°) = " << sin(radian) << std::endl;
std::cout << "cos(" << angle << "°) = " << cos(radian) << std::endl;
std::cout << "tan(" << angle << "°) = " << tan(radian) << std::endl;
// 反三角函数
double value = 0.5;
std::cout << "asin(" << value << ") = " << asin(value)*180/M_PI << "°" << std::endl;
std::cout << "acos(" << value << ") = " << acos(value)*180/M_PI << "°" << std::endl;
std::cout << "atan(" << value << ") = " << atan(value)*180/M_PI << "°" << std::endl;
int a = -10, b = 3;
double c = -5.5;
std::cout << "abs(" << a << ") = " << abs(a) << std::endl; // 整数绝对值
std::cout << "fabs(" << c << ") = " << fabs(c) << std::endl; // 浮点数绝对值
std::cout << "fmod(10.5, 3.2) = " << fmod(10.5, 3.2) << std::endl; // 浮点数取模
return 0;
}
数组中两个目标值的最短距离
给定一个字符串数组strs,再给定两个字符串str1和str2,返回在strs中str1和str2的最小距离,如果str1或str2为null,或不在strs中,返回-1。
解法:遍历数组的同时,用两个变量标记当前位置之前的最后出现的 str1 和 str2 的下标。
cpp
#include <iostream>
#include <string>
#include <climits>
using namespace std;
int main()
{
int n; cin >> n;
string s1,s2;cin >> s1 >> s2;
if(s1.size() == 0 || s2.size() == 0)
{
cout << -1 << endl;
return 0;
}
int i1 = -1,i2 = -1;
int ret = INT_MAX;
for(int i = 1; i <= n; i++)
{
string t; cin >> t;
if(t == s1)
{
if(i2 != -1) ret = min(ret,i - i2);
i1 = i;
}
else if(t == s2)
{
if(i1 != -1) ret = min(ret,i - i1);
i2 = i;
}
}
if(ret == INT_MAX) cout << -1 << endl;
else cout << ret << endl;
return 0;
}
两个链表的第一个公共结点
除了统计两个链表的长度,然后让长的链表的指针先走之外,还可以采用这种方式:cur1 和 cur2 同时从头结点出发,如果 cur1 为空(到末尾了),就将 cur1 重新从另一个链表的头节点开始遍历,cur2 同理。运用了等量关系:x1 + x + x2 = x2 + x + x1,该解法和上面提到的解法都可以处理没有公共结点的情况

cpp
/*
struct ListNode {
int val;
struct ListNode *next;
ListNode(int x) :
val(x), next(NULL) {
}
};*/
class Solution {
typedef long long LL;
public:
ListNode* FindFirstCommonNode( ListNode* pHead1, ListNode* pHead2) {
// LL len1 = 0;
// LL len2 = 0;
// ListNode* cur = pHead1;
// while(cur)
// {
// if(cur)
// {
// len1++;
// cur = cur->next;
// }
// }
// cur = pHead2;
// while(cur)
// {
// if(cur)
// {
// len2++;
// cur = cur->next;
// }
// }
// ListNode* cur1 = pHead1;
// ListNode* cur2 = pHead2;
// if(len1 > len2)
// {
// LL cnt = len1 - len2;
// while(cnt--) cur1 = cur1->next;
// }
// else if(len2 > len1)
// {
// LL cnt = len2 - len1;
// while(cnt--) cur2 = cur2->next;
// }
// while(cur1 != cur2)
// {
// cur1 = cur1->next;
// cur2 = cur2->next;
// }
// return cur1;
ListNode* cur1 = pHead1;
ListNode* cur2 = pHead2;
while(cur1 != cur2)
{
if(cur1) cur1 = cur1->next;
else cur1 = pHead2;
if(cur2) cur2 = cur2->next;
else cur2 = pHead1;
}
return cur1;
}
};
判断一个数是否是 2 的 n 次方
- 方法1:不断除 2,最终会变成 1
- 方法2:二进制表示只有一个"1",其余位都是"0" ----> 统计二进制表示"1" 的个数
- 方法3:在方法 2 的基础之上,进行 lowbit 操作:假设要判断的数是 x,如果 x - (x & -x) == 0 成立,则 x 是 2 的 n 次方,否则不是
- 方法4:在方法 2 的基础之上,假设要判断的数是 x,如果 x & (x - 1) == 0成立,则 x 是 2 的 n 次方,否则不是
n 条线段的最多重叠次数
题目描述:给出 n 条线段的起始坐标和结束坐标,判断这 n 条线段的最多重叠次数。

解析:1、先按照这 n 条线段的起始坐标排序,2、创建一个小根堆,3、从左往右依次遍历线段
最长上升子序列(贪心 + 二分)

这道题是动态规划的经典题目,但它有一个更优的解法,时间复杂度为 nlogn,这个解法是贪心 + 二分。解法:依次遍历数组,在遍历的过程中,我们只需要知道:长度为 1 的上升子序列的末尾元素、长度为 2 的上升子序列的末尾元素、长度为 3 的上升子序列的末尾元素等等,把这些末尾元素记录在 t 数组中,t[i] 表示长度为 i 的上升子序列的末尾元素,现在假设遍历到了一个新的元素 m ,我们就把该元素与上面记录的末尾元素比较,如果 m > t[i] ,说明 m 可以接在 t[i] 后面形成长度为 i + 1 的上升子序列,再把 m 与 t[i + 1] 比较,如果 m > t[i + 1] 仍然成立,说明 m 可以接在 t[i + 1] 后面形成长度为 i + 2 的上升子序列,就这样从 t 数组的首元素依次比较,直到 i <= t[j],说明 i 可以接在 t[j - 1] 后面形成长度为 j 的上升子序列,并且把 i 赋值给 t[j] ,因为 i <= t[j],这样做可以让后面要遍历的元素更有机会接在 j 的后面,因为 t[j] 变成一个比较小的数字 i 了,这是体现贪心的地方。我们在比较 i 与 t 数组的元素关系时,采用顺序遍历,其实可以用二分查找 i 的插入位置,i 插入位置其实就是 t[j] < i 与 t[j] >= i 的分界点。
在下面的代码中,由于二分查找需要确定待查找区间的左右端点,刚开始查找时,左端点是 0,右端点是 ret + 1,ret 就是最长上升子序列的长度,如果最后找到的插入位置是 ret + 1,那么 ret++
cpp
class Solution {
public:
/**
* 代码中的类名、方法名、参数名已经指定,请勿修改,直接返回方法规定的值即可
*
* 该数组最长严格上升子序列的长度
* @param a int整型vector 给定的数组
* @return int整型
*/
static const int N = 1e5 + 10;
int t[N];
int LIS(vector<int>& a) {
// write code here
// dp
// int sz = a.size();
// if(sz == 0) return 0;
// for(int i = 0; i < sz; i++) dp[i] = 1;
// for(int i = 1; i < sz; i++)
// {
// for(int j = 0; j < i; j++)
// {
// if(a[j] < a[i]) dp[i] = max(dp[i],dp[j] + 1);
// }
// }
// int ret = 1;
// for(int i = 0; i < sz; i++) ret = max(ret,dp[i]);
// return ret;
// 贪心 + 二分
int sz = a.size();
if(sz == 0) return 0;
memset(t,0x3f,sizeof(t));
t[1] = a[0];
int ret = 1;
for(int i = 1; i < sz; i++)
{
int l = 1;
int r = ret + 1;
while(l < r)
{
int mid = l + (r - l) / 2;
if(t[mid] < a[i]) l = mid + 1;
else r = mid;
}
t[l] = a[i];
if(l == ret + 1) ret++;
}
return ret;
}
};