【经典算法】LeetCode31. 下一个排列(Java/C/Python3/GO实现含注释说明,中等)

题目描述

整数数组的一个 排列  就是将其所有成员以序列或线性顺序排列。例如,arr = [1,2,3] ,以下这些都可以视作 arr 的排列:[1,2,3]、[1,3,2]、[3,1,2]、[2,3,1] 。
整数数组的 下一个排列 是指其整数的下一个字典序更大的排列。更正式地,如果数组的所有排列根据其字典顺序从小到大排列在一个容器中,那么数组的 下一个排列 就是在这个有序容器中排在它后面的那个排列。如果不存在下一个更大的排列,那么这个数组必须重排为字典序最小的排列(即,其元素按升序排列)。例如,arr = [1,2,3] 的下一个排列是 [1,3,2] 。
类似地,arr = [2,3,1] 的下一个排列是 [3,1,2] 。
而 arr = [3,2,1] 的下一个排列是 [1,2,3] ,因为 [3,2,1] 不存在一个字典序更大的排列。
给你一个整数数组 nums ,找出 nums 的下一个排列。必须 原地 修改,只允许使用额外常数空间。示例 1:输入:nums = [1,2,3]
输出:[1,3,2]
示例 2:输入:nums = [3,2,1]
输出:[1,2,3]
示例 3:输入:nums = [1,1,5]
输出:[1,5,1]提示:1 <= nums.length <= 100
0 <= nums[i] <= 100

原题:LeetCode31. 下一个排列

思路及实现

方式一:双指针法

思路

  1. 从右向左找到第一个相邻的升序对 (i, i+1),即 nums[i] < nums[i+1]。此时 i 左侧的元素(如果存在)必然降序排列。
  2. 如果找不到这样的升序对,说明整个数组是降序排列的,也就是最大的排列,那么直接翻转整个数组即可得到最小排列。
  3. 如果找到了升序对 (i, i+1),则从右向左找到第一个大于 nums[i] 的元素 nums[j]
  4. 交换 nums[i]nums[j]
  5. i+1 及其右边的所有元素反转,使其变为升序排列。

代码实现

Java版本
public class Solution {public void nextPermutation(int[] nums) {int n = nums.length;int i = n - 2;// 从右向左找到第一个升序对 (i, i+1)while (i >= 0 && nums[i] >= nums[i + 1]) {i--;}if (i >= 0) {int j = n - 1;// 从右向左找到第一个大于 nums[i] 的元素 nums[j]while (j >= 0 && nums[j] <= nums[i]) {j--;}// 交换 nums[i] 和 nums[j]swap(nums, i, j);}// 反转 i+1 及其右边的所有元素reverse(nums, i + 1, n - 1);}private void swap(int[] nums, int i, int j) {int temp = nums[i];nums[i] = nums[j];nums[j] = temp;}private void reverse(int[] nums, int start, int end) {while (start < end) {swap(nums, start, end);start++;end--;}}
}

说明:Java代码使用双指针法实现了寻找下一个排列的逻辑。首先通过i找到第一个需要变动的位置,然后通过j找到比nums[i]大的元素进行交换,最后反转i之后的元素得到下一个排列。

C语言版本
#include <stdio.h>void swap(int* a, int* b) {int t = *a;*a = *b;*b = t;
}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;while (i >= 0 && nums[i] >= nums[i + 1]) {i--;}if (i >= 0) {int j = numsSize - 1;while (j >= 0 && nums[j] <= nums[i]) {j--;}swap(&nums[i], &nums[j]);}reverse(nums, i + 1, numsSize - 1);
}

说明:C语言版本的实现和Java版本类似,只是用指针操作数组,并且去掉了类和方法的概念。

Python3版本
def nextPermutation(self, nums: List[int]) -> None:"""Do not return anything, modify nums in-place instead."""i = len(nums) - 2while i >= 0 and nums[i] >= nums[i + 1]:i -= 1if i >= 0:j =len(nums) - 1while j >= 0 and nums[j] <= nums[i]:j -= 1nums[i], nums[j] = nums[j], nums[i]left, right = i + 1, len(nums) - 1while left < right:nums[left], nums[right] = nums[right], nums[left]left += 1right -= 1

说明:Python版本实现同样使用双指针法,通过索引直接操作列表。在Python中,交换两个变量的值不需要临时变量,直接赋值即可。

Go语言版本
func nextPermutation(nums []int) {n := len(nums)i := n - 2for i >= 0 && nums[i] >= nums[i+1] {i--}if i >= 0 {j := n - 1for j >= 0 && nums[j] <= nums[i] {j--}nums[i], nums[j] = nums[j], nums[i]}reverse(nums[i+1:])
}func reverse(nums []int) {i, j := 0, len(nums)-1for i < j {nums[i], nums[j] = nums[j], nums[i]i++j--}
}

说明:Go语言版本的实现也遵循双指针法的思路。Go语言中没有内置的swap函数,所以需要手动实现。reverse函数使用切片来反转数组的一部分。

复杂度分析

  • 时间复杂度:O(n),其中n是数组的长度。整个过程中,最多只遍历了数组两次(寻找i和j,以及反转操作),因此时间复杂度是线性的。
  • 空间复杂度:O(1)。除了使用几个临时变量外,没有使用额外的空间。

方式二:使用库函数

思路

可以利用语言内置的排序函数对数组进行部分排序,以找到下一个排列。但这种方式并非题目要求的“原数组操作且只使用额外常量空间”,因此这里只作为一种扩展思路。

代码实现

方式二:使用库函数(非标准解法,违反了题目要求的只使用额外常量空间)

Java版本
import java.util.Arrays;
import java.util.Collections;public class Solution {public void nextPermutation(int[] nums) {int i = nums.length - 2;// 找到需要改变的位置while (i >= 0 && nums[i] >= nums[i + 1]) {i--;}// 如果整个数组是降序排列,则直接反转整个数组if (i < 0) {reverse(nums, 0, nums.length - 1);return;}// 找到比nums[i]大的最小元素,并交换int j = nums.length - 1;while (nums[j] <= nums[i]) {j--;}swap(nums, i, j);// 对i之后的元素进行升序排序Arrays.sort(nums, i + 1, nums.length);}// 反转数组指定范围的元素private void reverse(int[] nums, int start, int end) {while (start < end) {swap(nums, start, end);start++;end--;}}// 交换数组中两个位置上的元素private void swap(int[] nums, int i, int j) {int temp = nums[i];nums[i] = nums[j];nums[j] = temp;}
}
C++版本
#include <vector>
#include <algorithm>using namespace std;void nextPermutation(vector<int>& nums) {int i = nums.size() - 2;// 找到需要改变的位置while (i >= 0 && nums[i] >= nums[i + 1]) {i--;}// 如果整个数组是降序排列,则直接反转整个数组if (i < 0) {reverse(nums.begin(), nums.end());return;}// 找到比nums[i]大的最小元素,并交换int j = nums.size() - 1;while (nums[j] <= nums[i]) {j--;}swap(nums[i], nums[j]);// 对i之后的元素进行升序排序sort(nums.begin() + i + 1, nums.end());
}
Go版本
import ("sort"
)func nextPermutation(nums []int) {i := len(nums) - 2// 找到需要改变的位置for i >= 0 && nums[i] >= nums[i+1] {i--}// 如果整个数组是降序排列,则直接反转整个数组if i < 0 {reverse(nums)return}// 找到比nums[i]大的最小元素,并交换j := len(nums) - 1for nums[j] <= nums[i] {j--}nums[i], nums[j] = nums[j], nums[i]// 对i之后的元素进行升序排序sort.Ints(nums[i+1:])
}// 反转数组
func reverse(nums []int) {for i, j := 0, len(nums)-1; i < j; i, j = i+1, j-1 {nums[i], nums[j] = nums[j], nums[i]}
}

注意事项

这些使用库函数的

Python3版本(非标准解法)
from typing import List
import itertoolsdef nextPermutation(nums: List[int]) -> None:"""Do not return anything, modify nums in-place instead."""# 找到需要改变的位置i = len(nums) - 2while i >= 0 and nums[i] >= nums[i + 1]:i -= 1# 如果整个数组是降序排列,则直接反转整个数组if i < 0:nums.reverse()return# 找到比nums[i]大的最小元素,并交换j = len(nums) - 1while nums[j] <= nums[i]:j -= 1nums[i], nums[j] = nums[j], nums[i]# 对i之后的元素进行升序排序nums[i+1:] = sorted(nums[i+1:])

说明:Python版本使用了内置的sorted函数对数组部分进行排序,这违反了题目要求的“只使用额外常量空间”。因此,这种解法只作为扩展思路,并非标准解法。

复杂度分析

  • 时间复杂度:O(n log n),其中n是数组的长度。主要的时间消耗在排序操作上。
  • 空间复杂度:O(n),因为排序操作可能需要额外的空间来存储排序结果。

总结

方式优点缺点时间复杂度空间复杂度
方式一原数组操作,只使用额外常量空间代码实现相对复杂O(n)O(1)
方式二代码简洁,利用了内置函数违反了题目要求的原数组操作和常量空间限制O(n log n)O(n)

相似题目

相似题目难度链接
LeetCode 33中等力扣-33
LeetCode 34中等[

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/web/4361.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

微信小程序的常用API②

一、动画API &#xff08;1&#xff09;作用&#xff1a;用于在微信小程序中完成动画效果的制作 &#xff08;2&#xff09;使用&#xff1a;创建实例 wx.createAnimation() &#xff08;3&#xff09;常用属性&#xff1a; duration 【number型】 动画持续时间&…

探索Flutter 3.0:跨平台开发的新越界

Flutter 3.0 是谷歌推出的最新版本&#xff0c;它是一个开源的UI开发框架&#xff0c;可以用来创建高质量的原生接口在iOS和Android上。自从首次发布以来&#xff0c;Flutter 已经快速发展成为最受欢迎的跨平台移动开发框架之一。Flutter 3.0 带来了许多重要的更新和改进&#…

《C++学习笔记---入门篇2》---传值引用与传引用返回详解

先看这个程序&#xff0c;随着Count栈帧的销毁&#xff0c;会创建一个临时变量将n的值带回&#xff0c;可以实现我们的目的。 再看这个情况的时候&#xff0c;对于n来说他存放的位置在静态区&#xff0c;他不会随着函数栈帧的销毁而销毁&#xff0c;返回的时候依旧靠着临时变量…

Jmeter05:配置环境变量

1 Jmeter 环境 1.1 什么是环境变量&#xff1f;path什么用&#xff1f; 系统设置之一&#xff0c;通过设置PATH&#xff0c;可以让程序在DOS命令行直接启动 1.2 path怎么用 如果想让一个程序可以在DOS直接启动&#xff0c;需要将该程序目录配置进PATH 1.3 PATH和我们的关系…

golang调用阿里云发短信

之前用golang封装的一个发送阿里云短信的工具包&#xff0c;代码如下 client.go package smsimport ("context""github.com/go-playground/validator/v10""github.com/pkg/errors" )type Client interface {// Send 发送短信Send(ctx context.…

Python脚本抢票【笔记】

Python脚本抢票【笔记】 前言版权推荐Python脚本抢票【Python】microsoft edge驱动器下载以及使用最后 前言 2024-4-17 18:19:15 以下内容源自《【笔记】》 仅供学习交流使用 版权 禁止其他平台发布时删除以下此话 本文首次发布于CSDN平台 作者是CSDN日星月云 博客主页是ht…

容器工作流

背景 目前某平台使用计算容器和解析容器&#xff0c;这两种容器目前通过rabbitmq消息来进行链接&#xff0c;形成容器工作流&#xff0c;使用容器工作流框架可以省去两个容器中间环节的控制&#xff0c;不需要再使用java代码对容器的操作&#xff0c;通过容器工作流框架即可控…

SpringMVC进阶(数据格式化以及数据校验)

文章目录 1.数据格式化1.基本介绍1.基本说明2.环境搭建 2.基本数据类型和字符串转换1.需求分析2.环境搭建1.data_valid.jsp首页面2.Monster.java封装请求信息3.MonsterHandler.java处理请求信息4.monster_addUI.jsp添加妖怪界面5.单元测试 3.保存妖怪信息1.MonsterHandler.java…

路由嵌套是什么?怎么进行路由嵌套

路由嵌套&#xff1a; 此时我们从一个组件切换到另一个组件时&#xff0c;会直接跳转页面&#xff0c;所以我们需要一个东西让组件们可以在一个页面显示&#xff0c;这时候就需要进行路由嵌套。我们要找到组件之间的关系&#xff0c;大组件嵌套小组件&#xff0c;从大组件中打…

代码随想录算法训练营day6 | 242.有效的字母异位词、349. 两个数组的交集、202. 快乐数、1. 两数之和

242.有效的字母异位词 字符串仅包含小写字母&#xff0c;那么可以使用数组声明26位大小&#xff0c;遍历其中一个字符串&#xff0c;记录字符的个数&#xff0c;然后遍历另一个字符串&#xff0c;减去相应字符&#xff0c;最后都为0则符合条件 class Solution:def isAnagram(…

运维笔记:基于阿里云跨地域服务器通信(上)

运维笔记 阿里云&#xff1a;跨地域服务器通信&#xff08;上&#xff09; - 文章信息 - Author: 李俊才 (jcLee95) Visit me at CSDN: https://jclee95.blog.csdn.netMy WebSite&#xff1a;http://thispage.tech/Email: 291148484163.com. Shenzhen ChinaAddress of this a…

构建数据驱动的文化价值体系,还得靠数据分析

在您的业务中创建以数据为中心的文化需要思维方式的转变&#xff0c;这不会在一夜之间发生。但是&#xff0c;这是一个目标变得越来越容易实现&#xff0c;这是由您的员工以激情和热情朝着这个目标努力的驱动力所驱使的&#xff0c;而不是高层要求他们这样做的目标。 有多种因…

Redis面试题超详细(2024最新)

1、Redis是单线程执行还是多线程执行&#xff1f;它有线程安全问题吗&#xff1f;为什么吗&#xff1f; Redis版本在6.0之前都是使用的单线程运行的。所有的客户端的请求处理、命令执行以及数据读写操作都是在一个主线程中完成得。这种设计目的就是为了防止多线程环境下的锁竞争…

git变更远端仓库名之后如何修改本地仓库配置的另一种方法?(删remote指针、添加、绑定master)

背景 如果某个远端的仓库地址变化后&#xff0c;本地仓库可以修改对应的remote。 之前谈过几种方法&#xff0c;比如重新设置一个新的remote的指针&#xff0c;绑定到新地址。然后删除origin&#xff0c;然后把新指针mv到origin。比如直接seturl修改&#xff08;git remote se…

深度学习从入门到精通——词向量介绍及应用

词向量介绍 词向量&#xff08;Word embedding&#xff09;&#xff0c;即把词语表示成实数向量。“好”的词向量能体现词语直接的相近关系。词向量已经被证明可以提高NLP任务的性能&#xff0c;例如语法分析和情感分析。词向量与词嵌入技术的提出是为了解决onehot的缺陷。它把…

ESP32-S3的MQTT实战

昨天&#xff0c;我们讲了socket通信&#xff0c;当服务器和客户端建立起连接时&#xff0c;就可以互相通信了。在互联网应用大多使用WebSocket接口来传输数据。而在物联网的应用中&#xff0c;常常出现这种情况&#xff1a;海量的传感器&#xff0c;需要时刻保持在线&#xff…

微信小程序[黑马笔记]

简介 常用组件 视图组件 <!--pages/list/list.wxml--><scroll-view class"container1" scroll-y><view>A</view><view>B</view><view>A</view></scroll-view><!--pages/list2/list.wxml--><swiper …

❤mac使用Idea工具

❤mac使用Idea工具 1、安装 直接跳过&#xff0c;文章有 &#xff08;点击跳转&#xff09; 给自己的mac系统上安装java环境 2、使用 快捷键 Command , 系统首选项 设置Idea连接数据库 打开右侧的database&#xff08;或菜单里&#xff09;连接数据库&#xff0c;根据提…

ijkplayer iOS编译问题之[-Wincompatible-function-pointer-types]

编译环境 Apple M1 Pro Sonoma 14.1.2 编译的时候出现如下报错&#xff1a; libavcodec/aarch64/h264dsp_init_aarch64.c:84:38: error: incompatible function pointer types assigning to h264_weight_func (aka void (*)(unsigned char *, long, int, int, int, int)) from…

Ubuntu中的 Everything 搜索软件 ==> fsearch

本文所使用的 Ubuntu 系统版本是 Ubuntu 22.04 ! 在 Windows 中&#xff0c;我经常使用 Everything 来进行文件搜索&#xff0c;搜索效率比 Windows 自带的高出千百倍。 那么在 Ubuntu 系统中&#xff0c;有没有类似的软件呢&#xff1f;那必须有&#xff0c;它就是 FSearch 。…