41.最初の正の数がありません
ソートされていない整数配列が与えられた場合、そこに表示されない最小の正の整数を見つけてください。
例1:
输入: [1,2,0]
输出: 3
例2:
输入: [3,4,-1,1]
输出: 2
例3:
输入: [7,8,9,11,12]
输出: 1
促す:
アルゴリズムの時間の複雑さはO(n)である必要があり、一定レベルの追加スペースのみを使用できます。
ハッシュテーブル方式
// 时间复杂度 O(N) 空间复杂度 O(N)
public int firstMissingPositive(int[] nums) {
int n = nums.length;
Set<Integer> set = new HashSet<>(); for(int num : nums) set.add(num);
for(int i = 1; i <= n; i ++){
if(!set.contains(i)) return i;
} return n + 1;
}
仕分け方法
// 时间复杂度 O(Nlog(N)) 空
public int firstMissingPositive(int[] nums) {
// 先排序 Nlog(N)
Arrays.sort(nums); int pre = 0;
for(int i = 0; i < nums.length; i ++){
// 跳过非正整数和重复值
if(nums[i] <= 0 || nums[i] == pre) continue;
// 找到第一个突变的元素
else if(nums[i] > pre + 1) break;
pre ++; } return pre + 1;
}
インプレースハッシュ
// 时间复杂度 O(N) 空间复杂度 O(1)
// 原地哈希: 自定义哈希函数 将数值 i 映射到 i - 1的位置上
public int firstMissingPositive(int[] nums) {
int len = nums.length;
for(int i = 0; i < len; i ++){
// 在指定范围内, 且没有放在正确位置上, 交换
while(nums[i] > 0 && nums[i] <= len && nums[nums[i] - 1] != nums[i]){
swap(nums, nums[i] - 1, i);
} } // 找到不符合的位置 for(int i = 0; i < len; i ++){
if(nums[i] != i + 1) return i + 1;
} // 都正确则返回数组长度 + 1
return len + 1;
} void swap(int[] nums, int i, int j){
int temp = nums[i];
nums[i] = nums[j]; nums[j] = temp; }
42.雨を降らせる
幅1の各列の高さマップを表すn個の非負の整数が与えられた 場合、雨が降った後、このように配置された列が受け取ることができる雨の量を計算します。
上記は、配列[0,1,0,2,1,0,1,3,2,1,2,1]で表される高さマップです。この場合、6単位の雨水(青)一部は雨を意味します)。 この写真を提供してくれたMarcosに感謝します。
例:
输入: [0,1,0,2,1,0,1,3,2,1,2,1]
输出: 6
暴力的な解決策
// 时间复杂度 O(N^2)
public int trap(int[] height) {
int res = 0;
// 遍历每一列柱子
for(int i = 1; i < height.length - 1; i ++){
// 分别记录当前柱子向左 向右的最大高度
int leftMax = 0, rightMax = 0;
for(int j = 0; j <= i; j ++)
leftMax = Math.max(leftMax, height[j]); for(int j = i; j < height.length; j ++)
rightMax = Math.max(rightMax, height[j]); // 当前位置 water[i] = min(max(h[0->i], h(i->len-1))) - h[i]
res += Math.min(leftMax, rightMax) - height[i]; } return res;
}
動的プログラミング
// 时间复杂度 优化至 O(N) , 但空间复杂度为 O(N)
public int trap(int[] height) {
if(height.length == 0) return 0;
int n = height.length;
// 优化方案: 创建两个备忘录,分别记录leftMax 和 rightMax
int[] leftMax = new int[n];
int[] rightMax = new int[n];
int res = 0;
// 初始化
leftMax[0] = height[0];
rightMax[n - 1] = height[n - 1];
for(int i = 1; i < n; i ++)
leftMax[i] = Math.max(height[i], leftMax[i - 1]);
for(int i = n - 2; i >= 0; i --)
rightMax[i] = Math.max(height[i] , rightMax[i + 1]);
for(int i = 1; i < n - 1; i ++)
res += Math.min(leftMax[i], rightMax[i]) - height[i]; return res;
}
ダブルポインター
// 时间复杂度 O(N) 空间 O(1)
// 省去创建备忘录的空间, 边走变算 public int trap(int[] height) {
if(height.length == 0) return 0;
int n = height.length;
int l = 0, r = height.length - 1, res = 0;
int leftMax = height[0], rightMax = height[n - 1];
while(l <= r){
leftMax = Math.max(leftMax, height[l]); rightMax = Math.max(rightMax, height[r]); if(leftMax < rightMax)
res += leftMax - height[l ++]; else
res += rightMax - height[r --]; } return res;
}
43.文字列の乗算
文字列形式で表現された2つの非負の整数num1とnum2が与えられた場合、num1とnum2の積を返し、それらの積も文字列形式で表現されます。
例1:
输入: num1 = "2", num2 = "3"
输出: "6"
例2:
输入: num1 = "123", num2 = "456"
输出: "56088"
説明:
- num1とnum2の長さは110未満です。
- num1とnum2には、0〜9の数字のみが含まれます。
- num1もnum2も、それ自体が0でない限り、ゼロで始まることはありません。
- 多数のタイプの標準ライブラリ(BigIntegerなど)を使用したり、入力を処理のために整数に直接変換したりすることはできません。
/**
num1的第i位(高位从0开始)和num2的第j位相乘的结果在乘积中的位置是[i+j, i+j+1]
例: 123 * 45, 123的第1位 2 和45的第0位 4 乘积 08 存放在结果的第[1, 2]位中
index: 0 1 2 3 4
1 2 3
* 4 5
---------
1 5
1 0
0 5
---------
0 6 1 5
1 2
0 8
0 4
---------
0 5 5 3 5
这样我们就可以单独都对每一位进行相乘计算把结果存入相应的index中
**/
public String multiply(String num1, String num2) {
int m = num1.length(), n = num2.length();
// 记录每位数字的数组
int[] arr = new int[m + n];
// 从个位开始计算
for(int i = m - 1; i >= 0; i --){
for(int j = n - 1; j >= 0; j --){
// i+j, i+j+1
int mul = (num1.charAt(i) - '0') * (num2.charAt(j) - '0');
int p1 = i + j, p2 = i + j + 1;
// 本身乘积 + 原来的数
int sum = mul + arr[p2];
// 取个位
arr[p2] = sum % 10;
// 进位
arr[p1] += sum / 10;
}
}
// 移除前导零,只需记录第一个非0的位置即可
int i = 0;
while(i < arr.length && arr[i] == 0) i ++;
if(i == arr.length) return "0";
StringBuilder res = new StringBuilder();
while(i < arr.length){
res.append(arr[i++]);
}
return res.toString();
}
44.ワイルドカードマッチング
文字列(s)と文字パターン(p)を指定して、「?」と「*」をサポートするワイルドカードマッチングを実装します。
'?' 可以匹配任何单个字符。
'*' 可以匹配任意字符串(包括空字符串)。
2つの文字列が完全に一致する場合にのみ、一致が成功します。
説明:
- sは空で、azからの小文字のみが含まれている可能性があります。
- pは空の場合があり、azの小文字と、文字?および*のみが含まれます。
例1:
输入:
s = "aa"
p = "a"
输出: false
解释: "a" 无法匹配 "aa" 整个字符串。
例2:
输入:
s = "aa"
p = "*"
输出: true
解释: '*' 可以匹配任意字符串。
例3:
输入:
s = "cb"
p = "?a"
输出: false
解释: '?' 可以匹配 'c', 但第二个 'a' 无法匹配 'b'。
例4:
输入:
s = "adceb"
p = "*a*b"
输出: true
解释: 第一个 '*' 可以匹配空字符串, 第二个 '*' 可以匹配字符串 "dce".
例5:
输入:
s = "acdcb"
p = "a*c?b"
输出: false
// f[i][j] = 标识 s的 0 - i 与 p的 0 - j 是否匹配
public boolean isMatch(String s, String p) { int m = s.length(), n = p.length(); boolean[][] f = new boolean[m + 1][n + 1];
// 空串匹配 f[0][0] = true;
// s为空,只要p的开头是* 就代表匹配
for(int j = 1; j <= n; j ++){
if(p.charAt(j - 1) == '*') f[0][j] = true;
else break; } for(int i = 1; i <= m; i ++){
for(int j = 1; j <= n; j ++){
// f[i][j - 1]的情况 标识 ab和ab*
// f[i - 1][j]的情况 标识 abcd 和 ab*
if(p.charAt(j - 1) == '*'){
f[i][j] = f[i][j - 1] || f[i - 1][j];
}else if(p.charAt(j - 1) == '?' || s.charAt(i - 1) == p.charAt(j - 1)){
f[i][j] = f[i - 1][j - 1];
}
}
}
return f[m][n];
}
45.ジャンプゲームII
非負の整数の配列が与えられると、最初は配列の最初の位置にいます。
配列内の各要素は、その位置でジャンプできる最大長を表します。
あなたの目標は、アレイの最後の位置に到達するために最小数のジャンプを使用することです。
例:
输入: [2,3,1,1,4]
输出: 2
解释: 跳到最后一个位置的最小跳跃数是 2。
从下标为 0 跳到下标为 1 的位置,跳 1 步,然后跳 3 步到达数组的最后一个位置。
説明:
常に配列の最後の位置に到達できると仮定します。
お元気ですか
public int jump(int[] nums) {
int ans = 0, start = 0, end = 1;
while(end < nums.length){
int maxPos = 0;
for(int i = l; i < end; i ++){
//能跳到的最远的距离
maxPos = Math.max(maxPos, i + nums[i]);
}
//下一次起跳点范围开始的格子
start = end;
//下一次起跳点范围结束的格子
end = maxPos + 1;
ans ++;
}
return ans;
}
46.完全な配置
繰り返し 番号のないシーケンスが与えられた 場合、可能なすべての順列を返します。
例:
输入: [1,2,3]
输出:[ [1,2,3],
[1,3,2],
[2,1,3],
[2,3,1],
[3,1,2],
[3,2,1]
]class Solution {
List<List<Integer>> res = new ArrayList<>();
List<Integer> path = new ArrayList<>();
boolean[] used; public List<List<Integer>> permute(int[] nums) {
used = new boolean[nums.length];
dfs(nums); return res;
} void dfs(int[] nums){
if(path.size() == nums.length) {
res.add(new ArrayList<>(path));
return;
} for(int i = 0; i < nums.length; i ++){
if(!used[i]){
used[i] = true;
path.add(nums[i]);
dfs(nums); path.remove(path.size() - 1);
used[i] = false;
} } }}
47.フルアレンジメントII
繰り返される番号を含むことができるシーケンスが与えられた場合、繰り返されないすべての完全な順列を返します。
例:
输入: [1,1,2]
输出:[
[1,1,2],
[1,2,1],
[2,1,1]
]
このDFSプロセスのどこに重複があるのかを明確にする必要があります。この写真では、それはすでに非常に明白です:
- この検索の開始点は、前回の開始点であるvisited [i-1] == visited [i]と同じです。
- そして最後のカウントはキャンセルされました、visited [i-1] == false。
i-1が境界を越えないようにするために、i> 0の条件が追加されます。
if (i > 0 && nums[i] == nums[i - 1] && !visited[i - 1]) {
continue;
}List<List<Integer>> res = new ArrayList<>();
List<Integer> path = new ArrayList<>();
boolean[] visited;
public List<List<Integer>> permuteUnique(int[] nums) {
visited = new boolean[nums.length];
Arrays.sort(nums); //保证数组的有序性
dfs(nums);
return res;
}
void dfs(int[]nums){
if(path.size() == nums.length) {
res.add(new ArrayList<>(path));
return;
}
for(int i = 0; i < nums.length ; i++){
if( visited[i]) continue;
if( i > 0 && nums[i] == nums[i - 1] && !visited[i - 1]) continue; //此处剪枝
visited[i] = true;
path.add(nums[i]);
dfs(nums);
visited[i] = false;
path.remove(path.size()-1);
}
}
48.画像を回転させる
与えられた N × N 2次元マトリクスは、画像を表します。
画像を時計回りに90度回転させます。
説明:
画像を所定の位置で回転させる必要があります。つまり、入力された2次元マトリックスを直接変更する必要があります。画像を回転させるために別のマトリックスを使用しないでください。
例1:
给定 matrix =
[ [1,2,3], [4,5,6], [7,8,9]],原地旋转输入矩阵,使其变为:[ [7,4,1], [8,5,2], [9,6,3]]
例2:
给定 matrix =
[ [ 5, 1, 9,11],
[ 2, 4, 8,10],
[13, 3, 6, 7],
[15,14,12,16]
], 原地旋转输入矩阵,使其变为:[ [15,13, 2, 5],
[14, 3, 4, 1],
[12, 6, 8, 9],
[16, 7,10,11]
]public void rotate(int[][] matrix) {
int n = matrix.length; //转置 /*
1 2 3 --> 1 4 7
4 5 6 --> 2 5 8
7 8 9 --> 3 6 9
*/
for(int i = 0; i < n; i ++){
for(int j = i; j < n; j ++){
swap(matrix, i, j, j, i);
}
}
/*
1 4 7 --> 7 4 1
2 5 8 --> 8 5 2 3 6 9 --> 9 6 3 */ for(int i = 0; i < n; i ++){
for(int j = 0; j < n / 2; j ++){
swap(matrix, i, j, i, n - j - 1);
}
}
}
void swap(int[][] m, int i1, int j1, int i2, int j2){
int temp = m[i1][j1];
m[i1][j1] = m[i2][j2];
m[i2][j2] = temp;
}
49.レターエイリアンワードのグループ化
文字列の配列が与えられた場合、文字失読症を組み合わせます。脂肪族の単語は、同じ文字で配置が異なる文字列を指します。
例:
输入: ["eat", "tea", "tan", "ate", "nat", "bat"]
输出:[
["ate","eat","tea"],
["nat","tan"],
["bat"]
]
説明:
- すべてのエントリは小文字です。
- 回答が出力される順序は考慮されません。
public List<List<String>> groupAnagrams(String[] strs) {
//O(N KlogK) N = strs.length K = str.length()
if(strs.length == 0) return new ArrayList<>();
Map<String, List> hash = new HashMap<>();
for(String str : strs){
// 字符排序
char[] chs = str.toCharArray(); Arrays.sort(chs); String key = new String(chs);
if(!hash.containsKey(key)) hash.put(s, new ArrayList<List>());
hash.get(key).add(str); } return new ArrayList(hash.values());
}
50. Pow(x、n)
pow(x、n)を実現します。つまり、 xのn乗関数を計算します。
例1:
输入: 2.00000, 10
输出: 1024.00000
例2:
输入: 2.10000, 3
输出: 9.26100
例3:
输入: 2.00000, -2
输出: 0.25000
解释: 2-2 = 1/22 = 1/4 = 0.25
説明:
- -100.0 < x <100.0
- n は32ビットの符号付き整数であり、その値の範囲は[-231、231 -1]です。
public double myPow(double x, int n) {
if(x == 0.0f) return 0.0d;
long b = n;
double res = 1.0;
// 当n < 0时,转化为 n >= 0的情况
if(b < 0){
x = 1 / x;
b = -b;
}
while(b > 0){
if((b & 1) == 1) // b % 2 == 1
res *= x;
x *= x; // x = x ^ 2
b >>= 1; // b = b // 2
}
return res;
}
この記事があなたに役立つと思うなら、あなたはそれを好きでそれをサポートするためにそれに従うことができます、あるいはあなたは私の公開アカウントに従うことができます、より技術的な乾物記事と関連情報共有があります、誰もが一緒に学びそして進歩することができます!