Divide and Conquer 分治法
把一片领土分解成若干小块儿,然后一块儿一块儿分解。
- Divide the problem 原规模的n变小
- Conquer 递归的解每个小问题
- Commbine 将子问题的解合并为原问题的解。
Merge sort 归并排序 Θ(nlgn)————分治法
递归式
\(T(n)= \left\{\begin{matrix} Θ(1),n=1 \\ 2T(n/2)+Θ(n) n>1 \end{matrix}}\right.\)
伪代码
Merge-SORT(A, n) or SORTS A[1,n]
- If n-1, done
- 拆分输入A[1,…,[n/2]] and A[[n/2]+1,…,n] (向上取整)
(关键!)把排好序的两个表归并 a. 将待排序数组一分为二
b. 递归的对每一个子数组进行排序
c. 合并 O(n):两张已排序的列表中,最小元素是多少;将两个中较小的输出到最终的输出数组中;继续比较两个中最小的数,然后继续输出到最终数组中…
【注意:】每一步都是固定数目的操作,和数组尺寸无关
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
MERGE-SORT(A, p, r)
if p < r
q = (p + r) / 2 // 计算中间索引
MERGE-SORT(A, p, q) // 递归排序左半部分
MERGE-SORT(A, q + 1, r) // 递归排序右半部分
MERGE(A, p, q, r) // 合并已排序的两部分
MERGE(A, p, q, r)
n1 = q - p + 1 // 左半部分的元素个数
n2 = r - q // 右半部分的元素个数
Let L[0:n1-1] and R[0:n2-1] be new array // 创建临时数组L和R
for i = 1 to n1 // 将左半部分复制到临时数组L
L[i] = A[p + i - 1]
for j = 1 to n2 // 将右半部分复制到临时数组R
R[j] = A[q + j]
i = 1
j = 1
for k = p to r // 合并L和R到原数组A
if L[i] <= R[j]
A[k] = L[i]
i = i + 1
else
A[k] = R[j]
j = j + 1
java实现
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
50
51
52
53
54
55
56
57
58
59
public class MergeSort {
// 主排序方法
public static void mergeSort(int[] arr) {
if (arr.length > 1) {
// 找到数组的中间点
int q = arr.length / 2;
// 分割数组为两半
int[] left = new int[q];
int[] right = new int[arr.length - q];
System.arraycopy(arr, 0, left, 0, q);
System.arraycopy(arr, q, right, 0, arr.length - q);
// 递归排序左右两部分
mergeSort(left);
mergeSort(right);
// 合并排序后的数组
merge(arr, left, right);
}
}
// 合并两部分
private static void merge(int[] arr, int[] left, int[] right) {
int i = 0, j = 0, k = 0;
// 比较并合并左右数组的元素
while (i < left.length && j < right.length) {
if (left[i] < right[j]) {
arr[k++] = left[i++];
} else {
arr[k++] = right[j++];
}
}
// 将左边剩余的元素加入
while (i < left.length) {
arr[k++] = left[i++];
}
// 将右边剩余的元素加入
while (j < right.length) {
arr[k++] = right[j++];
}
}
// 测试归并排序
public static void main(String[] args) {
int[] arr = { 38, 27, 43, 3, 9, 82, 10 };
mergeSort(arr);
// 输出排序后的数组
for (int num : arr) {
System.out.print(num + " ");
}
}
}
Binary search 二分查找法Θ(logn)
设一个数为x,在一个已排序的数组中找到这个x \(T(n) = T(n/2)+Θ(1) = Θ(logn)\)
1
2
3
4
5
6
7
8
9
10
11
12
function binarySearch(array, target):
p = 0
r = length(array) - 1
while p <= r:
q = p + (r - p) / 2
if array[q] == target:
return q
else if array[q] < target:
p = q + 1
else:
r = q - 1
return -1 // target not found
JAVA实现
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
public class BinarySearch {
public static int binarySearch(int[] array, int target) {
int p = 0;
int r = array.length - 1;
while (p <= r) {
int q = p + (r - p) / 2;
if (array[q] == target) {
return q;
} else if (array[q] < target) {
p = q + 1;
} else {
r = q - 1;
}
}
return -1; // target not found
}
public static void main(String[] args) {
int[] array = {1, 3, 5, 7, 9, 11, 13};
int target = 7;
int result = binarySearch(array, target);
System.out.println(result); // Output: 3
}
}
Powering number 乘方问题Θ(lgn)
给一个实数或者浮点数x,再给一个正整数n,计算 $x^n$.
当n为偶数时:$x^n = x^(n/2)·x^(n/2)$ 当n为奇数时:$x^n = x^((n-1)/2)·x^((n-1)/2)·x$
$T(n)=T(n/2)+Θ(1)=Θ(lgn)$
伪代码
1
2
3
4
5
6
7
8
function power(x, n):
if n == 0:
return 1 // x^0 = 1
if n is even:
half = power(x, n / 2)
return half * half
else:
return x * power(x, n - 1)
JAVA实现
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
public class Power {
public static double power(double x, int n) {
if (n == 0) {
return 1; // x^0 = 1
}
// Handle negative exponent
if (n < 0) {
x = 1 / x;
n = -n;
}
// Fast exponentiation
double result = 1;
while (n > 0) {
if (n % 2 == 1) {
result *= x; // If n is odd
}
x *= x; // Square the base
n /= 2; // Halve the exponent
}
return result;
}
public static void main(String[] args) {
double x = 2;
int n = 10;
System.out.println(power(x, n)); // Output: 1024.0
}
}