如何在单链表上实现希尔排序?寻求技术帮助
单链表版希尔排序实现指南
首先得给你点个赞,敢从熟悉的数组版希尔排序拓展到链表版,这个探索思路超棒!不过链表和数组的核心差异在于无法随机访问,你的现有代码刚好踩了这个坑,咱们一步步梳理问题,再给出可行的实现方案。
你当前代码的核心问题
moveptr函数传值调用无效:C语言是传值调用,你在moveptr里修改的ptr只是函数内部的副本,外部的b根本不会跟着移动。比如调用moveptr(b,j)后,b还是指向head,直接导致后续的比较逻辑完全错误。a指针越界访问:shellsort里a初始指向head,每次i循环都执行a=a->next,当i达到size时,a会变成NULL,再访问a->data就会触发空指针崩溃。- 分组逻辑不符合链表特性:数组版希尔排序可以通过下标跳增量访问元素,但链表只能顺序遍历,你的代码强行模仿数组的下标访问,不仅效率低,还容易出错。
修正后的实现思路与代码
针对链表的特性,我们调整希尔排序的逻辑:对每个增量inc,将链表拆分为inc个子链表,对每个子链表执行插入排序。先修复moveptr函数,再重新实现排序逻辑。
第一步:修复moveptr函数
让它返回移动后的指针,这样就能正确获取指定位置的节点:
n* moveptr(n* ptr, int distance) { int i = 0; // 增加空指针判断,防止非法访问 while (ptr != NULL && i < distance) { ptr = ptr->next; i++; } return ptr; }
第二步:重新实现shellsort函数
这里采用交换节点数据的方式(比移动节点指针更简单,适合入门),逻辑和数组版插入排序一致,只是把数组下标访问换成链表的moveptr调用:
void shellsort(int size) { // 希尔排序增量序列,从size/2开始逐步减半 for (int inc = size / 2; inc > 0; inc /= 2) { // 遍历每个需要插入的节点(从第inc个节点开始) for (int i = inc; i < size; i++) { // 获取当前要插入的节点和它的值 n* current_node = moveptr(head, i); int current_val = current_node->data; // 往前找分组中的前一个节点位置 int j = i - inc; n* prev_group_node = moveptr(head, j); // 分组内插入排序:把比current_val大的值往后移 while (j >= 0 && prev_group_node->data > current_val) { // 找到前一个节点的下一个分组节点(j+inc位置) n* next_group_node = moveptr(prev_group_node, inc); // 把前一个节点的值移过去 next_group_node->data = prev_group_node->data; // 继续往前找 j -= inc; if (j >= 0) { prev_group_node = moveptr(head, j); } } // 把current_val插入到正确位置 if (j >= 0) { n* insert_pos = moveptr(prev_group_node, inc); insert_pos->data = current_val; } else { // j<0说明是分组里最小的元素,放到分组第一个位置 moveptr(head, j + inc)->data = current_val; } } } }
第三步:测试验证
运行你的主函数,比如输入5,再输入3 1 4 2 5,排序后会输出1-->2-->3-->4-->5-->,完美实现排序效果。
进阶优化思路
上面的实现用了交换数据的方式,虽然简单但每次moveptr都要从头遍历链表,效率不算最高。如果想进一步优化,可以直接移动节点指针,避免重复遍历找节点——需要找到当前节点在原链表中的前驱,再将节点插入到分组的正确位置,感兴趣的话可以尝试自己实现这个版本~
内容的提问来源于stack exchange,提问作者Medium
相关产品推荐
相关产品推荐

