目录
1、排序
1.1、章节思路
1.2 快速排序
暴力做法
优美做法 一个头指针,一个尾指针。
直到 i和j相遇为止。 任何时候i左边的数小于等于x,j右边的数大于等于x。当i>x时停止,j<=x时停止。当两个指针都停止时,交换两个数据。
模拟数据
准备交换。
交换之后,两个数字向中间移动一位。
此时i指针前(不包括i)所有数<=3
j指针后(不包括j)所有数>=3
1.2.1 快排模板
1.2.2 快排死循环
这样写会出现死循环
左l,r【0,-1】return 右l,r【0,1】 与原区间l,r【0,1】一样会陷入死循环。
因此在递归时,传入的参数含i时,x的值不能为q【l】。
同上 这样写也会出现死循环
1.2.3 快排最优解
位运算优先级低于加减
位运算等同于除以2.
1.3 归并排序
1.3.1 归并排序与快速排序时间复杂度证明
分析归并排序时间复杂度 o(n logn)以2为底
快速排序同归并排序 时间复杂度(o(nlogn))
快排每次划分 不一定时n/2,但是期望是n/2,递归层数期望也是logn 因此时间复杂度同归并。
1.3.2 归并排序模板
2、二分
2.1 整数二分
有单调性可以二分,没单调性也可以二分。单调性不是二分的本质。
根据二分得出不同的点,两个点。则需要两个不同的模板。
二分红色的点 check 是否满足红色性质
满足的话->在红色区间中 l=mid
不满足->不在红色区间中 r=mid-1
二分绿色的点,check是否满足绿色。
在绿色区间中->r=mid
不在->l=mid+1
2.1.1 代码
2.1.2 +1思路 为什么+1?
l=mid 则加1。
为什么+1。
c++下取整
循环一遍之后,没变。进入死循环。
需要+1
2.1.3 如何选择模板 习题思路
写一个check函数,观察l=mid? mid=(l+r+1)/2
否则执行其它的。不需要思考上述画的红绿图。
check 函数的编写
找到满足x的左边界值。
r=mid 不需要补齐+1
无解判断:如果数组中不包含x,此时二分出来的是,数组从前到候第一个>x的数值。
输出l与r均可,循环结束的时候,l与r相等
找到满足x的有边界值
2.1.4 数的范围
#include <iostream>
using namespace std;
const int N = 100010;
int n, m;
int q[N];
int main()
{
scanf("%d%d", &n, &m);
for (int i = 0; i < n; i ++ ) scanf("%d", &q[i]);
while (m -- )
{
int x;
scanf("%d", &x);
//位置从0开始计算
int l = 0, r = n - 1;
//找x的起始位置
while (l < r)
{
int mid = l + r >> 1;
//大于等于x的第一个数
if (q[mid] >= x) r = mid;
else l = mid + 1;
}
if (q[l] != x) cout << "-1 -1" << endl;
else
{
cout << l << ' ';
int l = 0, r = n - 1;
while (l < r)
{
int mid = l + r + 1 >> 1;
//小于等于x的最后一个数
if (q[mid] <= x) l = mid;
else r = mid - 1;
}
cout << l << endl;
}
}
return 0;
}
2.1.5 二分思想
在一个区间内部取二分边界
每一次选择的区间,选择答案所在的区间去处理。
每次把区间缩小一半,选择答案所在的区间。
当区间长度为1时,就是答案。
二分一定有解。题目可能无解。
举例:
判断条件为大于等于x,找到了第一个大于x的数。这是二分的结果。但是题目要求等于x,因此题目无解。
根据二分判断出的边界去判断题目有没有解。
2.2 浮点数二分
r减l为一个很小的数的时候,可以将l或r当做二分的结果。
2.2.1 开平方
通过提高精度,防止误差出现
代码有错误,应该为r=max(1,x);
因为计算0.01开根号时,r=0.01,l=0。取不到0.1。必须让r=1。在习题中有分析
经验
误差要比保留的有效数字位数大2。
3、数的三次方根
4、逆序对的数量
当q[i]>q[j]时,i包括i之后的所有数都大于q[j]
数据量是10万
最大逆序数对有几个?
5、求第k个数
用到快速选择算法。
对于本题 时间复杂度是o(n) 只需要求半边‘