有一个XxY的网格,一个机器人只能走格点且只能向右或向下走,要从左上角走到右下角。请设计一个算法,计算机器人有多少种走法。
给定两个正整数int x,int y,请返回机器人的走法数目。保证x+y小于等于12。
class Robot {
// public int times = 0;
// public int times1 = 0;
/*** 递归解法 **/
public int countWays1(int x, int y) {
if (x < 0 || y < 0)
return 0;
if (x == 0 || y == 0)
return 1;
// times1 ++;
return countWays1(x - 1, y) + countWays1(x, y - 1);
}
/** 优化的递归解法 **/
public int countWays(int x, int y) {
if (x < 0 || y < 0)
return 0;
int[][] counts = new int[x + 1][y + 1];
counts[0][0] = 1;
return countWays(x, y, counts);
}
private int countWays(int x, int y, int[][] counts) {
if (x < 0 || y < 0)
return 0;
if (counts[x][y] <= 0) {
counts[x][y] = countWays(x - 1, y, counts) + countWays(x, y - 1, counts);
// times ++;
}
return counts[x][y];
}
}
2、Minimum Path Sum (矩阵路径最小和问题)(leetcode 65)
Given a m x n grid filled with non-negative numbers, find a path from top left to bottom right which minimizes the sum of all numbers along its path.
Note: You can only move either down or right at any point in time.
即给定一个m*n的数组矩阵,矩阵中所有值都是非负的;从左上角开始往右或者往下走,记录最终到达右下角的路径上所有元素之和的最小值问题;public class Solution {
public int minPathSum(int[][] grid) {
// 创建一个sum数组来记录每个位置的sum值,这样可以减少递归中的重复计算
int[][] sum = new int[grid.length][grid[0].length];
// 进行初始化赋值
for (int[] sums : sum)
Arrays.fill(sums, -1);
return minPath(grid, 0, 0, sum);
}
private int minPath(int[][] grid, int x, int y, int[][] sum) {
// 因为下面需要用到Math.min获取到最小值,故而这里使用Integer.MAX_VALUE来表示超出边界值
if (x == grid.length || y == grid[0].length)
return Integer.MAX_VALUE;
if (sum[x][y] == -1) {
int next = Math.min(minPath(grid, x + 1, y, sum),
minPath(grid, x, y + 1, sum));
// 注意处理bottom位置,即右下角位置两个方向走都超出了边界,因而最后Math.min获得的值都是Integer.MAX_VALUE,这里需要将其置为0
next = (next == Integer.MAX_VALUE) ? 0 : next;
sum[x][y] = grid[x][y] + next;
}
return sum[x][y];
}
}
二、求子集问题
2、获得集合中的所有子集:
问题描述:
请编写一个方法,返回某集合的所有非空子集。
给定一个int数组A和数组的大小int n,请返回A的所有非空子集。保证A的元素个数小于等于20,且元素互异。各子集内部从大到小排序,子集之间字典逆序排序,见样例。
[123,456,789]
返回:{[789,456,123],[789,456],[789,123],[789],[456,123],[456],[123],{}}
问题分析:
注意子集问题不要忘了空集{},空集是每个集合的子集;
递归法:子集问题可以很简单地转化为递归问题;假设A数组中有n个元素,前n-1个元素的排列组合的集合P(n - 1)已经获得,则获得n个元素的排列组合问题,即转化为最后一个元素A[n]是否存在的问题;若不存在,则P'(n) = P(n - 1);若存在,则P''(n) = P(n - 1) + A[n],即P(n -1)中每个集合中加上A[n];则原问题转化为P(n)=P(n-1) + P''(n)的问题;用递归很容易实现;
迭代法:递归的时间复杂度为O(2^n);这里可以采用数学中组合方法来取巧,在构造一个集合时,每个元素无非存在两种状态:存在(记为1)和不存在(记为0),则可以用一个二进制数来记录每一个子集合;该二进制数的取值范围显然是0--2^n;遍历该范围,将二进制转换成为集合即可;
代码:
递归法:
class Subset {
/*@param: A--原始集合; n--数组大小*/
public ArrayList<ArrayList<Integer>> getSubsets(int[] A, int n) {
if (A == null || A.length != n) // 输入不合法情况
return null;
return getAllSubSets(A, 0);
}
// 递归函数,index表示当前递归的深度
private ArrayList<ArrayList<Integer>> getAllSubSets(int[] A, int index) {
ArrayList<ArrayList<Integer>> subSets;
if (A.length == index){ // 达到递归底层
subSets = new ArrayList<>();
subSets.add(new ArrayList<Integer>()); // 注意加上{}空集合
} else {
// 获得P(n - 1)集合
subSets = getAllSubSets(A, index + 1);
// 获得集合中当前元素
int item = A[index];
// 获取P(n - 1) + a(n)的集合
ArrayList<ArrayList<Integer>> addedSets = new ArrayList<>();
// 遍历所有的P(n-1)中子元素,加上a(n)
for (ArrayList<Integer> subSet : subSets) {
// 新创建一个ArrayList,用来保存添加item后的结果
ArrayList<Integer> tempList = new ArrayList<>(subSet);
tempList.add(item);
addedSets.add(tempList);
}
// P(n)即是P(n - 1) + addedSets
subSets.addAll(addedSets);
}
return subSets;
}
}
迭代法:
class Subset {
public ArrayList<ArrayList<Integer>> getSubsets(int[] A, int n) {
if (A == null || A.length != n)
return null;
ArrayList<ArrayList<Integer>> subSets = new ArrayList<>();
// 获得最大数值
int max = 1 << n;
// int max = (int)Math.pow(2, n);
// 遍历所有可能的结果
for (int i = 0; i < max; i ++) {
subSets.add(getSetsFromNum(A, i));
}
return subSets;
}
// 将二进制数值转化成为集合形式
private ArrayList<Integer> getSetsFromNum(int[] A, int num) {
ArrayList<Integer> subSet = new ArrayList<>();
int index = 0;
// 遍历A数组中每个元素对应的位置,若该位置上num二进制为1,则添加到结果集合中;
for (int k = num; k > 0; k >>= 1, index ++) {
if ((k & 1) == 1)
subSet.add(A[index]);
}
return subSet;
}
}
三、排列组合问题:
编写一个方法,确定某字符串的所有排列组合。
给定一个string A和一个int n,代表字符串和其长度,请返回所有该字符串字符的排列,保证字符串长度小于等于11且字符串中字符均为大写英文字符,排列中的字符串按字典序从大到小排序。(不合并重复字符串)
"ABC"
返回:["CBA","CAB","BCA","BAC","ACB","ABC"]
代码:
public class Permutation {
public ArrayList<String> getPermutation(String A) {
// 判断字符串A是否合法
if (A == null)
return null;
ArrayList<String> resultList = getThePermutation(A);
// 进行字典序排序(从大到小)
Collections.sort(resultList, new Comparator<String>() {
@Override
public int compare(String o1, String o2) {
if (o1.compareTo(o2) > 0) return -1;
if (o1.compareTo(o2) < 0) return 1;
return 0;
}
});
return resultList;
}
// 递归解法
private ArrayList<String> getThePermutation(String A) {
ArrayList<String> resultList = new ArrayList<>();
// 返回结果值
if (A.length() == 0) {
resultList.add("");
return resultList;
}
char item = A.charAt(0); // 获得字符串的首字符
A = A.substring(1); // 移除字符串的首字符
ArrayList<String> preList = getThePermutation(A); // 获得p(n - 1)
// 往P(n-1)中每个字符串中插入item字符,形成新的排列组合
for (String str : preList) {
for (int i = 0; i <= str.length(); i++) { // 注意是<=号
String newStr = insertIntoStr(str, item, i);
resultList.add(newStr);
}
}
return resultList;
}
// 往字符串的指定位置插入字符
private String insertIntoStr(String str, char item, int index) {
String start = str.substring(0, index);
String end = str.substring(index, str.length());
return start + item + end;
}
}<span style="widows: auto; font-family: 微软雅黑; background-color: inherit;"> </span>
在数组A[0..n-1]中,有所谓的魔术索引,满足条件A[i]=i。给定一个升序数组,元素值各不相同,编写一个方法,判断在数组A中是否存在魔术索引。请思考一种复杂度优于o(n)的方法。
给定一个int数组A和int n代表数组大小,请返回一个bool,代表是否存在魔术索引。
[1,2,3,4,5]
返回:false
public class MagicIndex {
public boolean findMagicIndex(int[] A, int n) {
// 直接二分查找
if (A == null || A.length == 0)
return false;
int start = 0;
int end = A.length -1;
while (start <= end) {
int mid = (end - start) / 2 + start;
// 找到魔术索引
if (A[mid] == mid)
return true;
if (A[mid] < mid) // 在右子序列
start = mid + 1;
else
end = mid - 1;
}
return false;
}
}
在数组A[0..n-1]中,有所谓的魔术索引,满足条件A[i]=i。给定一个不下降序列,元素值可能相同,编写一个方法,判断在数组A中是否存在魔术索引。请思考一种复杂度优于o(n)的方法。
给定一个int数组A和int n代表数组大小,请返回一个bool,代表是否存在魔术索引。
[1,1,3,4,5]
返回:true
public class MagicIndex {
public boolean findMagicIndex(int[] A, int n) {
if (A == null || A.length == 0)
return false;
return findMagicIndex(A, 0 , A.length - 1);
}
private boolean findMagicIndex(int[] A, int start, int end) {
if (start > end)
return false;
boolean result = false;
int mid = (end - start) / 2 + start;
if (A[mid] == mid)
return true;
if (A[mid] < mid) {
result = findMagicIndex(A, start, A[mid]) ||
findMagicIndex(A, mid + 1, end);
} else {
result = findMagicIndex(A, start, mid - 1) ||
findMagicIndex(A, A[mid], end);
}
return result;
}
}