友链
1、只出现一次的数字
给定一个非空整数数组,除了某个元素只出现一次以外,其余每个元素均出现两次。找出那个只出现了一次的元素。
要求:线性时间复杂度、不使用额外空间
解答:典型的位运算,相同的两个数异或为0,扩展形式为,找出给定非空整数数组中只出现一次的两个整数。
int singleNumber(vector<int>& nums) {
int ans=nums[0];
for(int i=1;i<nums.size();i++)
{
ans^=nums[i];
}
return ans;
}
2、直线上最多的点数【需二刷】
给定一个二维平面,平面上有 n 个点,求最多有多少个点在同一条直线上。
特殊情况:①是当两个点重合时,无法确定一条直线,但这也是共线的情况,需要特殊处理。②是斜率不存在的情况,由于两个点 (x1, y1) 和 (x2, y2) 的斜率k表示为 (y2 - y1) / (x2 - x1),那么当 x1 = x2 时斜率不存在,这种共线情况需要特殊处理。
哈希表:记录斜率和共线点个数之间的映射,其中第一种重合点的情况我们假定其斜率为 INT_MIN,第二种情况我们假定其斜率为 INT_MAX,这样都可以用 map 映射了。
class Solution {
public:
int maxPoints(vector<vector<int>>& points) {
int res = 0;
for (int i = 0; i < points.size(); ++i) {
int duplicate = 1;
for (int j = i + 1; j < points.size(); ++j) {
int cnt = 0;
long long x1 = points[i][0], y1 = points[i][1];
long long x2 = points[j][0], y2 = points[j][1];
if (x1 == x2 && y1 == y2) {++duplicate; continue;}
for (int k = 0; k < points.size(); ++k) {
int x3 = points[k][0], y3 = points[k][1];
if (x1 * y2 + x2 * y3 + x3 * y1 - x3 * y2 - x2 * y1 - x1 * y3 == 0) {
++cnt;
}
}
res = max(res, cnt);
}
res = max(res, duplicate);
}
return res;
}
};
3、分数到小数【需二刷】
给定两个整数,分别表示分数的分子 numerator 和分母 denominator,以字符串形式返回小数。如果小数部分为循环小数,则将循环的部分括在括号内。
①循环体出现的标志是什么?我们研究一下1/6。
最开始补零,变成10/6,写成0.1,这时候余数是4。
余数4再去除以6,变成40/6,写成0.16,这时候余数是4,。
余数4再去除以6……
这个时候我们都知道接下来必定是循环体结构了,因为出现了相同的被除数。
所以我们不能把两个整数变成double类型,直接相除,而是应该不断地整数相除,记录余数,余数再去除以除数。
在这个过程中记录余数,如果出现了重复的余数,那么必定是循环体结构了。
②边界条件,比如-2147483648/-1,-1/-2147483648,7/-12等等。
string fractionToDecimal(int numerator, int denominator)
{
if(numerator==INT_MIN&&denominator==-1)//边界条件,没法直接除,因为除完结果溢出
return "2147483648";
if(numerator==-1&&denominator==INT_MIN)//边界条件,都是int类型,没法除
return "0.0000000004656612873077392578125";
int shang=numerator/denominator,yushu=numerator%denominator;//记录商和余数
string res;//最终要返回的string
if(double(numerator)/double(denominator)<0)//如果两个数一正一负
{
if(shang==0)//如果商为0
res='-'+to_string(abs(shang));//可能有的同学疑惑为什么要这样处理,比如7/-12,除完shang为0,但是我们要的是-0
else
res=to_string(shang);//如果不为0,那么直接处理
}
else//如果都是正数或者都是负数
res=to_string(shang);//直接处理
if(yushu==0)//如果余数为0,那么到此为止,返回res就可以了
return res;
res+='.';//如果还有余数,那么要加个小数点
unordered_map<int,int>record;//记录出现过的余数和余数除以除数得到的商的位置
while(yushu!=0)
{
yushu=abs(yushu);//余数有可能是负的,全都转为正数
denominator=abs(denominator);//除数也转为正数
yushu*=10;//余数乘10,作为新的被除数
if(record.count(yushu))//如果之前出现过了这个余数,那么可以取出循环体了
{
int start=record[yushu],end=res.size()-1;//start和end表示循环体的开端和末尾
res=res.substr(0,start)+'('+res.substr(start,end-start+1)+')';//加一下括号
return res;//直接返回
}
record[yushu]=res.size();//如果没出现过,那么记录在record中,value是这个余数除以除数得到的商应该放的位置
shang=yushu/denominator;//更新商
yushu=yushu%denominator;//更新余数
res+=to_string(shang);//加入最新的商
}
return res;//如果一直没有出现重复的余数,那么最终跳出循环后直接返回res
}
4、阶乘后的零
给定一个整数 n,返回 n! 结果尾数中零的数量。
要求:时间复杂度应为 O(log n)。
思路:1~n之间有多少5的倍数。
class Solution {
public:
int trailingZeroes(int n) {
int res = 0;
while(n){
n /= 5;
res += n;
}
return res;
}
};
5、缺失数字
给定一个包含 0, 1, 2, …, n 中 n 个数的序列,找出 0 … n 中没有出现在序列中的那个数。
要求:算法应具有线性时间复杂度,仅使用额外常数空间来实现。
不缺失数字时候的和可以计算,缺失数字之后的和遍历一遍也可以计算出来,二者相减就是缺失的数字.
class Solution {
public:
int missingNumber(vector<int>& nums) {
int res=nums.size();
for(int i=0;i<nums.size();i++)
{
res +=(i-nums[i]);
}
return res;
}
};
6、3的幂
给定一个整数,写一个函数来判断它是否是 3 的幂次方。
进阶:你能不使用循环或者递归来完成本题吗?
class Solution {
public:
bool isPowerOfThree(int n) {
if(n == 0) return false;
while(n%3 == 0) n /= 3;
if(n == 1) return true;
return false;
}
};
7、颠倒二进制位【需二刷】
颠倒给定的 32 位无符号整数的二进制位。
进阶:如果多次调用这个函数,你将如何优化你的算法?
class Solution {
public:
uint32_t reverseBits(uint32_t n) {
uint32_t res = 0;
for (int i = 0; i < 32; ++i) {
if (n & 1 == 1) { // 最后一位为1
res = (res << 1) + 1;
} else {
res = res << 1;
}
n = n >> 1;
}
return res;
}
};
补充题目
Sum of Two Integers
Calculate the sum of two integers a and b, but you are not allowed to use the operator + and -.
由于异或是相同则位0,不同则位1,因此我们可以把异或看成是一种不进位的加减法
。
由于与是全部位1则位1,否则位0,因此我们可以求与之后左移一位来表示进位
。
class Solution {
public:
int getSum(int a, int b) {
long long carry; // 64-bit
while (b != 0) {
carry = a & b;
a = a ^ b;
b = ((carry & 0xffffffff) << 1); // limited to 32 bits
}
return a;
}
};