我遇到了使用链接列表进行冒泡排序的问题。我几个小时没有在我的简单代码中找到错误。你能看一下吗?
int listSort(node_t ** _list)
{
int size = listSize(_list);
for(int i = 0; i < size; i++)
{
node_t *pointer = *_list;
for(int j = 0 ; j < size - 1; j++)
{
if(strcmp(pointer->title, pointer->next->title) > 0)
listSwapWithNext(_list, j);
pointer = pointer->next;
}
}
return 0;
}
和这里的交换功能(但似乎工作正常,因为我手动测试了所有边界情况):
int listSwapWithNext(node_t **_list, size_t first)
{
node_t *pointer = *_list;
node_t *ptr_b;
node_t *ptr_c;
node_t *ptr_d;
if(!first)
ptr_b = pointer;
for(size_t i = 0; pointer->next->next; i++, pointer = pointer->next)
{
if(i == first - 1 || first == 0)
{
if(first)
ptr_b = pointer->next;
ptr_c = ptr_b->next;
ptr_d = ptr_c->next;
if(first)
pointer->next = ptr_c;
else
*_list = ptr_c;
ptr_c->next = ptr_b;
ptr_b->next = ptr_d;
break;
}
}
return 0;
}
导致此崩溃(在第229行):
当我将sort函数中内部循环的condidition修改为:
for(int j = 0; j < size - 1 && pointer->next; j++)
为了防止从坏地址读取,我注意到了奇怪的事情。内循环somtimes运行时间比它应该少一次。
谢谢!
修改 这里有一个修改版的sort函数,在内部循环和我的调试指示器(用printf())中有防止条件:
int listSort(node_t ** _list)
{
int size = listSize(_list);
int i;
for(i = 0; i < size; i++)
{
node_t *pointer = *_list;
int j;
for(j = 0 ; j < size - 1 && pointer->next; j++)
{
if(strcmp(pointer->title, pointer->next->title) > 0)
listSwapWithNext(_list, j);
printf("# %d %d\n", i, j);
pointer = pointer->next;
}
printf("\n");
}
return 0;
}
这是控制台中的结果。看,它并不是每个周期都做,所以它会给糟糕的排序带来好处。
EDIT2: 这是问题的根本所在。 http://pastebin.com/e5K6C1A2
仍然无法解决此问题。
答案 0 :(得分:2)
pointer = pointer->next
不正确。如果您进行了互换,pointer
应该保持pointer
,因为它已经向前移动了一个元素。
编辑:我的意思是这样做:
if(strcmp(pointer->title, pointer->next->title) > 0)
listSwapWithNext(_list, j);
else
pointer = pointer->next;
答案 1 :(得分:0)
根据您的说法,我相信我可以帮助您识别代码中的一些问题。我相信您所描述的问题是由您发布的第一个代码块引起的,特别是此部分。
for(int i = 0; i < size; i++)
{
node_t *pointer = *_list;
for(int j = 0 ; j < size - 1; j++)
{
if(strcmp(pointer->title, pointer->next->title) > 0)
listSwapWithNext(_list, j);
pointer = pointer->next;
}
这条线引发了这个问题:
if(strcmp(pointer->title, pointer->next->title) > 0)
因为这里构造了内循环:
for(int j = 0 ; j < size - 1; j++)
所以你要考虑的事实是,因为我们交换,我们需要比列表的总大小少1。但问题是,这导致您尝试在最后一次运行时取消引用空指针。链表中的节点包含指向某些数据的指针和指向下一个对象的指针。所以让我们想象一下这个for循环执行的倒数第二次会发生什么。如果strcmp
为真,它会进行交换,然后它会使指针前进以引用链接列表中的下一个元素。这是你出错的地方。因为你在我们再次执行循环时已经这样做了,所以这一行:
if(strcmp(pointer->title, pointer->next->title) > 0)
具体到这一部分:
pointer->next->title
将失败,因为当前对象是链接列表中的最后一个元素。因此,它的下一个指向NULL,并且您正在尝试获取NULL元素的标题对象,该元素不存在。
现在让我们看看你修改过的代码。
int listSort(node_t ** _list)
{
int size = listSize(_list);
int i;
for(i = 0; i < size; i++)
{
node_t *pointer = *_list;
int j;
for(j = 0 ; j < size - 1 && pointer->next; j++)
{
if(strcmp(pointer->title, pointer->next->title) > 0)
listSwapWithNext(_list, j);
printf("# %d %d\n", i, j);
pointer = pointer->next;
}
printf("\n");
}
return 0;
}
您不会遭受同样的错误,但结果不正确。这不是由排序函数引起的,这也是由于for循环的构造引起的:
for(j = 0 ; j < size - 1 && pointer->next; j++)
只要指定的条件为真,就会执行for循环。再次让我们想象倒数第二次执行。我们在我们的大小范围内,所以第一个条件为真,并且在此之后存在另一个元素,因此第二个条件也是如此。循环运行并将指针前进到下一个元素。现在我们仍然在我们的大小限制范围内,所以这是真的,但在此之后列表中没有更多元素,因此pointer->next
是NULL
。这意味着循环不会运行,因此不会对所有数据进行排序,从而导致结果不佳。