读取数据
您的数据文件的结构似乎是:
T
N1 V11 V12 V13 ... V1N S1
N2 V21 V22 V23 ... V2N S2
...
你有一个测试用例的总数,T。然后对于每个测试用例,你在数组中有许多条目(N1、N2 等),每次后面跟着适当数量的值,V 1 1 .. V 1 N,以及您应该搜索的备用值(所有描述都使用基于 1 的数组表示法;在 C 中,您将使用基于 0 的数组)。虽然我已经在一行中显示了每个测试集的数据,但实际的数据文件可以以任何顺序排列数字——所有内容都可以在一行上,或者每个数字在自己的一行上,可能带有空白分隔它们的行,或这些格式的任何混合。
您显示的main()
程序在读取该数据方面做得不是特别好。它缺乏所有的错误检查。它使用(合法但)奇怪的*(array + i)
符号而不是更容易理解的array[i]
符号,显然是相信它会更有效。当您使用指针来提高效率时,您不会i
在取消引用指针之前继续添加值。您的代码动态分配内存,但从不释放它,严重泄漏。
使用下标符号读取数据
在这个修改后的代码中,我return 1;
用来退出程序。它也应该打印一条错误消息,但我有点懒惰。
#include <stdio.h>
#include <stdlib.h>
int main(void)
{
int testcase, num, search;
int i, j, *array;
if (scanf("%d", &testcase) != 1)
return 1;
for (i = 0; i < testcase; i++)
{
if (scanf("%d", &num) != 1)
return 1;
array = malloc(sizeof(int)*num);
if (array == 0)
return 1;
for (j = 0; j < num; j++)
{
if (scanf("%d", &array[j]) != 1)
return 1;
}
//Quick_Sort(array, 0, num-1);
if (scanf("%d", &search) != 1)
return 1;
for (j = 0; j < num; j++)
{
if (search == array[j])
{
printf("%d\n", j+1);
break;
}
}
// Print the array - best encapsulated in a small function
for (j = 0; j < num; j++)
{
printf(" %d", array[j]);
if (j % 10 == 9)
putchar('\n');
}
if (j % 10 != 0)
putchar('\n');
// Prevent memory leaks
free(array);
}
return 0;
}
我顺便指出,无论快速排序是否对数据进行排序,搜索循环都将起作用。它没有利用数组已排序的事实。您可以在排序之前和之后打印数据。您应该标记输出以识别您正在打印的内容。例如,搜索代码可能会这样写:
printf("Found %d at %d\n", search, j);
您也无法确定何时未找到正在搜索的值。
在读取数据之后和处理数据之前打印数据通常也是一个好主意,以确保您的程序正在获取您期望的数据。如果程序未处理您认为正在处理的数据,则可能会导致混乱。
请注意,除了“它们是有效整数”之外,此代码不会对数组中的值做出任何假设。它确实检查每个输入操作。虽然看起来很乏味,但有必要避免麻烦。
使用指针表示法读取数据
以下是或多或少惯用指针的代码:
#include <stdio.h>
#include <stdlib.h>
int main(void)
{
int testcase;
if (scanf("%d", &testcase) != 1)
return 1;
for (int i = 0; i < testcase; i++)
{
int num;
if (scanf("%d", &num) != 1)
return 1;
int *array = malloc(sizeof(int)*num);
if (array == 0)
return 1;
int *end = array + num;
int *ptr;
for (ptr = array; ptr < end; ptr++)
{
if (scanf("%d", ptr) != 1)
return 1;
}
//Quick_Sort(array, 0, num-1);
int search;
if (scanf("%d", &search) != 1)
return 1;
for (ptr = array; ptr < end; ptr++)
{
if (search == *ptr)
{
break;
}
}
if (ptr < end)
printf("Found %d at %td\n", search, ptr - array + 1);
else
printf("Missing %d\n", search);
// Print the array - best encapsulated in a small function
printf("Array (%d):", num);
int j;
for (j = 0; j < num; j++)
{
printf(" %d", array[j]);
if (j % 10 == 9)
putchar('\n');
}
if (j % 10 != 0)
putchar('\n');
// Prevent memory leaks
free(array);
}
return 0;
}
如果使用索引编写打印循环是最简单的,所以我没有将其更改为使用指针。可以这样做,但是使用(ptr - array)
而不是j
在“是时候打印换行符”代码中使用它会变得不那么值得。该代码使用 C99 功能,例如在需要时声明变量以及值的t
限定符。它也可以写成使用 VLA 而不是.%td
ptrdiff_t
malloc()
样本输入数据
3
2 1 2 1
3 3 2 0 1
4 5 4 3 2 4
样本输出
Found 1 at 1
Array (2): 1 2
Missing 1
Array (3): 3 2 0
Found 4 at 2
Array (4): 5 4 3 2
工作快速排序代码
您的分区算法有缺陷。它是固定的,并标记了关键更改。我在整理问题时使用的调试脚手架留在原地,许多指导我的打印操作被注释掉了。阅读Jon Bentley 的Programming Pearls,尤其是“第 11 列:排序”(第 11 章,但这些章节最初是 ACM 通讯中的列,因此称为第 11 列)。这是解决问题时的宝贵指南。
#include <assert.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
void Quick_Sort(int *a, int p, int r);
static int partition(int *a, int p, int r);
static void dump_partition(char const *tag, int const *data, int lo, int hi);
/* Debugging functions */
static void check_sorted(int const *data, int lo, int hi);
static int *copy_partition(int const *data, int lo, int hi);
static void check_consistency(int const *a1, int const *a2, int lo, int hi);
int main(void)
{
int testcase, num, search;
int i, j, *array;
if (scanf("%d", &testcase) != 1)
return 1;
for (i = 0; i < testcase; i++)
{
if (scanf("%d", &num) != 1)
return 1;
array = malloc(sizeof(int)*num);
if (array == 0)
return 1;
for (j = 0; j < num; j++)
{
if (scanf("%d", &array[j]) != 1)
return 1;
}
printf("\nData set %d:\n", i);
int *copy = copy_partition(array, 0, num-1);
dump_partition("Before:", array, 0, num-1);
//dump_partition("Copy", copy, 0, num-1);
Quick_Sort(array, 0, num-1);
dump_partition("After: ", array, 0, num-1);
check_sorted(array, 0, num-1);
check_consistency(array, copy, 0, num-1);
free(copy);
if (scanf("%d", &search) != 1)
return 1;
for (j = 0; j < num; j++)
{
if (search == array[j])
break;
}
if (j < num && search == array[j])
printf("Found %d at %d\n", search, j+1);
else
printf("Missing %d\n", search);
// Prevent memory leaks
free(array);
}
return 0;
}
/* Although we're interested in data[lo]..data[hi], the copy must have data[0]..data[lo-1] too */
static int *copy_partition(int const *data, int lo, int hi)
{
assert(lo <= hi);
size_t nbytes = (hi + 1) * sizeof(int);
int *space = (int *)malloc(nbytes);
if (space == 0)
{
fputs("Out of memory\n", stderr);
exit(1);
}
memmove(space, data, nbytes);
return(space);
}
/* Check that the two arrays contain the same sets of data */
/* Each value in a1 must be present in a2 and vice versa */
static void check_consistency(int const *a1, int const *a2, int lo, int hi)
{
int *a3 = copy_partition(a1, lo, hi);
int a3_lo = lo;
int a3_hi = hi;
//printf("-->> check_consistency:\n");
//dump_partition("a1", a1, lo, hi);
//dump_partition("a2", a2, lo, hi);
//dump_partition("a3", a3, lo, hi);
for (int i = lo; i <= hi; i++)
{
int found = 0;
for (int j = a3_lo; j <= a3_hi; j++)
{
if (a2[i] == a3[j])
{
/* Found a match for a2[i] at a3[j] */
/* Move a3[j] to end of array and ignore it from here on */
//printf("Found a2[%d] = %d at a3[%d] = %d\n", i, a2[i], j, a3[j]);
int t = a3[a3_hi];
a3[a3_hi] = a3[j];
a3[j] = t;
a3_hi--;
//dump_partition("a3-free", a3, a3_lo, a3_hi);
//dump_partition("a3-used", a3, a3_hi+1, hi);
found = 1;
break;
}
}
if (!found)
{
printf("No value %d for a2[%d] in a1\n", a2[i], i);
dump_partition("a2", a2, lo, hi);
dump_partition("a1-free", a3, a3_lo, a3_hi);
dump_partition("a1-used", a3, a3_hi+1, hi);
}
}
free(a3);
//printf("<<-- check_consistency\n");
}
static void dump_partition(char const *tag, int const *data, int lo, int hi)
{
printf("%s [%d..%d]%s", tag, lo, hi, (hi - lo) > 10 ? "\n" : "");
int i;
for (i = lo; i <= hi; i++)
{
printf(" %2d", data[i]);
if ((i - lo) % 10 == 9)
putchar('\n');
}
if ((i - lo) % 10 != 0 || i == lo)
putchar('\n');
}
static void check_sorted(int const *data, int lo, int hi)
{
//printf("-->> check_sorted:\n");
for (int i = lo; i < hi; i++)
{
if (data[i] > data[i+1])
printf("Out of order: a[%d] = %d bigger than a[%d] = %d\n", i, data[i], i+1, data[i+1]);
}
//printf("<<-- check_sorted\n");
}
void Quick_Sort(int *a, int p, int r)
{
if (p < r)
{
int q = partition(a, p, r);
//dump_partition("Lo Range", a, p, q-1);
//printf("Pivot: a[%d] = %d\n", q, a[q]);
//dump_partition("Hi Range", a, q+1, r);
Quick_Sort(a, p, q-1); // JL: Optimization
Quick_Sort(a, q+1, r);
}
}
static int partition(int *a, int p, int r)
{
assert(p <= r);
if (p == r) // JL: Key change
return p; // JL: Key change
int i = p; // JL: Key change
int j = r + 1;
int x = a[p];
//printf("-->> partition: lo = %d, hi = %d, pivot = %d\n", p, r, x);
while (1)
{
do
{
j--;
//printf("---- partition 1: a[%d] = %d\n", j, a[j]);
} while (x < a[j]); // JL: Key change
do
{
i++;
//printf("---- partition 2: a[%d] = %d\n", i, a[i]);
} while (i <= r && x > a[i]); // JL: Key change
if (i <= j) // JL: Key change
{
//printf("---- partition: swap a[%d] = %d with a[%d] = %d\n", i, a[i], j, a[j]);
int temp = a[i];
a[i] = a[j];
a[j] = temp;
}
else
{
// This swap step is crucial.
int temp = a[p]; // JL: Key change
a[p] = a[j]; // JL: Key change
a[j] = temp; // JL: Key change
//dump_partition("a-lo", a, p, j-1);
//printf("a-pivot[%d] = %d\n", j, a[j]);
//dump_partition("a-hi", a, j+1, r);
//printf("<<-- partition: return j = %d; a[%d] = %d; (i = %d; a[%d] = %d)\n", j, j, a[j], i, i, a[i]);
return j;
}
}
}
扩展样本输入
10
2 1 2 1
3 3 2 0 1
4 5 4 3 2 4
4 3 1 9 3 8
5 3 4 1 9 3 8
9 3 6 4 9 5 1 9 3 3 8
10 3 6 4 9 6 5 1 9 3 3 8
16
3 6 4 9 6 5 1 9 3 3
8 2 1 7 3 5
3
26
3 6 4 9 6 5 1 9 3 3
2 7 8 2 0 8 4 4 7 5
8 2 1 7 3 5
7
96
3 6 4 9 6 5 1 9 3 3
4 0 5 0 7 5 6 3 6 0
1 2 0 7 3 1 7 6 2 3
0 4 6 6 9 8 9 5 3 4
1 9 2 9 2 7 5 9 8 9
4 7 5 8 7 8 5 8 2 7
5 8 2 9 8 3 7 6 5 3
9 1 2 0 3 4 6 5 1 0
2 7 8 2 0 8 4 4 7 5
8 2 1 7 3 5
6
扩展样本输出
Data set 0:
Before: [0..1] 1 2
After: [0..1] 1 2
Found 1 at 1
Data set 1:
Before: [0..2] 3 2 0
After: [0..2] 0 2 3
Missing 1
Data set 2:
Before: [0..3] 5 4 3 2
After: [0..3] 2 3 4 5
Found 4 at 3
Data set 3:
Before: [0..3] 3 1 9 3
After: [0..3] 1 3 3 9
Missing 8
Data set 4:
Before: [0..4] 3 4 1 9 3
After: [0..4] 1 3 3 4 9
Missing 8
Data set 5:
Before: [0..8] 3 6 4 9 5 1 9 3 3
After: [0..8] 1 3 3 3 4 5 6 9 9
Missing 8
Data set 6:
Before: [0..9] 3 6 4 9 6 5 1 9 3 3
After: [0..9] 1 3 3 3 4 5 6 6 9 9
Missing 8
Data set 7:
Before: [0..15]
3 6 4 9 6 5 1 9 3 3
8 2 1 7 3 5
After: [0..15]
1 1 2 3 3 3 3 4 5 5
6 6 7 8 9 9
Found 3 at 4
Data set 8:
Before: [0..25]
3 6 4 9 6 5 1 9 3 3
2 7 8 2 0 8 4 4 7 5
8 2 1 7 3 5
After: [0..25]
0 1 1 2 2 2 3 3 3 3
4 4 4 5 5 5 6 6 7 7
7 8 8 8 9 9
Found 7 at 19
Data set 9:
Before: [0..95]
3 6 4 9 6 5 1 9 3 3
4 0 5 0 7 5 6 3 6 0
1 2 0 7 3 1 7 6 2 3
0 4 6 6 9 8 9 5 3 4
1 9 2 9 2 7 5 9 8 9
4 7 5 8 7 8 5 8 2 7
5 8 2 9 8 3 7 6 5 3
9 1 2 0 3 4 6 5 1 0
2 7 8 2 0 8 4 4 7 5
8 2 1 7 3 5
After: [0..95]
0 0 0 0 0 0 0 0 1 1
1 1 1 1 1 2 2 2 2 2
2 2 2 2 2 3 3 3 3 3
3 3 3 3 3 3 4 4 4 4
4 4 4 4 5 5 5 5 5 5
5 5 5 5 5 5 6 6 6 6
6 6 6 6 6 7 7 7 7 7
7 7 7 7 7 7 8 8 8 8
8 8 8 8 8 8 9 9 9 9
9 9 9 9 9 9
Found 6 at 57
该代码还在一些更大的数据集(2097 个随机条目等)上进行了测试。当数据很大时,自动检查功能至关重要——因此check_sorted()
和check_consistency()
. 他们检查数据是否按排序顺序输出,以及守恒属性,即输入中的所有值是否出现在输出中(与它们出现在输入中的频率一样)。排序不应该添加新数据或删除预先存在的数据。