我已经编写了用于对整数数组进行快速排序的代码,但是在打印时,它在终端上显示了某种内存地址。我一直想弄清楚很长时间,但没有成功。
我试图在在线gdb编译器上编译它,但出现错误:Segmentation Fault(核心已转储),否则,我试图将代码修改数千次
#include<stdio.h>
void swap(int* a, int* b){
int t = *a;
*a = *b;
*b = t;
}
int part(int arr[], int l, int r){ //for partioning the array
int i=l, j=0;
while(j<r){
if(arr[j]<arr[r]){
swap(&arr[j], &arr[i]);
i++;
}
j++;
}
swap(&arr[i], &arr[r]);
return i;
}
void qsort(int arr[], int l, int r){ //recursive function for quicksort
if(l<r){
int p = part(arr, l, r);
qsort(arr, l, p-1);
qsort(arr, p+1, r);
}
}
int main(void){
int arr[] = {4, 10, 17, 5, 5, 6, 1 , 3, 0};
qsort(arr, 0, 8); //function called
for(int i=0; i<9; i++){
printf("%d ", arr[i]); //for printing the array
}
return 0;
}
实际结果:
11016472 1974520423 11016472 6356864 6 6356800 4214784 11015616 1974521024
我期望对数组进行排序。
答案 0 :(得分:1)
部分地,当l
大于0时,j
中的part
从0开始在处理中包含l
的“左”数组的一部分,并且可能会导致i
(已初始化为l
)增加了太多次,超过了r
。
将int i=l, j=0;
更改为int i=l, j=l;
会导致程序为问题案例打印所需的输出。
通过修改qsort
和part
以在每次调用它们时打印其参数,并在它们返回时进行打印或以当前递归深度缩进先前的打印,可以发现此问题。 (可以使用静态计数器对其进行跟踪(出于调试目的))。这会立即显示在数组之外将qsort
设置为9的对r
的调用,然后导致检查这种情况。
答案 1 :(得分:1)
在输入j
时,您需要将l
设置为0
,而不是part
:
...
int part(int arr[], int l, int r){ //for partioning the array
int i=l, j=l;
while(j<r){ // line 10
...
如果不这样做,那么当r
大于8
时,您将超出数组的边界(可能会写)。
答案 2 :(得分:1)
您的分区已损坏。它似乎是基于两种算法的,它们都没有正确实现。
int part(int arr[], int l, int r){ //for partitioning the array
这告诉我们我们有一些数组,我们想在l
和r
之间划分范围。尚不清楚该目标是否是这些索引中的 ,但这并不重要,因为无论如何后续代码都会被破坏。
int i=l, j=0; // HERE. Why is j 0 ??
此后,您多次访问arr[j]
,并且都进行了读取和写入。如果您要分区的范围在[l..r]中,则不能保证j
(从零开始)在该范围内。从j
开始l
。
但是有更好的方法。
首先,这是C。在part
中,标识arr
不是数组;这是一个指针。让我们假设我们有一个序列,其中l
总是从零开始,而r
只是长度。在这种情况下,l
参数将毫无意义,而我们的part
函数可以像这样开始:
int part(int arr[], int len)
足够公平。现在,我们可以实现一个简单的分区,该分区选择一个枢轴,并基于该枢轴进行分区,并返回枢轴结束位置的索引。我不会涉及选择“良好”枢轴的相关主题。有几种方法,我鼓励您进行调查。为了简单起见,在此示例中,我们将使用最右边的元素:
int part(int arr[], int len)
{
if (len <= 1)
return 0;
int pvt = 0;
for (int i=0; i<len; ++i)
{
if (arr[i] < arr[len-1])
swap(arr+i, arr + pvt++);
}
swap(arr+pvt, arr+len-1);
return pvt;
}
足够简单。现在,这如何帮助实际的快速排序?在对序列进行分区时,通过使用偏移地址作为前导参数可以提供帮助。这也有帮助,因为现在我们可以像执行q_sort
一样进行part
的操作,即摆脱参数之一,而只使用基地址和长度:
void q_sort(int arr[], int len)
{
if (len <= 1)
return;
int p = part(arr, len);
q_sort(arr, p);
q_sort(arr+p+1, len-p-1);
}
就是这样。请注意,第二个递归调用使用指针算法将目标序列的基地址定位在枢轴位置的前一位置。长度会相应调整。
完整的程序出现在下面,包括一个测试工具,该工具生成30个元素的序列,然后对结果进行排序并显示:
代码
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
void swap(int* a, int* b)
{
int t = *a;
*a = *b;
*b = t;
}
int part(int arr[], int len)
{
if (len <= 1)
return 0;
int pvt = 0;
for (int i=0; i<len; ++i)
{
if (arr[i] < arr[len-1])
swap(arr+i, arr + pvt++);
}
swap(arr+pvt, arr+len-1);
return pvt;
}
void q_sort(int arr[], int len)
{
if (len <= 1)
return;
int p = part(arr, len);
q_sort(arr, p);
q_sort(arr+p+1, len-p-1);
}
#define N_ELEMS 30
int main(void)
{
int arr[N_ELEMS];
srand((unsigned int)time(NULL));
for (int i=0; i<N_ELEMS; ++i)
{
arr[i] = 1 + (rand() % 99);
printf("%d ", arr[i]);
}
fputc('\n', stdout);
q_sort(arr, N_ELEMS);
for (int i=0; i<N_ELEMS; ++i)
{
printf("%d ", arr[i]);
}
fputc('\n', stdout);
return 0;
}
输出(很明显是可变的)
21 95 18 10 57 60 60 6 37 72 44 99 91 25 14 5 50 58 16 18 81 5 52 42 15 40 58 16 76 61
5 5 6 10 14 15 16 16 18 18 21 25 37 40 42 44 50 52 57 58 58 60 60 61 72 76 81 91 95 99
这里的好处有两个。首先,不要求初始调用者提供序列的两端。其次,这些功能实际上更简单。
希望有帮助。
答案 3 :(得分:-3)
因为没有值小于arr [8],所以第一次调用part()'i'将永远不会递增。该函数将返回“ i”,它为0。然后,您调用qsort(arr,l,p-1(-1))。此时,您将与arr [-1]交换。由于不正确的索引,大多数arr数组的值将与堆栈中的随机数据交换。这就是为什么您得到奇怪的值。
int part(int arr[], int l, int r){ //for partioning the array
int i=l, j=0;
while(j<r){
if(arr[j]<arr[r]){
swap(&arr[j], &arr[i]);
i++;
}
j++;
}
swap(&arr[i], &arr[r]);
return i;
}