LeetCode 刷题笔记(C语言版 11月)

1、独一无二的出现次数(LC 1207)

给你一个整数数组 arr,请你帮忙统计数组中每个数的出现次数。

如果每个数的出现次数都是独一无二的,就返回 true;否则返回 false。

示例

示例 1:
    输入:arr = [1,2,2,1,1,3]
    输出:true
    解释:在该数组中,1 出现了 3 次,2 出现了 2 次,3 只出现了 1 次。没有两个数的出现次数相同。
示例 2:
    输入:arr = [1,2]
    输出:false
示例 3:
    输入:arr = [-3,0,1,-3,1,1,1,-3,10,0]
    输出:true

代码

间接数组计算出现次数, 在对出现次数排序, 判断是否出现重复。

int cmp(const void *a,const void *b){
    return *(int*)a - *(int*)b;
}

bool uniqueOccurrences(int* arr, int arrSize){
    //1、快排 + 比较 记录出现次数, 最后比较
    //2、声明 N 个空间,记录出现次数 模拟 Hash 表
    //采用快排 + 比较 的方法
    qsort(arr,arrSize,sizeof(int),cmp);
    int * nums = malloc(sizeof(int)*arrSize);
    //初始化赋值 初始为 1
    int i;
    for(i = 0;i < arrSize;i++)  nums[i] = 1;
    //比较
    int cur = 0,tag = 0;
    for(i = 1;i < arrSize;i++,cur++){
        if(arr[cur] == arr[i]){
            nums[tag]++;
            continue;
        }
        tag++;
    }
    tag += 1;       //少加了一次, 因为比较的是下标
    //在判断 nums 数组是否重复
    qsort(nums,tag,sizeof(int),cmp);
    cur = nums[0];
    cur = 0;
    for(i = 1;i < tag;i++,cur++){
        //printf("%d ",nums[i]);
        if(nums[cur] == nums[i])
            return false;
    }
    return true;
}

2、玩筹码(LC 1217)

示例

数轴上放置了一些筹码,每个筹码的位置存在数组 chips 当中。

你可以对 任何筹码 执行下面两种操作之一(不限操作次数,0 次也可以):

将第 i 个筹码向左或者右移动 2 个单位,代价为 0。
将第 i 个筹码向左或者右移动 1 个单位,代价为 1。
最开始的时候,同一位置上也可能放着两个或者更多的筹码。

返回将所有筹码移动到同一位置(任意位置)上所需要的最小代价。

示例

示例 1:
输入:chips = [1,2,3]
输出:1
解释:第二个筹码移动到位置三的代价是 1,第一个筹码移动到位置三的代价是 0,总代价为 1。
示例 2:
输入:chips = [2,2,2,3,3]
输出:2
解释:第四和第五个筹码移动到位置二的代价都是 1,所以最小总代价为 2。

代码

1.首先分析发现:偶数位置转移到任意偶数位置代价为0,同理奇数位置转移到任意奇数位置代价也为0;
2.其次,我们简化筹码位置,将所有偶数集中到2X位置,所有奇数集中到2X+1(或2X-1)位置;
3.要想代价最小,就是移动数量最小的集合。
4.综合分析,发现实际上就是统计奇数和偶数的个数,取小者。

时间复杂度O(n),空间复杂度O(1)。

int minCostToMoveChips(int* chips, int chipsSize){
    int odd = 0, even = 0;
    for(int i = 0; i < chipsSize; i++)
    {
        if(chips[i]%2)
            odd++;
        else
            even++;
    }
    return (odd <= even)? odd: even;
}

3、距离顺序排列矩阵单元格(LC 1030)

给出 R 行 C 列的矩阵,其中的单元格的整数坐标为 (r, c),满足 0 <= r < R 且 0 <= c < C。

另外,我们在该矩阵中给出了一个坐标为 (r0, c0) 的单元格。

返回矩阵中的所有单元格的坐标,并按到 (r0, c0) 的距离从最小到最大的顺序排,其中,两单元格(r1, c1) 和 (r2, c2) 之间的距离是曼哈顿距离,|r1 - r2| + |c1 - c2|。(你可以按任何满足此条件的顺序返回答案。)

示例

示例 1:
    输入:R = 1, C = 2, r0 = 0, c0 = 0
    输出:[[0,0],[0,1]]
    解释:从 (r0, c0) 到其他单元格的距离为:[0,1]
示例 2:
    输入:R = 2, C = 2, r0 = 0, c0 = 1
    输出:[[0,1],[0,0],[1,1],[1,0]]
    解释:从 (r0, c0) 到其他单元格的距离为:[0,1,1,2]
    [[0,1],[1,1],[0,0],[1,0]] 也会被视作正确答案。
示例 3:
    输入:R = 2, C = 3, r0 = 1, c0 = 2
    输出:[[1,2],[0,2],[1,1],[0,1],[1,0],[0,0]]
    解释:从 (r0, c0) 到其他单元格的距离为:[0,1,1,2,2,3]
    其他满足题目要求的答案也会被视为正确,例如 [[1,2],[1,1],[0,2],[1,0],[0,1],[0,0]]。

代码

存放距离 + 排序

int r0, c0;
//比较函数, 根据 当前点到目标点的哈曼顿距离 返回比较结果
int cmp(void* _a, void* _b) {
    int *a = *(int**)_a, *b = *(int**)_b;
    return fabs(a[0] - r0) + fabs(a[1] - c0) - fabs(b[0] - r0) - fabs(b[1] - c0);
}

int** allCellsDistOrder(int rows, int cols, int rCenter, int cCenter, int* returnSize, int** returnColumnSizes){
    //计算二维矩阵距离 点 (r0,c0) 的距离并保存
    //定义间接数组存放保存数据
    r0 = rCenter, c0 = cCenter;     //修改参数名便于计算
    int length = rows * cols;       //数组空间大小
    int ** nums = malloc(sizeof(int*) * length);     //一维数组存放结果
    *returnColumnSizes = malloc(sizeof(int) * length);
    for (int i = 0; i < length; i++) {
        (*returnColumnSizes)[i] = 2;        //返回的数组大小每列 = 2
        nums[i] = malloc(sizeof(int) * 2);  //为结果数组分配空间
    }
    int tag = 0;
    for (int i = 0; i < rows; i++) {
        for (int j = 0; j < cols; j++) {
            nums[tag][0] = i;
            nums[tag][1] = j;
            tag++;
        }
    }
    *returnSize = tag;
    qsort(nums, length, sizeof(int*), cmp);
    return nums;
}

4、分糖果(LC 1103)

排排坐,分糖果。

我们买了一些糖果 candies,打算把它们分给排好队的 n = num_people 个小朋友。

给第一个小朋友 1 颗糖果,第二个小朋友 2 颗,依此类推,直到给最后一个小朋友 n 颗糖果。

然后,我们再回到队伍的起点,给第一个小朋友 n + 1 颗糖果,第二个小朋友 n + 2 颗,依此类推,直到给最后一个小朋友 2 * n 颗糖果。

重复上述过程(每次都比上一次多给出一颗糖果,当到达队伍终点后再次从队伍起点开始),直到我们分完所有的糖果。注意,就算我们手中的剩下糖果数不够(不比前一次发出的糖果多),这些糖果也会全部发给当前的小朋友。

返回一个长度为 num_people、元素之和为 candies 的数组,以表示糖果的最终分发情况(即 ans[i] 表示第 i 个小朋友分到的糖果数)。

示例

示例 1:
    输入:candies = 7, num_people = 4
    输出:[1,2,3,1]
    解释:
    第一次,ans[0] += 1,数组变为 [1,0,0,0]。
    第二次,ans[1] += 2,数组变为 [1,2,0,0]。
    第三次,ans[2] += 3,数组变为 [1,2,3,0]。
    第四次,ans[3] += 1(因为此时只剩下 1 颗糖果),最终数组变为 [1,2,3,1]。
示例 2:
    输入:candies = 10, num_people = 3
    输出:[5,2,3]
    解释:
    第一次,ans[0] += 1,数组变为 [1,0,0]。
    第二次,ans[1] += 2,数组变为 [1,2,0]。
    第三次,ans[2] += 3,数组变为 [1,2,3]。
    第四次,ans[0] += 4,最终数组变为 [5,2,3]。

代码

简单迭代 模拟发糖

/**
 * Note: The returned array must be malloced, assume caller calls free().
 */
int* distributeCandies(int candies, int num_people, int* returnSize){
    *returnSize = num_people;
    int * nums = malloc(sizeof(int) * num_people);
    int i = 0;
    //为数组赋值
    for(i = 0;i < num_people;i++) 
        nums[i] = 0;
    int n = 1;
    while(candies){
        for(i = 0;i < num_people;i++){
            //printf("%d %d ",n,i);
            
            if(candies > n){
                nums[i] += n;
                candies -= n;
            }
            else{
                nums[i] += candies;
                candies = 0;
            }
            n++;
        }
    }
    return nums;
}

5、IP 地址无效化(LC 1108)

给你一个有效的 IPv4 地址 address,返回这个 IP 地址的无效化版本。

所谓无效化 IP 地址,其实就是用 “[.]” 代替了每个 “.”。

示例

示例 1:
    输入:address = "1.1.1.1"
    输出:"1[.]1[.]1[.]1"
示例 2:
    输入:address = "255.100.50.0"
    输出:"255[.]100[.]50[.]0"

代码

替换即可

char * defangIPaddr(char * address){
    //间接替换
    char * change = malloc((strlen(address) + 10) * sizeof(char));
    char str[4] = "[.]";
    int i = 0,tag = 0,j;
    for(i = 0;i < strlen(address);i++){
        if(address[i] == '.'){
            for(j = 0;j < 3;j++)
                change[tag++] = str[j]; 
            continue;
        }
        change[tag++] = address[i];
    }
    change[tag] = '\0'; 
    printf("%s ",change);
    return change;
}

6、第 N 个斐波那契数(LC 1137)

泰波那契序列 Tn 定义如下:

T0 = 0, T1 = 1, T2 = 1, 且在 n >= 0 的条件下 Tn+3 = Tn + Tn+1 + Tn+2

给你整数 n,请返回第 n 个泰波那契数 Tn 的值。

示例

示例 1:
    输入:n = 4
    输出:4
    解释:
    T_3 = 0 + 1 + 1 = 2
    T_4 = 1 + 1 + 2 = 4
示例 2:
    输入:n = 25
    输出:1389537

代码

递归 超时,复杂度比较高

int fib(int n){
    if(n == 0)
        return 0;
    if(n == 1)
        return 1;
    if(n == 2)
        return 1;
    return fib(n-1) + fib(n-2) + fib(n-3);
}

int tribonacci(int n){
    return fib(n);
}

使用项值间距 迭代

int tribonacci(int n){
    //反向思考 T(2) = 1
    //T(3) = T(2) + T(1) + T(0)
    //  pre1 和 pre2 是 T(n) 与 T(n-1) 之间的差值
    int res = 0, pre1 = 0, pre2 = 1;
    while (n--)
    {
        res += pre1 + pre2;     // 2
        pre1 = res - pre1 - pre2;
        pre2 = res - pre1 - pre2;
    }
    return res;
}

7、拼写单词(LC 1160)

给你一份『词汇表』(字符串数组) words 和一张『字母表』(字符串) chars。

假如你可以用 chars 中的『字母』(字符)拼写出 words 中的某个『单词』(字符串),那么我们就认为你掌握了这个单词。

注意:每次拼写(指拼写词汇表中的一个单词)时,chars 中的每个字母都只能用一次。

返回词汇表 words 中你掌握的所有单词的 长度之和。

示例

示例 1:
    输入:words = ["cat","bt","hat","tree"], chars = "atach"
    输出:6
    解释: 
    可以形成字符串 "cat" 和 "hat",所以答案是 3 + 3 = 6。
示例 2:
    输入:words = ["hello","world","leetcode"], chars = "welldonehoneyr"
    输出:10
    解释:
    可以形成字符串 "hello" 和 "world",所以答案是 5 + 5 = 10。

代码

暴力法 记录主串出现情况和出现次数 字串 出现情况和出现次数进行比较即可

int countCharacters(char ** words, int wordsSize, char * chars){
    //去重
    int * nums = malloc(sizeof(int) * 26);
    int * times = malloc(sizeof(int) * 26);
    int result = 0;
    int i,j,k;
    for(i = 0;i < 26;i++)   times[i] = 0;

    while(*chars){
        nums[*chars - 'a']  = 1;
        times[*chars - 'a']++;
        chars++;
    }
    //比较
    for(i = 0;i < wordsSize;i++){
        int tag = 0;
        int * temp1 = malloc(sizeof(int) * 26);
        int * temp2 = malloc(sizeof(int) * 26);
        for(k = 0;k < 26;k++)   temp2[k] = 0;
        for(j = 0;j < strlen(words[i]);j++){
            temp1[words[i][j] - 'a']  = 1;
            temp2[words[i][j] - 'a']++;
        }

        for(j = 0;j < 26;j++){
            if(nums[j] != 1 && temp1[j] == 1){
                tag = 1;
                break;
            } 
            if(times[j] < temp2[j]){
                tag = 1;
                break;
            }
        }
        
        if(tag == 0)
            result += strlen(words[i]);
        free(temp1);
        free(temp2);
    }
    return result;
}

8、按既定顺序创建目标数组(LC 1389)

给你两个整数数组 nums 和 index。你需要按照以下规则创建目标数组:

目标数组 target 最初为空。
按从左到右的顺序依次读取 nums[i] 和 index[i],在 target 数组中的下标 index[i] 处插入值 nums[i] 。
重复上一步,直到在 nums 和 index 中都没有要读取的元素。
请你返回目标数组。

题目保证数字插入位置总是存在。

示例

示例 1:
    输入:nums = [0,1,2,3,4], index = [0,1,2,2,1]
    输出:[0,4,1,3,2]
    解释:
    nums       index     target
    0            0        [0]
    1            1        [0,1]
    2            2        [0,1,2]
    3            2        [0,1,3,2]
    4            1        [0,4,1,3,2]
示例 2:
    输入:nums = [1,2,3,4,0], index = [0,1,2,3,0]
    输出:[0,1,2,3,4]
    解释:
    nums       index     target
    1            0        [1]
    2            1        [1,2]
    3            2        [1,2,3]
    4            3        [1,2,3,4]
    0            0        [0,1,2,3,4]
示例 3:
    输入:nums = [1], index = [0]
    输出:[1]

代码

迭代 直接按照数组插入的方式进行插入即可, 也即向后整体移动, 然后插入到 index 位置。

int * createTargetArray(int* nums, int numsSize, int* index, int indexSize, int* returnSize){
    //插入问题
    * returnSize = numsSize;
    int i,j; 
    int * result = malloc(sizeof(int) * numsSize);
    for(i = 0;i < numsSize;i++){
        for(j = i - 1;j >= index[i];j--){
            result[j+1] = result[j];
        }
        result[index[i]] = nums[i];
    }
    return result;
}

9、找出数组中的幸运数(LC 1394)

在整数数组中,如果一个整数的出现频次和它的数值大小相等,我们就称这个整数为「幸运数」。

给你一个整数数组 arr,请你从中找出并返回一个幸运数。

如果数组中存在多个幸运数,只需返回 最大 的那个。
如果数组中不含幸运数,则返回 -1 。

示例

示例 1:
    输入:arr = [2,2,3,4]
    输出:2
    解释:数组中唯一的幸运数是 2 ,因为数值 2 的出现频次也是 2 。
示例 2:
    输入:arr = [1,2,2,3,3,3]
    输出:3
    解释:1、2 以及 3 都是幸运数,只需要返回其中最大的 3 。
示例 3:
    输入:arr = [2,2,2,3,3]
    输出:-1
    解释:数组中不存在幸运数。
示例 4:
    输入:arr = [5]
    输出:-1
示例 5:
    输入:arr = [7,7,7,7,7,7,7]
    输出:7

代码

快排 + 计数, 排序后根据次序,比较重复次数,然后再把次数和数据值进行比较, 最终返回其中最大的值

int cmp(const void *a,const void *b){
    return *(int*)a - *(int*)b;
}

int findLucky(int* arr, int arrSize){
    //思路: 快速排序 + 一次遍历
    qsort(arr,arrSize,sizeof(int),cmp);
    int tag = arr[0],flag = 1,i;
    int max = 0;
    for(i = 1;i < arrSize;i++){
        if(tag == arr[i])
            flag++;
        else if(tag != arr[i]){
            if(flag == tag)
                max = fmax(max,tag);
            //重置
            tag = arr[i];
            flag = 1;
        }
    }
    //最后一次没有比较
    if(flag == tag)
        max = fmax(max,tag);
    return max == 0 ? -1 : max;
}

10、好数对的数目(LC 1512)

给你一个整数数组 nums 。

如果一组数字 (i,j) 满足 nums[i] == nums[j] 且 i < j ,就可以认为这是一组 好数对 。

返回好数对的数目。

示例

示例 1:
    输入:nums = [1,2,3,1,1,3]
    输出:4
    解释:有 4 组好数对,分别是 (0,3), (0,4), (3,4), (2,5) ,下标从 0 开始
示例 2:
    输入:nums = [1,1,1,1]
    输出:6
    解释:数组中的每组数字都是好数对
示例 3:
    输入:nums = [1,2,3]
    输出:0

代码

暴力法 效率比较低

int numIdenticalPairs(int* nums, int numsSize){
    //思路: 按顺序找 重复次数
    //暴力法
    int i,j,count=0;
    for(i = 0;i < numsSize;i++){
        for(j = i+1;j < numsSize;j++){
            if(nums[i] == nums[j])
                count++;
        }
    }
    return count;
}

11、一维数组的动态和(LC 1480)

给你一个数组 nums 。数组「动态和」的计算公式为:runningSum[i] = sum(nums[0]…nums[i]) 。

请返回 nums 的动态和。

示例

示例 1:
    输入:nums = [1,2,3,4]
    输出:[1,3,6,10]
    解释:动态和计算过程为 [1, 1+2, 1+2+3, 1+2+3+4] 。
示例 2:
    输入:nums = [1,1,1,1,1]
    输出:[1,2,3,4,5]
    解释:动态和计算过程为 [1, 1+1, 1+1+1, 1+1+1+1, 1+1+1+1+1] 。
示例 3:
    输入:nums = [3,1,2,10,1]
    输出:[3,4,6,16,17]

代码

动态规划思想 + 迭代

int* runningSum(int* nums, int numsSize, int* returnSize){
    //一次遍历使用计数法 动态规划的思想, 保存中间值
    * returnSize = numsSize;
    int sum = 0,i = 0;
    int * result= malloc(sizeof(int) * numsSize);
    for(i = 0;i < numsSize;i++){
        result[i] = sum + nums[i];
        sum += nums[i];
    }
    return result;
}

12、重新排列数组(LC 1470)

给你一个数组 nums ,数组中有 2n 个元素,按 [x1,x2,…,xn,y1,y2,…,yn] 的格式排列。

请你将数组按 [x1,y1,x2,y2,…,xn,yn] 格式重新排列,返回重排后的数组。

示例

示例 1:
    输入:nums = [2,5,1,3,4,7], n = 3
    输出:[2,3,5,4,1,7] 
    解释:由于 x1=2, x2=5, x3=1, y1=3, y2=4, y3=7 ,所以答案为 [2,3,5,4,1,7]
示例 2:
    输入:nums = [1,2,3,4,4,3,2,1], n = 4
    输出:[1,4,2,3,3,2,4,1]
示例 3:
    输入:nums = [1,1,2,2], n = 2
    输出:[1,2,1,2]

代码

将数组分割为两半, 一半为x, 一般为y。

int* shuffle(int* nums, int numsSize, int n, int* returnSize){
    //取前n条数据分配
    * returnSize = numsSize;
    int * result = malloc(sizeof(int) * numsSize);
    int i,tag=0;
    for(i = 0;i < numsSize/2;i++){
        result[tag++] = nums[i];
        result[tag++] = nums[numsSize/2 + i];
    }
    return result;
}

13、数组中两元素的最大乘积(LC 1464)

给你一个整数数组 nums,请你选择数组的两个不同下标 i 和 j,使 (nums[i]-1)*(nums[j]-1) 取得最大值。

请你计算并返回该式的最大值。

示例

示例 1:
    输入:nums = [3,4,5,2]
    输出:12 
    解释:如果选择下标 i=1 和 j=2(下标从 0 开始),则可以获得最大值,(nums[1]-1)*(nums[2]-1) = (4-1)*(5-1) = 3*4 = 12 。 
示例 2:
    输入:nums = [1,5,4,5]
    输出:16
    解释:选择下标 i=1 和 j=3(下标从 0 开始),则可以获得最大值 (5-1)*(5-1) = 16 。
示例 3:
    输入:nums = [3,7]
    输出:12

代码

数组中所有数值值减1, 求最大值和次大值取乘积

int maxProduct(int* nums, int numsSize){
    //既然取的是 (nums[i]-1)*(nums[j]-1) 首先把数组值全部 -1
    int i;
    for(i = 0;i < numsSize;i++)     nums[i]--;
    //然后寻找最大值和次大值
    int max1 = 0,max2 = 0;
    for(i = 0;i < numsSize;i++){
        if(max1 <= nums[i]){
            max2 = max1;
            max1 = nums[i];
        }
        if(max1 != nums[i])
            max2 = fmax(max2,nums[i]);
    }
    return max1 * max2;
}

14、矩阵中的幸运数(LC 1380)

给你一个 m * n 的矩阵,矩阵中的数字 各不相同 。请你按 任意 顺序返回矩阵中的所有幸运数。

幸运数是指矩阵中满足同时下列两个条件的元素:

在同一行的所有元素中最小
在同一列的所有元素中最大

示例

示例 1:
    输入:matrix = [[3,7,8],[9,11,13],[15,16,17]]
    输出:[15]
    解释:15 是唯一的幸运数,因为它是其所在行中的最小值,也是所在列中的最大值。
示例 2:
    输入:matrix = [[1,10,4,2],[9,3,8,7],[15,16,17,12]]
    输出:[12]
    解释:12 是唯一的幸运数,因为它是其所在行中的最小值,也是所在列中的最大值。
示例 3:
    输入:matrix = [[7,8],[1,2]]
    输出:[7]

代码

计算出 每一行的最小值 每一列的最大值, 然后进行比较即可 比暴力法节省运算时间, 复杂度 O ( n 2 ) O(n^2) O(n2)

/**
 * Note: The returned array must be malloced, assume caller calls free().
 */
int* luckyNumbers (int** matrix, int matrixSize, int* matrixColSize, int* returnSize){
    //计算出 每一行的最小值 每一列的最大值, 然后进行比较即可 
    int i,j,tag = 0,tag1 = 0;
    int * nums = malloc(sizeof(int) * matrixSize * matrixColSize[0]);
    int * maxvalues = malloc(sizeof(int) * matrixColSize[0]);
    int * minvalues = malloc(sizeof(int) * matrixSize);
    //求行最小
    for(i = 0;i < matrixSize;i++){
        //判断是否是最大或是最小
        minvalues[i] = matrix[i][0];
        for(j = 0;j < matrixColSize[0];j++){
            minvalues[i] = fmin(matrix[i][j],minvalues[i]);
        }
    }
    //求列最大
    for(i = 0;i < matrixColSize[0];i++){
        //判断是否是最大或是最小
        maxvalues[i] = 0;
        for(j = 0;j < matrixSize;j++){
            maxvalues[i] = fmax(matrix[j][i],maxvalues[i]);
        }
    }

    for(i = 0;i < matrixSize;i++)
        for(j = 0;j < matrixColSize[0];j++){
            printf("%d %d %d\n",maxvalues[j],minvalues[i],matrix[i][j]);
            if(minvalues[i] == matrix[i][j] && maxvalues[j] == matrix[i][j]){
                nums[tag++] = matrix[i][j];
            }
        }
    * returnSize = tag;
    return nums;
}

15、非递增排序的最小子序列(LC 1403)

给你一个数组 nums,请你从中抽取一个子序列,满足该子序列的元素之和 严格 大于未包含在该子序列中的各元素之和。

如果存在多个解决方案,只需返回 长度最小 的子序列。如果仍然有多个解决方案,则返回 元素之和最大 的子序列。

与子数组不同的地方在于,「数组的子序列」不强调元素在原数组中的连续性,也就是说,它可以通过从数组中分离一些(也可能不分离)元素得到。

注意,题目数据保证满足所有约束条件的解决方案是 唯一 的。同时,返回的答案应当按 非递增顺序 排列。

示例

示例 1:
    输入:nums = [4,3,10,9,8]
    输出:[10,9] 
    解释:子序列 [10,9][10,8] 是最小的、满足元素之和大于其他各元素之和的子序列。但是 [10,9] 的元素之和最大。 
示例 2:
    输入:nums = [4,4,7,6,7]
    输出:[7,7,6] 
    解释:子序列 [7,7] 的和为 14 ,不严格大于剩下的其他元素之和(14 = 4 + 4 + 6)。因此,[7,6,7] 是满足题意的最小子序列。注意,元素按非递增顺序返回。  
示例 3:
    输入:nums = [6]
    输出:[6]

代码

排序 + 依次累加比较

int cmp(const void *a,const void *b){
    return *(int*)a - *(int*)b;
}

int* minSubsequence(int* nums, int numsSize, int* returnSize){
    //首先进行排序
    qsort(nums,numsSize,sizeof(int),cmp);
    //从最大的值开始取, 每次 + 1个值, 直到大于
    int i,j,sum = 0,sum1 = 0;
    for(i = numsSize - 1;i >= 0;i--){
        sum += nums[i];
        sum1 = 0;
        for(j = 0;j < i;j++){
            sum1 += nums[j];
        }
        if(sum > sum1)
            break;
    }

    //保存结果
    int * result = malloc(sizeof(int) * numsSize);
    int tag = 0;
    for(j = numsSize - 1;j >= i;j--)
        result[tag++] = nums[j];
    
    *returnSize = tag;
    return result;
}

16、逐步求和得到正数的最小值(LC 1413)

给你一个整数数组 nums 。你可以选定任意的 正数 startValue 作为初始值。

你需要从左到右遍历 nums 数组,并将 startValue 依次累加上 nums 数组中的值。

请你在确保累加和始终大于等于 1 的前提下,选出一个最小的 正数 作为 startValue 。

示例

示例 1:
    输入:nums = [-3,2,-3,4,2]
    输出:5
    解释:如果你选择 startValue = 4,在第三次累加时,和小于 1 。
                    累加求和
                    startValue = 4 | startValue = 5 | nums
                      (4 -3 ) = 1  | (5 -3 ) = 2    |  -3
                      (1 +2 ) = 3  | (2 +2 ) = 4    |   2
                      (3 -3 ) = 0  | (4 -3 ) = 1    |  -3
                      (0 +4 ) = 4  | (1 +4 ) = 5    |   4
                      (4 +2 ) = 6  | (5 +2 ) = 7    |   2
示例 2:
    输入:nums = [1,2]
    输出:1
    解释:最小的 startValue 需要是正数。
示例 3:
    输入:nums = [1,-2,-3]
    输出:5

代码

以下两个代码思路一样, 该题我们只需要保证数组每一项累加和 s u m + v a l u e > = 1 sum + value >= 1 sum+value>=1 , 也即只需要保证 v a l u e > = 1 − s u m value >= 1 - sum value>=1sum 。 对于该式我们只需要保证 1 − s u m 1 - sum 1sum 取最大, 也即 s u m sum sum 取最小, 即可保证 v a l u e value value始终满足 v a l u e value value与所有的累加和之和都大于1( 当然根据题意, v a l u e value value 应该取正数, 因此 v a l u e ∈ ( 1 , N ) value \in (1,N) value(1,N))。

int minStartValue(int* nums, int numsSize){
    int i,temp = 0;
    int * result = malloc(sizeof(int)*numsSize);
    //直接计算需要满足题意的每一项所对应的累加和值 , 然后取最大即可
    //value + sum[i] >= 1  => value >= 1 - sum[i]    
    //取最小的 sum[i] 或 0, value 取值应该在 1 - +无穷
    for(i = 0;i < numsSize;i++){
        temp += nums[i];
        result[i] = temp;
    }

    //取最小值 然后取反 + 1即可, 满足 value - min >= 1
    int min = 0;
    for(i = 0;i < numsSize;i++)
        min = fmin(min,result[i]);
    return abs(min) + 1;
}
int minStartValue(int* nums, int numsSize){
    int i,temp = 0;
    int * result = malloc(sizeof(int)*numsSize);
    //直接计算需要满足题意的每一项所对应的累加和值 , 然后取最大即可
    for(i = 0;i < numsSize;i++){
        temp += nums[i];
        result[i] = temp;
    }

    //取最小值 然后取反 + 1即可, 满足 value - min >= 1
    int min = 0;
    for(i = 0;i < numsSize;i++)
        min = fmin(min,result[i]);
    return abs(min) + 1;
}

17、拥有最多糖果的孩子(LC 1431)

给你一个数组 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]

代码

取最大值, 遍历数组分配额外糖果后跟 max 比较, 返回 truefalse

bool* kidsWithCandies(int* candies, int candiesSize, int extraCandies, int* returnSize){
    //首先取最大值
    int i,max = 0;
    * returnSize = candiesSize;
    for(i = 0;i < candiesSize;i++)
        max = fmax(max,candies[i]);

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

18、在既定时间内做作业的学生人数(LC 1450)

给你两个整数数组 startTime(开始时间)和 endTime(结束时间),并指定一个整数 queryTime 作为查询时间。

已知,第 i 名学生在 startTime[i] 时开始写作业并于 endTime[i] 时完成作业。

请返回在查询时间 queryTime 时正在做作业的学生人数。形式上,返回能够使 queryTime 处于区间 [startTime[i], endTime[i]](含)的学生人数。

示例

示例 1:
    输入:startTime = [1,2,3], endTime = [3,2,7], queryTime = 4
    输出:1
    解释:一共有 3 名学生。
    第一名学生在时间 1 开始写作业,并于时间 3 完成作业,在时间 4 没有处于做作业的状态。
    第二名学生在时间 2 开始写作业,并于时间 2 完成作业,在时间 4 没有处于做作业的状态。
    第三名学生在时间 3 开始写作业,预计于时间 7 完成作业,这是是唯一一名在时间 4 时正在做作业的学生。
示例 2:
    输入:startTime = [4], endTime = [4], queryTime = 4
    输出:1
    解释:在查询时间只有一名学生在做作业。
示例 3:
    输入:startTime = [4], endTime = [4], queryTime = 5
    输出:0
示例 4:
    输入:startTime = [1,1,1,1], endTime = [1,3,2,4], queryTime = 7
    输出:0
示例 5:
    输入:startTime = [9,8,7,6,5,4,3,2,1], endTime = [10,10,10,10,10,10,10,10,10], queryTime = 5
    输出:5

代码

int busyStudent(int* startTime, int startTimeSize, int* endTime, int endTimeSize, int queryTime){
    //暴力遍历
    int i,j,count = 0;
    for(i = 0,j = 0;i < startTimeSize && j < endTimeSize;i++,j++){
        if(queryTime <= endTime[j] && queryTime >= startTime[i])
            count++;
    }
    return count;
}

19、商品折扣后的最终价格(LC 1475)

给你一个数组 prices ,其中 prices[i] 是商店里第 i 件商品的价格。

商店里正在进行促销活动,如果你要买第 i 件商品,那么你可以得到与 prices[j] 相等的折扣,其中 j 是满足 j > i 且 prices[j] <= prices[i] 的 最小下标 ,如果没有满足条件的 j ,你将没有任何折扣。

请你返回一个数组,数组中第 i 个元素是折扣后你购买商品 i 最终需要支付的价格。

示例

示例 1:
    输入:prices = [8,4,6,2,3]
    输出:[4,2,4,2,3]
    解释:
    商品 0 的价格为 price[0]=8 ,你将得到 prices[1]=4 的折扣,所以最终价格为 8 - 4 = 4 。
    商品 1 的价格为 price[1]=4 ,你将得到 prices[3]=2 的折扣,所以最终价格为 4 - 2 = 2 。
    商品 2 的价格为 price[2]=6 ,你将得到 prices[3]=2 的折扣,所以最终价格为 6 - 2 = 4 。
    商品 3 和 4 都没有折扣。
示例 2:
    输入:prices = [1,2,3,4,5]
    输出:[1,2,3,4,5]
    解释:在这个例子中,所有商品都没有折扣。
示例 3:
    输入:prices = [10,1,1,6]
    输出:[9,0,1,6]

代码

暴力法

int* finalPrices(int* prices, int pricesSize, int* returnSize){
    //暴力法,直接依次计算
    int i,j;
    * returnSize = pricesSize;
    int * nums = malloc(sizeof(int)*pricesSize);
    for(i = 0;i < pricesSize;i++){
        //得出折扣
        for(j = i+1;j < pricesSize;j++)
            if(prices[j] <= prices[i])
                break;
        if(j == pricesSize)
            nums[i] = prices[i] - 0;
        else
            nums[i] = prices[i] - prices[j];
    }
    return nums;
}

20、去掉最低工资和最高工资的平均值(LC 1491)

给你一个整数数组 salary ,数组里每个数都是 唯一 的,其中 salary[i] 是第 i 个员工的工资。

请你返回去掉最低工资和最高工资以后,剩下员工工资的平均值。

示例

示例 1:
    输入:salary = [4000,3000,1000,2000]
    输出:2500.00000
    解释:最低工资和最高工资分别是 1000 和 4000 。
    去掉最低工资和最高工资以后的平均工资是 (2000+3000)/2= 2500
示例 2:
    输入:salary = [1000,2000,3000]
    输出:2000.00000
    解释:最低工资和最高工资分别是 1000 和 3000 。
    去掉最低工资和最高工资以后的平均工资是 (2000)/1= 2000
示例 3:
    输入:salary = [6000,5000,4000,3000,2000,1000]
    输出:3500.00000
示例 4:
    输入:salary = [8000,9000,2000,3000,6000,1000]
    输出:4750.00000

代码

暴力法

/*
 * 输入: 数组 数组大小 可返回的最大值下标位置
 * 输出: 最大值
*/
int getMax(int * nums,int numsSize,int * index){
    int i = 0,max = 0;
    for(i = 0;i < numsSize;i++){
        if(max < nums[i]){
            max = nums[i];
            *index = i;
        }
    }
    return max;
}

/*
 * 输入: 数组 数组大小 可返回的最小值下标位置
 * 输出: 最小值
*/
int getMin(int * nums,int numsSize,int * index){
    if(numsSize == -1)
        return -1;
    int i = 0,min = nums[0];
    for(i = 0;i < numsSize;i++){
        if(min > nums[i]){
            min = nums[i];
            *index = i;
        }
    }
    return min;
}

double average(int* salary, int salarySize){
    //取最大值和最小值
    int maxIndex,minIndex = 0;
    getMax(salary,salarySize,&maxIndex);
    getMin(salary,salarySize,&minIndex);
    int i;
    double avg = 0;
    for(i = 0;i < salarySize;i++){
        printf("%d %d ",maxIndex,minIndex);
        if(i == maxIndex || i == minIndex)
            continue;
        avg += salary[i];
    }
    avg /= (salarySize-2);
    return avg;
}

21、判断是否形成等差数列(LC 1502)

给你一个数字数组 arr 。

如果一个数列中,任意相邻两项的差总等于同一个常数,那么这个数列就称为 等差数列 。

如果可以重新排列数组形成等差数列,请返回 true ;否则,返回 false 。

示例

示例 1:
    输入:arr = [3,5,1]
    输出:true
    解释:对数组重新排序得到 [1,3,5] 或者 [5,3,1] ,任意相邻两项的差分别为 2 或 -2 ,可以形成等差数列。
示例 2:
    输入:arr = [1,2,4]
    输出:false
    解释:无法通过重新排序得到等差数列。

代码

根据等差数列的求和公式 以及 等差d, 判断排序后的数组是否满足条件。

int cmp(const void *a,const void *b){
    return *(int*)a - *(int*)b;
}


bool canMakeArithmeticProgression(int* arr, int arrSize){
    //排序 + 测试
    //1、排序
    qsort(arr,arrSize,sizeof(int),cmp);
    //首先判断其数列和能否满足 n(a1 + an)/2
    double sum = 0;
    int i;
    for(i = 0;i <arrSize;i++)   sum += arr[i];
    if(sum != (double)arrSize* (arr[0] + arr[arrSize-1]) / 2)
        return false;
    //然后判断是否等差
    int d = (arr[arrSize-1] - arr[0]) / (arrSize-1);
    for(i = arrSize - 1;i > 0;i--)
        if(arr[i] - arr[i-1] != d)
            return false;
    return true;
}

22、唯一元素的和(LC 1748)

给你一个整数数组 nums 。数组中唯一元素是那些只出现 恰好一次 的元素。

请你返回 nums 中唯一元素的 和 。

示例

示例 1:
    输入:nums = [1,2,3,2]
    输出:4
    解释:唯一元素为 [1,3] ,和为 4 。
示例 2:
    输入:nums = [1,1,1,1,1]
    输出:0
    解释:没有唯一元素,和为 0 。
示例 3 :
    输入:nums = [1,2,3,4,5]
    输出:15
    解释:唯一元素为 [1,2,3,4,5] ,和为 15 。

代码
快排 + 去重

int cmp(const void *a,const void *b){
    return *(int*)a - *(int*)b;
}

int sumOfUnique(int* nums, int numsSize){
    //边界条件
    if(numsSize == 1)
        return nums[0];
    qsort(nums,numsSize,sizeof(int),cmp);   //快速排序
    //得到不重复的数字
    int i = 0,sum = 0,value = 0;    //value 标识当前比较值
    for(i = 0;i < numsSize - 1;i++){
        if(nums[i] == nums[i+1]){
            value = nums[i];
            continue;
        }
        //标识完成 
        if(nums[i] == value)
            continue;
        sum += nums[i];
    }
    //判断最后一位
    if(nums[numsSize - 2] != nums[numsSize - 1])
        sum += nums[numsSize - 1];
    return sum;
}

23、最少操作使数组递增(LC 1827)

给你一个整数数组 nums (下标从 0 开始)。每一次操作中,你可以选择数组中一个元素,并将它增加 1 。

比方说,如果 nums = [1,2,3] ,你可以选择增加 nums[1] 得到 nums = [1,3,3] 。
请你返回使 nums 严格递增 的 最少 操作次数。

我们称数组 nums 是 严格递增的 ,当它满足对于所有的 0 <= i < nums.length - 1 都有 nums[i] < nums[i+1] 。一个长度为 1 的数组是严格递增的一种特殊情况。

示例

示例 1:
    输入:nums = [1,1,1]
    输出:3
    解释:你可以进行如下操作:

    1) 增加 nums[2] ,数组变为 [1,1,2] 。
    2) 增加 nums[1] ,数组变为 [1,2,2] 。
    3) 增加 nums[2] ,数组变为 [1,2,3] 。
示例 2:
    输入:nums = [1,5,2,4,1]
    输出:14
示例 3:
    输入:nums = [8]
    输出:0

代码

int minOperations(int* nums, int numsSize){
    //严格递增的含义就是元素必须是递增的,不能存在相等
    //1、查找到第一个冲突的位置,也即不递增
    //边界条件
    if(numsSize == 1)
        return 0;
    int i,index = 0,value = 0;
    for(i = 0;i < numsSize - 1;i++){
        //从下一个位置开始
        if(nums[i] >= nums[i+1]){
            index = i + 1;
            value = nums[i] + 1;
            break;
        }
    }
    //开始操作
    int times = 0;  //相加次数
    for(i = index;i < numsSize;i++){
        if(value > nums[i]){
            times += value - nums[i];
            value++;
        } else if(value < nums[i]){
            value = nums[i] + 1;
        } else  
            value++;
    }
    return times;
}

24、判断句子是否是全字母句(LC 1832)

全字母句 指包含英语字母表中每个字母至少一次的句子。

给你一个仅由小写英文字母组成的字符串 sentence ,请你判断 sentence 是否为 全字母句 。

如果是,返回 true ;否则,返回 false 。

示例

示例 1:
    输入:sentence = "thequickbrownfoxjumpsoverthelazydog"
    输出:true
    解释:sentence 包含英语字母表中每个字母至少一次。
示例 2:
    输入:sentence = "leetcode"
    输出:false

代码

间接数组 + 哈希思想

bool checkIfPangram(char * sentence){
    //间接数组 + 哈希思想
    int nums[26] = {0};
    int i;
    for(i = 0;i < strlen(sentence);i++)
        nums[sentence[i] - 'a'] = 1;
    for(i = 0;i < 26;i++)
        if(nums[i] == 0)
            break;
    return i == 26;
}

25、K进制表示下的各位数字总和(LC 1837)

给你一个整数 n(10 进制)和一个基数 k ,请你将 n 从 10 进制表示转换为 k 进制表示,计算并返回转换后各位数字的 总和 。

转换后,各位数字应当视作是 10 进制数字,且它们的总和也应当按 10 进制表示返回。

示例

示例 1:
    输入:n = 34, k = 6
    输出:9
    解释:34 (10 进制) 在 6 进制下表示为 54 。5 + 4 = 9 。
示例 2:
    输入:n = 10, k = 10
    输出:1
    解释:n 本身就是 10 进制。 1 + 0 = 1 。

代码

int sumBase(int n, int k){
    int sum = 0;
    while(n){
        sum += n % k;
        n /= k;
    }
    return sum;
}

26、将所有数字用字符替换(LC 1844)

给你一个下标从 0 开始的字符串 s ,它的 偶数 下标处为小写英文字母,奇数 下标处为数字。

定义一个函数 shift(c, x) ,其中 c 是一个字符且 x 是一个数字,函数返回字母表中 c 后面第 x 个字符。

比方说,shift(‘a’, 5) = ‘f’ 和 shift(‘x’, 0) = ‘x’ 。
对于每个 奇数 下标 i ,你需要将数字 s[i] 用 shift(s[i-1], s[i]) 替换。

请你替换所有数字以后,将字符串 s 返回。题目 保证 shift(s[i-1], s[i]) 不会超过 ‘z’ 。

示例

示例 1:
    输入:s = "a1c1e1"
    输出:"abcdef"
    解释:数字被替换结果如下:

    - s[1] -> shift('a',1) = 'b'
    - s[3] -> shift('c',1) = 'd'
    - s[5] -> shift('e',1) = 'f'
示例 2:
    输入:s = "a1b2c3d4e"
    输出:"abbdcfdhe"
    解释:数字被替换结果如下:

    - s[1] -> shift('a',1) = 'b'
    - s[3] -> shift('b',2) = 'd'
    - s[5] -> shift('c',3) = 'f'
    - s[7] -> shift('d',4) = 'h'

代码

char shift(char a,int count){
    return a + count;
}

char * replaceDigits(char * s){
    int i;
    for(i = 0;i < strlen(s);i++){
        if(s[i] >= '0' && s[i] <= '9')
            s[i] = shift(s[i-1],s[i] - '0');
    }
    return s;
}

27、矩阵对角线元素的和(LC 1572)

给你一个正方形矩阵 mat,请你返回矩阵对角线元素的和。

请你返回在矩阵主对角线上的元素和副对角线上且不在主对角线上元素的和。

示例

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ANn08cew-1638271163718)(img/sample_1911.png)]

示例  1:
    输入:mat = [[1,2,3],
                [4,5,6],
                [7,8,9]]
    输出:25
    解释:对角线的和为:1 + 5 + 9 + 3 + 7 = 25
    请注意,元素 mat[1][1] = 5 只会被计算一次。
示例  2:
    输入:mat = [[1,1,1,1],
                [1,1,1,1],
                [1,1,1,1],
                [1,1,1,1]]
    输出:8
示例 3:
    输入:mat = [[5]]
    输出:5

代码

简单迭代

int diagonalSum(int** mat, int matSize, int* matColSize){
    //计算主副对角线元素之和 去除重复元素
    //计算元素和
    int i,j,sum = 0;
    for(i = 0;i < matSize;i++){
        for(j = 0;j < matColSize[0];j++){
            //主对角线元素
            if(i == j)  sum += mat[i][j];  
            //副对角线元素
            else if(matSize - i - 1 == j)   sum += mat[i][j];  
        }
    }
    return sum;
}

28、所有奇数长度子数组的和(LC 1588)

给你一个正整数数组 arr ,请你计算所有可能的奇数长度子数组的和。

子数组 定义为原数组中的一个连续子序列。

请你返回 arr 中 所有奇数长度子数组的和 。

示例

示例 1:
    输入:arr = [1,4,2,5,3]
    输出:58
    解释:所有奇数长度子数组和它们的和为:
    [1] = 1
    [4] = 4
    [2] = 2
    [5] = 5
    [3] = 3
    [1,4,2] = 7
    [4,2,5] = 11
    [2,5,3] = 10
    [1,4,2,5,3] = 15
    我们将所有值求和得到 1 + 4 + 2 + 5 + 3 + 7 + 11 + 10 + 15 = 58
示例 2:
    输入:arr = [1,2]
    输出:3
    解释:总共只有 2 个长度为奇数的子数组,[1] 和 [2]。它们的和为 3 。
示例 3:
    输入:arr = [10,11,12]
    输出:66

代码

暴力法 根据题目逐步划分数组然后求和, 这里实现了取子数组的算法

/*
 * 输入: 数组 数组大小 起始下标 终止下标 结果数组大小
 * 输出: 数组值 / NULL
*/
int * getSubArr(int * nums,int numsSize,int start,int end,int * returnSize){
    int i,j,tag = 0;
    //判断错误
    if(start < 0 || end > numsSize - 1){
        printf("参数错误!");
        return NULL;
    }

    int * result = malloc(sizeof(int) * (end - start + 1));
    for(i = start;i <= end;i++)
        result[tag++] = nums[i];
    *returnSize = tag;
    return result;
}

int sumOddLengthSubarrays(int* arr, int arrSize){
    //暴力法
    int divide = 1,i,j,tag = 0,sum = 0;
    while(divide <= arrSize){
        for(i = 0;i < arrSize;i++){
            if(i + divide > arrSize)
                break;
            //取值
            int size;
            int * temp = getSubArr(arr,arrSize,i,i+divide-1,&size);
            for(j = 0;j < size;j++){
                sum += temp[j];
            }
        }
        divide += 2;
    }
    return sum;
}

29、括号的最大嵌套深度(LC 1614)

如果字符串满足以下条件之一,则可以称之为 有效括号字符串(valid parentheses string,可以简写为 VPS):

字符串是一个空字符串 “”,或者是一个不为 “(” 或 “)” 的单字符。
字符串可以写为 AB(A 与 B 字符串连接),其中 A 和 B 都是 有效括号字符串 。
字符串可以写为 (A),其中 A 是一个 有效括号字符串 。
类似地,可以定义任何有效括号字符串 S 的 嵌套深度 depth(S):

depth("") = 0
depth© = 0,其中 C 是单个字符的字符串,且该字符不是 “(” 或者 “)”
depth(A + B) = max(depth(A), depth(B)),其中 A 和 B 都是 有效括号字符串
depth("(" + A + “)”) = 1 + depth(A),其中 A 是一个 有效括号字符串
例如:""、"()()"、"()(()())" 都是 有效括号字符串(嵌套深度分别为 0、1、2),而 “)(” 、"(()" 都不是 有效括号字符串 。

给你一个 有效括号字符串 s,返回该字符串的 s 嵌套深度 。

示例

示例 1:
    输入:s = "(1+(2*3)+((8)/4))+1"
    输出:3
    解释:数字 8 在嵌套的 3 层括号中。
示例 2:
    输入:s = "(1)+((2))+(((3)))"
    输出:3
示例 3:
    输入:s = "1+(2*3)/(2-1)"
    输出:1
示例 4:
    输入:s = "1"
    输出:0

代码

直接遍历所有的嵌套层数, 然后得出最大的嵌套层数即可

int maxDepth(char * s){
    //按照括号的匹配 进行层次的计算
    //实质上就是用 cur 记录当前字符被 几层括号 包裹
    //然后使用 max 记录 cur 遍历过字符的最大嵌套层数
    int i,max = 0,tag = -1;      //tag 用来识别是 0 左括号 还是 1 右括号
    int cur = 0;        //记录当前遍历字符前 有几层括号嵌套
    for(i = 0;i < strlen(s);i++){
        //如果有'(' 代表多一层嵌套
        if(s[i] == '('){
            tag = 0;
            cur++;      //嵌套层数 + 1
        }
            
        //如果有')' 代表少一层嵌套 嵌套终止 ()
        if(s[i] == ')'){
            cur--;
            tag = 1;
        }

        max = fmax(cur,max);
    }
    return max;
}

30、删除某些元素后的数组均值(LC 1619)

给你一个整数数组 arr ,请你删除最小 5% 的数字和最大 5% 的数字后,剩余数字的平均值。

与 标准答案 误差在 10-5 的结果都被视为正确结果。

示例

示例 1:
    输入:arr = [1,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,3]
    输出:2.00000
    解释:删除数组中最大和最小的元素后,所有元素都等于 2,所以平均值为 2 。
示例 2:
    输入:arr = [6,2,7,5,1,2,0,3,10,2,5,0,5,5,0,8,7,6,8,0]
    输出:4.00000
示例 3:
    输入:arr = [6,0,7,0,7,5,7,8,3,4,0,7,8,1,6,8,1,1,2,4,8,1,9,5,4,3,8,5,10,8,6,6,1,0,6,10,8,2,3,4]
    输出:4.77778
示例 4:
    输入:arr = [9,7,8,7,7,8,4,4,6,8,8,7,6,8,8,9,2,6,0,0,1,10,8,6,3,3,5,1,10,9,0,7,10,0,10,4,1,10,6,9,3,6,0,0,2,7,0,6,7,2,9,7,7,3,0,1,6,1,10,3]
    输出:5.27778
示例 5:
    输入:arr = [4,8,4,10,0,7,1,3,7,8,8,3,4,1,6,2,1,1,8,0,9,8,0,3,9,10,3,10,1,10,7,3,2,1,4,9,10,7,6,4,0,8,5,1,2,1,6,2,5,0,7,10,9,10,3,7,10,5,8,5,7,6,7,6,10,9,5,10,5,5,7,2,10,7,7,8,2,0,1,1]
    输出:5.29167

代码

快排 + 删除

int cmp(const void *a,const void *b){
    return *(int*)a - *(int*)b;
}

double trimMean(int* arr, int arrSize){
    //删除最大、最小的 5% 元素
    int delRange = arrSize * 0.05;
    int i;
    //排序
    qsort(arr,arrSize,sizeof(int),cmp);
    //删除最小 设为0
    for(i = 0;i < delRange;i++)
        arr[i] = 0;
    //删除最大
    for(i = arrSize - delRange;i < arrSize;i++)
        arr[i] = 0;
    //相加
    double sum = 0;
    for(i = 0;i < arrSize;i++)
        sum += arr[i];

    sum /= (arrSize - 2 * delRange);
    return sum;
}

31、根据频率将数组升序排序(LC 1636)

示例

给你一个整数数组 nums ,请你将数组按照每个值的频率 升序 排序。如果有多个值的频率相同,请你按照数值本身将它们 降序 排序。 
请你返回排序后的数组。
示例 1:
    输入:nums = [1,1,2,2,2,3]
    输出:[3,1,1,2,2,2]
    解释:'3' 频率为 1'1' 频率为 2'2' 频率为 3 。
示例 2:
    输入:nums = [2,3,1,3,2]
    输出:[1,3,3,2,2]
    解释:'2''3' 频率都为 2 ,所以它们之间按照数值本身降序排序。
示例 3:
    输入:nums = [-1,1,-6,4,5,-6,1,4,1]
    输出:[5,-1,4,4,-6,-6,1,1,1]

代码

哈希思想 + 快排

#define NUM_MAX 201
int g_hash[NUM_MAX];
//最烦的点在于 指针访问的越界, 定义 Map 结构体无法实现,产生未知的错误
//该方法实际上是是空间换时间的方法, 它将所有的数按照hash 的方式记录出现次数存放到 hash table
//然后排序的时候根据 hashtable 的值按照从大到小的顺序排序

//cmp 比较的不是两个数的 value 而是 两个数对应 hashtable 的值
int cmp(const void *a, const void *b)
{
    int tmpa = *(int *)a + 100;
    int tmpb = *(int *)b + 100;
    // 如果频率相等,数字降序排序
    if (g_hash[tmpb] == g_hash[tmpa]) {
        return tmpb - tmpa;
    }
    // 如果频率不等,频率升序排序
    return g_hash[tmpa] - g_hash[tmpb];
}

int *frequencySort(int *nums, int numsSize, int *returnSize)
{
    int i;
    memset(g_hash, 0, sizeof(int) * NUM_MAX);
    for (i = 0; i < numsSize; i++) {
        g_hash[nums[i] + 100]++;
    }
    qsort(nums, numsSize, sizeof(int), cmp);

    *returnSize = numsSize;
    return nums;
}

32、能否连接形成数组(LC 1640)

示例

给你一个整数数组 arr ,数组中的每个整数 互不相同 。另有一个由整数数组构成的数组 pieces,其中的整数也 互不相同 。请你以 任意顺序 连接 pieces 中的数组以形成 arr 。但是,不允许 对每个数组 pieces[i] 中的整数重新排序。
如果可以连接 pieces 中的数组形成 arr ,返回 true ;否则,返回 false 。
示例 1:
    输入:arr = [85], pieces = [[85]]
    输出:true
示例 2:
    输入:arr = [15,88], pieces = [[88],[15]]
    输出:true
    解释:依次连接 [15] 和 [88]
示例 3:
    输入:arr = [49,18,16], pieces = [[16,18,49]]
    输出:false
    解释:即便数字相符,也不能重新排列 pieces[0]
示例 4:
    输入:arr = [91,4,64,78], pieces = [[78],[4,64],[91]]
    输出:true
    解释:依次连接 [91]、[4,64] 和 [78]
示例 5:
    输入:arr = [1,3,5,7], pieces = [[2,4,6,8]]
    输出:false

代码

判断内部顺序是否出错或者内部元素是否出现在 arr 数组中即可, 因此我们对其进行遍历, 复杂度为 O ( n 2 ) O(n^2) O(n2)

bool canFormArray(int* arr, int arrSize, int** pieces, int piecesSize, int* piecesColSize){
    //实质上就是判断链接pieces 是否能够构成 数组 arr
    // 判断 pieces 内部数组的排序是否符合 arr 的排序
    // 由于无法修改 pieces 内部的排序情况, 因此只要判断内部顺序是否出错
    //暴力法判断
    int i,j;
    for(i = 0;i < piecesSize;i++){
        //寻找初始下标
        for(j = 0;j < arrSize;j++){
            if(pieces[i][0] == arr[j])
                break;
        }
        //存在了不在 arr 出现的值, false
        if(j == arrSize)
            return false;
        int index = j;
        //开始依次比较
        for(j = 0;j < piecesColSize[i] && index < arrSize;j++,index++){
            if(pieces[i][j] != arr[index])
                break; 
        }
        //没有匹配上, 代表顺序出错/没有该数字
        if(j != piecesColSize[i])
            return false;
    }
    return true;
}

33、拆炸弹(LC 1652)

代码

你有一个炸弹需要拆除,时间紧迫!你的情报员会给你一个长度为 n 的 循环 数组 code 以及一个密钥 k 。

为了获得正确的密码,你需要替换掉每一个数字。所有数字会 同时 被替换。

如果 k > 0 ,将第 i 个数字用 接下来 k 个数字之和替换。
如果 k < 0 ,将第 i 个数字用 之前 k 个数字之和替换。
如果 k == 0 ,将第 i 个数字用 0 替换。
由于 code 是循环的, code[n-1] 下一个元素是 code[0] ,且 code[0] 前一个元素是 code[n-1] 。

给你 循环 数组 code 和整数密钥 k ,请你返回解密后的结果来拆除炸弹!

示例

示例 1:
    输入:code = [5,7,1,4], k = 3
    输出:[12,10,16,13]
    解释:每个数字都被接下来 3 个数字之和替换。解密后的密码为 [7+1+4, 1+4+5, 4+5+7, 5+7+1]。注意到数组是循环连接的。
示例 2:
    输入:code = [1,2,3,4], k = 0
    输出:[0,0,0,0]
    解释:当 k 为 0 时,所有数字都被 0 替换。
示例 3:
    输入:code = [2,4,9,3], k = -2
    输出:[12,5,6,13]
    解释:解密后的密码为 [3+9, 2+3, 4+2, 9+4] 。注意到数组是循环连接的。如果 k 是负数,那么和为 之前 的数字。

代码

暴力法 按照题意进行迭代即可, k > 0 k > 0 k>0 遍历 k 次 元素 i 之后 k 个元素的和(取循环 % )

/**
 * Note: The returned array must be malloced, assume caller calls free().
 */
int* decrypt(int* code, int codeSize, int k, int* returnSize)
{
    *returnSize = codeSize;
    int i,j;
    int * nums=(int*)malloc(sizeof(int)*codeSize);
    memset(nums,0,sizeof(int)*codeSize);
    if(k==0)
        return nums;
    if(k > 0)
    {
        for(i = 0;i < codeSize;i++)
        {
            int sum = 0,count = 1;
            j = i+1;
            while(count<=k)
            {
                j %= codeSize;      //循环取模
                sum += code[j++];
                count++;
            }
            nums[i]=sum;
        }
    }
    else
    {
        k = -k;
        for(i = 0;i < codeSize;i++)
        {
            int sum = 0,count = 1;
            j = i-1;
            while(count <= k)
            {
                if(j < 0)
                    j += codeSize;
                sum += code[j--];
                count++;
            }
            nums[i] = sum;
        }
    }
    return nums;
}

34、检查两个字符串数组是否相等(LC 1662)

给你两个字符串数组 word1 和 word2 。如果两个数组表示的字符串相同,返回 true ;否则,返回 false 。

数组表示的字符串 是由数组中的所有元素 按顺序 连接形成的字符串。

示例

示例 1:
    输入:word1 = ["ab", "c"], word2 = ["a", "bc"]
    输出:true
    解释:
    word1 表示的字符串为 "ab" + "c" -> "abc"
    word2 表示的字符串为 "a" + "bc" -> "abc"
    两个字符串相同,返回 true
示例 2:
    输入:word1 = ["a", "cb"], word2 = ["ab", "c"]
    输出:false
示例 3:
    输入:word1  = ["abc", "d", "defg"], word2 = ["abcddefg"]
    输出:true

代码

二维字符数组转一维数组, 然后按位比较即可。 注意改代码仅可在 LeetCode 编译器上运行, 其他 C IDE 编译无法通过

/*
 * 输入: 数组 数组大小
 * 输出: 平铺后的一维字符数组
*/
char * flatten(char ** str,int strSize){
    //统计长度
    int i,j,length = 0;
    for(i = 0;i < strSize;i++)
        for(j = 0;j < strlen(str[i]);j++)
            length++;
    //平铺
    char * result = malloc(sizeof(char) * (length+1));
    int tag = 0;    //记录下标
    for(i = 0;i < strSize;i++)
        for(j = 0;j < strlen(str[i]);j++)
            result[tag++] = str[i][j];
    result[tag] = '\0';     //标识终止符
    return result;
}


bool arrayStringsAreEqual(char ** word1, int word1Size, char ** word2, int word2Size){
    //二维字符串转一维
    char * str1 = flatten(word1,word1Size);
    char * str2 = flatten(word2,word2Size);
    if(strlen(str1) != strlen(str2))
        return false;
    while(*str1){
        if(*str1 != *str2)
            return false;
        str1++;
        str2++;
    }
    return true;
}


该刷题笔记完全使用C语言编码, 每天3道题。当然,我是这么对自己要求的,由于有事或者休息等其它不可抗力因素,可能会延时上传,也即 今 日 事 明 日 毕 − 圈 起 来 这 里 要 考 今日事明日毕-圈起来这里要考 所有代码同步上传 gitee 仓库链接
老实说, 纯 C 语言刷题难度挺大的, 可用的轮子很少, 但是也很锻炼人, 自己造轮子的感觉不要太爽。 我的目标是在几年时间内刷完所有的 LeetCode 题库, 按照目前的更新来看, 2500 道可能需要 3 年左右吧, 希望自己能坚持下去, 加油。

由于使用的 Markdwon 文档编辑代码, CSDN 上传图片太过麻烦 , 如果遇到图片显示不出来, 请移步 gitee 仓库链接

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值