7.7 二刷 二分查找
This commit is contained in:
parent
ce64f93c76
commit
730f26f950
9
pom.xml
9
pom.xml
@ -16,7 +16,14 @@
|
||||
<dependency>
|
||||
<groupId>junit</groupId>
|
||||
<artifactId>junit</artifactId>
|
||||
<version>4.12</version>
|
||||
<version>4.9</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<!-- 显式添加 Hamcrest 核心库 -->
|
||||
<dependency>
|
||||
<groupId>org.hamcrest</groupId>
|
||||
<artifactId>hamcrest-core</artifactId>
|
||||
<version>1.3</version> <!-- 或者用最新的 2.2 -->
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
71
src/main/java/backtrack/SolveNQueens.java
Normal file
71
src/main/java/backtrack/SolveNQueens.java
Normal file
@ -0,0 +1,71 @@
|
||||
package backtrack;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 题目: 51. N 皇后 (SolveNQueens)
|
||||
* 描述:按照国际象棋的规则,皇后可以攻击与之处在同一行或同一列或同一斜线上的棋子。
|
||||
* n 皇后问题 研究的是如何将 n 个皇后放置在 n×n 的棋盘上,并且使皇后彼此之间不能相互攻击。
|
||||
* 给你一个整数 n ,返回所有不同的 n 皇后问题 的解决方案。
|
||||
* 每一种解法包含一个不同的 n 皇后问题 的棋子放置方案,该方案中 'Q' 和 '.' 分别代表了皇后和空位。
|
||||
|
||||
示例 1:
|
||||
输入:n = 4
|
||||
输出:[[".Q..","...Q","Q...","..Q."],["..Q.","Q...","...Q",".Q.."]]
|
||||
解释:如上图所示,4 皇后问题存在两个不同的解法。
|
||||
|
||||
* 链接:https://leetcode.cn/problems/n-queens/
|
||||
*/
|
||||
//困难题,二刷不会
|
||||
public class SolveNQueens {
|
||||
|
||||
/**
|
||||
* 因为是从上到下 因此只判断是否与左上和右上和正上冲突 下面的内容肯定不冲突
|
||||
*/
|
||||
boolean isValid(char[][] matrix, int row, int col, int n) {
|
||||
// 1. 同列检查
|
||||
for (int i = 0; i < row; i++) {
|
||||
if (matrix[i][col] == 'Q') return false;
|
||||
}
|
||||
// 2. 左上 ↖ 对角线检查
|
||||
for (int i = row - 1, j = col - 1; i >= 0 && j >= 0; i--, j--) {
|
||||
if (matrix[i][j] == 'Q') return false;
|
||||
}
|
||||
// 3. 右上 ↗ 对角线检查
|
||||
for (int i = row - 1, j = col + 1; i >= 0 && j < n; i--, j++) {
|
||||
if (matrix[i][j] == 'Q') return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
void dfs(int n, int row, List<List<String>> res, char[][] matrix) {
|
||||
if (row == n) {
|
||||
res.add(char2list(matrix));
|
||||
return; // 记得加上 return,避免继续往下走
|
||||
}
|
||||
for (int col = 0; col < n; col++) {
|
||||
if (isValid(matrix, row, col, n)) {
|
||||
matrix[row][col] = 'Q';
|
||||
dfs(n, row + 1, res, matrix);
|
||||
matrix[row][col] = '.';
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
List<String> char2list(char[][] matrix) {
|
||||
List<String> res = new ArrayList<>();
|
||||
for (char[] row : matrix) {
|
||||
res.add(new String(row)); // 直接用 String 构造函数
|
||||
|
||||
}
|
||||
return res;
|
||||
}
|
||||
public List<List<String>> solveNQueens(int n) {
|
||||
List<List<String>> res = new ArrayList<>();
|
||||
char[][] matrix = new char[n][n];
|
||||
for (int i = 0; i < n; i++) Arrays.fill(matrix[i], '.');
|
||||
dfs(n, 0, res, matrix);
|
||||
return res;
|
||||
}
|
||||
}
|
@ -14,51 +14,69 @@ package binary_search;
|
||||
|
||||
*/
|
||||
//不会
|
||||
//二刷不会
|
||||
public class FindMedianSortedArrays {
|
||||
|
||||
public double findMedianSortedArrays(int[] nums1, int[] nums2) {
|
||||
int m = nums1.length, n = nums2.length;
|
||||
int total = m + n;
|
||||
if (total % 2 == 1) {
|
||||
// 奇数长度,直接取第 (total+1)/2 小
|
||||
return getKth(nums1, 0, nums2, 0, (total + 1) / 2);
|
||||
int length1 = nums1.length, length2 = nums2.length;
|
||||
int totalLength = length1 + length2;
|
||||
if (totalLength % 2 == 1) {
|
||||
int midIndex = totalLength / 2;
|
||||
double median = getKthElement(nums1, nums2, midIndex + 1);
|
||||
return median;
|
||||
} else {
|
||||
// 偶数长度,取第 total/2 和 total/2+1 两个数的平均
|
||||
double left = getKth(nums1, 0, nums2, 0, total / 2);
|
||||
double right= getKth(nums1, 0, nums2, 0, total / 2 + 1);
|
||||
return (left + right) / 2.0;
|
||||
int midIndex1 = totalLength / 2 - 1, midIndex2 = totalLength / 2;
|
||||
double median = (getKthElement(nums1, nums2, midIndex1 + 1) + getKthElement(nums1, nums2, midIndex2 + 1)) / 2.0;
|
||||
return median;
|
||||
}
|
||||
}
|
||||
|
||||
// 返回 nums1[start1...] 和 nums2[start2...] 中,第 k 小的数
|
||||
private double getKth(int[] nums1, int start1, int[] nums2, int start2, int k) {
|
||||
// 如果 nums1 已经“耗尽”,直接从 nums2 里取
|
||||
if (start1 >= nums1.length) {
|
||||
return nums2[start2 + k - 1];
|
||||
public int getKthElement(int[] nums1, int[] nums2, int k) {
|
||||
/* 主要思路:要找到第 k (k>1) 小的元素,那么就取 pivot1 = nums1[k/2-1] 和 pivot2 = nums2[k/2-1] 进行比较
|
||||
* 这里的 "/" 表示整除
|
||||
* nums1 中小于等于 pivot1 的元素有 nums1[0 .. k/2-2] 共计 k/2-1 个
|
||||
* nums2 中小于等于 pivot2 的元素有 nums2[0 .. k/2-2] 共计 k/2-1 个
|
||||
* 取 pivot = min(pivot1, pivot2),两个数组中小于等于 pivot 的元素共计不会超过 (k/2-1) + (k/2-1) <= k-2 个
|
||||
* 这样 pivot 本身最大也只能是第 k-1 小的元素
|
||||
* 如果 pivot = pivot1,那么 nums1[0 .. k/2-1] 都不可能是第 k 小的元素。把这些元素全部 "删除",剩下的作为新的 nums1 数组
|
||||
* 如果 pivot = pivot2,那么 nums2[0 .. k/2-1] 都不可能是第 k 小的元素。把这些元素全部 "删除",剩下的作为新的 nums2 数组
|
||||
* 由于我们 "删除" 了一些元素(这些元素都比第 k 小的元素要小),因此需要修改 k 的值,减去删除的数的个数
|
||||
*/
|
||||
|
||||
int index1 = 0, index2 = 0;
|
||||
int len1 = nums1.length, len2 = nums2.length;
|
||||
|
||||
while (true) {
|
||||
// 如果 nums1 已经全部“删除”,第 k 小就是 nums2[index2 + k - 1]
|
||||
if (index1 == len1) {
|
||||
return nums2[index2 + k - 1];
|
||||
}
|
||||
// 如果 nums2 耗尽
|
||||
if (start2 >= nums2.length) {
|
||||
return nums1[start1 + k - 1];
|
||||
// 如果 nums2 已经全部“删除”,第 k 小就是 nums1[index1 + k - 1]
|
||||
if (index2 == len2) {
|
||||
return nums1[index1 + k - 1];
|
||||
}
|
||||
// 如果 k == 1,取两数组当前头部较小者
|
||||
// 如果 k == 1,直接返回两数组当前指针处的较小值
|
||||
if (k == 1) {
|
||||
return Math.min(nums1[start1], nums2[start2]);
|
||||
return Math.min(nums1[index1], nums2[index2]);
|
||||
}
|
||||
|
||||
// 各自看 k/2 位置的元素,如果超出边界,则视作 +∞
|
||||
int midVal1 = (start1 + k/2 - 1 < nums1.length)
|
||||
? nums1[start1 + k/2 - 1]
|
||||
: Integer.MAX_VALUE;
|
||||
int midVal2 = (start2 + k/2 - 1 < nums2.length)
|
||||
? nums2[start2 + k/2 - 1]
|
||||
: Integer.MAX_VALUE;
|
||||
// 每次比较 nums1 和 nums2 各自第 k/2 个候选(如果不足 k/2,则取末尾)
|
||||
int half = k / 2;
|
||||
int newIndex1 = Math.min(index1 + half, len1) - 1;
|
||||
int newIndex2 = Math.min(index2 + half, len2) - 1;
|
||||
int pivot1 = nums1[newIndex1];
|
||||
int pivot2 = nums2[newIndex2];
|
||||
|
||||
// 抛弃较小者及其前面 k/2 个元素
|
||||
if (midVal1 < midVal2) {
|
||||
// 抛弃 nums1[start1 ... start1 + k/2 - 1]
|
||||
return getKth(nums1, start1 + k/2, nums2, start2, k - k/2);
|
||||
// 丢弃 pivot 值较小的那半段
|
||||
if (pivot1 <= pivot2) {
|
||||
// 丢弃 nums1[index1 … newIndex1] 这部分,共 newIndex1 - index1 + 1 个元素
|
||||
k -= (newIndex1 - index1 + 1);
|
||||
index1 = newIndex1 + 1;
|
||||
} else {
|
||||
// 抛弃 nums2[start2 ... start2 + k/2 - 1]
|
||||
return getKth(nums1, start1, nums2, start2 + k/2, k - k/2);
|
||||
// 丢弃 nums2[index2 … newIndex2] 这部分
|
||||
k -= (newIndex2 - index2 + 1);
|
||||
index2 = newIndex2 + 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -16,7 +16,9 @@ package binary_search;
|
||||
* <p>
|
||||
* 链接:https://leetcode.cn/problems/find-minimum-in-rotated-sorted-array/
|
||||
*/
|
||||
//二刷不会
|
||||
public class FindMin {
|
||||
|
||||
/**
|
||||
* 判断区间是否已经有序
|
||||
*
|
||||
@ -50,4 +52,57 @@ public class FindMin {
|
||||
return nums[left];
|
||||
}
|
||||
|
||||
public int findMax(int[] nums) {
|
||||
int left = 0, right = nums.length - 1;
|
||||
// 当区间尚未缩到只剩一个元素
|
||||
while (left < right) {
|
||||
// 如果当前区间已经是严格升序(没有经过旋转),那么最大值就是区间末尾
|
||||
if (nums[left] < nums[right]) {
|
||||
return nums[right];
|
||||
}
|
||||
int mid = left + (right - left) / 2;
|
||||
// 如果 nums[mid] 比 nums[left] 大,说明 [left…mid] 是严格升序的,
|
||||
// 这段的最大值就是 nums[mid],而且全局上一定存在比 nums[mid] 更小的元素在右边
|
||||
// ——但是我们要找的是最大值,此时候,要把搜索区间缩到 [mid…right]
|
||||
if (nums[mid] > nums[left]) {
|
||||
left = mid;
|
||||
}
|
||||
// 否则,nums[mid] ≤ nums[left],说明旋转点(最大值)在 [left…mid-1] 里
|
||||
else {
|
||||
right = mid - 1;
|
||||
}
|
||||
}
|
||||
// 最终 left == right,指向的就是最大值
|
||||
return nums[left];
|
||||
}
|
||||
|
||||
private int findMinIndex(int[] nums) {
|
||||
int left = 0, right = nums.length - 1;
|
||||
while (left < right) {
|
||||
// 如果当前区间完全有序
|
||||
if (nums[left] < nums[right]) {
|
||||
return left;
|
||||
}
|
||||
int mid = left + (right - left) / 2;
|
||||
if (nums[mid] >= nums[left]) {
|
||||
// [left…mid] 单调,最小值一定在 mid+1…right
|
||||
left = mid + 1;
|
||||
} else {
|
||||
// mid 在左侧的旋转段里,min 可能就是 mid
|
||||
right = mid;
|
||||
}
|
||||
}
|
||||
return left;
|
||||
}
|
||||
|
||||
/**
|
||||
* 用一次二分找到最小值,然后取它左边的元素作为最大值
|
||||
*/
|
||||
public int findMax2(int[] nums) {
|
||||
int n = nums.length;
|
||||
int pivot = findMinIndex(nums);
|
||||
// pivot 左边的元素是最大值
|
||||
return nums[(pivot - 1 + n) % n];
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -13,35 +13,57 @@ package binary_search;
|
||||
* 链接:https://leetcode.cn/problems/search-in-rotated-sorted-array/
|
||||
*/
|
||||
//不会
|
||||
//二刷不会
|
||||
public class Search {
|
||||
|
||||
/**
|
||||
* 在旋转排序数组 nums 中查找目标值 target
|
||||
* @param nums 旋转排序后的数组
|
||||
* @param target 目标值
|
||||
* @return 目标值在数组中的下标,如果不存在则返回 -1
|
||||
*/
|
||||
public int search(int[] nums, int target) {
|
||||
int n = nums.length;
|
||||
// 数组为空时,直接返回 -1
|
||||
if (n == 0) {
|
||||
return -1;
|
||||
}
|
||||
// 数组长度为 1,直接判断唯一元素是否为 target
|
||||
if (n == 1) {
|
||||
return nums[0] == target ? 0 : -1;
|
||||
}
|
||||
|
||||
// 双指针定义搜索区间 [l, r]
|
||||
int l = 0, r = n - 1;
|
||||
// 进入二分查找
|
||||
while (l <= r) {
|
||||
int mid = (l + r) / 2;
|
||||
// 防止 (l + r) 溢出,计算中点
|
||||
int mid = l + (r - l) / 2;
|
||||
// 如果中点即为目标,直接返回
|
||||
if (nums[mid] == target) {
|
||||
return mid;
|
||||
}
|
||||
|
||||
// 判断哪一段是单调升序的:
|
||||
// 如果 nums[0] <= nums[mid],说明从索引 0 到 mid 是升序的
|
||||
if (nums[0] <= nums[mid]) {
|
||||
// 如果 target 在这段升序区间内,则向左继续搜索,否则搜索右半区
|
||||
if (nums[0] <= target && target < nums[mid]) {
|
||||
r = mid - 1;
|
||||
r = mid - 1; // 缩小右边界
|
||||
} else {
|
||||
l = mid + 1;
|
||||
l = mid + 1; // 移动左边界
|
||||
}
|
||||
} else {
|
||||
// 否则,右半区 [mid, n-1] 是升序的
|
||||
// 如果 target 在右半区范围内,则向右继续搜索,否则搜索左半区
|
||||
if (nums[mid] < target && target <= nums[n - 1]) {
|
||||
l = mid + 1;
|
||||
l = mid + 1; // 移动左边界
|
||||
} else {
|
||||
r = mid - 1;
|
||||
r = mid - 1; // 缩小右边界
|
||||
}
|
||||
}
|
||||
}
|
||||
// 未找到目标,返回 -1
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
@ -12,7 +12,10 @@ package binary_search;
|
||||
* 链接:https://leetcode.cn/problems/search-insert-position/
|
||||
*/
|
||||
//第一道二分法的题,不熟练
|
||||
// 二刷会做
|
||||
public class SearchInsert {
|
||||
|
||||
|
||||
public int searchInsert(int[] nums, int target) {
|
||||
int left = 0, right = nums.length - 1;
|
||||
while (left <= right) {
|
||||
|
@ -14,6 +14,7 @@ package binary_search;
|
||||
|
||||
* 链接:https://leetcode.cn/problems/search-a-2d-matrix/
|
||||
*/
|
||||
//二刷会做
|
||||
public class SearchMatrix {
|
||||
public boolean searchMatrix(int[][] matrix, int target) {
|
||||
int r=matrix.length;
|
||||
|
@ -11,6 +11,7 @@ package binary_search;
|
||||
|
||||
* 链接:https://leetcode.cn/problems/find-first-and-last-position-of-element-in-sorted-array/
|
||||
*/
|
||||
//二刷不会高效方法
|
||||
public class SearchRange {
|
||||
//极端情况下不是O(log n)
|
||||
public int[] searchRange(int[] nums, int target) {
|
||||
@ -46,19 +47,33 @@ public class SearchRange {
|
||||
}
|
||||
|
||||
private int findLeft(int[] nums, int target) {
|
||||
// 搜索区间为 [left, right]
|
||||
int left = 0, right = nums.length - 1;
|
||||
// 用来记录 target 最左出现的位置,初始为 -1 表示未找到
|
||||
int index = -1;
|
||||
|
||||
// 当搜索区间有效时,继续二分
|
||||
while (left <= right) {
|
||||
// 计算中点,写法能避免 (left+right) 溢出
|
||||
int mid = left + (right - left) / 2;
|
||||
|
||||
// 如果中点元素 >= target,说明左边可能还存在 target
|
||||
if (nums[mid] >= target) {
|
||||
// 收缩右边界到 mid - 1,继续在左半区查找
|
||||
right = mid - 1;
|
||||
} else {
|
||||
// nums[mid] < target,说明 target 只能出现在右半区
|
||||
left = mid + 1;
|
||||
}
|
||||
|
||||
// 如果在 mid 处发现了 target,就更新 index 为 mid
|
||||
// 但不立即返回,因为还要继续向左看看是否还有更左的 target
|
||||
if (nums[mid] == target) {
|
||||
index = mid;
|
||||
}
|
||||
}
|
||||
|
||||
// 循环结束后,index 要么是最左的 target 位置,要么仍为 -1(没找到)
|
||||
return index;
|
||||
}
|
||||
|
||||
|
@ -2,7 +2,7 @@ package array;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
import static org.junit.Assert.*;
|
||||
import java.util.Arrays;
|
||||
|
||||
public class MaxSubArrayTest {
|
||||
|
||||
|
10
src/test/java/binary_search/SearchTest.java
Normal file
10
src/test/java/binary_search/SearchTest.java
Normal file
@ -0,0 +1,10 @@
|
||||
package binary_search;
|
||||
|
||||
import junit.framework.TestCase;
|
||||
|
||||
public class SearchTest extends TestCase {
|
||||
|
||||
public void testSearch() {
|
||||
|
||||
}
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user