在我们的日常生活中,字符串处理和数组运算无处不在。今天,我们将通过两个有趣的问题,探索如何用算法揭示字符与数字的隐藏模式。
首先,我们将探讨反转字符串中的元音字母问题,看看如何在字符串中找到并反转所有的元音字母。接着,我们将分析除自身以外数组的乘积问题,探讨如何高效计算每个元素的乘积,同时避免使用除法。通过这两个案例,你将看到算法如何在实际字符串处理和数组运算中揭示隐藏的模式。
让我们一起进入算法的世界,探索这些隐藏模式背后的奥秘!
场景描述:
给定一个字符串s,要求反转其中所有的元音字母(包括大写和小写),并返回结果字符串。元音字母包括'a'、'e'、'i'、'o'、'u',且可能以大小写形式出现多次。
算法核心:双指针与字符交换
这个问题可以通过双指针和字符交换来解决。具体来说,我们可以使用两个指针,一个从左向右遍历,另一个从右向左遍历,当两个指针都指向元音字母时,交换它们的位置。
详细分析:
验证示例:
示例1:s = "IceCreAm",输出为"AceCreIm"。
示例2:s = "leetcode",输出为"leotcede"。
/* 包含标准输入输出库 */
#include
/* 包含字符串操作库 */
#include
/* 包含布尔类型支持库 */
#include
/* 判断字符是否为元音字母(包括大小写) */
bool isVowel(char c) {
/* 定义一个变量保存小写字符 */
char lower = c;
/* 如果是大写字母,转换为小写 */
if (c >= 'A' && c <= 'Z') {
lower = c + ('a' - 'A');
}
/* 检查是否为元音字母 */
return lower == 'a' || lower == 'e' ||
lower == 'i' || lower == 'o' ||
lower == 'u';
}
/* 反转字符串中的元音字母 */
char* reverseVowels(char* s) {
/* 定义左指针,从字符串开头开始 */
int left = 0;
/* 定义右指针,从字符串末尾开始 */
int right = strlen(s) - 1;
/* 当左指针小于右指针时循环 */
while (left < right) {
/* 移动左指针直到找到元音字母 */
while (left < right && !isVowel(s[left])) {
left++;
}
/* 移动右指针直到找到元音字母 */
while (left < right && !isVowel(s[right])) {
right--;
}
/* 如果左指针仍小于右指针,交换两个元音字母 */
if (left < right) {
/* 保存左指针的字符 */
char temp = s[left];
/* 将右指针的字符赋值给左指针位置 */
s[left] = s[right];
/* 将保存的字符赋值给右指针位置 */
s[right] = temp;
/* 移动指针 */
left++;
right--;
}
}
/* 返回处理后的字符串 */
return s;
}
/* 程序入口函数 */
int main() {
/* 测试用例1 */
char test1[] = "IceCreAm";
printf("原始字符串: %s\n", test1);
printf("反转元音后: %s\n\n", reverseVowels(test1));
/* 测试用例2 */
char test2[] = "leetcode";
printf("原始字符串: %s\n", test2);
printf("反转元音后: %s\n\n", reverseVowels(test2));
/* 测试用例3(空字符串) */
char test3[] = "";
printf("原始字符串: %s\n", test3);
printf("反转元音后: %s\n\n", reverseVowels(test3));
/* 测试用例4(无双元音) */
char test4[] = "xyz";
printf("原始字符串: %s\n", test4);
printf("反转元音后: %s\n\n", reverseVowels(test4));
/* 测试用例5(全元音) */
char test5[] = "AEIOU";
printf("原始字符串: %s\n", test5);
printf("反转元音后: %s\n", reverseVowels(test5));
/* 返回程序结束 */
return 0;
}
输出结果;
场景描述:
给定一个整数数组nums,要求返回一个新数组answer,其中answer[i]等于nums中除nums[i]之外其余各元素的乘积。不能使用除法,并且要在O(n)的时间复杂度内完成。
算法核心:两次遍历与左右乘积数组
这个问题可以通过两次遍历和左右乘积数组来解决。具体步骤如下:
详细分析:
验证示例:
示例1:nums = [1,2,3,4],输出为[24,12,8,6]。
示例2:nums = [-1,1,0,-3,3],输出为[0,0,9,0,0]。
#include // 标准输入输出头文件,提供printf等函数
#include // 标准库头文件,提供malloc、free等内存管理函数
/**
* @brief 计算除自身以外数组的乘积
* @param nums 输入数组指针
* @param numsSize 输入数组的大小
* @param returnSize 用于返回结果数组大小的指针
* @return 返回结果数组指针
*/
int* productExceptSelf(int* nums, int numsSize, int* returnSize) {
// 设置返回数组的大小为输入数组大小
*returnSize = numsSize;
// 为结果数组动态分配内存空间,大小为numsSize个int
int* answer = (int*)malloc(numsSize * sizeof(int));
// 检查内存分配是否成功
if (answer == NULL) {
perror("Memory allocation failed"); // 打印错误信息
exit(EXIT_FAILURE); // 分配失败时退出程序
}
// 处理空数组的特殊情况
if (numsSize == 0) {
return answer; // 直接返回空数组
}
// 为左乘积数组动态分配内存空间
int* left = (int*)malloc(numsSize * sizeof(int));
// 检查内存分配是否成功
if (left == NULL) {
perror("Memory allocation failed");
exit(EXIT_FAILURE);
}
// 为右乘积数组动态分配内存空间
int* right = (int*)malloc(numsSize * sizeof(int));
// 检查内存分配是否成功
if (right == NULL) {
perror("Memory allocation failed");
exit(EXIT_FAILURE);
}
// 计算左乘积数组
left[0] = 1; // 第一个元素左侧没有元素,乘积初始化为1
// 从左到右遍历数组计算左乘积
for (int i = 1; i < numsSize; i++) {
left[i] = left[i - 1] * nums[i - 1]; // 当前左乘积 = 前一个左乘积 * 前一个元素
}
// 计算右乘积数组
right[numsSize - 1] = 1; // 最后一个元素右侧没有元素,乘积初始化为1
// 从右到左遍历数组计算右乘积
for (int i = numsSize - 2; i >= 0; i--) {
right[i] = right[i + 1] * nums[i + 1]; // 当前右乘积 = 后一个右乘积 * 后一个元素
}
// 计算最终结果数组
for (int i = 0; i < numsSize; i++) {
answer[i] = left[i] * right[i]; // 结果 = 左乘积 * 右乘积
}
// 释放左乘积数组内存
free(left);
// 释放右乘积数组内存
free(right);
// 返回结果数组指针
return answer;
}
/**
* @brief 打印整型数组内容
* @param arr 要打印的数组指针
* @param size 数组的大小
*/
void printArray(int* arr, int size) {
printf("["); // 打印数组开始符号
// 遍历数组每个元素
for (int i = 0; i < size; i++) {
printf("%d", arr[i]); // 打印当前元素
// 如果不是最后一个元素,打印逗号分隔符
if (i < size - 1) {
printf(", ");
}
}
printf("]\n"); // 打印数组结束符号并换行
}
int main() {
// 测试用例1: [1, 2, 3, 4]
int nums1[] = {1, 2, 3, 4}; // 定义测试数组1
int size1 = sizeof(nums1) / sizeof(nums1[0]); // 计算数组1大小
int returnSize1; // 用于接收结果数组大小
printf("测试用例1: "); // 打印测试用例提示
printArray(nums1, size1); // 打印原始数组
int* result1 = productExceptSelf(nums1, size1, &returnSize1); // 调用计算函数
printf("结果数组: "); // 打印结果提示
printArray(result1, returnSize1); // 打印结果数组
printf("\n"); // 打印空行分隔
free(result1); // 释放结果数组内存
// 测试用例2: [-1, 1, 0, -3, 3]
int nums2[] = {-1, 1, 0, -3, 3}; // 定义测试数组2
int size2 = sizeof(nums2) / sizeof(nums2[0]); // 计算数组2大小
int returnSize2; // 用于接收结果数组大小
printf("测试用例2: "); // 打印测试用例提示
printArray(nums2, size2); // 打印原始数组
int* result2 = productExceptSelf(nums2, size2, &returnSize2); // 调用计算函数
printf("结果数组: "); // 打印结果提示
printArray(result2, returnSize2); // 打印结果数组
printf("\n"); // 打印空行分隔
free(result2); // 释放结果数组内存
// 测试用例3: 空数组
int* nums3 = NULL; // 定义空数组指针
int size3 = 0; // 数组大小为0
int returnSize3; // 用于接收结果数组大小
printf("测试用例3: 空数组\n"); // 打印测试用例提示
int* result3 = productExceptSelf(nums3, size3, &returnSize3); // 调用计算函数
printf("结果数组: "); // 打印结果提示
printArray(result3, returnSize3); // 打印结果数组
printf("\n"); // 打印空行分隔
free(result3); // 释放结果数组内存
// 测试用例4: 单个元素数组
int nums4[] = {5}; // 定义单元素数组
int size4 = sizeof(nums4) / sizeof(nums4[0]); // 计算数组大小(1)
int returnSize4; // 用于接收结果数组大小
printf("测试用例4: "); // 打印测试用例提示
printArray(nums4, size4); // 打印原始数组
int* result4 = productExceptSelf(nums4, size4, &returnSize4); // 调用计算函数
printf("结果数组: "); // 打印结果提示
printArray(result4, returnSize4); // 打印结果数组
printf("\n"); // 打印空行分隔
free(result4); // 释放结果数组内存
return 0; // 程序正常结束
}
输出结果;
对比维度 | 反转字符串中的元音字母 | 除自身以外数组的乘积 |
---|---|---|
问题类型 | 字符串处理、字符交换 | 数组运算、乘积计算 |
算法核心 | 双指针、字符交换 | 两次遍历、左右乘积数组 |
复杂度 | 时间O(n),空间O(1) | 时间O(n), 空间O(n) |
应用场景 | 字符串处理、字符位置调整 | 数组运算、乘积计算 |
优化目标 | 高效反转元音字母的位置 | 高效计算每个元素的乘积,避免除法 |
通过今天的分析,我们看到算法不仅仅是冰冷的代码,它还能帮助我们在实际字符串处理和数组运算中揭示隐藏的模式。无论是反转字符串中的元音字母,还是计算除自身以外数组的乘积,背后的算法都在默默发挥作用,帮助我们更高效地处理数据。
希望这篇文章能让你对这些算法问题有更深入的了解,也期待你在生活中发现更多有趣的场景,用算法的视角去探索它们!
感谢你的耐心阅读!如果你觉得这篇文章有趣,不妨在评论区分享你生活中遇到的字符串处理或数组运算问题,或者你认为可以用算法优化的地方。让我们一起用算法的视角去探索数据的奥秘!