问题5.1 二分插入法
插入式排序法是一个极为简单的方法,但它的效率十分低,因为比较次数与n^2成正比,此处n是数据个数,而且还有大量移动数据的动作,请写一个程序,在这两点上改进,使得比较次数与nlogn成正比,而且移动数据的速度加快~
思路:为啥用二分查找不解释,memmov原型如下:void* memmov(void *dest, const void *src, size_t count);
#include <iostream>
#include <algorithm>
#include <iterator>
using namespace std;
#define YES 1
#define NO 0
#define SIZE sizeof(int) / sizeof(char)
void sort(int *input, int size)
{
if (input == NULL || size <= 0)
return;
int current, pos;
int low, high, mid;
int x;
for (current = 1; current < size; current++)
{
x = input[current];
pos = -1;
if (x < input[0])
pos = 0;
else if (x <= input[current - 1])
{
low = 0;
high = current - 1;
while (high - low > 1)
{
mid = (low + high) / 2;
if (x >= input[mid])
low = mid;
else
high = mid;
}
pos = low + 1;
}
if (pos >= 0)
{
memmove((void*)&input[pos + 1], (void*)&input[pos], SIZE * (current - pos));
input[pos] = x;
}
}
}
void main()
{
int array[] = {5, 2, 1, 4, 3, 7, 6};
const int size = sizeof array / sizeof *array;
sort(array, size);
copy(array, array + size, ostream_iterator<int>(cout, " "));
}
问题5.2 SHELL
在传统的教科书中,Shell排列法都是直接饮用D.L.Shell当年原著中的办法,在要排序的数组中,先把间隔为n/2的元素排好,接着把间隔为n/4的元素排好,再排间隔为n/8 ... , 4, 2, 1的元素,最后就是一个依顺序排列完成的结果了。D.Knuth曾经在他的著作中证明,Shell方法要用与n^1.5成正比的比较次数(当然已经比插入排序要快了一些),不过最近Sedgewick等人却找出了更快的办法,他证明可以把Shell方法加快到只与n^4/3成正比的比较次数。请写一个程序,找出一组更快的间隔。
思路:哎,这道题很bug,不解释了。。。
问题5.3 快速排列法
可以找一本书,研究一下快速排列法的观点,写成程序。
快排写N次了,不写了~
问题5.4 保持等值的原来顺序
快速排序法有一个常被人批评的缺点,这就是相等的数据在排序完成后不会保持原来的顺序,不过,使用连接串行的技巧就可以轻易克服它,请写一个程序来完成这件工作。
思路:正是由于存在swap操作,快速排序法才会在相等的数据在排序完成后不会保持原来的顺序~
#include <iostream>
#include <algorithm>
#include <iterator>
using namespace std;
typedef struct node* Node;
struct node
{
int data;
Node next;
};
void list_quicksort(Node *, Node *);
Node insert(Node *, Node *, Node);
void join(Node *, Node *, Node, Node);
void printLink(Node head);
void sort(int *x, int n)
{
if (x == NULL || n <= 0)
return;
Node first = NULL;
Node last = NULL;
Node p, q;
int i;
for (i = 0; i < n; i++)
{
p = (Node)malloc(sizeof(struct node));
p->data = x[i];
p->next = NULL;
insert(&first, &last, p);
}
printLink(first);
system("pause");
list_quicksort(&first, &last);
for (p = first, i = 0; i < n; i++)
{
x[i] = p->data;
q = p;
p = p->next;
free(q);
}
}
void printLink(Node head)
{
while (head)
{
cout << head->data << " ";
head = head->next;
}
}
void list_quicksort(Node *first, Node *last)
{
Node less_first = NULL;
Node less_last = NULL;
Node equal_first = NULL;
Node equal_last = NULL;
Node greater_first = NULL;
Node greater_last = NULL;
Node p;
int pivot;
pivot = (*first)->data;
p = insert(&equal_first, &equal_last, *first);
while (p != NULL)
{
if (p->data < pivot)
p = insert(&less_first, &less_last, p);
else if (p->data > pivot)
p = insert(&greater_first, &greater_last, p);
else
p = insert(&equal_first, &equal_last, p);
if (less_first != NULL)
list_quicksort(&less_first, &less_last);
if (greater_first != NULL)
list_quicksort(&greater_first, &greater_last);
}
join(&less_first, &less_last, equal_first, equal_last);
join(&less_first, &less_last, greater_first, greater_last);
*first = less_first;
*last = less_last;
}
Node insert(Node *first, Node *last, Node work)
{
Node p;
if (*first == NULL)
*first = *last = work;
else
{
(*last)->next = work;
*last = work;
}
p = work->next;
work->next = NULL;
return p;
}
void join(Node *first, Node *last, Node head, Node tail)
{
if (*first == NULL)
*first = head, *last = tail;
else if (head != NULL)
{
(*last)->next = head;
*last = tail;
}
}
void main()
{
int array[] = {5, 3, 2, 1, 6, 7, 4};
const int size = sizeof array / sizeof *array;
sort(array, size);
copy(array, array + size, ostream_iterator<int>(cout, " "));
}
问题5.5 非递归、无堆栈快速排序法
许多人都批评快速排序法,但观点却集中在快速排序法使用了递归的方法。但事实上这并不是快速排序法的最严重缺电。无论如何,写一个不用递归的快速排序法也可以让不少人安心,请问能不能写一个不用递归,但也不用堆栈的快速排序法来排正整数~
#include <iostream>
#include <algorithm>
#include <iterator>
using namespace std;
#define YES 1
#define NO 1
#define ALWAYS 1
#define SWAP(array, y) do { int t; t = array; array = y; y = t; } while (0);
const int size = 7;
int array[size] = {3, 2, 1, 6, 4, 7, 5};
void sort(int (&array)[size], int size)
{
if (array == NULL || size <= 0)
return;
int sorted;
int split;
int next;
int key;
for (sorted = 0; sorted < size; sorted++)
{
while (array[sorted] > 0)
{
key = array[sorted];
split = sorted;
for (next = sorted + 1; array[next] > 0; next++)
{
if (array[next] < key)
{
split++;
SWAP(array[split], array[next]);
}
}
SWAP(array[sorted], array[split]);
array[split] = -array[split];
}
array[sorted] = -array[sorted];
copy(array, array + size, ostream_iterator<int>(cout, " "));
system("pause");
}
}
void main()
{
sort(array, size);
copy(array, array + size, ostream_iterator<int>(cout, " "));
cout << endl;
}
问题5.6 求中位数
一组数的中位数,就是把那一组数从小到大排好后位居中间的那一个;如果有奇数个,那么在中间的那一个是存在的,但若有偶数个,就没有中间的那一个数了,因此就取位于中间那两个数的平均数。例如,3, 1, 7, 5, 9经过排列后是1, 3, 5, 7, 9,所以中位数是5;但3, 1, 7, 5, 9, 4,经过排序得到1, 3, 4, 5, 7, 9,所以中位数就是(4+5)/2=4,为了方便用整数运算,请写一个程序,接收一个整数数组,不必排大小而找出该数组的中位数。
思路:利用partition操作来完成此题!
问题5.7 堆积法
请写一个堆积排序法的程序,可以参考任何手头上的书籍,但注意一点,即程序必须写得有效率
思路:堆排序写N遍了,不想再写了~
程序猿最拿手的是神马??? COPY!!!
#include <iostream>
#include <algorithm>
#include <iterator>
using namespace std;
#define YES 1
#define NO 0
void fix_heap(int *, int, int, int);
void sort(int *old_x, int n)
{
int *x = old_x - 1;
int temp;
int size, i;
for (i = n / 2; i >= 1; i--)
fix_heap(x, i, x[i], n);
for (size = n; size >= 2; size--)
{
temp = x[1];
fix_heap(x, 1, x[size], size - 1);
x[size] = temp;
}
}
void fix_heap(int *x, int root, int key, int bound)
{
int father, son;
int done;
father = root;
son = father + father;
done = NO;
while (son <= bound && !done)
{
if (son < bound && x[son + 1] > x[son])
son++;
if (key < x[son])
{
x[father] = x[son];
father = son;
}
else
done = YES;
son = father + father;
}
x[father] = key;
}
void main()
{
const int size = 7;
int array[size] = {3, 2, 1, 6, 4, 7, 5};
sort(array, size);
copy(array, array + size, ostream_iterator<int>(cout, " "));
}
堆排序的另一种更好的写法:
#include <iostream>
#include <string>
#include <iterator>
#include <algorithm>
using namespace std;
void fix_heap(int *array, int size, int index)
{
if (array == NULL || size <= 0)
return;
int key = array[index];
while (index < size)
{
int leftChild = index * 2;
int rightChild = index * 2 + 1;
int bigIndex = index;
if (leftChild <= size && array[bigIndex] < array[leftChild])
bigIndex = leftChild;
if (rightChild <= size && array[bigIndex] < array[rightChild])
bigIndex = rightChild;
if (bigIndex != index)
{
swap(array[index], array[bigIndex]);
index = bigIndex;
}
else
{
break;
}
}
}
void sort(int *array, int size)
{
if (array == NULL || size <= 0)
return;
int *x = array - 1;
for (int i = size / 2; i >= 1; i--)
{
fix_heap(x, size, i);
}
for (int i = size; i >= 1; i--)
{
fix_heap(x, i, 1);
swap(x[i], x[1]);
}
}
void main()
{
const int size = 7;
int array[size] = {3, 2, 1, 6, 4, 7, 5};
sort(array, size);
copy(array, array + size, ostream_iterator<int>(cout, " "));
}
问题5.8 改良的堆积法
在用堆积法排序时会有一个明显的缺点,许多优秀的程序员可能都会看出来。如果现在要决定x[i]是不是应该在目前的位置,要找出在堆积中x[i]的两个后代,x[2*i]与x[2*i+1],再用这两者中大的一个与x[i]相比,如果x[i]比较大,问题就解决了;但若x[i]比较小,就把x[2*i]与x[2*i+1]中大的那个与x[i]互换,再去处理在新位置的x[i]值。很明显,把一个值每往下移一层就需要两次比较,是不是太多了一点?请发展一项技巧来克服这个短处~
思路:下面的图。。。
#include <iostream>
#include <algorithm>
#include <iterator>
using namespace std;
#define YES 1
#define NO 0
void fix_heap(int *, int, int, int);
void sort(int *old_x, int n)
{
int *x = old_x - 1;
int temp;
int size, i;
for (i = n / 2; i >= 1; i--)
fix_heap(x, i, x[i], n);
for (size = n - 1; size >= 1; size--)
{
temp = x[size + 1];
x[size + 1] = x[1];
fix_heap(x, 1, temp, size);
}
}
void fix_heap(int *x, int root, int key, int bound)
{
int son, level;
int top, bottom, mid;
son = root + root;
level = 1;
for (; son < bound; level++)
son = (x[son] < x[son + 1] ? (son + 1) << 1 : son << 1);
if (son > bound)
level--, son >>= 1;
top = level;
bottom = 0;
while (top > bottom)
{
mid = (top + bottom) / 2;
if (key <= x[son >> mid])
top = mid;
else
bottom = mid + 1;
}
for (mid = level - 1; mid >= top; mid--)
x[son >> (mid + 1)] = x[son >> mid];
x[son >> top] = key;
}
void main()
{
const int size = 7;
int array[size] = {3, 2, 1, 6, 4, 7, 5};
sort(array, size);
copy(array, array + size, ostream_iterator<int>(cout, " "));
}
问题5.9 合并法
有一个叫做合并排序法的技巧是这样的:把要排序的资料分成大约相等的两组,把这两组排序排好之后,通过合并的技巧而合成一个全部都排好的结果。请写一个程序来实现这个算法。
思路:归并排序写N次了~
问题5.10 桶子法
快速排序法、二分插入法、堆积法、合并法等,都至少需要用nlogn成正比的比较次数来排n个数据,当然不一定是数值。但如果要排的不过是一些正整数,有没有更快的办法?请利用所学的知识写出这个特殊的排序的程序,理想是打破nlogn的限制
思路:先按个位排序、再按十位排序、再按百位排序、再。。。
问题5.11 单一重复元素排序
如果一个数组中有n个元素,但并非每一个元素都不相同。事实上,只有大约alogn个值是不同的,其余n-alogn个值全部一样,请发展一个特别快的程序把数组的元素排好顺序。a是常数,为了方便起见,可以当成1;另外,n-alogn>0,而且n比alogn大很多,请问程序用了多少次比较?
思路:先找出那个重复的元素,然后只计入1次进行排序,排序之后再重新写入重复元素
#include <iostream>
#include <algorithm>
#include <iterator>
using namespace std;
#define YES 1
#define NO 0
void dup_sort(int *x, int n)
{
int dup;
int count, index, i, j;
for (i = 1; i < n; i++)
{
if (x[i] == x[i - 1])
{
dup = x[i];
break;
}
}
for (count = i = 0; i < n; i++)
{
if (x[i] != dup)
x[count++] = x[i];
}
x[count++] = dup;
sort(x, x + count);
for (index = n - 1, i = count - 1; i >= 0 && x[i] != dup; i--)
x[index--] = x[i];
if (i >= 0 && x[i] == dup)
for (j = i + 1; j <= index; j++)
x[j] = dup;
}
void main()
{
int array[] = {3, 2, 1, 2, 6, 2, 4, 7, 2, 2, 5};
const int size = sizeof array /sizeof *array;
dup_sort(array, size);
copy(array, array + size, ostream_iterator<int>(cout, " "));
}
问题5.12 均匀重复元素排序
如果一个数组有n个元素,但有alogn不同的值,换句话说,数组中有alogn不同的值,每一个都重复出现了n/alogn次。请写一个特别快的程序把数组的元素排好顺序。假设n很大,比alogn大很多,a是一个常数,请问程序用了多少次比较,为了方便起见,把a当成1
#include <iostream>
#include <algorithm>
#include <iterator>
#include <cmath>
using namespace std;
#define MIN(x, y) (x) <= (y) ? (x) : (y)
#define SWAP(x, y) {int *t; t = x; x = y; y = t; }
int *in1_data, *in2_data, *out_data;
int *in1_count, *in2_count, *out_count;
int number;
int array[] = {3, 4, 3, 2, 1, 3, 1, 1, 4, 3, 2, 2, 4, 2, 1, 4};
const int size = sizeof array / sizeof *array;
void LOG_sort(int *, int);
int LOG_merge(int *, int, int);
int compress(int *, int, int *, int *);
void LOG_expand(int *);
void sort(int *, int);
int Partition(int *array, int start, int end);
void _QuickSort(int *array, int start, int end) {
if(start < end) {
int pivot = Partition(array, start, end);
_QuickSort(array, start, pivot - 1);
_QuickSort(array, pivot + 1, end);
}
}
int Partition(int *array, int start, int end) {
//别一个不小心写出 int pivot = array[end];
int& pivot = array[end];
int i = start - 1, j = start;
while(j < end) {
if(array[j] < pivot) {
i++;
swap(array[i], array[j]);
}
j++;
}
swap(array[i + 1], pivot);
return i + 1;
}
void LOG_sort(int *array, int n)
{
if (array == NULL || n <= 0)
return;
int log_n = (int)(log((double)n) + 0.5);
int i;
cout << "n = " << n << endl;
cout << "log_n = " << log_n << endl;
cout << "before sort" << endl;
copy(array, array + n, ostream_iterator<int>(cout, " "));
cout << endl;
for (i = 0; i < n; i += log_n)
{
cout << "i = " << i << endl;
cout << "i + min(log_n, n - i) = " << i + min(log_n, n - i) << endl;
_QuickSort(array, i, i + min(log_n, n - i) - 1);
for (int i = 0; i < n; i++)
cout << array[i] << " ";
cout << endl;
}
cout << "after sort" << endl;
for (int i = 0; i < n; i++)
cout << array[i] << " ";
cout << endl;
in1_data = (int *) malloc (sizeof(int) * log_n);
in2_data = (int *) malloc (sizeof(int) * log_n);
out_data = (int *) malloc (sizeof(int) * log_n);
in1_count = (int *) malloc (sizeof(int) * log_n);
in2_count = (int *) malloc (sizeof(int) * log_n);
out_count = (int *) malloc (sizeof(int) * log_n);
number = LOG_merge(array, n, log_n);
LOG_expand(array);
/*
free(in1_data);
free(in2_data);
free(out_data);
free(in1_count);
free(in2_count);
free(out_count);*/
}
int LOG_merge(int *array, int n, int seg_size)
{
if (array == NULL || n <= 0)
return -1;
int no1, no2;
int start, len;
int i, j, k;
no1 = compress(array, seg_size, in1_data, in1_count);
cout << "**************" << endl;
for (int i = 0; i < no1; i++)
{
cout << in1_data[i] << " ";
}
cout << endl;
cout << "**************" << endl;
for (start = seg_size; start < n; start += len)
{
len = MIN(seg_size, n - start);
no2 = compress(array + start, len, in2_data, in2_count);
cout << "**************" << endl;
for (int i = 0; i < no2; i++)
{
cout << in2_data[i] << " ";
}
cout << endl;
cout << "**************" << endl;
for (i = j = k = 0; i < no1 && j < no2;)
{
if (in1_data[i] < in2_data[j])
{
out_data[k] = in1_data[i];
out_count[k] = in1_count[i];
i++, k++;
}
else if (in1_data[i] > in2_data[j])
{
out_data[k] = in2_data[j];
out_count[k] = in2_count[j];
j++, k++;
}
else
{
out_data[k] = in1_data[i];
out_count[k] = in1_count[i] + in2_count[j];
i++, j++, k++;
}
cout << "!!!" << endl;
}
for (; i < no1; i++, k++)
{
out_data[k] = in1_data[i];
out_count[k] = in1_count[i];
}
for (; j < no2; j++, k++)
{
out_data[k] = in2_data[j];
out_count[k] = in2_count[j];
}
no1 = k;
SWAP(in1_data, out_data);
SWAP(in1_count, out_count);
cout << "-------------------" << endl;
cout << "k = " << k << endl;
for (int i = 0; i < k; i++)
{
cout << "in" << in1_data[i] << " ";
cout << "count" << in1_count[i] << " ";
}
cout << endl;
cout << "-------------------" << endl;
}
return no1;
}
int compress(int *array, int n, int *data, int *count)
{
int i;
int no = 0;
if (n == 0)
return 0;
else
{
data[0] = array[0];
count[0] = 1;
for (i = 1; i < n; i++)
{
if (array[i] == array[i - 1])
count[no]++;
else
{
data[++no] = array[i];
count[no] = 1;
}
}
no++;
return no;
}
}
void LOG_expand(int *array)
{
int total;
int i, j;
for (total = i = 0; i < number; i++)
{
for (j = 0; j < in1_count[i]; j++)
{
array[total++] = in1_data[i];
}
}
}
void main()
{
LOG_sort(array, size);
for (int i = 0; i < size; i++)
cout << array[i] << " ";
cout << endl;
}
问题5.13 堆积式合并
已知m个数组,每一个数组都有n个元素,为了方便起见,这m个数组正好是x[][]矩阵中的m列。如果这m列中的元素都各自按照从小到大的顺序排列好了,请写一个程序把这m*n个合并到一个大矩阵去。不过在做这个题目时有进一步的限制,因为内存有限,大概最多只有与m成正比的内存可以使用。请问要如何克服这一层限制,当然程序的速度就会变慢了~
问题5.14 检查数组元素是否相异
已知一个数组,请写一个程序把相同的元素去掉而只留下一个
思路:利用快排先排序,之后就不说了,代码很简单,不写了~
问题5.15 数组中和为零的段落
一直一个整数数组,其中可能有正整数、负整数和0,而且元素也不会重复出现,请写一个程序找出这个数组中是否有一个子数组元素的和为0.所谓的子数组,就是原来数组中连续的若干个元素。例如,如果数组为1,2,3,-5,4,那么2,3,-5这个子数组中元素的和为0
思路:这是一道非常有意思的题,看下面的两个式子:
x1 + x2 = 0
x1 + x2 + x3 + x4 + x5 + x6 = 3
不难得出:x3 + x4 + x5 + x6 = 0
#include <iostream>
#include <algorithm>
#include <iterator>
using namespace std;
#define YES 1
#define NO 0
int zero_sum(int *x, int n)
{
int i;
for (i = 1; i < n; i++)
x[i] += x[i - 1];
sort(x, x + n);
for (i = 1; i < n && x[i] != x[i - 1]; i++)
;
return (i == n) ? NO : YES;
}
void main()
{
int array[] = {4, -1, 2, 1, -2, -1, 5};
const int size = sizeof array / sizeof *array;
int result = zero_sum(array, size);
if (result == YES)
cout << "exist" << endl;
else
cout << "not exist" << endl;
}
如果要求出具体序列,则需要用稳定的排序算法排序,而且用一个结构体保存好下标index~对这个结构体排序
问题5.16 平面上的极大点
在平面上如果有两个点(x, y)与(a, b),说(x, y)支配了(a, b),这就是指x >= a而且y >= b;用图来看就是(a,b)坐落在以(x, y)为右上角的一个无限的区域中。对于平面上的任意一个有限点集合而言,一定存在若干个点,它们不会被集合中任何一个点所支配,这些点就构成一个所谓的极大集合。请写一个程序,读入一个新的集合(以(x,y)的坐标形式),找出这个集合中的极大集合。
问题5.17 宴会中访问数目的极大值
在一个宴会中一共有n位来宾,依照来宾的到达和离去的登记时间,知道第i位来宾在xi时到达,在yi时离开,因此第i位来宾在宴会场中的时间是[xi, yi),亦即xi <= t <= yi中所有可能的t,请写一个程序,读入xi与yi,1<=i<=n,找出同一时刻之内最多会有多少人同时在宴会场中。
思路:按时间排序,如果out和in是同时的,那么out在前,in在后
代码比较简单,原封不动的copy:
#include <iostream>
#include <algorithm>
#include <iterator>
using namespace std;
#define IN 1
#define OUT 0
struct table
{
int clock;
int status;
};
typedef struct table TABLE;
void sort(TABLE *, int);
void qsort(TABLE *, int, int);
void split(TABLE *, int, int, int *);
void swap(TABLE *, TABLE*);
int max_visitors(int *x, int *y, int n)
{
if (x == NULL || y == NULL || n <= 0)
return -1;
TABLE *time_table;
int max_count;
int count;
int i;
time_table = (TABLE *) malloc (sizeof(TABLE) * (2 * n));
for (i = 0; i < n; i++)
{
(time_table + i)->clock = x[i];
(time_table + i)->status = IN;
}
for (i = 0; i < n; i++)
{
(time_table + i + n)->clock = y[i];
(time_table + i + n)->status = OUT;
}
sort(time_table, time_table + 2 * n);
for (max_count = count = i = 0; i < 2 * n; i++)
{
if ((time_table + i)->status == OUT)
count--;
else
{
count++;
max_count = (max_count < count) ? count : max_count;
}
}
free(time_table);
return max_count;
}
void sort(TABLE *x, int n)
{
int first = 0;
int last = n - 1;
qsort(x, first, last);
}
void qsort(TABLE *x, int first, int last)
{
int split_point;
if (first < last)
{
split(x, first, last, &split_point);
if (split_point - first < last - split_point)
{
qsort(x, first, split_point - 1);
qsort(x, split_point + 1, last);
}
else
{
qsort(x, split_point + 1, last);
qsort(x, first, split_point - 1);
}
}
}
void split(TABLE *x, int first, int last, int *split_point)
{
int current_split, next;
int temp;
temp = x[first].clock;
current_split = first;
for (next = first + 1; next <= last; next++)
{
if (x[next].clock < temp || (x[next].clock == temp && x[next].status == OUT))
{
current_split++;
swap(&x[current_split], &x[next]);
}
}
swap(&x[first], &x[current_split]);
*split_point = current_split;
}
void swap(TABLE *p, TABLE *q)
{
int temp;
temp = p->clock;
p->clock = q->clock;
q->clock = temp;
}
问题5.18 包含在其他区间中的区间
已知一组区间[ai, bi],i = 1, 2, ..., n;请写一个程序,找出这些区间中有哪些会包含在其他的区间中~