反转字符串中的元音字母与除自身以外数组的乘积:算法揭示字符与数字的隐藏模式

博客引言:

在我们的日常生活中,字符串处理和数组运算无处不在。今天,我们将通过两个有趣的问题,探索如何用算法揭示字符与数字的隐藏模式。

首先,我们将探讨反转字符串中的元音字母问题,看看如何在字符串中找到并反转所有的元音字母。接着,我们将分析除自身以外数组的乘积问题,探讨如何高效计算每个元素的乘积,同时避免使用除法。通过这两个案例,你将看到算法如何在实际字符串处理和数组运算中揭示隐藏的模式。

让我们一起进入算法的世界,探索这些隐藏模式背后的奥秘!


博客正文:

一、反转字符串中的元音字母:字符的优雅交换

场景描述:
给定一个字符串s,要求反转其中所有的元音字母(包括大写和小写),并返回结果字符串。元音字母包括'a'、'e'、'i'、'o'、'u',且可能以大小写形式出现多次。

算法核心:双指针与字符交换
这个问题可以通过双指针和字符交换来解决。具体来说,我们可以使用两个指针,一个从左向右遍历,另一个从右向左遍历,当两个指针都指向元音字母时,交换它们的位置。

详细分析:

  1. 初始化指针:left指针从字符串的开头开始,right指针从字符串的末尾开始。
  2. 遍历字符串:当left < right时,检查当前字符是否为元音字母。
    • 如果left指向的字符不是元音字母,left指针右移。
    • 如果right指向的字符不是元音字母,right指针左移。
    • 如果left和right都指向元音字母,则交换它们的位置,并将left指针右移,right指针左移。
  3. 返回结果:当left >= right时,遍历结束,返回处理后的字符串。

验证示例:

示例1:s = "IceCreAm",输出为"AceCreIm"。

  • 元音字母为'I', 'e', 'e', 'A'。
  • 反转后,字符串变为"AceCreIm"。

示例2:s = "leetcode",输出为"leotcede"。

  • 元音字母为'e', 'o', 'e'。
  • 反转后,字符串变为"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;
    }

输出结果;

反转字符串中的元音字母与除自身以外数组的乘积:算法揭示字符与数字的隐藏模式_第1张图片


二、除自身以外数组的乘积:数值的高效计算

场景描述:
给定一个整数数组nums,要求返回一个新数组answer,其中answer[i]等于nums中除nums[i]之外其余各元素的乘积。不能使用除法,并且要在O(n)的时间复杂度内完成。

算法核心:两次遍历与左右乘积数组
这个问题可以通过两次遍历和左右乘积数组来解决。具体步骤如下:

  1. 初始化两个数组:left和right。left[i]表示从左到右到i的乘积,right[i]表示从右到左到i的乘积。
  2. 计算left数组:从左到右遍历数组,left[i] = left[i-1] * nums[i-1]。
  3. 计算right数组:从右到左遍历数组,right[i] = right[i+1] * nums[i+1]。
  4. 计算answer数组:answer[i] = left[i] * right[i]。

详细分析:

  1. 初始化:创建两个数组left和right,大小与nums相同。
  2. 计算left数组:left[0] = 1,然后从i=1到n-1,left[i] = left[i-1] * nums[i-1]。
  3. 计算right数组:right[n-1] = 1,然后从i=n-2到0,right[i] = right[i+1] * nums[i+1]。
  4. 计算answer数组:answer[i] = left[i] * right[i]。

验证示例:

示例1:nums = [1,2,3,4],输出为[24,12,8,6]。

  • left = [1,1,2,6]
  • right = [24,12,4,1]
  • answer = [124, 112, 24, 61] = [24,12,8,6]

示例2:nums = [-1,1,0,-3,3],输出为[0,0,9,0,0]。

  • left = [1, -1, -1, 0, 0]
  • right = [0, 0, 9, 3, 1]
  • answer = [10, -10, -19, 03, 0*1] = [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;  // 程序正常结束
    }

输出结果;

反转字符串中的元音字母与除自身以外数组的乘积:算法揭示字符与数字的隐藏模式_第2张图片


三、全方位对比:反转字符串中的元音字母 vs 除自身以外数组的乘积

对比维度 反转字符串中的元音字母 除自身以外数组的乘积
问题类型 字符串处理、字符交换 数组运算、乘积计算
算法核心 双指针、字符交换 两次遍历、左右乘积数组
复杂度 时间O(n),空间O(1) 时间O(n), 空间O(n)
应用场景 字符串处理、字符位置调整 数组运算、乘积计算
优化目标 高效反转元音字母的位置 高效计算每个元素的乘积,避免除法

博客总结:

通过今天的分析,我们看到算法不仅仅是冰冷的代码,它还能帮助我们在实际字符串处理和数组运算中揭示隐藏的模式。无论是反转字符串中的元音字母,还是计算除自身以外数组的乘积,背后的算法都在默默发挥作用,帮助我们更高效地处理数据。

希望这篇文章能让你对这些算法问题有更深入的了解,也期待你在生活中发现更多有趣的场景,用算法的视角去探索它们!


博客谢言:

感谢你的耐心阅读!如果你觉得这篇文章有趣,不妨在评论区分享你生活中遇到的字符串处理或数组运算问题,或者你认为可以用算法优化的地方。让我们一起用算法的视角去探索数据的奥秘!

你可能感兴趣的:(java,数据结构,算法)