梦开始的地方
目录
快速排序----分治
时间复杂度分析
平均复杂情况=O(nlogn) 用数学期望证明此处省略
稳定分析
如果原序列中有两个相等的数,排序后这两个数并不交换位置,那么称这个排序为稳定的排序
快排不稳定:将快排变稳定的方法把相同的元素开二元组,下标作为区分条件
①确定分界点:x
1.左边界 q[l]
2.右边界 q[r]
3.中间位置 q[(l+r)/2]
4.随机
②调整区间 ☆
所有小于x在左面大于x在右边
③递归处理左右两端
调整区间的做法:
①暴力做法: 开两个数组a[] b[],扫描q[],小于x插入a大于x插入b,再将a、b插入q
空间复杂度比较高,要开很多数组
②优美做法 开两个指针i,j
模版
void quick_sort(int q[], int l, int r)
{
if (l >= r) return;
int i = l - 1, j = r + 1, x = q[l + r >> 1];
// l+r的值右移1位,相当l+r的值除以2取整。取中间值为边界
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);
}
值得注意的是,在快速排序算法中,选择枢轴元素的策略是重要的,它可以影响算法的性能。原始的快速排序算法选择的枢轴元素是
q[l + r >> 1]
,即取中间位置的元素作为枢轴元素。如果将选择的枢轴元素改为
q[l]
,即取第一个元素作为枢轴元素,可能导致算法的性能下降。这是因为如果原始数组已经是有序或接近有序的,如果每次选择第一个元素作为枢轴元素,就会导致分区的不平衡,因此快速排序算法的时间复杂度可能会增加。当原始数组接近有序时,选择中间位置的元素作为枢轴元素可以尽可能地保证分区的平衡,从而使得算法的时间复杂度更接近O(nlogn)。
如果将选择的枢轴元素改为
q[l]
,相当于总是选择第一个元素作为枢轴元素,而不管原始数组的有序程度。这种情况下可能会出现分区的不平衡,导致算法的时间复杂度变为O(n^2),从而导致超时。
例题:活动 - AcWing
归并排序----分治
时间复杂度分析
最好=最坏=平均复杂情况=O(nlogn)
共有logn行数(log以2为底)
每一行都是O(n) 如第二行2*O(n/2)=O(n)
总复杂度为n*O(logn)=O(nlogn)
稳定分析
归并排序是稳定的
①确定分界点 mid=(l+r)/2
②递归排序 left、right
③归并---合二为一 ☆
归并的方法 合二为一
归并方法,假设有两个有序数组a b
再开一个数组q,比较两个指针指向的值,哪一个小就插入哪一个,并将指针后移,重复上述过程,当某一个数组元素全被插入以后,另一个数组剩余部分直接插入q数组
注:当两个数的值相等时,课随意插入某一个,但一般插入第一个数组的元素
模版:
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;
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 ++ ];
for (i = l, j = 0; i <= r; i ++, j ++ ) q[i] = tmp[j];
}
例题:活动 - AcWing
整数二分 二分查找
思想:将一个有序的序列每次都分成两个序列,查找中间值是否满足要查找的性质,改变左右指针的值缩小查找范围
情况一:满足左边的性质,也可以理解为查找一个数最大的右边界(假设序列从左往右由小变大)
情况二:满足右边的性质,也可以理解为查找一个数最小的左边界(假设序列从左往右由小变大)
两种情况分别对应以下模版的bsearch_1和bsearch_2
模版:
bool check(int x) {/* ... */} // 检查x是否满足某种性质
// 区间[l, r]被划分成[l, mid]和[mid + 1, r]时使用:
int bsearch_1(int l, int r)
{
while (l < r)
{
int mid = l + r >> 1;
if (check(mid)) r = mid; // check()判断mid是否满足性质
else l = mid + 1;
}
return l;
}
// 区间[l, r]被划分成[l, mid - 1]和[mid, r]时使用:
int bsearch_2(int l, int r)
{
while (l < r)
{
int mid = l + r + 1 >> 1;
if (check(mid)) l = mid;
else r = mid - 1;
}
return l;
}
例题:活动 - AcWing
浮点数二分
由于整数二分需要处理是否能整除问题因此要分两种情况,浮点数可直接除,循环的条件可以改为l-r<1e-6等,当l和r无限接近就可认为查找完毕
模版
bool check(double x) {/* ... */} // 检查x是否满足某种性质
double bsearch_3(double l, double r)
{
const double eps = 1e-6; // eps 表示精度,取决于题目对精度的要求
while (r - l > eps)
{
double mid = (l + r) / 2;
if (check(mid)) r = mid;
else l = mid;
}
return l;
}
例题:活动 - AcWing