完整可执行代码见 sun2ot/algorithm-learning
基本思想
将一个规模为 n 的问题,分解为 k 个规模较小的子问题。这些子问题相互独立且与原问题相同。递归求解这些子问题,然后将各子问题的解合并得到原问题的解。
二分搜索
算法思想
算法的核心思想是通过不断缩小搜索范围来逼近目标元素。在每一次迭代中,算法首先计算出中间元素的索引 mid
,然后将其与目标元素进行比较。
- 如果中间元素等于目标元素,则直接返回中间元素的索引
- 如果中间元素小于目标元素,则将搜索范围缩小为右半部分,将左边界更新为
mid + 1
- 如果中间元素大于目标元素,则将搜索范围缩小为左半部分,将右边界更新为
mid - 1
通过不断缩小搜索范围,最终可以找到目标元素或确定其不存在于数组中。
请注意,二分搜索假设输入的数组已经按升序排列。如果数组未排序,需要在进行二分搜索之前先对数组进行排序。
代码实现
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29
|
int binarySearch(int nums[], int size, int target) { int left = 0; int right = size - 1;
while (left <= right) { int mid = left + (right - left) / 2;
if (nums[mid] == target) { return mid; } else if (nums[mid] < target) { left = mid + 1; } else { right = mid - 1; } }
return -1; }
|
复杂度分析
每执行一次 while 循环,待搜索数组的大小减少一半。因此最坏情况下,时间复杂度为 O(logn)
Q&A
[!question] 为什么别的算法书是 mid = (left + right) / 2;
?
这是因为在计算中间索引时,使用 (right - left) / 2
可以避免整数溢出的问题。
假设 left
和 right
都是很大的正整数,它们的和可能会超过 int
类型的最大值。如果我们使用 (right + left) / 2
来计算中间索引,可能会导致溢出错误。通过使用 (right - left) / 2
,我们可以确保在计算中间索引时不会发生溢出。
棋盘覆盖
问题定义
假设你有一个 2n×2n 大小的棋盘,其中一个方格已经被预先填充(或者说被“覆盖”)。你的任务是使用 L 型的三个方格形状的骨牌(也就是说,它覆盖了一个 2x2 的方格,但是缺少了一个角落)来覆盖剩余的所有方格,且任何 2 个 L 型骨牌不得重叠覆盖,如此使得整个棋盘都被填满。

算法思想
这个问题可以通过分治算法来解决。基本的思路是将棋盘分成四个更小的棋盘,每个棋盘的大小是原来的一半(指边长为 2 的正方形),则特殊方格必定位于 4 个较小的子棋盘之一。
为了将无特殊方格的子棋盘转化为特殊棋盘,可以用一个 L 型的骨牌覆盖这三个较小棋盘的交汇处。

然后,对每个更小的棋盘递归地应用同样的过程,直到达到基本情况,即棋盘的大小为 1×1 。
代码实现
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49
|
void chessboard(int tr, int tc, int dr, int dc, int size) { if (size == 1) { return; }
int t = tile++; int s = size / 2;
if (dr < tr + s && dc < tc + s) { chessboard(tr, tc, dr, dc, s); } else { board[tr + s - 1][tc + s - 1] = t; chessboard(tr, tc, tr + s - 1, tc + s - 1, s); }
if (dr < tr + s && dc >= tc + s) { chessboard(tr, tc + s, dr, dc, s); } else { board[tr + s - 1][tc + s] = t; chessboard(tr, tc + s, tr + s - 1, tc + s, s); }
if (dr >= tr + s && dc < tc + s) { chessboard(tr + s, tc, dr, dc, s); } else { board[tr + s][tc + s - 1] = t; chessboard(tr + s, tc, tr + s, tc + s - 1, s); }
if (dr >= tr + s && dc >= tc + s) { chessboard(tr + s, tc + s, dr, dc, s); } else { board[tr + s][tc + s] = t; chessboard(tr + s, tc + s, tr + s, tc + s, s); } }
|
请注意,这段代码中假设棋盘的大小为 8×8 ,并且特殊位置的行号和列号为 0。你可以根据需要修改这些值。另外,代码中使用 #define
定义了一些常量,你也可以根据需要进行修改。
最终结果如下:
