2020年6月 leetcode每日一題 C語言版本

1 擁有最多糖果的孩子

2020-6-1

給你一個數組 candies 和一個整數 extraCandies ,其中 candies[i] 代表第 i 個孩子擁有的糖果數目。對每一個孩子,檢查是否存在一種方案,將額外的 extraCandies 個糖果分配給孩子們之後,此孩子有 最多 的糖果。注意,允許有多個孩子同時擁有 最多 的糖果數目。

示例 1:
輸入:candies = [2,3,5,1,3], extraCandies = 3
輸出:[true,true,true,false,true]
解釋:
孩子 1 有 2 個糖果,如果他得到所有額外的糖果(3個),那麼他總共有 5 個糖果,他將成爲擁有最多糖果的孩子。
孩子 2 有 3 個糖果,如果他得到至少 2 個額外糖果,那麼他將成爲擁有最多糖果的孩子。
孩子 3 有 5 個糖果,他已經是擁有最多糖果的孩子。
孩子 4 有 1 個糖果,即使他得到所有額外的糖果,他也只有 4 個糖果,無法成爲擁有糖果最多的孩子。
孩子 5 有 3 個糖果,如果他得到至少 2 個額外糖果,那麼他將成爲擁有最多糖果的孩子。

示例 2:
輸入:candies = [4,2,1,1,2], extraCandies = 1
輸出:[true,false,false,false,false]
解釋:只有 1 個額外糖果,所以不管額外糖果給誰,只有孩子 1 可以成爲擁有糖果最多的孩子。

示例 3:
輸入:candies = [12,1,12], extraCandies = 10
輸出:[true,false,true]

提示:

2 <= candies.length <= 100
1 <= candies[i] <= 100
1 <= extraCandies <= 50

/**
 * Note: The returned array must be malloced, assume caller calls free().
 */
bool* kidsWithCandies(int* candies, int candiesSize, int extraCandies, int* returnSize){

    bool* ans=(bool*)malloc(candiesSize*sizeof(bool));
    *returnSize=0;
    int max=0;
    for(int i=0;i<candiesSize;i++){
        if(candies[i]>max)max=candies[i];
    }
    for(int i=0;i<candiesSize;i++){
        if(candies[i]+extraCandies>=max)ans[*returnSize]=true;
        else ans[*returnSize]=false;
        (*returnSize)++;
    }
    return ans;
}

兒童節快樂☺ ♪ ☑

2 求1+2+…+n

2020-6-2

求 1+2+…+n ,要求不能使用乘除法、for、while、if、else、switch、case等關鍵字及條件判斷語句(A?B:C)

示例 :
輸入: n = 9
輸出: 45

限制:
1 <= n <= 10000

可以使用的工具:加減法,賦值,位運算符以及邏輯運算符

遞歸

int sumNums(int n){
    if(n==0)return 0;
    return n+sumNums(n-1);
}

被限制的方向:遞歸的條件判斷

邏輯運算符的短路性質

以邏輯運算符 &&\&\& 爲例,對於 A&&BA\&\&B 這個表達式,如果 A 表達式返回 FalseFalse,那麼 A&&BA \&\& B 已經確定爲 FalseFalse ,此時不會去執行表達式 B。同理,對於邏輯運算符 ||, 對於 A || B 這個表達式,如果 A 表達式返回 TrueTrue ,那麼 A || B 已經確定爲TrueTrue ,此時不會去執行表達式 B。

int sumNums(int n){
    n&&(n+=sumNums(n-1));
    return n;
}

時間複雜度:O(n)O(n)
空間複雜度:O(n)O(n)

函數指針+兩次反運算

typedef unsigned int (*fun)(unsigned int);
unsigned int Teminator(unsigned int n){
    return 0;
}

int sumNums(int n){
    static fun f[2]={Teminator,sumNums};
    return n+f[!!n](n-1);  //連續做兩次反運算,非零轉換爲true,0轉換爲false

}

快速乘

使用加法和移位運算
乘以2的冪
遍歷二進制展開

俄羅斯農民乘法

int quickMulti(int A, int B) {
    int ans = 0;
    for ( ; B; B >>= 1) {
        if (B & 1) {
            ans += A;
        }
        A <<= 1;
    }
    return ans;
}

手動展開 1414 層代替循環

int sumNums(int n){

    int ans = 0, A = n, B = n + 1;

    (B & 1) && (ans += A);
    A <<= 1;  //右移一位 乘2
    B >>= 1;  //左移一位 除2

    (B & 1) && (ans += A);
    A <<= 1;
    B >>= 1;

    (B & 1) && (ans += A);
    A <<= 1;
    B >>= 1;

    (B & 1) && (ans += A);
    A <<= 1;
    B >>= 1;

    (B & 1) && (ans += A);
    A <<= 1;
    B >>= 1;

    (B & 1) && (ans += A);
    A <<= 1;
    B >>= 1;

    (B & 1) && (ans += A);
    A <<= 1;
    B >>= 1;

    (B & 1) && (ans += A);
    A <<= 1;
    B >>= 1;

    (B & 1) && (ans += A);
    A <<= 1;
    B >>= 1;

    (B & 1) && (ans += A);
    A <<= 1;
    B >>= 1;

    (B & 1) && (ans += A);
    A <<= 1;
    B >>= 1;

    (B & 1) && (ans += A);
    A <<= 1;
    B >>= 1;

    (B & 1) && (ans += A);
    A <<= 1;
    B >>= 1;

    (B & 1) && (ans += A);
    A <<= 1;
    B >>= 1;

    return ans >> 1;
}

回顧

  • 遞歸函數的空間複雜度取決於遞歸調用棧的深度

3 新21點

2020-6-3

愛麗絲參與一個大致基於紙牌遊戲 “21點” 規則的遊戲,描述如下:
愛麗絲以 0 分開始,並在她的得分少於 K 分時抽取數字。 抽取時,她從 [1, W] 的範圍中隨機獲得一個整數作爲分數進行累計,其中 W 是整數。 每次抽取都是獨立的,其結果具有相同的概率。當愛麗絲獲得不少於 K 分時,她就停止抽取數字。 愛麗絲的分數不超過 N 的概率是多少?

示例 1:
輸入:N = 10, K = 1, W = 10
輸出:1.00000

示例 2:
輸入:N = 6, K = 1, W = 10
輸出:0.60000

示例 3:
輸入:N = 21, K = 17, W = 10
輸出:0.73278

提示:
0 <= K <= N <= 10000
1 <= W <= 10000
如果答案與正確答案的誤差不超過 10^-5,則該答案將被視爲正確答案通過。

21點:荷官發牌,你的目的是拿到手上的牌總點數儘可能大。不高於某個值(K)的時候你可以一直叫牌,但萬一牌的總點數超過某個值(N)你就爆牌了,此局判負。

那麼本題就是在求贏的概率

遞歸 超時

double new21Game(int N, int K, int W){
    double result = 0;
    if(K == 0) {
        return 1.0;
    }
    for(int i=1; i<W+1; i++) {
        if(i < K) {
            double sub = new21Game(N-i, K-i, W);
            result += (1/(double)W) * sub;
        } else {
            if(i <= N) {
                result += 1/(double)W;
            }
        }
    }
    return result;
}

動態規劃

dp[x]dp[x]表示從得分爲 xx 的情況開始遊戲並且獲勝的概率,目標是求 dp[0]dp[0] 的值
狀態轉移方程:
dp[x]=dp[x+1]+dp[x+2]++dp[x+W]Wdp[x]=\frac{dp[x+1]+dp[x+2]+⋯+dp[x+W]}{W}

停止抽牌時,最大牌面爲K+W1K+W-1

double new21Game(int N, int K, int W){
    double s=0.0;
    double *dp=(double*)malloc((K+W)*sizeof(double));

    for(int i = K;i < K + W;i++){
        if(i <= N){
            dp[i]=1;
        }else{
            dp[i]=0;
        }  
        s=s+dp[i];   
    }
    for(int i = K-1;i >= 0;i--){
        dp[i]=s/W;
        s = s-dp[i + W]+dp[i];
    }
    return dp[0];
}

時間複雜度:O(min(N,K+W))O(\min(N, K+W))
空間複雜度:O(K+W)O(K+W)

4 除自身以外數組的乘積

2020-6-4

給你一個長度爲 n 的整數數組 nums,其中 n > 1,返回輸出數組 output ,其中 output[i] 等於 nums 中除 nums[i] 之外其餘各元素的乘積。

示例:
輸入: [1,2,3,4]
輸出: [24,12,8,6]

提示:題目數據保證數組之中任意元素的全部前綴元素和後綴(甚至是整個數組)的乘積都在 32 位整數範圍內。
說明: 請不要使用除法

當前數左邊乘積*當前數右邊乘積

/**
 * Note: The returned array must be malloced, assume caller calls free().
 */
int* productExceptSelf(int* nums, int numsSize, int* returnSize){
    *returnSize=0;
    int *L=(int *)malloc(numsSize*sizeof(int));
    int *R=(int *)malloc(numsSize*sizeof(int));
    int *ans=(int *)malloc(numsSize*sizeof(int));
    L[0]=1;
    R[numsSize-1]=1;
    for(int i=1;i<numsSize;i++){
        L[i]=L[i-1]*nums[i-1];
    }
    for(int i=numsSize-2;i>=0;i--){
        R[i]=R[i+1]*nums[i+1];
    }
    for(int i=0;i<numsSize;i++){
        ans[i]=L[i]*R[i];
        (*returnSize)++;
    }
    return ans;
}

時間複雜度:O(n)O(n)
空間複雜度:O(n)O(n)

/**
 * Note: The returned array must be malloced, assume caller calls free().
 */
int* productExceptSelf(int* nums, int numsSize, int* returnSize){
    *returnSize=0;
    int *L=(int *)malloc(numsSize*sizeof(int));
    L[0]=1;
    int R=1;
    for(int i=1;i<numsSize;i++){
        L[i]=L[i-1]*nums[i-1];
    }
    for(int i=numsSize-1;i>=0;i--){   
        L[i]=L[i]*R;
        R=R*nums[i];
        (*returnSize)++;
    }
    return L;
}

5 順時針打印矩陣

2020-6-5

輸入一個矩陣,按照從外向裏以順時針的順序依次打印出每一個數字。

示例 :
輸入:matrix = [[1,2,3,4],[5,6,7,8],[9,10,11,12]]
輸出:[1,2,3,4,8,12,11,10,9,5,6,7]

限制:
0 <= matrix.length <= 100
0 <= matrix[i].length <= 100

/**
 * Note: The returned array must be malloced, assume caller calls free().
 */
int* spiralOrder(int** matrix, int matrixSize, int* matrixColSize, int* returnSize){
    
    int m=matrixSize;   //行
    *returnSize=0;
    if(m==0)return NULL;
    int n=matrixColSize[0];   //列
    int *ans=(int *)malloc((m*n)*sizeof(int));
 
    int top=0,bottom=m-1,left=0,right=n-1;
    while(left<=right&&top<=bottom){
        for (int column = left; column <= right; column++) {  //上
            ans[(*returnSize)++] = matrix[top][column];
        }
        for (int row = top + 1; row <= bottom; row++) {   //右
            ans[(*returnSize)++] = matrix[row][right];
        }
        if (left < right && top < bottom) {   
            for (int column = right - 1; column > left; column--) {   //下
                ans[(*returnSize)++]= matrix[bottom][column];
            }
            for (int row = bottom; row > top; row--) {   //左
                ans[(*returnSize)++] = matrix[row][left];
            }
        }
        left++;
        right--;
        top++;
        bottom--;
    }
    return ans;
}

時間複雜度:O(mn)O(mn)
空間複雜度:O(1)O(1)

6 最長連續序列

2020-6-6

給定一個未排序的整數數組,找出最長連續序列的長,要求算法的時間複雜度爲 O(n)。

示例:
輸入: [100, 4, 200, 1, 3, 2]
輸出: 4
解釋: 最長連續序列是 [1, 2, 3, 4]。它的長度爲 4。

哈希

  1. 用哈希表查找,查找的時間複雜度爲O(1)O(1)
  2. 鏈表哈希,修改插入函數使之不插入重複數字
  3. 哈希表直接存入數組中的數字
  4. 爲減少枚舉,加入跳過判斷條件,即:對於當前數xx,若x1x-1不存在,則此xx爲子序列的第一個數,進入循環,進行對x+kx+k的判斷。由此,數組中的每個數只會進入內層循環一次,則整體的時間複雜度爲O(n)O(n)
struct hashNode {
    int key;
    struct hashNode *next;//衝突時,用指針連接
};

struct hashMap {
    int size;
    struct hashNode *data;
};

struct hashMap * hashCreat(int size)
{
    //新建一個哈希表
    struct hashMap * hashMapInfo;
    //爲哈希表分配地址
    hashMapInfo = (struct hashMap *)malloc(sizeof(struct hashMap));
    //設置哈希表的大小
    hashMapInfo->size = size;
    //爲哈希表的data部分分配地址
    hashMapInfo->data = (struct hashNode *)malloc(sizeof(struct hashNode) * size);
    //初始化哈希表每個哈希節點的對象
    for (int i = 0; i < size; i++) {
        hashMapInfo->data[i].key = INT_MIN;
        hashMapInfo->data[i].next = NULL;
    }
    return hashMapInfo;
}

//將鍵爲key的節點放入到哈希表中
void Put(int key, struct hashMap * hashMapInfo)
{
    //計算節點的位置
    int pos = abs(key) % hashMapInfo->size;
    //定義哈希節點,其值爲 key所在位置的數據的鏈表地址
    struct hashNode *data = &hashMapInfo->data[pos];
    //定義一個新的哈希節點,爲空
    struct hashNode *newNode = NULL;

    if (data->key == INT_MIN) {  //鏈表未佔用
        data->key = key;
        return;
    }
    
    while (data != NULL) {//while循環要考慮當前節點,不能從data->next開始
        if (data->key == key) { //當前鏈表已存在該key,直接返回不插入
            return;
        } 
        if (data->next == NULL) { //爲後續添加Node做準備
            break;
        }
        data = data->next; //鏈表next節點
    }

    /* 添加節點 */
    newNode = (struct hashNode *)malloc(sizeof(struct hashNode));
    newNode->key = key;
    newNode->next = NULL;
    data->next = newNode;

    return;
}

//查找
bool Contain(long key, struct hashMap * hashMapInfo)
{
    //計算節點的位置
    int pos;
    if(key==INT_MIN)pos = abs(INT_MAX) % hashMapInfo->size;
    else pos = abs(key) % hashMapInfo->size;
    //定義哈希節點,其值爲 key所在位置的數據的鏈表地址
    struct hashNode *data = &hashMapInfo->data[pos];

    if (data->key == INT_MIN) {  //鏈表未佔用
        return false;
    }

    while (data != NULL) {//while循環要考慮當前節點,不能從data->next開始
        if (data->key == key) { //當前鏈表已存在該key
            return true;
        } 
        data = data->next; //鏈表next節點
    }
    return false;
}


int longestConsecutive(int* nums, int numsSize){
    struct hashMap *hashMapInfo;
    hashMapInfo=hashCreat(numsSize);
    for(int i=0;i < numsSize;i++) {
        //printf("TTTT\n");
        Put(nums[i], hashMapInfo);
    }

    int longestStreak = 0;

    for (int i=0;i<numsSize;i++) {
        if (!Contain(nums[i]-1,hashMapInfo)) { //前一個數不存在,那我此數即爲序列第一個
            int currentNum = nums[i];
            int currentStreak = 1;

            while (Contain(currentNum + 1,hashMapInfo)) {
                currentNum += 1;
                currentStreak += 1;
            }
            longestStreak = fmax(longestStreak, currentStreak);
        }
    }

    return longestStreak;
}

並查集

https://leetcode-cn.com/problems/longest-consecutive-sequence/solution/bing-cha-ji-ting-qi-lai-nan-shi-ji-yi-dian-du-bu-n/

下次一定

回顧

  1. 使用了空指針
Line 35: Char 37: runtime error: member access within null pointer of type 'struct hashMap' (solution.c)
  1. 取絕對值越界
Line 72: Char 15: runtime error: negation of -2147483648 cannot be represented in type 'int'; cast to an unsigned type to negate this value to itself (solution.c)

7 等式方程的可滿足性

2020-6-8

給定一個由表示變量之間關係的字符串方程組成的數組,每個字符串方程 equations[i] 的長度爲 4,並採用兩種不同的形式之一:“a==b” 或 “a!=b”。在這裏,a 和 b 是小寫字母(不一定不同),表示單字母變量名。只有當可以將整數分配給變量名,以便滿足所有給定的方程時才返回 true,否則返回 false。

示例 1:
輸入:[“a==b”,“b!=a”]
輸出:false
解釋:如果我們指定,a = 1 且 b = 1,那麼可以滿足第一個方程,但無法滿足第二個方程。沒有辦法分配變量同時滿足這兩個方程。

示例 2:
輸出:[“b==a”,“a==b”]
輸入:true
解釋:我們可以指定 a = 1 且 b = 1 以滿足滿足這兩個方程。

示例 3:
輸入:[“a==b”,“b==c”,“a==c”]
輸出:true

示例 4:
輸入:[“a==b”,“b!=c”,“c==a”]
輸出:false

示例 5:
輸入:[“c==c”,“b==d”,“x!=z”]
輸出:true

提示:
1 <= equations.length <= 500
equations[i].length == 4
equations[i][0] 和 equations[i][3] 是小寫字母
equations[i][1] 要麼是 ‘=’,要麼是 ‘!’
equations[i][2] 是 ‘=’

並查集

並查集

#define MAXN 26
int fa[MAXN];

//初始化
void init(int n)
{
    for (int i = 0; i < n; ++i){
        fa[i] = i;
    }     
}

//查詢
int find(int x)
{
    if(fa[x] == x)
        return x;
    else
        return find(fa[x]);
}

//合併
void merge(int i, int j)
{
    fa[find(i)] = find(j);
}

bool equationsPossible(char ** equations, int equationsSize){
    
    init(MAXN);

    for(int i=0;i<equationsSize;i++){
        if(equations[i][1]=='='){
            int index1=equations[i][0]-'a';
            int index2=equations[i][3]-'a';
            merge(index1,index2);
        }
    }

    for(int i=0;i<equationsSize;i++){
        if(equations[i][1]=='!'){
            int index1=equations[i][0]-'a';
            int index2=equations[i][3]-'a';
            if (find(index1) == find(index2)) {
                return false;
            }
        }
    }
    
    return true;
}

8 把數字翻譯成字符串

2020-6-9

給定一個數字,我們按照如下規則把它翻譯爲字符串:0 翻譯成 “a” ,1 翻譯成 “b”,……,11 翻譯成 “l”,……,25 翻譯成 “z”。一個數字可能有多個翻譯。請編程實現一個函數,用來計算一個數字有多少種不同的翻譯方法。

示例 1:
輸入: 12258
輸出: 5
解釋: 12258有5種不同的翻譯,分別是"bccfi", “bwfi”, “bczi”, “mcfi"和"mzi”

提示:
0<=num<2310 <= num < 2^{31}

動態規劃

dp[i]dp[i]xix_i爲開頭的數字的翻譯方案數量
dp[i]=dp[i+1]+g(i,i+1)dp[i+2]dp[i]=dp[i+1]+g(i,i+1)*dp[i+2]

從後往前計算

int translateNum(int num){
    if(num<0)return 0;
    if(num==0)return 1;
    int count[11];  
    int n=0;  //位數

    //從低位到高位存儲數字中每一位數
    int number[11];
    int temp=num,i=0;
    while(temp){
        int complement=temp%10;
        temp=temp/10;
        n++;
        number[i++]=complement;
    }
    //翻轉,從高位到低位
    int Number[11];
    for(int i=0;i<n;i++){
        Number[i]=number[n-i-1];
    }

    if(n==1)return 1;  //個位數直接輸出

    //計算後兩位
    count[n-1]=1;
    int two=Number[n-2]*10+Number[n-1];
    if((two>=10)&&(two<=25))count[n-2]=2;
    else count[n-2]=1;

    for(int i=n-3;i>=0;i--){
        two=Number[i]*10+Number[i+1];
        int g;
        if((two>=10)&&(two<=25))g=1;
        else g=0;
        if(i<n-1){
            count[i]=count[i+1]+g*count[i+2];
        }    
    }
    return count[0];
}

dp[i]dp[i]xix_i爲結尾的數字的翻譯方案數量

dp[i]=dp[i1]+g(i,i1)dp[i2]dp[i] = dp[i - 1] + g(i,i-1)*dp[i - 2]

初始狀態: dp[0]=dp[1]=1dp[0] = dp[1] = 1

返回值: dp[n]dp[n]

int translateNum(int num){
    if(num<0)return 0;
    if(num==0)return 1;
    int count[11];  
    int n=0;  //位數

    //從低位到高位存儲數字中每一位數
    int number[11];
    int temp=num,i=0;
    while(temp){
        int complement=temp%10;
        temp=temp/10;
        n++;
        number[i++]=complement;
    }
    //翻轉,從高位到低位
    int Number[11];
    for(int i=0;i<n;i++){
        Number[i]=number[n-i-1];
    }

    if(n==1)return 1;  //個位數直接輸出

    count[0]=1;
    int two=Number[0]*10+Number[1];
    if((two>=10)&&(two<=25))count[1]=2;
    else count[1]=1;
    
    for(int i=2;i<n;i++){
        int two=Number[i]+Number[i-1]*10;
        int g;
        if((two>=10)&&(two<=25))g=1;
        else g=0;
        count[i]=count[i-1]+g*count[i-2];  
    }
    return count[n-1];
}

邊取餘邊計算
dp[i]=dp[i+1]+g(i,i+1)dp[i+2]dp[i]=dp[i+1]+g(i,i+1)*dp[i+2]

dp[i]dp[i]xix_i開頭的數字的翻譯方案數量,從後往前計算

int translateNum(int num){
    if(num<0)return 0;
    if(num==0)return 1;

    int dp0=1;  //dp[i]
    int dp1=1;  //dp[i+1]
    int dp2=1;   //dp[i+2]
 
    int n1;  //第i位
    int n2=num%10;  //第i+1位
    
    while(num){
        num=num/10;
        n1=num%10;
        int two=10*n1+n2;
        if((two>=10)&&(two<=25))dp0=dp1+dp2;
        else dp0=dp1;
        //向左移動
        dp2=dp1;
        dp1=dp0;
        n2=n1;  
    }
    return dp0;  
}

9 迴文數

2020-6-10

判斷一個整數是否是迴文數。迴文數是指正序(從左向右)和倒序(從右向左)讀都是一樣的整數。

示例 1:
輸入: 121
輸出: true

示例 2:
輸入: -121
輸出: false
解釋: 從左向右讀, 爲 -121 。 從右向左讀, 爲 121- 。因此它不是一個迴文數。

轉化爲數組

bool isPalindrome(int x){
    if(x==0)return true;
    if(x<0)return false;
    int num[12],n=0,i=0;
    while(x){
        num[i]=x%10;
        x=x/10;
        n++;
        i++;
    }
    for(int j=0,k=n-1;j<=k;j++,k--){
        if(num[j]!=num[k])return false;
    }
    return true;
}

反轉一半數字 厲害

bool isPalindrome(int x){
    if(x == 0)return true;
    if (x < 0 || (x % 10 == 0 && x != 0)) {
        return false;
    }

    int revertedNumber = 0;
    while (x > revertedNumber) {
        revertedNumber = revertedNumber * 10 + x % 10;
        x = x / 10;
    }

    // 當數字長度爲奇數時,我們可以通過 revertedNumber/10 去除處於中位的數字。
    // 例如,當輸入爲 12321 時,在 while 循環的末尾我們可以得到 x = 12,revertedNumber = 123,
    // 由於處於中位的數字不影響迴文(它總是與自己相等),所以我們可以簡單地將其去除。
    return x == revertedNumber || x == revertedNumber / 10;
}

10 迴文鏈表

2020-6-10

請判斷一個鏈表是否爲迴文鏈表,用 O(n) 時間複雜度和 O(1) 空間複雜度

翻轉鏈表+快慢指針

/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     struct ListNode *next;
 * };
 */

bool isPalindrome(struct ListNode* head){
    if(!head || !head->next)
        return true;
    struct ListNode *fast = head, *slow = head;
    struct ListNode *p, *pre = NULL;
    while(fast && fast->next){
        p = slow;
        slow = slow->next;    //快慢遍歷
        fast = fast->next->next;
        p->next = pre;  //翻轉
        pre = p;
        //printf("pre=%d,p=%d,slow=%d,fast=%d\n",pre->val,p->val,slow->val,fast->val);
    }
    if(fast)  //奇數個節點時跳過中間節點
        slow = slow->next;

    while(p){       //前半部分和後半部分比較
        if(p->val != slow->val)
            return false;
        p = p->next;
        slow = slow->next;
    }
    return true;
}

翻轉鏈表

void reverse(struct ListNode* head){
    struct ListNode *h=head;
    while(h!=NULL){
        printf("%d  ",h->val);
        h=h->next;
    }
    printf("\n");
    struct ListNode *temp = head;
    struct ListNode *p, *pre = NULL;
    while(temp!=NULL){
        if(pre!=NULL)printf("pre=%d ",pre->val);
        if(p!=NULL)printf("p=%d ",p->val);
        if(temp!=NULL)printf("temp=%d ",temp->val);
        printf("\n");
        p = temp;
        temp=temp->next;
        p->next = pre;  //翻轉
        pre = p; 
        
    }
    while(p!=NULL){
        printf("%d  ",p->val);
        p=p->next;
    }
    while(pre!=NULL){
        printf("%d  ",pre->val);
        pre=pre->next;
    }
}

在這裏插入圖片描述

11 每日溫度

2020-6-11

根據每日氣溫列表,請重新生成一個列表,對應位置的輸出是需要再等待多久溫度纔會升高超過該日的天數。如果之後都不會升高,請在該位置用 0 來代替。

例如,給定一個列表 temperatures = [73, 74, 75, 71, 69, 72, 76, 73],你的輸出應該是 [1, 1, 4, 2, 1, 1, 0, 0]。

提示:氣溫 列表長度的範圍是 [1, 30000]。每個氣溫的值的均爲華氏度,都是在 [30, 100] 範圍內的整數。

暴力超時

/**
 * Note: The returned array must be malloced, assume caller calls free().
 */
int* dailyTemperatures(int* T, int TSize, int* returnSize){
    *returnSize=0;
    int *ans=(int *)malloc(TSize*sizeof(int));
    for(int i=0;i<TSize;i++){
        int num=0;
        for(int j=i+1;j<TSize;j++){
            if(T[j]>T[i]){
                num=j-i;
                break;
            }
        }
        ans[i]=num;
        (*returnSize)++;
    }
    return ans;
}

時間複雜度:O(n2)O(n^2)
空間複雜度:O(1)O(1)

暴力不超時

  • 反向遍歷:從後往前依次將各個溫度的下標存入next數組,對於每個溫度,查找next數組中比他大的溫度中角標最小的,相減即爲結果
  • 因爲是從後往前,所以查找的一定是當前溫度後面的溫度,√
  • 結果數組初始化爲零,當沒有找到比該溫度大的溫度,使用初始值
/**
 * Note: The returned array must be malloced, assume caller calls free().
 */
int* dailyTemperatures(int* T, int TSize, int* returnSize){
    *returnSize=0;
    
    int *ans=(int *)malloc(TSize*sizeof(int));
    for(int s=0;s<TSize;s++){
        ans[s]=0;
    }
    int next[101]={0};
    for(int i=0;i<101;i++){
        next[i]+=INT_MAX;
    }

    for (int i = TSize - 1; i >= 0; --i) {
        int warmerIndex = INT_MAX;
        for (int t = T[i] + 1; t <= 100; ++t) {
            warmerIndex = fmin(warmerIndex, next[t]);
        }
        if (warmerIndex != INT_MAX) {
            ans[i] = warmerIndex - i;
        }
        next[T[i]] = i;
        (*returnSize)++;
    }
    return ans;
}

時間複雜度:O(nm)O(nm)
空間複雜度:O(m)O(m)

單調棧

  • 棧內存下標
  • 從棧底到棧頂的下標對應的溫度列表中的溫度依次遞減
  • 棧爲空或者當前元素小於棧頂元素,進棧
  • 當前元素大於棧頂元素,出棧,計算棧頂元素與當前元素之差即爲所求,一直出棧知道棧頂元素大於當前元素或者棧爲空,進棧
#define MaxSize 30000
typedef struct Stack{
    int top;
    int num[MaxSize];
}Stack;

Stack S;

void init(){
    S.top=0;
}

void push(int a){
    S.top++;
    S.num[S.top]=a;
}

int pop(){
    if(S.top==0)return 0;
    S.top--;
    return S.num[S.top+1];
}

int Top(){
    return S.num[S.top];
}

int empty(){
    if(S.top==0)return 1;
    else return 0;
}
/**
 * Note: The returned array must be malloced, assume caller calls free().
 */
int* dailyTemperatures(int* T, int TSize, int* returnSize){
    init();
    *returnSize=0;
    int *ans=(int *)malloc(TSize*sizeof(int));
    for(int s=0;s<TSize;s++){
        ans[s]=0;
    }
    for(int i=0;i<TSize;i++){
        while(!empty()&&T[Top()]<T[i]){
            int index=Top();
            ans[index]=i-index;
            pop();
        }
        push(i);
        (*returnSize)++;
    }
    return ans;
}

時間複雜度:O(n)O(n)
空間複雜度:O(n)O(n)

回顧

我初始化數組時像個人工智障 稍後再看

//數組初始化實在是頭疼
    int *ans=(int *)malloc(TSize*sizeof(int));
    for(int s=0;s<TSize;s++){
        ans[s]=0;
    }
    int next[101]={0};
    for(int i=0;i<101;i++){
        next[i]+=INT_MAX;
    }

12 四數之和

2020-6-12

三數之和之前搞過了,整個類似的四數之和回憶一下:D

給定一個包含 n 個整數的數組 nums 和一個目標值 target,判斷 nums 中是否存在四個元素 a,b,c 和 d ,使得 a + b + c + d 的值與 target 相等,找出所有滿足條件且不重複的四元組。答案中不可以包含重複的四元組。

示例:
給定數組 nums = [1, 0, -1, 0, -2, 2],和 target = 0。
滿足要求的四元組集合爲:
[[-1, 0, 0, 1],[-2, -1, 1, 2],[-2, 0, 0, 2]]

雙指針

/**
 * Return an array of arrays of size *returnSize.
 * The sizes of the arrays are returned as *returnColumnSizes array.
 * Note: Both returned array and *columnSizes array must be malloced, assume caller calls free().
 */
int compInc(const void *a, const void *b)   //遞增
{
    return *(int *)a - *(int *)b;
}
int** fourSum(int* nums, int numsSize, int target, int* returnSize, int** returnColumnSizes){
    *returnSize = 0;   // 參數returnSize用來作爲二維數組行數下標的指針
    if (numsSize < 4)
    {
        return NULL;
    }
    qsort(nums, numsSize, sizeof(int), compInc);
    int maxSize=numsSize*10;
    int** returnArray = (int**)malloc(sizeof(int*) * maxSize);
    *returnColumnSizes = (int*)malloc(sizeof(int) * maxSize);

    for(int i=0;i<numsSize-3;i++){
        for(int j=i+1;j<numsSize-2;j++){          
            int L=j+1,R=numsSize-1;
            while(L<R){
                int sum=nums[i]+nums[j]+nums[L]+nums[R];
                if(sum==target){
                    returnArray[*returnSize] = (int*)malloc(sizeof(int) * 4); 
                    (*returnColumnSizes)[*returnSize] = 4;
                    returnArray[*returnSize][0] = nums[i];
                    returnArray[*returnSize][1] = nums[j];
                    returnArray[*returnSize][2] = nums[L];
                    returnArray[*returnSize][3] = nums[R];
                    (*returnSize)++;
                    while (nums[L + 1] == nums[L] &&(L+1)< R)
                    {
                        L++;
                    }
                    while (nums[R - 1] == nums[R] && L < (R-1))
                    {
                        R--;
                    }
                    L++;
                    R--;
                }
                else if(sum<target){
                    L++;
                }
                else if(sum>target){
                    R--;
                }
            }
            while(nums[j]==nums[j+1]&&j<numsSize-2){
            j++;
        }
        }
        while(nums[i]==nums[i+1]&&i<numsSize-3){
            i++;
        }
    }
    return returnArray;
}

時間複雜度:O(n3)O(n^3)

13 使用最小花費爬樓梯

2020-6-13

爬樓梯

假設你正在爬樓梯。需要 n 階你才能到達樓頂。每次你可以爬 1 或 2 個臺階。你有多少種不同的方法可以爬到樓頂呢?
dp[i]=dp[i1]+dp[i2]dp[i]=dp[i-1]+dp[i-2]

int climbStairs(int n){
    if(n==1)return 1;
    if(n==2)return 2;
    int dp1=1,dp2=2,dp=0;
    for(int i=3;i<=n;i++){
        //printf("dp1= %d dp2= %d dp= %d\n",dp1,dp2,dp);
        dp=dp1+dp2;
        dp1=dp2;
        dp2=dp;
    }
    return dp;
}

使用最小花費爬樓梯

數組的每個索引作爲一個階梯,第 i個階梯對應着一個非負數的體力花費值 cost[i]cost[i](索引從0開始)。每當你爬上一個階梯你都要花費對應的體力花費值,然後你可以選擇繼續爬一個階梯或者爬兩個階梯。您需要找到達到樓層頂部的最低花費。在開始時,你可以選擇從索引爲 0 或 1 的元素作爲初始階梯。

示例 1:
輸入: cost = [10, 15, 20]
輸出: 15
解釋: 最低花費是從cost[1]開始,然後走兩步即可到階梯頂,一共花費15。

示例 2:
輸入: cost = [1, 100, 1, 1, 1, 100, 1, 1, 100, 1]
輸出: 6
解釋: 最低花費方式是從cost[0]開始,逐個經過那些1,跳過cost[3],一共花費6。

注意:
cost 的長度將會在 [2, 1000]。
每一個 cost[i] 將會是一個Integer類型,範圍爲 [0, 999]。

動態規劃

dp[i]=cost[i]+min(dp[i+1],dp[i+2])dp[i] = cost[i] + min(dp[i+1], dp[i+2])

dp[i]dp[i]表示從第ii個階梯開始爬並經過第ii個階梯,需要的最小花費

int minCostClimbingStairs(int* cost, int costSize){
    int dp1 = 0, dp2 = 0;
    for (int i = costSize - 1; i >= 0; --i) {
        int dp0 = cost[i] + fmin(dp1, dp2);
        dp2 = dp1;
        dp1 = dp0;
    }
    return fmin(dp1, dp2);
}

dp[i]=cost[i]+min(dp[i1],dp[i2])dp[i] = cost[i] + min( dp[i-1] , dp[i-2] )

dp[i]dp[i]表示第 ii 級階梯的總花費 = 第 ii 級的costcost + 前兩級階梯的總花費的較小者

//dp[i] = cost[i] + min(dp[i-1], dp[i-2])
int minCostClimbingStairs(int* cost, int costSize){
    int dp1 = 0, dp2 = 0;
    for (int i = 0; i < costSize; i++) {
        int dp0 = cost[i] + fmin(dp1, dp2);
        dp2 = dp1;
        dp1 = dp0;
    }
    return fmin(dp1, dp2);
}

dp[n]=min(dp[n1]+cost[n1],dp[n2]+cost[n2])dp[n] = min(dp[n-1] + cost[n-1], dp[n-2] + cost[n-2])

初始可以選擇第0或第1個階梯,所以dp[0]=0dp[1]=0dp[0]=0,dp[1]=0,題目就是要求出dp[costSize]dp[costSize]

int minCostClimbingStairs(int* cost, int costSize){
    int *dp=(int *)malloc((costSize+1)*sizeof(int));
    dp[0]=0;
    dp[1]=0;
    for (int i = 2; i < costSize+1; i++) {
        dp[i]= fmin(dp[i-2]+ cost[i-2], dp[i-1] + cost[i-1]);
    }
    return dp[costSize];
}

14 最長公共前綴

2020-6-15

編寫一個函數來查找字符串數組中的最長公共前綴。

如果不存在公共前綴,返回空字符串 “”。

示例 1:
輸入: [“flower”,“flow”,“flight”]
輸出: “fl”

示例 2:
輸入: [“dog”,“racecar”,“car”]
輸出: “”
解釋: 輸入不存在公共前綴。

char * longestCommonPrefix(char ** strs, int strsSize){
    
    int n=10000;
    for(int i=0;i<strsSize;i++){
        if(n>strlen(strs[i]))n=strlen(strs[i]);
    }
    char *ans=(char *)malloc((n+1)*sizeof(char));
    int s=0;
    if(strsSize==0)
    {
        ans[s]='\0';
        return ans;
    }
    for(int j=0;j<n;j++){
        for(int k=0;k<strsSize-1;k++){
            if(strs[k][j]!=strs[k+1][j]){
                ans[s]='\0';
                return ans;
            }
        }
        ans[s]=strs[0][j];
        s++;
    }
    ans[s]='\0';
    return ans;
}

官方題解給了四種方法:縱向、橫向、分治、二分,但從解題角度來講,縱向就挺好,又快。還有一個字典樹,下次一定。

15 二叉樹的序列化與反序列化

2020-6-16

在這裏插入圖片描述

  • 節點數值之間用逗號隔開

遞歸 前序遍歷

/**
 * Definition for a binary tree node.
 * struct TreeNode {
 *     int val;
 *     struct TreeNode *left;
 *     struct TreeNode *right;
 * };
 */
/** Encodes a tree to a single string. */
#define MAX_SIZE 100000
#define STR_SIZE 10

void pre_order(struct TreeNode *root, char * data){  //前序遍歷
    if(root == NULL){
        strcat(data, "#");
        strcat(data, ",");
        return;
    }
    char tmp[STR_SIZE] = "";
    sprintf(tmp, "%d", root->val); // 把結果輸出到字符串tmp中
    strcat(data, tmp);
    strcat(data, ",");
    pre_order(root->left, data);
    pre_order(root->right, data);
}

char* serialize(struct TreeNode* root) {
    char * ans = malloc(sizeof(char) * MAX_SIZE);
    memset(ans, '\0', sizeof(char) * MAX_SIZE);
    pre_order(root, ans);
    return ans;  
}

struct TreeNode * createTree(char *data, int *index){
    if(data[*index] == '#'){
        (*index)++; // ‘#’
        (*index)++; // ‘,’
        return NULL;
    }
    struct TreeNode * root = malloc(sizeof(struct TreeNode));
    char tmp[STR_SIZE] = "";
    int k = 0;
    while(data[*index] != ','){
        tmp[k++] = data[*index];
        (*index)++;
    }
    (*index)++; // 因爲此時data[*index] = ','
    root->val = atoi(tmp); // 獲取數值
    root->left = createTree(data, index); // 遞歸左子樹
    root->right = createTree(data, index); // 遞歸右子樹
    return root;
}

/** Decodes your encoded data to tree. */
struct TreeNode* deserialize(char* data) {
    int index = 0; // index表示數組下標,不需要回溯
    return createTree(data, &index);  
}

// Your functions will be called as such:
// char* data = serialize(root);
// deserialize(data);

序列化 層序遍歷 非遞歸 隊列

/**
 * Definition for a binary tree node.
 * struct TreeNode {
 *     int val;
 *     struct TreeNode *left;
 *     struct TreeNode *right;
 * };
 */
/** Encodes a tree to a single string. */

#define MaxSize 100000
typedef struct queue {
    struct TreeNode* data[MaxSize];
    int front;
    int rear;
}Queue;


void initilize(Queue *Q) { 
    Q->front = 0;
    Q->rear = 0;
}

void Push(struct TreeNode* root,Queue *Q) { 
    Q->data[++Q->rear] = root;
}

struct TreeNode* Pop(Queue *Q) { 
    return Q->data[++Q->front];
}

struct TreeNode* Top(Queue *Q) { 
    return Q->data[Q->front];
}

int empty(Queue *Q) { 
    return Q->rear == Q->front;
}

char* serialize(struct TreeNode* root) {

    Queue * Q=(Queue*)malloc(sizeof(Queue));
    initilize(Q);
    struct TreeNode *cur= (struct TreeNode*) calloc (1, sizeof(struct TreeNode));
    char *ans=(char *)malloc(MaxSize*sizeof(char));
    ans[0]='\0';  ////爲方便使用strcat
    char tmp[10] = "";

    if (root == NULL) return 0;  //空樹

    Push(root,Q);
    sprintf(tmp, "%d", root->val);
    strcat(ans, tmp);
    strcat(ans, ",");

    while (!empty(Q)) {
        cur = Pop(Q);
        if(cur->left == NULL){
            strcat(ans, "*");
            strcat(ans, ",");
        }else{
            sprintf(tmp, "%d", cur->left->val);
            strcat(ans, tmp);
            strcat(ans, ",");
            Push(cur->left,Q);
        }
        if(cur->right == NULL){
            strcat(ans, "*");
            strcat(ans, ",");
        }else{
            sprintf(tmp, "%d", cur->right->val);
            strcat(ans, tmp);
            strcat(ans, ",");
            Push(cur->right,Q);
        } 
    }
    //輸出轉換後的序列
    int t=0;
    while(ans[t]!='\0'){
        printf("%c ",ans[t]);
        t++;
    }
    
    return ans;
}

16 最長公共前綴

2020-6-17

給定正整數數組 AAA[i]A[i]表示第ii個觀光景點的評分,並且兩個景點$ i $和 jj 之間的距離爲 jij - i。一對景點(i<j)(i < j)組成的觀光組合的得分爲(A[i]+A[j]+ij)(A[i] + A[j] + i - j):景點的評分之和減去它們兩者之間的距離。返回一對觀光景點能取得的最高分。

示例:
輸入:[8,1,5,2,6]
輸出:11
解釋:i = 0, j = 2, A[i] + A[j] + i - j = 8 + 5 + 0 - 2 = 11

提示:
2 <= A.length <= 50000
1 <= A[i] <= 1000

A[i]+A[j]+ij(A[i]+i)+(A[j]j)A[i] + A[j] + i - j\rightarrow (A[i] + i)+(A[j] - j)

  • 次序問題,ii一定在jj之前,則對ii的計算可以與jj同時進行
  • 對於每一個A[j]jA[j] - j,其對應的A[i]+iA[i] + i中的ii一定是在jj之前,因此可以在迭代A[j]jA[j] - j的同時計算最大的A[i]+iA[i] + i,可以保證對於每一個A[j]jA[j] - j,最大的A[i]+iA[i] + i都是在jj之前的數
  • 有點前綴和的意思
int maxScoreSightseeingPair(int* A, int ASize){
    int ans = 0, mx = A[0] + 0;
    for (int j = 1; j < ASize; ++j) {
        ans = fmax(ans, mx + A[j] - j);
        mx = fmax(mx, A[j] + j);
    }
    return ans;
}

17 最長公共前綴

2020-6-18

我們從二叉樹的根節點 root 開始進行深度優先搜索。
在遍歷中的每個節點處,我們輸出 D 條短劃線(其中 D 是該節點的深度),然後輸出該節點的值。(如果節點的深度爲 D,則其直接子節點的深度爲 D + 1。根節點的深度爲 0)。
如果節點只有一個子節點,那麼保證該子節點爲左子節點。
給出遍歷輸出 S,還原樹並返回其根節點 root。

迭代 棧

  • 從前向後依次遍歷
  • 計算當前節點的層數與數值
  • 當節點的層數等於當前棧的長度,則當前節點爲棧頂元素的左孩子
  • 如果節點的層數不等於當前棧的長度,出棧直到等於,且當前節點爲棧頂元素的右孩子
  • 每個節點都在最後入棧
  • 最後出棧直到只剩第一個元素,即爲根節點,返回即可

在這裏插入圖片描述

循環 level value length pop action push stack
1 0 1 0 - - 1 1
2 1 2 1 - 1>left=21->left=2 2 1 2
3 2 3 2 - 2>left=32->left=3 3 1 2 3
4 2 4 3 3 2>right=42->right=4 4 1 2 4
5 1 5 3 4 2 1>right=51->right=5 5 1 5
6 2 6 2 - 5>left=65->left=6 6 1 5 6
7 2 7 1 6 5>right=75->right=7 7 1 5 7
8 - - - 7 5 - - 1
/**
 * Definition for a binary tree node.
 * struct TreeNode {
 *     int val;
 *     struct TreeNode *left;
 *     struct TreeNode *right;
 * };
 */

#define MaxSize 100000
typedef struct Stack {
    struct TreeNode* data[MaxSize];
    int top;
}Stack;

Stack S;

void Initilize() { 
    S.top=0;
}

void Push(struct TreeNode* root) { 
    S.top++;
    S.data[S.top]=root;
}

struct TreeNode* Pop() { 
    S.top--;
    return S.data[S.top+1];
}

struct TreeNode* Top() { 
    return S.data[S.top];
}

int Empty() { 
    return S.top==0;
}

int Length(){
    return S.top;
}

struct TreeNode* recoverFromPreorder(char * S){
    int n=strlen(S);

    Initilize();
    for(int i=0;i<n;){
        int level=0;
        while(S[i]=='-'){  //計算每一個節點的深度
            i++;
            level++;
        }
       // printf("level: %d   ",level);
        int value=0;
        while(i<n&&isdigit(S[i])){  //計算節點的數字,需要逐位翻譯
            value=value*10+(int)(S[i]-'0');
            i++;
        }
        //printf("value: %d   ",value);
        struct TreeNode* node=malloc(sizeof(struct TreeNode));
        node->val=value;
        node->left=NULL;
        node->right=NULL;
        //printf("length: %d  ",Length());
        if(level==Length()){
            if(!Empty()){
                Top()->left=node;
            }
        }else{
            while(level!=Length()){
                Pop();
            }
            Top()->right=node;
        }
        Push(node);
    }
    while(Length()>1){
        Pop();
    }
    return Top();
}

18 驗證迴文串

2020-6-19

給定一個字符串,驗證它是否是迴文串,只考慮字母和數字字符,可以忽略字母的大小寫。

說明:本題中,我們將空字符串定義爲有效的迴文串。

示例 1:
輸入: “A man, a plan, a canal: Panama”
輸出: true

示例 2:
輸入: “race a car”
輸出: false

雙指針

bool isPalindrome(char * s){
    int n=strlen(s);
    //printf("%d",n);
    if(n==0)return true;
    for(int i=0;i<n;i++){
        if(s[i]>='A'&&s[i]<='Z'){
            s[i]=s[i]+32;
            printf("%c ",s[i]);
        }
    }
    for(int left=0,right=n-1;left<right;left++,right--){
        //printf("left: %d,right: %d \n",left,right);
        while(!((s[left]>='0'&&s[left]<='9')||(s[left]>='a'&&s[left]<='z'))&&left<right){
            left++;
        }
        while(!((s[right]>='0'&&s[right]<='9')||(s[right]>='a'&&s[right]<='z'))&&left<right){
            right--;
        }
        if(s[left]!=s[right]){
            return false;
        }
    }
    return true;
}

特殊的測試用例

""     //空字符串
",.,"    //不含有數字字母的字符串

回顧

  • 官方題解說:“本題考查的是語言中常用字符(串)相關 API 的使用”,使用C語言的我留下不學無術的淚水
  • 迴文的常見的3種做法:雙指針,棧,reverse

19 二叉樹中的最大路徑和

2020-6-21

給定一個非空二叉樹,返回其最大路徑和。
本題中,路徑被定義爲一條從樹中任意節點出發,達到任意節點的序列。該路徑至少包含一個節點,且不一定經過根節點。

在這裏插入圖片描述

遞歸

  • 只有當前節點
  • 當前節點+左節點
  • 當前節點+由節點
  • 當前節點+左節點+右節點
/**
 * Definition for a binary tree node.
 * struct TreeNode {
 *     int val;
 *     struct TreeNode *left;
 *     struct TreeNode *right;
 * };
 */

int maxGain(struct TreeNode *root,int *maxValue){
    if(root==NULL){
        return 0;
    }
    int leftGain=fmax(maxGain(root->left,maxValue),0);
    int rightGain=fmax(maxGain(root->right,maxValue),0);
    int curValue=root->val+leftGain+rightGain;
    //printf("curValue: %d maxValue: %d\n",curValue,*maxValue);
    *maxValue=fmax(*maxValue,curValue);
    return root->val+fmax(leftGain,rightGain);
}
int maxPathSum(struct TreeNode* root)
{
    int maxValue = INT_MIN; // 最小值
    maxGain(root, &maxValue);
    return maxValue;
}

20 二進制加法

2020-6-23

給你兩個二進制字符串,返回它們的和(用二進制表示)。
輸入爲 非空 字符串且只包含數字 1 和 0。
示例 1:
輸入: a = “11”, b = “1”
輸出: “100”

示例 2:
輸入: a = “1010”, b = “1011”
輸出: “10101”

模擬加法

  • 翻轉字符串
  • n=max{a,b}n = \max\{ |a|, |b| \},循環 nn 次,從最低位開始遍歷。
  • carrycarry 表示上一個位置的進位,初始值爲 0。
  • 記當前位置對其的兩個位爲 aia_ibib_i
  • 每一位的答案爲 (carry+ai+bi)mod2({\rm carry} + a_i + b_i) \bmod{2},下一位的進位爲 carry+ai+bi2]\lfloor \frac{{\rm carry} + a_i + b_i}{2}]
  • 循環計算直到數字 a和 b 的每一位計算完畢
  • carrycarry 的不爲0,則添加1到末尾。
  • 翻轉字符串
//翻轉字符串
void reserve(char* s) {
    int len = strlen(s);
    for (int i = 0; i < len / 2; i++) {
        char t = s[i];
        s[i] = s[len - i - 1], s[len - i - 1] = t;
    }
}

char* addBinary(char* a, char* b) {
    reserve(a);
    reserve(b);

    int len_a = strlen(a), len_b = strlen(b);
    int n = fmax(len_a, len_b), carry = 0, len = 0;
    char* ans = (char*)malloc(sizeof(char) * (n + 2));
    for (int i = 0; i < n; ++i) {
        carry += i < len_a ? (a[i] == '1') : 0;
        carry += i < len_b ? (b[i] == '1') : 0;
        ans[len++] = carry % 2 + '0';
        carry /= 2;
    }

    if (carry) {
        ans[len++] = '1';
    }
    ans[len] = '\0'; //重要操作
    reserve(ans);

    return ans;
}

位運算

  • ans = x ^ y:計算當前 x 和 y 的無進位相加結果
  • carry = (x & y) << 1:計算當前 x 和 y 的進位
  • 循環直到進位爲零

半成品代碼

void reserve(char* s) {
    int len = strlen(s);
    printf("len: %d ",len);
    for (int i = 0; i < len / 2; i++) {
        char t = s[i];
        s[i] = s[len - i - 1];
        s[len - i - 1] = t;
    }
}

char * addBinary(char * a, char * b){
    int n1=strlen(a);
    int n2=strlen(b);
    int A=0;
    int B=0;
    int ans,carry;

    //字符串轉化爲整數
    for(int i=0;i<n1;i++){
        A=A*10+a[ i ]-'0';
    }
    for(int i=0;i<n2;i++){
        B=B*10+b[ i ]-'0';
    }
    while(B){
        ans=(A ^ B);
        carry=(A & B) << 1;
        A=ans;
        B=carry;
    }

    //整數轉化爲字符串
    int i=0;
    char* Ans=malloc((fmax(n1,n2)+2)*sizeof(char));
    while(ans != 0){
       Ans[ i ]=ans % 10+'0';
       i++;
       ans=ans/10;
    }
    Ans[i]='\0'; //重要操作
    reserve(Ans);
    return Ans;
}

21 最接近的三數之和

2020-6-24

給定一個包括 n 個整數的數組 nums 和 一個目標值 target。找出 nums 中的三個整數,使得它們的和與 target 最接近。返回這三個數的和。假定每組輸入只存在唯一答案。

示例:
輸入:nums = [-1,2,1,-4], target = 1
輸出:2
解釋:與 target 最接近的和是 2 (-1 + 2 + 1 = 2) 。

提示:
3<=nums.length<=1033 <= nums.length <= 10^3
103<=nums[i]<=103-10^3 <= nums[i] <= 10^3
104<=target<=104-10^4 <= target <= 10^4

雙指針

與三數之和相同

int compInc(const void *a, const void *b)   //遞增
{
    return *(int *)a - *(int *)b;
}
int threeSumClosest(int* nums, int numsSize, int target){
    int error=INT_MAX,ans;
    qsort(nums, numsSize, sizeof(int), compInc);
    for(int i=0;i<numsSize;i++){
        int L=i+1;
        int R=numsSize-1;
        while(L<R){
            int sum=nums[i]+nums[L]+nums[R];
            if(sum==target){
                return target;
            }
            if(fabs(sum-target)<error){
                error=fabs(sum-target);
                ans=sum;
            }
            if(sum<target){
                while(nums[L]==nums[L+1]&&(L+1)<R){
                    L++;
                }
                L++;
            }else{
                while(nums[R]==nums[R-1]&&L<(R-1)){
                    R--;
                }
                R--;
            }
        }
    }
    return ans;
}

22 缺失的第一個正數

2020-6-27

給你一個未排序的整數數組,請你找出其中沒有出現的最小的正整數。

示例 1: 輸入: [1,2,0] 輸出: 3

示例 2: 輸入: [3,4,-1,1] 輸出: 2

示例 3: 輸入: [7,8,9,11,12] 輸出: 1

提示:
你的算法的時間複雜度應爲O(n),並且只能使用常數級別的額外空間。

關鍵思路
對於一個長度爲 NN 的數組,其中沒有出現的最小正整數只能在 [1,N+1][1, N+1] 中。這是因爲如果 [1,N][1, N] 都出現了,那麼答案是 N+1N+1,否則答案是 [1,N][1, N] 中沒有出現的最小正整數

變相哈希

  1. 數據處理,負數不予考慮,轉換爲一個較大的數,比如n+1
  2. 建立一個長度爲N的哈希數組分別存放1到N
  3. 遍歷原數組,若值小於等於N,則將哈希數組中的此值劃掉(取負)
  4. 只取一次負
  5. 遍歷哈希數組,出現的第一個沒有被劃掉的數即爲所求,若所有的都被劃掉,則結果爲N+1

我的抄答案版本

int firstMissingPositive(int* nums, int numsSize){
    int* NUM=malloc(numsSize*sizeof(int));
    for(int i=0;i<numsSize;i++){
        NUM[i]=i+1;
    }
    for(int i=0;i<numsSize;i++){
        if(nums[i]<=0){
            nums[i]=numsSize+1;
        }
    }
    for(int j=0;j<numsSize;j++){
        if(nums[j]<=numsSize&&NUM[nums[j]-1]>0){
            NUM[nums[j]-1]=-NUM[nums[j]-1];
        }
    }
    for(int k=0;k<numsSize;k++){
        if(NUM[k]>0)return NUM[k];
    }
    return numsSize+1;
}

官方題解版本,不新建數組

  1. 利用角標的關係:數組中的數小於N時,將當前的數作爲角標,此角標對應的數取負,則最後的結果數組中沒有被取負的數對應的角標即爲所求,都取負爲N+1
  2. 在比較當前數與N的大小關係時需要先取絕對值,因爲可能會在之前的操作中已經被取負
int firstMissingPositive(int* nums, int numsSize) {
    for (int i = 0; i < numsSize; ++i) {
        if (nums[i] <= 0) {
            nums[i] = numsSize + 1;
        }
    }
    for (int i = 0; i < numsSize; ++i) {
        int num = abs(nums[i]);
        if (num <= numsSize) {
            nums[num - 1] = -abs(nums[num - 1]);
        }
    }
    for (int i = 0; i < numsSize; ++i) {
        if (nums[i] > 0) {
            return i + 1;
        }
    }
    return numsSize + 1;
}

置換

  • 對數組進行一次遍歷,對於遍歷到的數 x=nums[i]x=nums[i],如果 x[1,N]x∈[1,N],我們就知道 xx 應當出現在數組中的 x1x−1 的位置,因此交換 nums[i]nums[i]nums[x1]nums[x−1],這樣 xx 就出現在了正確的位置。在完成交換後,新的 nums[i]nums[i] 可能還在 [1,N][1, N] 的範圍內,我們需要繼續進行交換操作,直到 x[1,N]x \notin [1, N]

  • 如果nums[i]nums[i] 恰好與 nums[x1]nums[x−1] 相等,那麼就會無限交換下去。此時我們有 nums[i]=x=nums[x1]nums[i]=x=nums[x−1],說明 xx 已經出現在了正確的位置。因此我們可以跳出循環,開始遍歷下一個數。

  • 由於每次的交換操作都會使得某一個數交換到正確的位置,因此交換的次數最多爲 NN,整個方法的時間複雜度爲 O(N)O(N)

int firstMissingPositive(int* nums, int numsSize) {
    for (int i = 0; i < numsSize; ++i) {
        while (nums[i] > 0 && nums[i] <= numsSize &&
               nums[nums[i] - 1] != nums[i]) {
            int t = nums[nums[i] - 1];
            nums[nums[i] - 1] = nums[i], nums[i] = t;
        }
    }
    for (int i = 0; i < numsSize; ++i) {
        if (nums[i] != i + 1) {
            return i + 1;
        }
    }
    return numsSize + 1;
}

給定一個含有 n 個正整數的數組和一個正整數 s ,找出該數組中滿足其和 ≥ s 的長度最小的連續子數組,並返回其長度。如果不存在符合條件的連續子數組,返回 0。

示例:
輸入: s = 7, nums = [2,3,1,2,4,3]
輸出: 2
解釋: 子數組 [4,3] 是該條件下的長度最小的連續子數組。

23 長度最小的子數組

2020-6-28

暴力

int minSubArrayLen(int s, int* nums, int numsSize){
    if(numsSize==0)return 0;
    int ans=INT_MAX;
    for(int i=0;i<numsSize;i++){
        int sum=0;
        for(int j=i;j<numsSize;j++){
            sum=sum+nums[j];
            int count=j-i+1;
            printf("sum= %d  count= %d\n",sum,count);
            if(sum>=s&&count<ans){
                ans=count;
                break;
            }
        }
    }
    if(ans==INT_MAX)ans=0;
    return ans;
}

時間複雜度:O(n2)O(n^2)
空間複雜度:O(1)O(1)

雙指針

int minSubArrayLen(int s, int* nums, int numsSize) {
    if (numsSize == 0) {
        return 0;
    }
    int ans = INT_MAX;
    int L=0,R=0,sum=0;
    while(R<numsSize){
        sum+=nums[R];
        while(sum>=s){
            ans=fmin(ans,R-L+1);
            sum-=nums[L];
            L++;
        }
        R++;
    }
    return ans == INT_MAX ? 0 : ans;
}

時間複雜度:O(n)O(n)
空間複雜度:O(1)O(1)

前綴和+二分查找

  • 確定每個子數組的開始下標後,使用二分查找,找到長度最小的子數組需要 O(logn)O(logn) 的時間
  • 數組中每個元素都爲正,所以前綴和一定是遞增的,這一點保證了二分的正確性
int lower_bound(int *a, int l, int r, int q) {
    if (a[r] < q) return -1;
    while (l < r) {
        int mid = (l + r) >> 1;
        if (a[mid] >= q) {
            r = mid;
        } else {
            l = mid + 1;
        }
    }
    return l;
}
int minSubArrayLen(int s, int *nums, int numsSize) {
    if (numsSize == 0) {
        return 0;
    }
    int ans = INT_MAX;
    int *sums = (int *)malloc(sizeof(int) * (numsSize + 1));
    // 爲了方便計算,令 size = n + 1
    // sums[0] = 0 意味着前 0 個元素的前綴和爲 0
    // sums[1] = A[0] 前 1 個元素的前綴和爲 A[0]
    // 以此類推
    for (int i = 1; i <= numsSize; i++) {
        sums[i] = sums[i - 1] + nums[i - 1];
    }
    for (int i = 1; i <= numsSize; i++) {
        int target = s + sums[i - 1];
        int bound = lower_bound(sums, 1, numsSize, target);
        if (bound != -1) {
            ans = fmin(ans, bound - (i - 1));
        }
    }
    return ans == INT_MAX ? 0 : ans;
}

時間複雜度:O(nlogn)O(nlogn)
空間複雜度:O(n)O(n)

24 數組中的第K個最大元素

2020-6-29

在未排序的數組中找到第 k 個最大的元素。請注意,你需要找的是數組排序後的第 k 個最大的元素,而不是第 k 個不同的元素。

示例 1:
輸入: [3,2,1,5,6,4] 和 k = 2
輸出: 5

示例 2:
輸入: [3,2,3,1,2,4,5,5,6] 和 k = 4
輸出: 4

說明:
你可以假設 k 總是有效的,且 1 ≤ k ≤ 數組的長度。

暴力冒泡排序

int findKthLargest(int* nums, int numsSize, int k){
    for(int i=numsSize-1;i>=numsSize-k;i--){
        for(int j=0;j<i;j++){
            if(nums[j]>nums[j+1]){
                int t=nums[j];
                nums[j]=nums[j+1];
                nums[j+1]=t;
            }
        }
    }
    return nums[numsSize-k];
}

暴力快排超時

//嚴奶奶版本快排
int Partition(int *A,int low,int high){
    int key=A[low];
    while(low < high){
        while(low < high && A[high] >= key){
            high--;
        }
        int t=A[low];
        A[low]=A[high];
        A[high]=t;
        while(low < high && A[low] <= key){
            low++;
        }
        t=A[low];
        A[low]=A[high];
        A[high]=t;
    }
    A[low]=key;
    return low;
}
void QSort(int* A,int low,int high){
    if(low<high){
        int piv=Partition(A,low,high);
        QSort(A,low,high-1);
        QSort(A,low+1,high);
    }
}

int findKthLargest(int* nums, int numsSize, int k){
    QSort(nums,0,numsSize-1);
    for(int i=0;i < numsSize;i++){
        printf("%d ",nums[i]);
    }
    return nums[numsSize-k];
}

快速選擇排序

  • 對於快速排序,每一趟排序之後,作爲樞軸的關鍵字就被交換到正確的位置k,在他左邊是小於他的數,右邊是大於他的數,此關鍵字就是第K大的數字
  • 因此可以在每一趟排序之後判斷此關鍵字的角標k與K的大小關係,若k小於K,則快排右半部分,若k大於K,則快排左半部分
//算法導論版本快排
int Partition(int* a, int l, int r) {
    int x = a[r], i = l - 1;
    for (int j = l; j < r; ++j) {
        if (a[j] <= x) {
            int t = a[++i];
            a[i] = a[j], a[j] = t;
        }
    }
    int t = a[i + 1];
    a[i + 1] = a[r], a[r] = t;
    return i + 1;
}

int quickSelect(int* a, int l, int r, int index) {
    int q = Partition(a, l, r);
    if (q == index) {
        return a[q];
    } else {
        return q < index ? quickSelect(a, q + 1, r, index): quickSelect(a, l, q - 1, index);
    }
}

int findKthLargest(int* nums, int numsSize, int k) {
    return quickSelect(nums, 0, numsSize - 1, numsSize - k);
}

快速排序隨機化版本

隨機選擇主元:隨機選擇一個元素與A[r]交換

inline int partition(int* a, int l, int r) {
    int x = a[r], i = l - 1;
    for (int j = l; j < r; ++j) {
        if (a[j] <= x) {
            int t = a[++i];
            a[i] = a[j], a[j] = t;
        }
    }
    int t = a[i + 1];
    a[i + 1] = a[r], a[r] = t;
    return i + 1;
}

inline int randomPartition(int* a, int l, int r) {
    int i = rand() % (r - l + 1) + l;
    int t = a[i];
    a[i] = a[r], a[r] = t;
    return partition(a, l, r);
}

int quickSelect(int* a, int l, int r, int index) {
    int q = randomPartition(a, l, r);
    if (q == index) {
        return a[q];
    } else {
        return q < index ? quickSelect(a, q + 1, r, index)
                         : quickSelect(a, l, q - 1, index);
    }
}

int findKthLargest(int* nums, int numsSize, int k) {
    srand(time(0));
    return quickSelect(nums, 0, numsSize - 1, numsSize - k);
}

堆排序

void maxHeapify(int* a, int i, int heapSize) {
    int l = i * 2 + 1, r = i * 2 + 2, largest = i;
    if (l < heapSize && a[l] > a[largest]) {
        largest = l;
    }
    if (r < heapSize && a[r] > a[largest]) {
        largest = r;
    }
    if (largest != i) {
        int t = a[i];
        a[i] = a[largest], a[largest] = t;
        maxHeapify(a, largest, heapSize);
    }
}

void buildMaxHeap(int* a, int heapSize) {
    for (int i = heapSize / 2; i >= 0; --i) {
        maxHeapify(a, i, heapSize);
    }
}

int findKthLargest(int* nums, int numsSize, int k) {
    int heapSize = numsSize;
    buildMaxHeap(nums, heapSize);
    for (int i = numsSize - 1; i >= numsSize - k + 1; --i) {
        int t = nums[0];
        nums[0] = nums[i], nums[i] = t;
        --heapSize;
        maxHeapify(nums, 0, heapSize);
    }
    return nums[0];
}
  • 時間複雜度:O(nlogn)O(n \log n),建堆的時間代價是 O(n)O(n),刪除的總代價是 O(klogn)O(k \log n),因爲 k<nk < n,故漸進時間複雜爲 O(n+klogn)=O(nlogn)O(n + k \log n) = O(n \log n)
  • 空間複雜度:O(logn)O(\log n),即遞歸使用棧空間的空間代價。

25 用兩個棧實現隊列

2020-6-30

用兩個棧實現一個隊列。隊列的聲明如下,請實現它的兩個函數 appendTail 和 deleteHead ,分別完成在隊列尾部插入整數和在隊列頭部刪除整數的功能。(若隊列中沒有元素,deleteHead 操作返回 -1 )

示例 1:
輸入:
[“CQueue”,“appendTail”,“deleteHead”,“deleteHead”]
[[],[3],[],[]]
輸出:[null,null,3,-1]

示例 2:
輸入:
[“CQueue”,“deleteHead”,“appendTail”,“appendTail”,“deleteHead”,“deleteHead”]
[[],[],[5],[2],[],[]]
輸出:[null,-1,null,null,5,2]

提示:
1 <= values <= 10000
最多會對 appendTail、deleteHead 進行 10000 次調用

#define MaxSize 10000

typedef struct {
    int data1[MaxSize];
    int data2[MaxSize];
    int top1;
    int top2;
} CQueue;


CQueue* cQueueCreate() {
    CQueue* obj=malloc(sizeof(CQueue));
    obj->top1=0;
    obj->top2=0;
    return obj;
}

void cQueueAppendTail(CQueue* obj, int value) {
    obj->top1++;
    obj->data1[obj->top1]=value;
}

int cQueueDeleteHead(CQueue* obj) {
    if(obj->top2!=0){
        obj->top2--;
        return obj->data2[obj->top2+1];
    }
    while(obj->top1!=0){
        //出棧
        int t=obj->data1[obj->top1];
        obj->top1--;
        //進棧
        obj->top2++;
        obj->data2[obj->top2]=t;
    }
    if(obj->top2==0)return -1;
    obj->top2--;
    return obj->data2[obj->top2+1];

}

void cQueueFree(CQueue* obj) {
	free(obj);
    obj = NULL;
}

/**
 * Your CQueue struct will be instantiated and called as such:
 * CQueue* obj = cQueueCreate();
 * cQueueAppendTail(obj, value);
 
 * int param_2 = cQueueDeleteHead(obj);
 
 * cQueueFree(obj);
*/

基礎知識

sprintf

函數功能:格式化字符串,將格式化的數據寫入字符串中。

函數原型:

int sprintf(char *buffer, const char *format, [argument]...)

// buffer:是char類型的指針,指向寫入的字符串指針
// format:格式化字符串,即在程序中想要的格式
// argument:可選參數,可以爲任意類型的數據

函數返回值:buffer指向的字符串的長度;

sprintf函數的使用

C語言中sprintf()函數的用法

strcat 字符串連接

頭文件:

#include<string.h>  
#include<cstring>

聲明:

char *strcat(char *dest, const char *src)

參數:

  • dest – 指向目標字符串,該數組包含了一個 C 字符串,且足夠容納追加後的字符串
  • src – 指向要追加的字符串,該字符串不會覆蓋目標的字符串

功能:

把src所指字符串添加到dest結尾處(覆蓋dest結尾處的’\0’)並添加’\0’

C字符串操作strcat/strcat_s詳解

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章