博客标题《数字迷阵与指针华尔兹:破解「下一个更大数」与「单词反转」的算法密码》

#王者杯·14天创作挑战营·第1期#

引言

想象一下,你手握一串数字,像解开古老符文的探险家;或是在字符的海洋中,用指针跳起一支优雅的华尔兹。
今天,我们将用数学的魔法赋予数字新的排列生命,再用指针的舞步颠倒文字乾坤。
无需魔法书,只需逻辑与想象力——这场数字与字符的双重奏,现在开始!


正文


一、谜题一:下一个更大元素 III(数学排列+指针)

题目核心
给定一个32位整数,将其数字重新排列成下一个更大的整数。若不存在则返回-1。
限制条件:仅通过指针操作模拟数字交换,不可暴力枚举所有排列。


算法解密
关键思路:模拟 next_permutation 算法,寻找“最小增量式”排列。

  1. 逆向扫描:从右向左找到第一个“下降点” nums[i] < nums[i+1],标记为分界点。

  2. 交换策略:在分界点右侧找到比 nums[i] 大的最小数字 nums[j],交换二者。

  3. 局部反转:将分界点右侧的数字升序排列(反转即可),确保新数是“最小更大值”。

示例解析
输入 12

  • 下降点为 1(对应数字1),右侧找到最小更大值2,交换得 21
    输入 21:无下降点,直接返回-1。

时间复杂度O(n),仅需两次线性扫描和一次局部反转。

/* 标准输入输出头文件,提供printf/scanf等函数 */
#include <stdio.h>
/* 动态内存管理头文件,提供malloc/free等函数 */
#include <stdlib.h>
/* 整型极限值定义头文件,包含INT_MAX等常量 */
#include <limits.h>
/* 字符串操作头文件,提供strlen/strcpy等函数 */
#include <string.h>

/**
 * 计算下一个更大排列的核心算法
 * @param n 输入的正整数
 * @return 下一个更大排列的数值,不存在则返回-1
 */
int nextGreaterElement(int n) {
    /* 处理非正整数的情况(负数与零无有效排列) */
    if (n <= 0) return -1;  // 直接返回-1,因为题目要求正整数

    /* 将整数转换为字符数组便于处理单个数字 */
    char s[20];             // 预留20字节空间(考虑符号位和结束符)
    sprintf(s, "%d", n);    // 将整数格式化为字符串存入s数组
    int len = strlen(s);    // 计算数字字符串的实际长度

    /* ==== 阶段一:寻找第一个下降点 ==== */
    int i;  // 声明下降点索引变量
    /* 从倒数第二个字符开始向左扫描 */
    for (i = len - 2; i >= 0; --i) {  // len-2因为要比较i和i+1
        /* 发现前一个字符小于后一个字符时停止 */
        if (s[i] < s[i + 1]) break;  // 找到递减趋势的转折点
    }
    /* 未找到下降点时返回-1(已是最大排列) */
    if (i < 0) return -1;  // 全部数字呈递减排列,没有更大可能

    /* ==== 阶段二:寻找最小交换点 ==== */
    int j;  // 声明交换点索引变量
    /* 从字符串末尾向左扫描找第一个大于s[i]的字符 */
    for (j = len - 1; j > i; --j) {  // 限定在下降点右侧寻找
        if (s[j] > s[i]) break;      // 找到最小的较大值
    }

    /* ==== 执行数字交换操作 ==== */
    char temp = s[i];  // 临时存储要交换的字符
    s[i] = s[j];       // 将较大的字符换到前位置
    s[j] = temp;       // 将原字符换到后位置

    /* ==== 阶段三:反转右侧数字序列 ==== */
    /* 使用双指针法反转i+1到末尾的字符 */
    int left = i + 1;          // 左指针从下降点右侧开始
    int right = len - 1;       // 右指针指向字符串末尾
    while (left < right) {     // 当左右指针未相遇时持续交换
        temp = s[left];        // 临时存储左指针字符
        s[left] = s[right];    // 将右指针字符赋给左位置
        s[right] = temp;       // 将临时字符赋给右位置
        left++;                // 左指针右移
        right--;               // 右指针左移
    }

    /* ==== 结果验证与转换 ==== */
    /* 将字符数组转换为长整型(防止32位溢出) */
    char* endptr;              // 用于检测转换错误的指针
    long result = strtol(s, &endptr, 10);  // 十进制转换
    /* 验证转换有效性及数值范围 */
    if (*endptr != '\0' ||     // 存在非法字符(理论上不会触发)
        result > INT_MAX ||   // 超过32位有符号整数最大值
        result <= n) {        // 结果不大于原始值(理论上不会触发)
        return -1;             // 返回错误标识
    }

    return (int)result;  // 安全转换为int类型后返回
}

/**
 * 主函数:处理程序输入输出
 * @return 程序退出状态码
 */
int main() {
    int input_num;  // 声明存储用户输入的变量
    /* 获取用户输入 */
    printf("Please enter a 32-bit integer: ");
    scanf("%d", &input_num);  // 读取整数值到input_num
    printf("The initial values are:%d\n", input_num);  // 回显输入

    /* 计算并输出结果 */
    int result = nextGreaterElement(input_num);  // 调用核心算法
    printf("The next larger permutation is: %d\n", result);

    return 0;  // 正常退出程序
}

 输出结果:


二、谜题二:全指针字符串单词反转

题目核心
原地反转字符串中的单词顺序(如 "hello world" → "world hello"),仅用指针操作,禁止库函数或额外空间。


算法解密
关键思路:两次反转法,用指针精准切割单词边界。

  1. 全局反转:先反转整个字符串,将单词顺序倒置(如 "hello world" → "dlrow olleh")。

  2. 单词级反转

    • 用双指针定位单词的起止位置(跳过空格)。

    • 逐个反转单词,恢复其原始顺序(如 "dlrow" → "world""olleh" → "hello")。

关键难点

  • 指针移动时需处理连续空格和字符串末尾。

  • 必须完全依赖指针算术,无临时变量存储位置。

示例解析
输入 "hello world"

  • 全局反转 → "dlrow olleh"

  • 反转单词 "dlrow" → "world""olleh" → "hello",最终得 "world hello"

时间复杂度O(n),两次线性遍历。

#include <stdio.h>  // 引入标准输入输出库,用于printf函数

// 定义反转函数,接收两个指针参数:start(起始位置)和end(结束位置)
void reverse(char *start, char *end) {
    // 当start指针地址小于end时,持续交换字符,直到指针相遇或交叉
    while (start < end) {
        char temp = *start;  // 临时变量存储start当前指向的字符
        *start = *end;       // 将end指向的字符赋值给start位置
        *end = temp;         // 将临时变量中的字符赋值给end位置
        start++;             // 将start指针向后移动一位
        end--;               // 将end指针向前移动一位
    }
}

int main() {
    char str[] = "hello world";  // 定义并初始化可修改的字符数组(注意:不能用指针形式,否则无法修改)
    printf("The initial string is:  %s\n", str);  // 打印原始字符串
    
    // 步骤1:全局反转整个字符串
    char *start = str;  // 定义起始指针,指向字符串开头
    char *end = str;    // 定义结束指针,初始同样指向开头
    
    // 将end指针移动到字符串末尾(即'\0'的位置)
    while (*end != '\0') {
        end++;          // 逐字符向后移动,直到遇到字符串结束符
    }
    // 处理非空字符串的情况(避免对空字符串操作导致错误)
    if (end > str) {    // 如果end指针地址大于start(即字符串非空)
        end--;          // end回退一位,指向最后一个有效字符(跳过'\0')
        reverse(start, end);  // 调用反转函数,反转整个字符串
    }
    
    // 步骤2:逐个反转单词,恢复单词原始顺序
    char *p = str;      // 定义遍历指针p,从字符串开头开始扫描
    while (*p != '\0') {  // 循环直到字符串结束
        // 跳过连续的空格,定位到单词的起始位置
        while (*p == ' ') {
            p++;        // 若当前字符是空格,继续向后移动指针
        }
        if (*p == '\0') {  // 若跳过空格后直接到达字符串末尾,说明没有单词需要处理
            break;      // 退出循环
        }
        
        // 定位单词的起始和结束位置
        char *word_start = p;  // 记录单词起始位置(此时p指向单词的第一个字符)
        // 移动指针p直到遇到空格或字符串结束符
        while (*p != ' ' && *p != '\0') {
            p++;        // 持续后移,p最终指向单词后的空格或'\0'
        }
        char *word_end = p - 1;  // 单词的最后一个字符是p的前一个位置
        
        reverse(word_start, word_end);  // 反转当前单词,恢复其原始顺序
    }
    
    // 输出最终结果
    printf("The reversed string is:  %s\n", str);
    return 0;  // 程序正常退出
}

 输出结果为:


三、对比分析:数学之静 vs 指针之动
对比维度下一个更大元素 III全指针单词反转
核心技巧数学排列的贪心策略指针操作的精确舞蹈
时间复杂度O(n)O(n)
空间复杂度O(1)O(1)
关键突破点逆向扫描与局部最优交换全局与局部反转的嵌套逻辑
适用场景数字序列的增量式生成字符串原地操作的极致优化

四、总结
  • 下一个更大数问题:通过逆向思维与局部调整,展现了数学排列的简洁美学

  • 单词反转问题:用指针的“双重反转”策略,诠释了空间压缩与操作优雅性的平衡。
    共同启示:无论是数字还是字符,算法本质是逻辑与操作的精准交响。


谢言

感谢你与我共舞于数字与字符的奇幻世界!
若你想挑战更高难度,不妨尝试:

  • 将「下一个更大数」扩展到负数或浮点数。

  • 用纯指针实现多语言混合字符串的反转(如中英文混杂)。
    记住:算法的边界,只由想象力定义。
    下期预告:递归分形与量子漫步——我们不见不散!


互动彩蛋

评论区挑战:若允许数字中有前导零,下一个更大数的算法需要如何调整?指针反转法能否处理包含标点符号的句子?分享你的脑洞!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

司铭鸿

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值