归并排序 - 分治
时间复杂度 O(NlogN)
java 代码
import java.io.*; // 输入输出
class Main{
public static void main(String[] args) throws IOException {
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
int n = Integer.valueOf(br.readLine());
int[] q = new int[n];
args = br.readLine().split(" ");
for (int i = 0; i < n; ++i) {
q[i] = Integer.valueOf(args[i]);
}
//quick_sort(q, 0, n - 1);
tmp = new int[n];
merge_sort(q, 0, n - 1);
System.out.print(q[0]);
for (int i = 1; i < n; ++i) {
System.out.print(" " + q[i]);
}
}
static int[] tmp; // 归并排序辅助数组
public static void merge_sort(int[] q, int l, int r) {
// 递归边界条件
if (l >= r) return;
// 求中间下标
int mid = (l + r) / 2;
// 归并排序两边
merge_sort(q, l, mid); merge_sort(q, mid + 1, r);
// 合并排序好的两边
int k = 0, i = l, j = mid + 1;
while (i <= mid && j <= r) {
if (q[i] <= q[j]) tmp[k++] = q[i++];
else tmp[k++] = q[j++];
}
while (i <= mid) {
tmp[k++] = q[i++];
}
while (j <= r) {
tmp[k++] = q[j++];
}
// 这里合并需要 i 从 l开始 而 j是从0开始
for (i = l, j = 0; i <= r; ++i, ++j) {
q[i] = tmp[j];
}
}
public static void quick_sort(int[] q, int l, int r) {
// 判断递归边界
if (l >= r) return;
// 枢纽为中间值
int x = q[(l + r + 1) / 2], i = l - 1, j = r + 1;
while (i < j) {
do ++i; while (q[i] < x); // 跳过那些小于x的
do --j; while (q[j] > x); // 跳过那些大于x的
// 交换
if (i < j) {
int tmp = q[i];
q[i] = q[j];
q[j] = tmp;
}
}
quick_sort(q, l, i - 1); // 需要注意的是这里 用 j 上面 x就不能是右边界,反之 用i就不能是左边界
quick_sort(q, i, r);
/*
int x = q[(l + r) / 2], i = l - 1, j = r + 1;
quick_sort(q, l, j); // 需要注意的是这里 用 j 上面 x就不能是右边界,反之 用i就不能是左边界
quick_sort(q, j + 1, r);
*/
}
}
区分第一个满足,以及最后一个满足的条件。在第一个满足里,if 满足 一定是r变 mid 否则的话是l = mid + 1.
在最后一个满足里 mid = (l + r + 1) / 2,if 满足 那么 l = mid 否则 r = mid - 1
二分的本质是边界,左边不满足,右边满足的性质
一般写二分的思考顺序是这样的:首先通过题目背景和check(mid)函数的逻辑,判断答案落在左半区间还是右半区间。
左右半区间的划分方式一共有两种:
中点mid属于左半区间,则左半区间是[l, mid],右半区间是[mid+1, r],更新方式是r = mid;或者 l = mid + 1;,此时用第一个模板;
中点mid属于右半区间,则左半区间是[l, mid-1],右半区间是[mid, r],更新方式是r = mid - 1;或者 l = mid;,此时用第二个模板;