友鏈
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;
}
};