当前位置: 首页 > article >正文

C语言实现力扣第31题:下一个排列

C语言实现力扣第31题:下一个排列(Next Permutation)

题目描述

题目要求找到一个数组的下一个排列,即字典序中比当前数组更大的下一个排列。如果不存在这样的排列,就将数组重置为最小的排列(即按升序排序)。我们只需在原地对数组进行修改,不能使用额外的空间。

示例

  • 输入:{1, 2, 3}
  • 输出:{1, 3, 2}

思路分析

  1. 从后向前查找第一个下降的元素
    从数组末尾开始,找到第一个比其右侧元素小的元素位置 i。这个元素是需要调整的位置。

  2. 从后向前查找第一个比 nums[i] 大的元素
    再次从数组末尾出发,找到第一个比 nums[i] 大的元素,将其位置记为 j

  3. 交换 nums[i]nums[j]
    交换 ij 位置的元素,以保证找到的排列比当前排列更大。

  4. 反转 i 后面的元素
    为了得到字典序最小的下一个排列,将 i 后面的元素(降序部分)进行反转,使其变为升序。

通过这四步,我们可以得到所需的下一个排列。

C语言代码实现

#include <stdio.h>

// 辅助函数:交换两个整数的值
void swap(int* a, int* b) {
    int temp = *a;
    *a = *b;
    *b = temp;
}

// 辅助函数:反转数组指定范围内的元素
void reverse(int* nums, int start, int end) {
    while (start < end) {
        swap(&nums[start], &nums[end]);
        start++;
        end--;
    }
}

// 实现下一个排列的函数
void nextPermutation(int* nums, int numsSize) {
    int i = numsSize - 2;
    
    // Step 1: 从右到左找到第一个下降的位置
    while (i >= 0 && nums[i] >= nums[i + 1]) {
        i--;
    }
    
    if (i >= 0) {
        // Step 2: 从右到左找到第一个大于 nums[i] 的元素
        int j = numsSize - 1;
        while (nums[j] <= nums[i]) {
            j--;
        }
        // Step 3: 交换 nums[i] 和 nums[j]
        swap(&nums[i], &nums[j]);
    }
    
    // Step 4: 反转 i+1 到末尾的元素
    reverse(nums, i + 1, numsSize - 1);
}

// 主函数:测试用例
int main() {
    int nums[] = {1, 2, 3};
    int numsSize = sizeof(nums) / sizeof(nums[0]);

    nextPermutation(nums, numsSize);

    printf("Next permutation: ");
    for (int i = 0; i < numsSize; i++) {
        printf("%d ", nums[i]);
    }
    printf("\n");

    return 0;
}

代码逐行讲解

辅助函数 swap
void swap(int* a, int* b) {
    int temp = *a;
    *a = *b;
    *b = temp;
}

这个函数用于交换两个整数的值。我们在交换 nums[i]nums[j] 时会用到它。

辅助函数 reverse
void reverse(int* nums, int start, int end) {
    while (start < end) {
        swap(&nums[start], &nums[end]);
        start++;
        end--;
    }
}

reverse 函数用来反转数组的指定范围,从索引 startend。通过交换两端元素并逐渐向中间靠拢,最终可以将数组局部反转。

主函数 nextPermutation
void nextPermutation(int* nums, int numsSize) {
    int i = numsSize - 2;
    
    // Step 1: 从右到左找到第一个下降的位置
    while (i >= 0 && nums[i] >= nums[i + 1]) {
        i--;
    }
  • 我们从数组的倒数第二个元素开始向左遍历,直到找到第一个 nums[i] < nums[i+1] 的位置。这是变化的起点。
  • 如果数组已经是降序排列(如 {3, 2, 1}),则 i 会最终为 -1。
    if (i >= 0) {
        // Step 2: 从右到左找到第一个大于 nums[i] 的元素
        int j = numsSize - 1;
        while (nums[j] <= nums[i]) {
            j--;
        }
        // Step 3: 交换 nums[i] 和 nums[j]
        swap(&nums[i], &nums[j]);
    }
  • 如果 i >= 0,说明我们找到了一个下降点 i,则从数组右侧找到第一个大于 nums[i] 的元素 nums[j]
  • 交换 nums[i]nums[j],使排列变为比当前排列稍大的状态。
    // Step 4: 反转 i+1 到末尾的元素
    reverse(nums, i + 1, numsSize - 1);
}
  • 最后,我们将 i+1 到末尾的部分反转,使其成为升序,从而达到字典序最小的要求。
主函数 main
int main() {
    int nums[] = {1, 2, 3};
    int numsSize = sizeof(nums) / sizeof(nums[0]);

    nextPermutation(nums, numsSize);

    printf("Next permutation: ");
    for (int i = 0; i < numsSize; i++) {
        printf("%d ", nums[i]);
    }
    printf("\n");

    return 0;
}
  • main 函数用于测试。初始化一个数组 nums,调用 nextPermutation 函数后,打印得到的下一个排列。

运行示例

假设输入数组为 {1, 2, 3},输出如下:

Next permutation: 1 3 2

复杂度分析

  • 时间复杂度:O(n),其中 n 是数组的长度,最多进行两次遍历。
  • 空间复杂度:O(1),我们在原地修改数组,不需要额外空间。

http://www.kler.cn/a/379509.html

相关文章:

  • Lucene分析器的详细使用(5)
  • Linux和,FreeRTOS 任务调度原理,r0-r15寄存器,以及移植freertos(一)
  • 操作系统(10) (并发(2)------基于软件/硬件/操作系统层面解决两个进程之间的临界区问题/抢占式/非抢占式内核)
  • solidity selfdestruct合约销毁
  • 面试题:JVM(三)
  • WebStorm技巧
  • 重大917该如何复习?难度大不大?重点是啥?
  • Bacnet+springboot部署到linux后,无法检测到网络中的其他设备
  • 项目解决方案:跨不同的物理网络实现视频监控多画面的实时视频的顺畅访问
  • 代码笔录1
  • LeetCode25:K个一组翻转链表
  • LeetCode 19. 删除链表的倒数第 N 个结点(java)
  • Java Iterator 实现杨辉三角
  • Redis 补充概念
  • Unity 6 基础教程(Unity 界面)
  • 百度搜索引擎的工作原理
  • javaScript-----一维数组和数组对象去重的多种方法
  • 使用 MySQL Workbench 创建和管理用户
  • 手册更新 | RK3568开发板Openwrt文件系统构建
  • ClkLog企业版(CDP)预售开启,更有鸿蒙SDK前来助力
  • Win/Linux/Kylin 系统安装指定版本 jdk(8u171为例)
  • 学习记录:js算法(八十四):子集 II
  • vue系列==vue组件
  • sparkSQL面试题
  • Go语言sync.WaitGroup与errgroup.Group用法详解
  • 迅为itop-3568开发板AMP双系统使用手册之烧写AMP镜像