LeetCode高频题:请你打印int数字x的所有全排列,x只包含且必须包含1-9的每一个数字

LeetCode高频题:请你打印int数字x的所有全排列,x只包含且必须包含1-9的每一个数字

提示:本题是系列LeetCode的150道高频题,你未来遇到的互联网大厂的笔试和面试考题,基本都是从这上面改编而来的题目
互联网大厂们在公司养了一大批ACM竞赛的大佬们,吃完饭就是设计考题,然后去考应聘人员,你要做的就是学基础树结构与算法,然后打通任督二脉,以应对波云诡谲的大厂笔试面试题!
你要是不扎实学习数据结构与算法,好好动手手撕代码,锻炼解题能力,你可能会在笔试面试过程中,连题目都看不懂!比如华为,字节啥的,足够让你读不懂题
在这里插入图片描述


题目

请你打印int数字x的所有全排列,x只包含且必须包含1-9的每一个数字
x只包含1-9
而且x必须包含1-9
请你输出x的所有排列情况


一、审题

示例:
123456789
213456789
321456789
423156789
523416789
623451789
723456189
823456719
923456781

……
总之,只包含1-9的每个数字

各种情况
挺多的


二、解题

其实从示例中你也能看出来的
不就是高位那个1跟所有低位分别交换一次吗????

int类型的数字,如何快速交换,我们讲过的!
【1】LeetCode高频题:如何不用int转字符数组,实现int类型数字x的第L位和R位数字交换

int类型的数字x将L和R位置交换,是这么干的

    //就把x的L=7位置和R=2位置的俩数字交换了
    public static final int[] arr = {1,10,100,1000,10000,100000,1000000,10000000,100000000};
    //准备好常量arri表示1后面跟i个0

    public static int swap(int x, int L, int R){
        //将x的L和R处数字交换
        //首先把L和R处数字提取出来,就可以利用加减法换L和R
        int numL = x / arr[L] % 10;
        int numR = x / arr[R] % 10;
        //L这里是x-([L]-[R])*10的L次方=a
        //R这里是a+([L]-[R])*10的R次方
        int a = x - (numL - numR) * arr[L];//完成L变R
        int b = (numL - numR) * arr[R];//完成R变L

        return a + b;//最终L和R调换,以后千万别把int转char[],这样很费时间的
    }

普通的数组是这么交换的:

        public void swap(int[] arr, int i, int j){
            int tmp = arr[i];
            arr[i] = arr[j];
            arr[j] = tmp;
        }

显然数组更方便,但是int数字的交换不能转化为数组,否是速度很慢!

而当初我们也求过字符数组,或者int数组的全排列,跟现在要求的int数字的全排列,一模一样,我也讲过的,无非就是让目标index位置
跟index–N-1所有的位置都交换一波,剩下的当一个整体,继续递归玩

【2】LeetCode高频题46. 全排列

数组类型的arr,全排列用数组交换办,很简单


        //复习:依次从arr的i位置决定谁做排头,然后去i+1继续做决定
        public void f(int[] arr, int i, List<List<Integer>> ans){
            //直到i越界,收集此刻的arr
            if (i == arr.length){
                //每次都要把arr转List
                List<Integer> cur = new ArrayList<>();
                for(Integer x : arr) cur.add(x);//全部加入
                ans.add(cur);//作为一个排列组合结果,放入ans
            }else {//没有越界,决定让i跟谁换?
                //来此来到i,让i和j=i--N-1交换一次,剩下的数字继续递归i+1位置,但是干的事情,又是这个递归
                for (int j = i; j < arr.length; j++) {
                    swap(arr, i, j);
                    //不断往下,基本就让所有的数字做了开头,然后还考虑别的数字依次做了开头的情况
                    //就是排列组合,每个数字做一次开头,剩下的数字继续缩短长度,
                    // 又玩一次每个数字做一次开头,这样枚举下去
                    f(arr, i + 1, ans);
                    //返回上一层,你上次怎么交换的,给我换回来,恢复现场,
                    // 我方便在i-1层继续让i和别的j位置交换,让另外的j来做排头
                    swap(arr, j, i);
                }
            }
        }

        //主函数,就从i=0开始调用递归
        public List<List<Integer>> permuteReview(int[] nums) {
            List<List<Integer>> ans = new ArrayList<>();

            //从i=0开始决定谁做排头,收集一波答案
            f(nums, 0, ans);

            return ans;
        }

【1】【2】俩文章综合就是咱们要办的事情:只不过int交换和数组交换不是一个做法,思想一样
目的就是让index处,从1–9固定做一次派头之后,剩下的数字又从index+1开始当新的数字x,
去递归固定index处的数字,,从1–9固定做一次派头之后,剩下的数字又从index+1开始当新的数字x……
在这里插入图片描述
我想想需要恢复现场吗?
这个好像不用,你f递归完之后,目前的x,它不是数组,x就是x本身,你怎么玩都是没关系的

我定义递归函数f(x,index)
来到x的index位,先把x固定一个数字做排头(1–9都要尝试排头),剩下的x从index+1位置往后去继续递归

撸代码:

    //我定义递归函数**f(x,index)**
    //来到x的index位,先把x固定一个数字做排头(1--9都要尝试排头),剩下的x从index+1位置往后去继续递归
    public static void f(int x, int index){
        if (index == -1){
            System.out.println(x);//index来到-1处,说明8--0位置的排头决定好了
        }else {
            //来到index,让谁来做排头?index--N-1位置每个数字轮流做,不是数组,不需要恢复现场
            for (int j = index; j >= 0; j--) {//从高位L跟低位R=j处换
                int next = swap(x, index, j);//index和j换数字
                f(next, index - 1);//然后去index=1处玩剩下的数字——不需要恢复现场,x没有动过
                //待会f结束,x依然是x,然j轮流做排头就行了
            }
        }
    }

非常容易的,x之所有不需要恢复现场,是因为咱们next操作的时候,没有动过x
所以f递归出来之后啊,x还是x没动,该交换谁跟谁交换即可
每次index–,咱们从高位8—0位去交换,不是index+1哦

测试一把:

    public static void test(){
        int x = 123456789;
//        System.out.println(swap(x, 7, 2));
        f(x, 8);//8--0这9个数字
    }

    public static void main(String[] args) {
        test();
    }

速度非常快,就枚举完了,所以我只打印最后几个数字
就是9做了排头,然后枚举完成

912345876
912345867
912345687
912345678

太多了

复杂度多少呢???
9个数字全排列,1个位置9种情况,剩下的就是8种情况了,……
实际上就是9!=362880次交换操作
才36万次枚举
这远远小于10的8次方【java要求的4s内数据运行次数】

就是这么暴力地枚举,速度也足够快!!!


总结

提示:重要经验:

1)和之前讲过的全排列一样,就是让arr【x】的index和index–N-1位置交换,每一个位置做一次排头即可
2)只不过arr的交换很容易,但是int数字的x交换稍微做个除法取模加减法转换一下,速度快,千万不能把int类型x转化为字符数组去交换,再连乘去转数字,这样速度极其慢的
3)笔试求AC,可以不考虑空间复杂度,但是面试既要考虑时间复杂度最优,也要考虑空间复杂度最优。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

冰露可乐

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

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

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

打赏作者

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

抵扣说明:

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

余额充值