目录
快速排序
归并排序
二分算法
整数二分
浮点数二分模板
总结:
//快速排序
void quick_sort(int q[], int l, int r)
{
if (l >= r) return;
//向下取整可能使得x取到q[l]
int i = l - 1, j = r + 1, x = q[l + r >> 1];
while (i < j)
{
do i++; while (q[i] < x);
do j--; while (q[j] > x);
if (i < j) swap(q[i], q[j]);
}
quick_sort(q, l, j), quick_sort(q, j + 1, r);
}
x取中间的值鲁棒性更强;
若取中是向下取整,则以j作为分界;
由于最后一轮的if语句一定不执行
所以,只能保证:
q[l…i-1] <= x, q[i] >= x
q[j+1…r] >= x, q[j] <= x
i >= j
const int n = 1e6 ;//根据题目要求
void merge_sort(int q[], int l, int r)
{
if (l >= r) return;
//分成子问题
int mid = l + r >> 1;
//递归处理子问题
merge_sort(q, l, mid), merge_sort(q, mid + 1, r);
//合并子问题
int k = 0, i = l, j = mid + 1, tmp[n];
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++];
//tmp 保存的是 q[l..mid] , q[mid+1..r] 中从小到大排序的所有数
for (i = l, k = 0; i <= r; i++, k++)
q[i] = tmp[k];
}
为什么不用 mid - 1 作为分隔线呢
即 merge_sort(q, l, mid - 1 ), merge_sort(q, mid, r)
因为 mid = l + r >> 1 是向下取整,mid 有可能取到 l (数组只有两个数时),造成无限划分
二分查找分为整数二分和浮点数二分,一般所说的、常用的二分查找也都是指整数二分。
初学最大的误区就是把满足单调性当作可以使用二分查找的充要条件
事实上,满足单调性的数组一定可以使用二分查找,但可以使用二分查找的数组不一定需要满足单调性。
整数二分其实是一种性质的查找,把数组元素的某种性质作为判断标准,左半部分和右半部分其中一个满足该性质另一个不满足该性质,然后我们使用二分查找可以找到左半部分的右边界和右半部分的左边界。
另外,整数二分是离散的!
//整数二分(查左边界 or 查右边界)
//查左边界
int left_bound(int l, int r)
{
while (l < r)
{
int mid = l + r >> 1;
if (chek(mid))//chek:在右半部分为真
r = mid;
else
l = mid + 1;
}
return l;
}
//查右边界
int right_bound(int l, int r)
{
while (l < r)
{
int mid = l + r >> 1;
if (chek(mid))//chek:在左半部分为真
l = mid;
else
r = mid - 1;
}
return l;
}
浮点数二分是连续的,不像整数二分有复杂的边界问题,要简单许多。
浮点数二分常用来求某个数的近似值
//eps是精度
double fbsearch(double l, double r, double eps) {
while (r - l > eps) {
double mid = (l + r) / 2;
if (check(mid)) r = mid;
else l = mid;
}
return l;
}
快排和归并都属于分治算法,
分治算法都有三步:1、分成子问题。2、递归处理子问题。3、子问题合并;
另外,整数二分使用时要特别注意是左右边界的哪个模板,细节忘记时,可以先写if里的chek来推理判断。(求哪半部分的边界,就chek(mid)在那半部分时为真)