Leetcode 41-50刷题笔记(非困难题目)

46.全排列

全排列的概念我们在高中就学习过了,就题目所给的[1,2,3]三个数的全排列我们都知道一共有3 * 2 = 6个。

简单点就是第一位有3种选择,第二位有2种选择,第三位只有1中选择
,所以是3 * 2 * 1 = 6。

根据上面我们可以思考得到,其实就是针对每一位的放置需要进行特殊的考虑。

所以该题的话,我们可以采用回溯算法,通过更改对应位置(第一位,第二位。。。)的元素并添加到list中,并且递归到下一位(此时可选元素会 - 1),这样的方式完成该题目

    //全排列
    public List<List<Integer>> permute(int[] nums) {
        List<List<Integer>> lists = new ArrayList<>();
        if(nums.length == 0) {
            return lists;
        }

        dfs(lists,new ArrayList<>(), nums, 0);
        return lists;
    }

    public void dfs(List<List<Integer>> lists, List<Integer> list, int[] nums, int index) {
        if(list.size() == nums.length) {
            lists.add(new ArrayList<>(list));
            return;
        }

        for(int i=index; i<nums.length; i++) {
            //交换
            swap(nums,index,i);
            //添加自身(这里注意是添加 index 位置)
            list.add(nums[index]);
            dfs(lists, list, nums, index + 1);
            //回溯
            list.remove(list.size() - 1);
            //交换回来(记得复原)
            swap(nums,index,i);
        }
    }

    public void swap(int[] nums, int index1, int index2) {
        if(index1 == index2) {
            return;
        }

        int temp = nums[index1];
        nums[index1] = nums[index2];
        nums[index2] = temp;
    }

47.全排列2

该题与46不同点在于,它数组存在重复元素。

针对于重复元素不外乎就是防止重复处理,比如两个 0(1) 0(2)(我们这里对两个0进行标记),我们应该只处理一次 1 2 或者 2 1。

这道题我一开始使用的和46题相同的方法(交换的方法),但是由于交换会导致数组顺序改变(所以无法完成对重复元素的判断),所以这里的话我们采取另一个方式进行全排列的回溯搜(添加了一个boolean数组,对元素的访问进行记录)。

这里我们采用将重复元素按照顺序进行访问的方式进行使用(即如果碰到重复元素,你的前一个重复元素如果未使用则无法使用当前重复元素),通俗点说就是如果0(1)未使用则无法使用0(2)。


    boolean[] flag;
    //全排列
    public List<List<Integer>> permuteUnique(int[] nums) {
        Arrays.sort(nums);
        List<List<Integer>> lists = new ArrayList<>();
        if(nums.length == 0) {
            return lists;
        }
        flag = new boolean[nums.length];
        dfs(lists,new ArrayList<>(), nums, 0);
        return lists;
    }

    public void dfs(List<List<Integer>> lists, List<Integer> list, int[] nums, int index) {
        if(list.size() == nums.length) {
            lists.add(new ArrayList<>(list));
            return;
        }

        for(int i=0; i<nums.length; i++) {
            //处理重复情况(代表重复部分是有顺序的)
            //比如两个0(0(1),0(2),应该只处理一次,而不是处理两次)
            if(flag[i] || (i > 0 && nums[i] == nums[i - 1] && flag[i - 1])) {
                continue;
            }
            //代表已被访问
            flag[i] = true;
            //添加自身(这里注意是添加 index 位置)
            list.add(nums[index]);
            dfs(lists, list, nums, index + 1);
            //回溯
            list.remove(index);
            flag[i] = false;
        }
    }

48.旋转图像

简单点说就是顺时针旋转90°一个二维数组。

这里的思路是先对该二维数组进行转置处理,然后再对每一行的数组进行数组反转。(该题更偏向一个数学的方向)

  public void rotate(int[][] matrix) {
    int n = matrix.length;

    // transpose matrix
    for (int i = 0; i < n; i++) {
      for (int j = i; j < n; j++) {
        int tmp = matrix[j][i];
        matrix[j][i] = matrix[i][j];
        matrix[i][j] = tmp;
      }
    }
    // reverse each row
    for (int i = 0; i < n; i++) {
      for (int j = 0; j < n / 2; j++) {
        int tmp = matrix[i][j];
        matrix[i][j] = matrix[i][n - j - 1];
        matrix[i][n - j - 1] = tmp;
      }
    }
  }

49.字母异位词分组

字母异位即代表同样的字符个数和种类, 但是排列顺序不同。

这个题有个技巧是:使用字符排序,这样就可以判定那些字符串是异位词。(主要就是使用一个Map)

    public List<List<String>> groupAnagrams(String[] strs) {
        List<List<String>> lists = new ArrayList<>();
        Map<String,List<String>> map = new HashMap<>();

        for(String str : strs) {
            char[] a = str.toCharArray();
            Arrays.sort(a);
            String ans = String.valueOf(a);
            if(map.containsKey(ans)) {
                map.get(ans).add(str);
            }else {
                List<String> list = new ArrayList<>();
                list.add(str);
                map.put(ans,list);
            }
        }
        Set<String> set = map.keySet();
        for(String str : set) {
            lists.add(map.get(str));
        }

        return lists;
    }

50.Pow(x,n)

该题目就是一个手写pow()方法的具体实现。

如果使用暴力法O(N)会导致超过规定时间,这里的话可以根据指数特性进行拆分(即二分法)。

众所周知,指数特性:(a为底数,n为指数)

  1. 当n为偶数时。a^n = a^(n / 2) * a^(n / 2)。
  2. 当n为奇数时,a^n = a^(n / 2) * a^(n / 2) * a。

通过这个数学公式,我们可以采用递归的方法,获得拆分后的数,再根据是否能被2整除进行选用采取哪种返回方式。

    public double myPow(double x, int n) {
        //底数为 0 则返回 0
        if(x == 0.0) {
            return 0.0;
        }
        //指数为 0,幂数不为 0,则返回 1
        if(n == 0) {
            return 1.0;
        }
        //标记指数的政府,后面
        boolean symbol = true;
        if(n < 0) {
            symbol = false;
            //如果为负数,则先反转并标记,等返回值时候再进行处理
            n = -n;
        }

        //将指数折半,指数翻倍(这样时间复杂度为logn)
        return symbol ? dfs(x,n) : 1.0 / dfs(x,n);
    }

    //将a^n 拆分成 a^n/2 * a^n/2 / a^n/2 * a^(n / 2) + 1 = a^n/2 * a^(n / 2) * a
    //主要是分指数能否被2整除
    public double dfs(double num, int n) {
        if(n == 0) {
            return 1.0;
        }
        double half = dfs(num,n/2);

        return n % 2 == 0 ? half * half : half * half * num;
    }
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值