实验目的
1)了解分治策略算法思想及基本原理;
2)掌握使用分治算法求解问题的一般特征;
3)掌握分解、治理的方法;
4)能够针对实际问题,能够正确的分解、治理,设计分治算法;
5)能够正确分析算法的时间复杂度和空间复杂度。
实验内容(要求)
代码实现快速排序
算法设计(问题分析、建模、算法描述)
问题分析:
基本思想为通过一次排序将需要排序的序列分割成独立的两部分,首先设置一个基准数,进行一次排序后,其中一部分的所有数据比基准数小,另一部分的所有数据都比基准数大,这样基准数当前位置就是其在有序序列的位置,然后对新分出的两个序列递归地进行排序处理(由于两方互不影响,其满足最优子结构,有独立性),直到序列大小最小,这样通过一次分治的排序可以将所有元素放到有序序列的正确位置上,从而将整个序列有序化。
算法描述:
- 确定分界点: q[i] or q[(l+r)/2] or q[r] or 随机;
- 调整区间:把小于等于x的数放在x左边,大于等于x的数放在右边(等于x的在哪无所谓);
- 递归处理左右两段
优化版:(一种不太优美的暴力做法,浪费一点空间,但是思路简单)
- 开两个额外的数组a[ ],b[ ];
- 扫描整个区间q[ l ~ r ]
q[i] <= x , x -> a[ ];
q[i] >= x , x -> b[ ];
3.先把a[ ]放入q[ ],再把b[ ]放入q[ ]。
超级优化版:(很优美的方法,不需要额外空间,用两个指针i, j实现)
- i -> l , j -> r
- 两个指针向中间移动(先移动还是后移动看1.步骤的指向)i 先向右移动,当 i 扫到 <= x的数时,停下。j 向左移动,当 j 扫到 >= x的数时,停下,交换两个指针指向的值。
- i 和 j 相遇,停止。
- 递归分别判左区域(l, i)和右区域(i+1, r)。
注意边界问题,取到左边界就会死循环。
算法源码
#include<iostream>
using namespace std;
const int N = 1e6 + 10;
int n;
int q[N];
void quick_sort(int q[], int l, int r)
{
if(l >= r) return;
int x = q[l], i = l - 1, j = 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);
}
int main()
{
scanf("%d", &n);
for(int i = 0; i < n; i ++ ) scanf("%d", &q[i]);
quick_sort(q, 0, n - 1);
for(int i = 0; i < n; i ++ ) printf("%d ", q[i]);
return 0;
}
测试数据及运算结果(要求:截图说明算法运行的结果)
测试数据1:
6
19 78 56 45 30 74
输出结果:
测试数据2:
10
2 8 7 5 8 8 1 0 3 9
输出结果:
算法分析(分析算法的时间复杂度和空间复杂度)
时间复杂度: O(n log n)
空间复杂度: O( n )