Leetcode初体验生存向——Day6

51. N-Queens(Hard)

Question:

The n-queens puzzle is the problem of placing n queens on an n×n chessboard such that no two queens attack each other.


Given an integer n, return all distinct solutions to the n-queens puzzle.

Each solution contains a distinct board configuration of the n-queens’ placement, where ‘Q’ and ‘.’ both indicate a queen and an empty space respectively.

Example:

Input: 4
Output: [
 [".Q..",  // Solution 1
  "...Q",
  "Q...",
  "..Q."],

 ["..Q.",  // Solution 2
  "Q...",
  "...Q",
  ".Q.."]
]
Explanation: There exist two distinct solutions to the 4-queens puzzle as shown above.

Solution:

/**
 * 典型的回溯问题
 * 但是这道题的题解没用回溯,把二维数组降成了一维数组的全排列问题
 * 象棋中皇后的走位是横竖与两个对角,如果要实现使皇后互相吃不到摆放,也就是必须要避开每一个皇后的这几个方位
 * 横向很简单,只要每次摆放皇后都换行就行了
 * 竖向就把n列的下标设为0到n-1
 * 左上到右下的对角线的每个位置满足行下标减列下标相等,就在一条对角线上
 * 右上到左下的对角线的每个位置满足行下标加列下标相等,就在一条对角线上
 * 由此利用回溯算法,判断这些方位上有没有皇后就行了
 *
 * 因为行都不一样,因此只需要考虑列,那么做一个一维数组pre用来存放皇后所出现过的每一列
 */
class Solution {

    List<List<String>> ans=new ArrayList<>(); // 存放结果集

    private void helper(int n, int row, int[] pre) {

        // 所有的皇后位置都找完了
        if(row == n) {
            List<String> cur = generate(pre,n);
            ans.add(new ArrayList<>(cur));
            return;
        }

        // 检查之前几行的皇后是否冲突
        // 判断每一列
        for(int col = 0 ; col < n ; col++){
            boolean flag = true; // flag = true表示皇后没有冲突
            // 判断截止到row的前几行
            for(int j = 0 ; j < row ; j++){
                // 行列下标相加相等说明是左斜线上 ,行减去列下标相等说明是右斜线上,列和列相等说明在同一列
                // 如果在,就说明皇后冲突,退出当前for循环,继续
                // 这里row+col是当前位置的行和列相加
                // pre[j]看当前行有没有皇后,有的话就会返回皇后所在的列
                // pre[j]+j=当前遍历的行上皇后所在的列+当前遍历的行
                // 这样等于是判断当前位置在不在之前放置的皇后在的对角线上
                // 但凡一个在,都要返回错
                if(row + col == pre[j] + j || row - col == j - pre[j] || col == pre[j]) {
                    flag = false; // 出现冲突
                    break;
                }
            }
            // 如果没有冲突,放置皇后
            if(flag){
                pre[row] = col; // 把皇后所在列对应行放入数组
                helper(n, row + 1, pre); // 继续找下一行的皇后
            }
        }
    }

    // 把皇后位置写出来
    private List<String> generate(int[] queens, int n) {
        List<String> board = new ArrayList<String>();
        for (int i = 0 ; i < n ; i++) {
            char[] row = new char[n];
            Arrays.fill(row, '.');
            row[queens[i]] = 'Q';
            board.add(new String(row));
        }
        return board;
    }

    public List<List<String>> solveNQueens(int n) {
        // 一维数组pre存放皇后所在列的位置,数组下标为行,存放数字为列
        // 如[0,-1,1,-1]代表第0行0列有一个皇后,2行1列有一个皇后
        int[] pre = new int[n];
        Arrays.fill(pre, -1); // 把数组用-1填满
        helper(n, 0, pre); // 从第一行开始,row=0
        return ans;
    }
}

链接:https://leetcode-cn.com/problems/n-queens

52. N-Queens II(Easy)

Question:

The n-queens puzzle is the problem of placing n queens on an n×n chessboard such that no two queens attack each other.

Given an integer n, return the number of distinct solutions to the n-queens puzzle.

Example:

Input: 4
Output: 2
Explanation: There are two distinct solutions to the 4-queens puzzle as shown below.
[
 [".Q..",  // Solution 1
  "...Q",
  "Q...",
  "..Q."],

 ["..Q.",  // Solution 2
  "Q...",
  "...Q",
  ".Q.."]
]

Solution:

和上一题比起来只是去掉了画结果,找完一组就加1而已。

class Solution {

    int ans = 0;

    private void helper(int n, int row, int[] pre) {

        // 所有的皇后位置都找完了
        if(row == n) {
            ans++;
            return;
        }

        // 检查之前几行的皇后是否冲突
        // 判断每一列
        for(int col = 0 ; col < n ; col++){
            boolean flag = true; // flag = true表示皇后没有冲突
            // 判断截止到row的前几行
            for(int j = 0 ; j < row ; j++){
                // 行列下标相加相等说明是左斜线上 ,行减去列下标相等说明是右斜线上,列和列相等说明在同一列
                // 如果在,就说明皇后冲突,退出当前for循环,继续
                // 这里row+col是当前位置的行和列相加
                // pre[j]看当前行有没有皇后,有的话就会返回皇后所在的列
                // pre[j]+j=当前遍历的行上皇后所在的列+当前遍历的行
                // 这样等于是判断当前位置在不在之前放置的皇后在的对角线上
                // 但凡一个在,都要返回错
                if(row + col == pre[j] + j || row - col == j - pre[j] || col == pre[j]) {
                    flag = false; // 出现冲突
                    break;
                }
            }
            // 如果没有冲突,放置皇后
            if(flag){
                pre[row] = col; // 把皇后所在列对应行放入数组
                helper(n, row + 1, pre); // 继续找下一行的皇后
            }
        }
    }

    public int totalNQueens(int n) {
        // 一维数组pre存放皇后所在列的位置,数组下标为行,存放数字为列
        // 如[0,-1,1,-1]代表第0行0列有一个皇后,2行1列有一个皇后
        int[] pre = new int[n];
        Arrays.fill(pre, -1); // 把数组用-1填满
        helper(n, 0, pre); // 从第一行开始,row=0
        return ans;
    }
}

链接:https://leetcode-cn.com/problems/n-queens-ii

53. Maximum Subarray(Easy)

Question:

Given an integer array nums, find the contiguous subarray (containing at least one number) which has the largest sum and return its sum.

Follow up: If you have figured out the O(n) solution, try coding another solution using the divide and conquer approach, which is more subtle.

Example 1:

Input: nums = [-2,1,-3,4,-1,2,1,-5,4]
Output: 6
Explanation: [4,-1,2,1] has the largest sum = 6.

Example 2:

Input: nums = [1]
Output: 1

Example 3:

Input: nums = [0]
Output: 0

Example 4:

Input: nums = [-1]
Output: -1

Example 5:

Input: nums = [-2147483647]
Output: -2147483647

Constraints:

  • 1 <= nums.length <= 2 * 104
  • -231 <= nums[i] <= 231 - 1

Solution:

/*
 * 动态规划法
 * 给定例子:[3,-3,-3,1,3,6]
 * pre = Math.max(pre+x, x)一句的作用是判断继续使用之前的子数组还是选用从当前开始
 * 如判断第二位时:pre=3, x=-3, pre+x=0 前两位的和大于第二位,因此不改变子数组[3,-3]
 *   判断第三位时:pre=0, x=-3, pre+x=-3 两者相同,从哪里开始都一样,子数组为[3,-3,-3]
 *   判断第四位时:pre=-3, x=1, pre+x=-2 明显从第四位开始数目更大,因此改变子数组为[1]
 * maxAns = Math.max(maxAns, pre)一句的作用是避免前面有更大的数字因为负数被掩盖(这里的pre是max后新的pre)
 * 如判断第一位时: pre=3, maxAns=3 对于答案来说的子数组为[3]
 *   判断第二位时:pre=0, maxAns=3 对于答案来说的子数组为[3]
 *   判断第三位时:pre=-3, maxAns=3 对于答案来说的子数组为[3]
 *   判断第四位时:pre=1, maxAns=3 对于答案来说的子数组为[3]
 *   判断第五位时:pre=4, maxAns=4 对于答案来说的子数组为[1,3]
 *   判断第六位时:pre=10, maxAns=10 对于答案来说的子数组为[1,3,6]
 *
 * 如上所述,在本题中要得到的答案可以分解为子问题:max{f(i)},f(i)为以第i个数结尾的连续子数组的最大和
 * 状态转移即是当前是否为最大值f(i)=max{f(i-1)+ai, ai}
 * 
 * 分治法可太难了:https://leetcode-cn.com/problems/maximum-subarray/solution/zui-da-zi-xu-he-by-leetcode-solution/
 *
 */
class Solution {
    public int maxSubArray(int[] nums) {
        int pre = 0, maxAns = nums[0];
        for (int x : nums) {
            // 这一句妙啊,用来推断当前最大值,即是继续选前面的还是当前开始
            pre = Math.max(pre + x, x);
            maxAns = Math.max(maxAns, pre);
        }
        return maxAns;
    }
}

链接:https://leetcode-cn.com/problems/maximum-subarray

54. Spiral Matrix(Middle)

Question:

Given a matrix of m x n elements (m rows, n columns), return all elements of the matrix in spiral order.

Example 1:

Input:
[
[ 1, 2, 3 ],
[ 4, 5, 6 ],
[ 7, 8, 9 ]
]
Output: [1,2,3,6,9,8,7,4,5]

Example 2:

Input:
[
[1, 2, 3, 4],
[5, 6, 7, 8],
[9,10,11,12]
]
Output: [1,2,3,4,8,12,11,10,9,5,6,7]

Solution:

/**
 * 没什么可说的,转圈遍历就完了,主要是改变元素下标,然后判断遍历何时结束
 */
class Solution {
    public List<Integer> spiralOrder(int[][] matrix) {

        // 定义一个结果集
        LinkedList<Integer> res = new LinkedList<Integer>();

        // 老规矩,上来先把空矩阵的情况排除
        if (matrix == null || matrix.length == 0) return res;

        // 初始化一些变量
        int left = 0; // 矩阵左指针
        int right = matrix[0].length - 1; // 矩阵右指针
        int top = 0; // 矩阵上指针
        int bottom = matrix.length - 1; // 矩阵下指针
        int eleNum = matrix[0].length * matrix.length; // 矩阵中元素数量(矩阵大小),长*宽获得

        // 开始螺旋遍历,结束条件为矩阵中不再有元素
        while (eleNum > 0) {
            // 遍历最上面一排
            for (int i = left ; i <= right && eleNum > 0 ; i++) {
                res.add(matrix[top][i]); // 把遍历的元素加入结果集
                eleNum--; // 矩阵元素个数减1
            }
            // 最上面一排遍历结束,top指针下移一排
            top++;
            // 遍历最右边一列
            for (int i = top ; i <= bottom && eleNum > 0 ; i++) {
                res.add(matrix[i][right]);
                eleNum--;
            }
            // 最右边一列遍历结束,right指针左移一列
            right--;
            // 遍历最下面一排
            for (int i = right ; i >= left && eleNum > 0 ; i--) {
                res.add(matrix[bottom][i]);
                eleNum--;
            }
            // 最下面一排遍历结束,bottom指针上移一排
            bottom--;
            // 遍历最左面一列
            for (int i = bottom ; i >= top && eleNum > 0 ; i--) {
                res.add(matrix[i][left]);
                eleNum--;
            }
            // 最左面一列遍历结束,left指针右移一列
            left++;
        }
        return res;
    }
}

链接:https://leetcode-cn.com/problems/spiral-matrix

55. Jump Game(Middle)

Question:

Given an array of non-negative integers, you are initially positioned at the first index of the array.

Each element in the array represents your maximum jump length at that position.

Determine if you are able to reach the last index.

Example 1:

Input: nums = [2,3,1,1,4]
Output: true
Explanation: Jump 1 step from index 0 to 1, then 3 steps to the last index.

Example 2:

Input: nums = [3,2,1,0,4]
Output: false
Explanation: You will always arrive at index 3 no matter what. Its maximum jump length is 0, which makes it impossible to reach the last index.

Constraints:

  • 1 <= nums.length <= 3 * 10^4
  • 0 <= nums[i][j] <= 10^5

Solution:

/**
 * 贪心算法
 * 与前面有一道题类似
 */
class Solution {
    public boolean canJump(int[] nums) {
        int len = nums.length;
        int rightmost = 0;
        for (int i = 0 ; i < len ; i++) {
            if (i <= rightmost) {
                // rightmost是当前位置能走的最大距离,i+nums[i]是先走小于rightmost的距离再走当时的最大距离
                rightmost = Math.max(rightmost, i + nums[i]);
                if (rightmost >= len - 1) return true;
            }
        }
        return false;
    }
}

链接:https://leetcode-cn.com/problems/jump-game

56. Merge Intervals(Middle)

Question:

Given a collection of intervals, merge all overlapping intervals.

Example 1:

Input: intervals = [[1,3],[2,6],[8,10],[15,18]]
Output: [[1,6],[8,10],[15,18]]
Explanation: Since intervals [1,3] and [2,6] overlaps, merge them into [1,6].

Example 2:

Input: intervals = [[1,4],[4,5]]
Output: [[1,5]]
Explanation: Intervals [1,4] and [4,5] are considered overlapping.

NOTE: input types have been changed on April 15, 2019. Please reset to default code definition to get new method signature.

Constraints:

  • intervals[i][0] <= intervals[i][1]

Solution:

/**
 * 排序法,重写Comparator比较器
 * 数学思想略有些难都
 * 首先如果要判断多个区间的话,就要排除左右两个边界都要考虑的情况,即先按照左边界进行排序
 * 按照左边界进行排序后,只考虑右边界就可以了,当a[1]<b[0]说明无交集,a[1]>=b[1]说明有交集
 * 结果集设为merged,因为左边界已经排序完毕,当出现无交集的情况,则说明当前集合已经混合好了,该看下一个没交集的那个集合了
 */
class Solution {
    public int[][] merge(int[][] intervals) {
        // 排除intervals空数组的情况
        if (intervals.length == 0) return new int[0][2];

        // 重写比较器方法,接口如下:
        // public interface Comparator<T> {
        //   int compare(T o1, T o2);
        //   boolean equals(Object obj);
        // }
        Arrays.sort(intervals, new Comparator<int[]>() {
            public int compare(int[] interval1, int[] interval2) {
                // 比较左边界,即数组的0位
                // 如果返回值为负数,interval1<interval2
                //     返回值为0,   interval1=interval2
                //     返回值为正数,interval1>interval2
                return interval1[0] - interval2[0];
            }
        });

        // 定义返回数组
        List<int[]> merged = new ArrayList<int[]>();
        // 开始比较
        for (int i = 0 ; i < intervals.length ; i++) {
            // 定义一下左右两个边界值
            int L = intervals[i][0], R = intervals[i][1];
            // 当a[1]<b[0]说明无交集,把无交集的数组加入merged(每次判断都是merged的最后一个数组,因为已经排序了)
            if (merged.size() == 0 || merged.get(merged.size() - 1)[1] < L) {
                merged.add(new int[]{L, R});
            }
            // a[1]>=b[1]说明有交集,进行混合
            else {
                merged.get(merged.size() - 1)[1] = Math.max(merged.get(merged.size() - 1)[1], R);
            }
        }
        return merged.toArray(new int[merged.size()][]);
    }
}

链接:https://leetcode-cn.com/problems/merge-intervals

57. Insert Interval(Hard)

Question:

Given a set of non-overlapping intervals, insert a new interval into the intervals (merge if necessary).

You may assume that the intervals were initially sorted according to their start times.

Example 1:

Input: intervals = [[1,3],[6,9]], newInterval = [2,5]
Output: [[1,5],[6,9]]

Example 2:

Input: intervals = [[1,2],[3,5],[6,7],[8,10],[12,16]], newInterval = [4,8]
Output: [[1,2],[3,10],[12,16]]
Explanation: Because the new interval [4,8] overlaps with [3,5],[6,7],[8,10].

Example 3:

Input: intervals = [], newInterval = [5,7]
Output: [[5,7]]

Example 4:

Input: intervals = [[1,5]], newInterval = [2,3]
Output: [[1,5]]

Example 5:

Input: intervals = [[1,5]], newInterval = [2,7]
Output: [[1,7]]

Constraints:

  • 0 <= intervals.length <= 104
  • intervals[i].length == 2
  • 0 <= intervals[i][0] <= intervals[i][1] <= 105
  • intervals is sorted by intervals[i][0] in ascending order.
  • newInterval.length == 2
  • 0 <= newInterval[0] <= newInterval[1] <= 105

Solution:

/**
 * 贪心算法
 * 刚开始想简单改一下上一题代码,即把新的数组插入二维数组,再排序,合并
 * 但时间复杂度为nlog(n),因此使用贪心算法将时间复杂度降为n
 * 思路:
 *      1. 在区间newInterval之前的intervals的区间们,全部添加到结果集中
 *      2. 将newInterval添加到输出中,如果与输出中的最后一个区间重合,就合并
 *      3. 一个个添加后续的区间,如果重合就合并
 */
class Solution {
    public int[][] insert(int[][] intervals, int[] newInterval) {
        // 初始化newInterval的左右边界
        int L = newInterval[0], R = newInterval[1];
        int index = 0; // 初始化指针
        int len = intervals.length;
        LinkedList<int[]> merged = new LinkedList<int[]>(); // 初始化结果集

        // 1. 在区间newInterval之前的intervals的区间们,全部添加到结果集中
        while (index < len && L > intervals[index][0]) {
            merged.add(intervals[index++]);
        }

        // 2. 将newInterval添加到输出中,如果与输出中的最后一个区间重合,就合并
        int[] interval = new int[2];
        // 如果没有重合,直接添加
        if (merged.isEmpty() || merged.getLast()[1] < L) {
            merged.add(newInterval);
        }
        // 如果有重合,混合后添加
        else {
            interval = merged.removeLast();
            interval[1] = Math.max(interval[1], R);
            merged.add(interval);
        }

        // 3. 一个个添加后续的区间,如果重合就合并
        while ( index < len) {
            interval = intervals[index++];
            int start = interval[0], end = interval[1];
            // 如果没有重合,直接添加
            if (merged.getLast()[1] < start) {
                merged.add(interval);
            }
            // 如果有重合,混合后添加
            else {
                interval = merged.removeLast();
                interval[1] = Math.max(interval[1], end);
                merged.add(interval);
            }
        }

        return merged.toArray(new int[merged.size()][2]);
    }
}

链接:https://leetcode-cn.com/problems/insert-interval

58. Length of Last Word(Easy)

Question:

Given a string s consists of upper/lower-case alphabets and empty space characters ’ ', return the length of last word (last word means the last appearing word if we loop from left to right) in the string.

If the last word does not exist, return 0.

Note: A word is defined as a maximal substring consisting of non-space characters only.

Example:

Input: “Hello World”
Output: 5

Solution:

/**
 * 双指针
 * 注意结尾的空格问题
 */
class Solution {
    public int lengthOfLastWord(String s) {
        // 初始化末尾指针
        int end = s.length() - 1;
        // 排除字符串末尾的空格
        while (end >= 0 && s.charAt(end) == ' ') end--;
        // 无最后的单词,返回0
        if (end < 0) return 0;
        // 初始化起始指针
        int start = end;
        // 找到单词的开头(其实找到的是单词开头的前一个空格)
        while (start >= 0 && s.charAt(start) != ' ') start--;
        return end - start;
    }
}

链接:https://leetcode-cn.com/problems/length-of-last-word

59. Spiral Matrix II(Middle)

Question:

Given a positive integer n, generate a square matrix filled with elements from 1 to n2 in spiral order.

Example:

Input: 3
Output:
[
 [ 1, 2, 3 ],
 [ 8, 9, 4 ],
 [ 7, 6, 5 ]
]

Solution:

class Solution {
    public int[][] generateMatrix(int n) {

        // 定义一个结果集
        int[][] res = new int[n][n];
        // 初始化一些变量
        int left = 0; // 矩阵左指针
        int right = n - 1; // 矩阵右指针
        int top = 0; // 矩阵上指针
        int bottom = n - 1; // 矩阵下指针
        int eleNum = n * n; // 矩阵中元素数量(矩阵大小),长*宽获得
        // 初始化起始数字
        int num = 1;

        // 开始转圈圈
        while (num <= eleNum) {
            // 左到右
            for (int i = left ; i <= right ; i++) res[top][i] = num++;
            top++;
            // 上到下
            for (int i = top ; i <= bottom ; i++) res[i][right] = num++;
            right--;
            // 右到左
            for (int i = right ; i >= left ; i--) res[bottom][i] = num++;
            bottom--;
            // 下到上
            for (int i = bottom ; i >= top ; i--) res[i][left] = num++;
            left++;
        }
        return res;
    }
}

链接:https://leetcode-cn.com/problems/spiral-matrix-ii

60. Permutation Sequence(Hard)

Question:

The set [1,2,3,…,n] contains a total of n! unique permutations.

By listing and labeling all of the permutations in order, we get the following sequence for n = 3:

  1. “123”
  2. “132”
  3. “213”
  4. “231”
  5. “312”
  6. “321”

Given n and k, return the kth permutation sequence.

Note:

  • Given n will be between 1 and 9 inclusive.
  • Given k will be between 1 and n! inclusive.

Example 1:

Input: n = 3, k = 3
Output: “213”

Example 2:

Input: n = 4, k = 9
Output: “2314”

Solution:

/**
 * 康托展开:ans = an*(n-1)!+...+a2*1!+a1*0! + 1
 * 设有n个数(1,2,3,...,n)可以有组成不同(n!种)的排列组合,康托展开表示的是当前排列组合在n个不同元素的全排列中的名次
 * 如4213在(1,2,3,4)的全排列中的名次
 * 其中4位于(1,2,3,4)的第3位(从0开始数)
 *     2位于(1,2,3)的第1位
 *     1位于(1,3)的第0位
 *     3位于(3)的第0位
 * 其康托展开为:ans = 3*3!+1*2!+0*1!+0*0! + 1= 21
 * 4213在所有排列中排在第21
 *
 * 逆康托展开
 * 如(1,2,3,4)中的第21名
 * 21-1=20
 * 20/(3!)=3 说明比第1位小的数有3个,第1位为4,序列中还有(1,2,3) 20%(3!)=2
 * 2/(2!)=1 说明比第2位小的数有1个,第2位为2,序列中还有(1,3) 2%(2!)=0
 * 0/(1!)=0 说明比第3位小的数有0个,第3位为1,序列中还有(3) 0%(1!)=0
 * 最后一位是3
 * 序列为4213
 */
class Solution {
    public String getPermutation(int n, int k) {
        
        // 把集合[1,2,3,...,n]放入list,方便删减操作
        List<Integer> list = new ArrayList<>();
        for (int i = 1 ; i <= n ; i++) {
            list.add(i);
        }

        // 写结果
        StringBuilder sb = new StringBuilder();
        // 先给k-1,方便后面逆展开
        k--;
        while (n > 0) {
            // 计算阶乘
            int curCom = cal(--n); // --n是因为阶乘是(n-1)! (n-2)! ... 0!
            // 开始逆展开
            int group = k / curCom; // 看第一位数是list中的哪一个
            sb.append(list.get(group)); // 拿到第一位数加到sb中
            list.remove(group); // 从list里移除
            k = k % curCom; // 得到余数继续计算
        }
        return sb.toString();
    }

    // 阶乘
    private int cal(int num) {
        int res = 1;
        for (int i = 1 ; i <= num ; i++) {
            res *= i;
        }
        return res;
    }
}

链接:https://leetcode-cn.com/problems/permutation-sequence

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值