转自:http://blog.chinaunix.net/space.php?uid=12454074&do=blog&id=2935121
作者:tyc611.cublog.cn,2008-03-02
问题描述
问题描述
考虑这样的一种排序问题,即无法准确地知道待排序的各个数字到底是多少。对于其中的每个数字,我们只知道它落在实轴上的某个区间内。亦即,给定的是n个形如[a(i), b(i)]的闭区间(这里小括后起下标的作用,后同),其中a(i) <= b(i)。算法的目标是对这些区间进行模糊排序(fuzzy-sort),亦即,产生各区间的一个排列<i(1), i(2), ..., i(n)>,使得存在一个c(j)属于区间[a(i(j)), b(i(j))],满足c(1) <= c(2) <= c(3) <= ... <= c(n)。
a) 为n个区间的模糊排序设计一个算法。你的算法应该具有算法的一般结构,它可以快速排序左部端点(即各a(i)),也要能充分利用重叠区间来改善运行时间。(随着各区间重叠得越来越多,对各区间进行模糊排序的问题会变得越来越容易。你的算法应能充分利用这种重叠。)
b) 证明:在一般情况下,你的算法的期望运行时间为Θ(nlgn),但当所有的区间都重叠时,期望的运行时间为Θ(n)(亦即,当存在一个值x,使得对所有的i,都有x∈[a(i), b(i)])。你的算法不应显式地检查这种情况,而是应随着重叠量的增加,性能自然地有所改善。
算法思想
利用快速排序算法的思想,对区间序列进行划分。但此时的主元区间元素是一个区间元素集合中所有区间元素的公共区间(交集),即是说该集合中的所有区间元素都是“相等的”或者说“任意序都是有序的”。
初始时,算法任选一个区间元素作为主元(同快速排序)。算法运行过程中,如果某个区间元素严格小于主元,则将其放到序列左边;如果其严格大于主元,则将其放到序列右边;否则,说明该区间元素与主元相交,则更新主元区间大小为该相交的区间部分,并将该区间元素加入到与主元“相等”的区间元素集合中。处理完当前序列后,再递归处理左边和右边的子序列(分治思想嘛,同快速排序)。
算法实现
本文使用C++实现如下:
/**
* FuzzySort.cpp
* @Author Tu Yongce <yongce (at) 126 (dot) com>
* @Created 2008-2-26
* @Modified 2008-2-26
* @Version 0.1
*/
// 《算法导论》(第二版)P164,思考题7-6:对区间的模糊排序
/**
* 考虑这样的一种排序问题,即无法准确地知道待排序的各个数字到底是多少。对于其中的每个数字,
* 我们只知道它落在实轴上的某个区间内。亦即,给定的是n个形如[a(i), b(i)]的闭区间(这里小括
* 后起下标的作用,后同),其中a(i) <= b(i)。算法的目标是对这些区间进行模糊排序
* (fuzzy-sort),亦即,产生各区间的一个排列<i(1), i(2), ..., i(n)>,使得存在一个c(j)属于
* 区间[a(i(j)), b(i(j))],满足c(1) <= c(2) <= c(3) <= ... <= c(n)。
* a) 为n个区间的模糊排序设计一个算法。你的算法应该具有算法的一般结构,它可以快速排序左部
* 端点(即各a(i)),也要能充分利用重叠区间来改善运行时间。(随着各区间重叠得越来越多,
* 对各区间进行模糊排序的问题会变得越来越容易。你的算法应能充分利用这种重叠。)
* b) 证明:在一般情况下,你的算法的期望运行时间为Θ(nlgn),但当所有的区间都重叠时,期望的
* 运行时间为Θ(n)(亦即,当存在一个值x,使得对所有的i,都有x∈[a(i), b(i)])。你的算法
* 不应显式地检查这种情况,而是应随着重叠量的增加,性能自然地有所改善。
*/
#include <iostream>
#include <utility>
using namespace std;
typedef pair<int, int> pair_int;
template <typename T>
pair_int fuzzy_partition(pair<T, T> *arr, int p, int r)
{
// 类似快速排序的划分,arr[i..k-1]范围内的区间元素有共同的重叠区间,该重叠区间作为主元
// 时间复杂度为O(n)
int i = p, k = p + 1, j = r;
pair<T, T> x = arr[p]; // 主元区间
while (k <= j) {
if (arr[k].second < x.first) {
// 比主元区间小
swap(arr[k], arr[i]);
++i;
++k;
} else if (arr[k].first > x.second) {
// 比主元区间大
swap(arr[k], arr[j]);
--j;
} else {
// 与主元区间有重叠,则更新主元为重叠区间(交集)
x.first = max(x.first, arr[k].first);
x.second = min(x.second, arr[k].second);
++k;
}
}
return pair_int(i, j);
}
template <typename T>
void fuzzy_sort(pair<T, T> *arr, int p, int r)
{
// 对区间的模糊排序,类似于快速排序算法
if (p < r) {
pair_int q = fuzzy_partition(arr, p, r);
fuzzy_sort(arr, p, q.first - 1);
fuzzy_sort(arr, q.second + 1, r);
}
}
void fuzzy_sort_test()
{
pair_int arr1[] = {pair_int(7, 9), pair_int(2, 5)};
pair_int arr2[] = {pair_int(4, 8), pair_int(2, 5)};
pair_int arr3[] = {pair_int(5, 9), pair_int(2, 7), pair_int(4, 8), pair_int(3, 6)};
pair_int arr4[] = {pair_int(7, 9), pair_int(9, 10), pair_int(2, 5), pair_int(4, 8), pair_int(1, 10)};
pair_int arr5[] = {pair_int(1, 2), pair_int(2, 3), pair_int(3, 4), pair_int(4, 5), pair_int(5, 6)};
pair_int *arr[] = {arr1, arr2, arr3, arr4, arr5};
const size_t NUM2[] = {
sizeof(arr1) / sizeof(arr1[0]),
sizeof(arr2) / sizeof(arr2[0]),
sizeof(arr3) / sizeof(arr3[0]),
sizeof(arr4) / sizeof(arr4[0]),
sizeof(arr5) / sizeof(arr5[0]),
};
const size_t NUM = sizeof(arr) / sizeof(arr[0]);
for (size_t i = 0; i < NUM; ++i) {
cout << "初始区间序列:" << endl;
for (size_t j = 0; j < NUM2[i]; ++j)
cout << "(" << arr[i][j].first << "," << arr[i][j].second << ") ";
cout << endl;
fuzzy_sort(arr[i], 0, NUM2[i] - 1);
cout << "模糊排序后的区间序列:" << endl;
for (size_t j = 0; j < NUM2[i]; ++j)
cout << "(" << arr[i][j].first << "," << arr[i][j].second << ") ";
cout << "\n" << endl;
}
}
算法运行结果如下:
初始区间序列:
(7,9) (2,5)
模糊排序后的区间序列:
(2,5) (7,9)
初始区间序列:
(4,8) (2,5)
模糊排序后的区间序列:
(4,8) (2,5)
初始区间序列:
(5,9) (2,7) (4,8) (3,6)
模糊排序后的区间序列:
(5,9) (2,7) (4,8) (3,6)
初始区间序列:
(7,9) (9,10) (2,5) (4,8) (1,10)
模糊排序后的区间序列:
(2,5) (4,8) (7,9) (9,10) (1,10)
初始区间序列:
(1,2) (2,3) (3,4) (4,5) (5,6)
模糊排序后的区间序列:
(1,2) (2,3) (3,4) (5,6) (4,5)