leetcode筆記之數學

未完待續…

一.簡單計算

7. 反轉整數

反轉類的經常出現,鏈表,數組,字符串、整數、棧等等,

while(x)
{
    int tmp = res;//1.保存前一個值用於下面的溢出判斷
    res=res*10+x%10;//2.累計
    x/=10;//3.更新x
    if(res/10!=tmp)//4.溢出判斷
        return 0;
}
return res;

9. 迴文數判斷

整數逆序,然後看是否相等就可以了

//三種特殊情況
if(x<0) return false;//1.
if(x == 0) return true;//2.
if(x % 10 == 0) return false;//3.
while(x) {//4.
    num = num * 10 + x % 10;//累計
    x /= 10;//更新
}

29. 兩數相除

if (divisor == 0 || (dividend == INT_MIN && divisor == -1)) return INT_MAX;//1.溢出判斷
long long m=abs((long long) dividend), n=abs((long long) divisor),res=0;//2.定義爲long long類型是爲了左移位
int sign=((dividend<0)^(divisor<0))?-1:1;//3.確定符號
while(m>=n)//4.只要被除數大於等於除數就繼續循環
{
    long long t=n,mul=1;
    while(m>=(t<<1))//只要被除數大於等於除數就繼續循環
    {
        t=t<<1;
        mul=mul<<1;//累計mul
    }
    res+=mul;//累計res
    m-=t;//減去已經被除過的部分(每次最少減去1*t,這時商的累積量爲1,其餘情況累積量爲2^k)
}
return sign==1?res:-res;

43. 字符串相乘

1.申請一個大小爲len1 + len2的數組tmp,用來保存中間值:vector<int> temp(len1 + len2, 0);
2.對兩個字符串嵌套for循環,假設str1的每一個字符->數字分別 * str2的第0,1,…,n-1個字符->數字,得到的結果保存在i+j+1的位置:temp[i + j + 1] += d1 * d2
3.tmp數組中的各個位置保存的是d1 * d2直接相乘的值,有可能是大於9的,進一步處理進位:

for (int i=len1 + len2-1;i>0;--i) {//3.用數組保存中間值2
    temp[i-1] += temp[i] / 10;
    temp[i] %= 10;
}

4.找到tmp中第一個不爲0的位置:while(temp[i]==0) ++i;
5.sring res,然後從i位置開始轉化爲字符串:for(int j=i;j<len1 + len2;++j) res+=temp[j] + '0';

66.加一

1.先把最後一位+1,digits[n-1]=sum%10;int sign=sum/10;如果sign==0,無進位則直接返回digits;
2.有進位則繼續計算數組中前面的各個值,直到sign==0(則直接返回digits)

for(int i=n-2;i>=0 && sign != 0;--i){
    sum=digits[i]+sign;
    digits[i]=sum%10;
    sign=sum/10;
}

3.或者數組遍歷完成,但是此時sign!=0,說明數組遍歷結束此時sign==1,要在數組前面插入一個1,此時需要把數組放到新的內存位置了。

67.二進制求和

1.開一個string,大小爲a,b長度中較大的
2.遍歷兩個string,從後往前相加,別忘了進位標誌位

while(i >= 0 || j >= 0) //遍歷兩個string
{
     int sum = (i >= 0 ? a[i--]-'0' : 0) + (j >= 0 ? b[j--]-'0' : 0 ) + sign;
     res[max(i,j)+1] = (sum % 2) + '0';
     sign = sum/2;
}

3.如果最後標誌位不爲0,要在前面加個1
if (sign) res = "1" + res;

求和、積(還是求和)問題,注意進位標誌位的更新,和數值的統計

1.和:

int sum = (i >= 0 ? a[i--]-'0' : 0) + (j >= 0 ? b[j--]-'0' : 0 ) + sign;//1.求當前和
res[max(i,j)+1] = (sum % 2) + '0';//2.max(i,j)+1位置處放新的值
sign = sum/2;//3.更新標誌位

2.積:temp[i + j + 1] += d1 * d2;//i + j + 1位置處累積

50. Pow(x, n)

1024=(2^5)^2 = ((2^2)^2*2)^2 =((2*2) * (2*2) 2) ((2*2) * (2*2) *2)
1.採用遞歸的方法,double myPow(double x, int n)截止條件如下:

if( n==0 )
  return 1;
if( n==1 )
    return x;

2.遞歸過程中,指數沒次減半int t = n/2;
3.if(n<0) { t = -t;x = 1/x; }
4.遞歸:result = myPow(x,t);
5.最後,如果指數爲偶數,就result * result,否則result * result * x:return (n%2==0)?result*result:result*result*x;

69.x 的平方根

367. 有效的完全平方數

1.牛頓迭代法:
p=x^2;求x
f(x)=x^2-p;找到f(x)的根xn(xn>0),
(1)求f(x)的切線:座標點:(xn,f(xn))
y=f’(xn)(x-xn)+f(xn)=0;
(2)更新:令x=xn+1整理得:
xn+1=xn-f(xn)/f’(xn);=> xn+1=xn-(xn^2-p)/2xn;
=>xn+1=xn/2 + P/2xn; =>r = (r + x/r) / 2
(3)這樣跌待下去就找到了根值xn

int mySqrt(int x) {
    long r = x;//r即爲xn
    while (r*r > x)
        r = (r + x/r) / 2;
    return r;//有效完全平方數則返回即可:return r*r == x;
}

2.二分法:

int mySqrt(int x) {
if(x<=1)    
    return x;
int l=1,r=x;
while(l<=r)//1.二分
{
    int mid=l+(r-l)/2;//2.找到中間位置,防止溢出
    if (mid == x/mid)/3.1/恰好找到,返回
        return mid;
    if (mid < x/mid)//3.2 l=mid+1,防止溢出
        l=mid+1;
    else if (mid > x/mid)//3.3 r=mid-1,防止溢出
        r=mid-1;
}
return r;//因爲l爲需要返回的值時,它是小於根號x的,會右移,而r恰好大於根號x時,會左移,正好得到要返回的值*/
}

二.性質數,公式找規律

231. 判斷2的冪

保證n>0 && n&(n-1)各個位置恰好爲0

 return n > 0 && !(n&(n-1));//n&(n-1)==0說明恰好爲2的冪

326. 判斷3的冪

1.//判斷一個浮點數恰好爲整數,fmod(f,1)==0,即沒有小數部分
2.log10(n)/log10(3)=log3(n) <=> 指數 k=log3(n) <=> n=3^k,
3.log是e爲底的對數;log10是10爲底的對數;但是爲啥用log就不行呢??????

//log3(n)沒有小數部分,log10(n)/log10(3)返回的是浮點數
return fmod(log10(n)/log10(3) , 1) ==0;

258.給一個非負整數 num,反覆添加所有的數字,直到結果只有一個數字

對num一直對9取餘就可以了,但是要注意值得範圍是0 ~ 9,因此 num=9時,返回9
1.(x + y) % z = (x % z + y % z) % z;
2.x % z % z = x % z;
3.對n % 9 做一些變形,注意,這裏已經不是等價變形了,而是:=>
n % 9 = [(n - 1) + 1] % 9 =[ (n - 1) % 9 + 1 % 9 ] % 9 變形=> 1+(n - 1) % 9

return 1 + (num - 1) % 9;//對num % 9做變形

172.階乘後的零

這裏我們要求n!末尾有多少個0,因爲我們知道0是2和5相乘得到的,而在1到n這個範圍內,2的個數要遠多於5的個數,所以這裏只需計算從1到n這個範圍內有多少個5就可以了。
除了計算n/5, 還要計算n/5/5, n/5/5/5, n/5/5/5/5, …, n/5/5/5,,,/5直到商爲0,然後就和,就是最後的結果。

int trailingZeroes(int n) {
    int res=0;
    while(n)//1到n這個範圍內有多少個5^k(k=0,1,...只要5^k<=n)
    {
        res+=n/5;//累積5的個數
        n/=5;//更新n值
    }
    return res;
}

202. 快樂數

對於一個正整數,每一次將該數替換爲它每個位置上的數字的平方和,然後重複這個過程直到這個數變爲 1,也可能是無限循環但始終變不到 1。如果可以變爲 1,那麼這個數就是快樂數。
在一個while(1)循環中:
1.n==1時,返回true;
2.申請一個數組vector<int> ans,ans中不包含n,則在ans中加入n
3.ans中包含n,返回false,(此時說明計算的平方和又回到n值了,說明它不是快樂數)
4.得到每一輪的新的n值

bool isHappy(int n) {
//unordered_map<int,int> ans;//也可以用hash_table
vector<int> ans;
    while(1)
    {
       if(n == 1)//n==1時,返回true
           return true;         
       if(find(ans.begin(), ans.end(), n) == ans.end())//ans中不包含n,則在ans中加入n
           ans.push_back(n);
       else//ans中包含n,返回false
           return false;

       //每一輪得到一個新的n值
       int sum = 0;
       while(n)
       {
           sum += (n % 10) * (n % 10);
           n = n /10;               
       }
       n = sum;
    }
}

223.矩形面積

在二維平面上計算出兩個由直線構成的矩形疊加覆蓋後的面積。
1.先求兩個矩形的總面積
2.把四個橫座標和四個縱座標分別放在數組x和y中,並且在分別x,y中做排序
3.計算中間兩個座標值的差的乘積就是重疊的面積
total = total - (x[2] - x[1]) * (y[2] - y[1]);

268. 缺失數字

相比於[0…n]數組,該數組缺少了一個數,
那麼先計算出[0…n]的數組的和,再減去待計算數組的和,那麼缺少的數就出來了。

int ans=0;
int n=nums.size();
int sum=(1+n)*n/2;//1.0~n的和sum
for(int i=0;i<n;++i)//2.待計算數組的和ans
    ans+=nums[i];
return sum-ans;//3.sum-ans 即爲所求

263.醜數

醜數就是隻包含質因子 2, 3, 5 的正整數。1 也可以被當做醜數。
1.從2開始(2,3,4,5):
2.如果恰好可以整除num則繼續整除,否則下一個(2,3,4,5)
3.最後num恰好爲1,返回true,否則返回false

bool isUgly(int num) {
    for (int i=2; i<6 && num; i++)//從2開始:2,3,4,5,//4可以分解成2*2,不影響,實際在這裏4是不會有機會整除的,因爲2都不行了
        while (num % i == 0)//如果恰好整除,則繼續除
            num /= i;
    return num == 1;//最後num恰好爲1,返回true:說明確實是只包含質因子 2, 3, 5;否則就是false
}

264.醜數 II

編寫程序找第 n 個醜數。例如, 1, 2, 3, 4, 5, 6, 8, 9, 10, 12 就是前10個醜數。1 一般也被當做醜數。

如果我們把所有的醜數依次按升序存儲到一個列表裏面,那麼這個列表後面的某個元素,一定是前面的某個元素乘2、乘3、或乘5得到的;
所以,假設現在我們已知這樣一個列表的一部分,想要往列表裏面繼續添加新元素的話,因爲這個列表已經是排好序的,所以我們可以設定三個指針index2, index3, index5,他們所指向的元素,乘2,乘3,乘5分別爲此時的M2, M3, M5;
每次遍歷結束之後,只需要從這三個指針的位置(含)開始向後掃描即可。DP

int nthUglyNumber(int n) {
    if(n <= 0) 
        return false; 
    if(n == 1) 
        return true; 
    int t2 = 0, t3 = 0, t5 = 0; //pointers for 2, 3, 5
    vector<int> v(n,0);
    v[0] = 1;
    for(int i  = 1; i < n ; i ++)
    {
        v[i] = min(v[t2]*2,min(v[t3]*3,v[t5]*5));//找到2,3,5上一位置處 *2,3,5 後最小的數
        if(v[i] == v[t2]*2) t2++; 
        if(v[i] == v[t3]*3) t3++;
        if(v[i] == v[t5]*5) t5++;
    }
    return v[n-1];
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章