LeetCode75(二)
LeetCode75 Day2
1431.拥有最多糖果的孩子
给你一个数组 candies
和一个整数 extraCandies
,其中 candies[i]
代表第 i
个孩子拥有的糖果数目。
对每一个孩子,检查是否存在一种方案,将额外的 extraCandies
个糖果分配给孩子们之后,此孩子有 最多 的糖果。注意,允许有多个孩子同时拥有 最多 的糖果数目。
示例 1:
1 | 输入:candies = [2,3,5,1,3], extraCandies = 3 |
示例 2:
1 | 输入:candies = [4,2,1,1,2], extraCandies = 1 |
示例 3:
1 | 输入:candies = [12,1,12], extraCandies = 10 |
提示:
2 <= candies.length <= 100
1 <= candies[i] <= 100
1 <= extraCandies <= 50
1 | bool* kidsWithCandies(int* candies, int candiesSize, int extraCandies, int* returnSize){ |
- 首先,函数接受四个参数:
candies
数组表示每个孩子拥有的糖果数量,candiesSize
表示数组的长度,extraCandies
表示额外的糖果数量,returnSize
是一个指向整数的指针,用于存储返回的布尔数组的长度。 - 使用
malloc
函数动态分配了一个布尔数组res
,其长度为candiesSize
。如果分配失败,则返回NULL
。 - 使用变量
maxNum
记录数组candies
中的最大值,初始化为 0。 - 使用循环遍历数组
candies
,找到其中的最大值,并将其赋值给变量maxNum
。 - 使用循环遍历数组
candies
,对于每个孩子,判断其拥有的糖果数量加上额外的糖果数量是否大于等于最大糖果数量maxNum
。如果是,则表示这个孩子拥有的糖果数量加上额外的糖果数量足以使其成为拥有最多糖果的孩子,将对应位置的布尔数组res
设为true
;否则设为false
。 - 在循环结束后,将
returnSize
的值设为布尔数组res
的长度,并返回res
。
这段代码的主要逻辑是通过遍历数组两次,第一次找出最大糖果数量,第二次判断每个孩子是否拥有超过其他孩子的糖果数量。最终返回一个布尔数组,其中 true
表示拥有超过其他孩子的糖果数量,false
表示没有。
在上述代码中,res
指针确实指向了一个动态分配的布尔数组,但是需要注意的是,这个布尔数组的实际空间是在堆内存中分配的,而不是在代码中显式地定义的。
具体来说,malloc
函数会在堆内存中动态分配一块连续的内存空间,大小为 candiesSize * sizeof(bool)
字节,用于存储布尔数组的各个元素。这个内存空间的地址会被返回给 res
指针变量,因此 res
指针指向的就是这个分配好的内存空间的起始地址。
在代码中并没有显式地声明布尔数组的名称,因为它是通过动态内存分配而来的,不需要像静态数组一样提前声明。这种情况下,我们只需要使用指针变量 res
来操作和访问这个布尔数组即可,而无需使用数组名称。
605.种花问题
假设有一个很长的花坛,一部分地块种植了花,另一部分却没有。可是,花不能种植在相邻的地块上,它们会争夺水源,两者都会死去。
给你一个整数数组 flowerbed
表示花坛,由若干 0
和 1
组成,其中 0
表示没种植花,1
表示种植了花。另有一个数 n
,能否在不打破种植规则的情况下种入 n
朵花?能则返回 true
,不能则返回 false
。
示例 1:
1 | 输入:flowerbed = [1,0,0,0,1], n = 1 |
示例 2:
1 | 输入:flowerbed = [1,0,0,0,1], n = 2 |
提示:
1 <= flowerbed.length <= 2 * 104
flowerbed[i]
为0
或1
flowerbed
中不存在相邻的两朵花0 <= n <= flowerbed.length
1 | bool canPlaceFlowers(int* flowerbed, int flowerbedSize, int n) { |
官方题解中使用贪心算法理解起来太复杂,从题目讨论中发现“Harrytsz”的防御式编程思想:在 flowerbed 数组两端各增加一个 0, 这样处理的好处在于不用考虑边界条件,任意位置处只要连续出现三个 0 就可以栽上一棵花。
他的代码是Python写的,比较简洁
1 | class Solution(object): |
我在这边试着用C语言进行了重写,麻烦的重点就在于怎么让数组两边都加一个0,最开始是直接用变长数组(在一些编译器中,允许使用变量来定义数组的大小),后来想想C 语言的标准规范中不支持在数组定义时使用变量来指定数组的大小,还是使用动态内存分配函数 malloc
,如下所示:
1 | int *newFlowerbed = (int*)malloc(newFlowerbedSize * sizeof(int)); |
其实一般在用动态内存分配的时候,要加一些检测判断,如下:
1 | if (newFlowerbed == NULL) { |
上述代码可以通过检测,就是执行用时分布还不够好。
345.反转字符串中的元音字母
给你一个字符串 s
,仅反转字符串中的所有元音字母,并返回结果字符串。
元音字母包括 'a'
、'e'
、'i'
、'o'
、'u'
,且可能以大小写两种形式出现不止一次。
示例 1:
1 | 输入:s = "hello" |
示例 2:
1 | 输入:s = "leetcode" |
提示:
1 <= s.length <= 3 * 105
s
由 可打印的 ASCII 字符组成
根据讨论区的“Ticstc”大佬的解释说题目的意思,是把a、e、i、o、u这几个字母按顺序存起来,最后这些存起来的字符串整体反转插入到原数组中,并不是相邻的两两交换。
1 | char vowel[] = "aeiouAEIOU"; |
这段代码实现了一个函数 reverseVowels
,其作用是将输入字符串 s
中的元音字母反转,其他字符保持不变。函数中用到了一个辅助函数 isVowel
来判断一个字符是否为元音字母。
vowel[]
数组定义了所有的元音字母,包括小写和大写。这个数组用于在isVowel
函数中检查一个字符是否为元音字母。isVowel
函数接收一个字符ch
作为输入,在一个循环中遍历vowel[]
数组,如果ch
等于vowel[i]
中的任何一个元素,则返回true
,表示该字符是一个元音字母。如果循环结束时仍然没有找到相等的元音字母,则返回false
。reverseVowels
函数首先获取输入字符串s
的长度n
,然后初始化两个指针i
和j
,分别指向字符串的起始和末尾。使用
while
循环来对字符串进行遍历。在循环中,首先检查i
指向的字符是否为元音字母,如果不是,则i
向后移动,直到找到一个元音字母为止;类似地,检查j
指向的字符是否为元音字母,如果不是,则j
向前移动,直到找到一个元音字母为止。如果
i
小于j
,说明找到了一对需要交换的元音字母,将它们进行交换,并且i
向后移动一步,j
向前移动一步,以继续寻找下一对需要交换的元音字母。当
i >= j
时,表示已经遍历完整个字符串,此时完成了元音字母的反转操作,函数返回原始字符串s
的指针。
注意事项:
交换字符的语句
char* tmp = s[i]; s[i] = s[j], s[j] = tmp;
可能会引发编译器的警告或错误,因为tmp
是一个字符指针,而s[i]
和s[j]
是字符。正确的做法应该是交换字符而不是交换指针,可以修改为char tmp = s[i]; s[i] = s[j], s[j] = tmp;
。在这段代码中,
++i
和--j
是分别对变量i
和j
进行递增和递减操作。而i++
是先取i
的当前值,然后再将i
的值加 1。这里使用
++i
和--j
的原因是它们与i++
和j--
在这个上下文中的效果是一样的,但是它们的性能稍微更好一些。这是因为++i
和--j
是“前缀递增”和“前缀递减”操作符,它们直接修改了变量的值,并返回这个修改后的值。相比之下,i++
和j--
是“后缀递增”和“后缀递减”操作符,它们会先返回变量当前的值,然后再修改这个变量的值。在这种情况下,无论是使用
++i
还是i++
都不会影响代码的功能,因为这里只是简单地递增或递减i
和j
的值。然而,使用++i
和--j
可以避免不必要的内存分配和拷贝,因为它们直接在原始变量上进行操作,而不需要为返回值创建一个临时变量。所以,在 C 或 C++ 中,建议在不需要后缀操作符的情况下尽可能使用前缀操作符,以获得更好的性能。