【发布时间】:2021-09-15 22:09:00
【问题描述】:
前言:
我不关心我的特定快速排序算法的可行性。这不是问题所在。我很好奇为什么我已经编写的程序的行为方式如此,而不是为什么我应该使用 STL 或其他什么。这是出于学习目的。 (例如,我知道选择枢轴,因为头部不是最好的情况——但这不是我要问的)
问题:
我有一个由Node*s 组成的链表。我专门为它写了一个快速排序算法。它的工作原理是获取给定的链表,并根据选择的pivot 递归地将其拆分为子列表。枢轴始终是列表的head(前面),而子列表left 和right 是分别包含小于和大于/等于枢轴的值的列表。我几乎把它弄下来了,但我不知道如何正确地 add_link 枢轴。在以前的迭代中,我总是将枢轴添加_链接到正确的子列表,但这会导致无限循环,因此在比较值时我最终完全跳过了枢轴。结果列表已排序,但缺少用于枢轴的每个值。我应该如何解决这个问题?
这是一个最小的可重现示例:
#include <iostream>
struct Link {
Link(int d=int(), Link* n=nullptr) {
data = d;
next = n;
}
int data;
Link* next;
};
struct LinkList {
Link* sentinel;
size_t size;
LinkList(Link* h=new Link, size_t s=0) {
size = s;
sentinel = h;
}
~LinkList() {
Link* current = sentinel;
while (current != 0) {
Link* next = current->next;
delete current;
current = next;
}
sentinel = 0;
}
};
// Prototypes
Link* sort_q(Link* sentinel);
void divide(Link* sentinel, Link* cmp, Link*& lower, Link*& greater);
Link* concatenate(Link* lower, Link* greater);
// helpful function calls quick sort
void sort(LinkList& l) {
l.sentinel = sort_q(l.sentinel);
}
// returns false if there sentinel = null; true if add_link was successful
bool add_link(Link* sentinel, Link* add_link, Link*& end) {
if (sentinel == nullptr) {
return false;
}
Link* curr = sentinel;
for (; curr->next; curr = curr->next) {}
curr->next = add_link;
end = curr->next;
return true;
}
Link* sort_q(Link* sentinel) {
Link* lower = nullptr;
Link* greater = nullptr;
Link* cmp = sentinel;
// base case LinkList = null or sentinel->null
if (sentinel == nullptr || sentinel->next == nullptr) {
return sentinel;
}
divide(sentinel, cmp, lower, greater);
lower = sort_q(lower);
greater = sort_q(greater);
return concatenate(lower, greater);
}
void divide(Link* sentinel, Link* cmp, Link*& lower, Link*& greater) {
lower = new Link, greater = new Link;
// lend is pointer to end of lower subLinkList
// rend is pointer to end of greater subLinkList
Link* lend = nullptr, * rend = nullptr;
// loop through LinkList until end
while (sentinel != nullptr) {
if (sentinel == cmp) {
sentinel = sentinel->next; continue;
}
if (sentinel->data < cmp->data) {
// break current link
Link* tmp = sentinel;
sentinel = sentinel->next;
tmp->next = nullptr;
// if subLinkList is not empty, add_link current Link to subLinkList and update end pointer
if (add_link(lend, tmp, lend))
continue;
// otherwise, "add_link" current Link to empty subLinkList and update end pointer manually
lower->next = tmp;
lend = lower->next;
}
else {
// break current link
Link* tmp = sentinel;
sentinel = sentinel->next;
tmp->next = nullptr;
// if subLinkList is not empty, add_link current Link to subLinkList and update end pointer
if (add_link(rend, tmp, rend))
continue;
// otherwise, "add_link" current Link to empty subLinkList and update end pointer manually
greater->next = tmp;
rend = greater->next;
}
}
// remove dummy Link(s)
if (lower->next)
lower = lower->next;
else
lower = cmp;
if (greater->next)
greater = greater->next;
else
greater = cmp;
// unlink cmp
cmp->next = nullptr;
}
// connected subLinkLists
Link* concatenate(Link* lower, Link* greater) {
Link* sentinel;
sentinel = lower;
while (lower->next != nullptr) {
lower = lower->next;
}
lower->next = greater;
return sentinel;
}
void print(LinkList &l) {
for (Link* n = l.sentinel; n != NULL; n = n->next) {
std::cout << n->data << '\n';
}
}
int main() {
// set up linked LinkList 8->4->5->11->7->5->3->9->null
Link* sentinel = new Link(8 , new Link(4, new Link(5, new Link(11, new Link(7, new Link(5, new Link(3, new Link(9))))))));
LinkList l(sentinel,5);
sort(l);
print(l);
return 0;
}
我想要的输出是
3
4 // pivot missing from output
5
5
7
8 // pivot missing from output
9
11
但它输出
3
5
5
7
9
11
编辑#1:
我尝试在连接之间添加枢轴,但这也不起作用。它产生不同的结果,但并不完全相同。像这样修改sort_q() 和partition():
Link* qsort(Link* sentinel) {
// ... other stuff before modification
return concatenate(lower, cmp); // changed greater argument to pass cmp which is now cmp->next = greater once finishing partition()
}
void partition(Link* sentinel, Link*& cmp, Link*& lower, Link*& greater) {
// .. other stuff before modifications
// remove dummy Link
if (lower->next)
lower = lower->next;
if (greater->next)
greater = greater->next;
cmp->next = greater; // cmp points to greater (greater) sublist
}
输出变成
3
4
5
7
0
8
11
0
【问题讨论】:
-
我们是否可以假设您已经使用调试器逐行浏览了代码,并观察了发生的情况?
-
您的枢轴节点需要位于中间。当您调用
concatenate时,您会跳过枢轴节点 -
有多种解决方法。我想你可以将
pivot->next设置为right然后concatenate(left, pivot)。 -
你不想读这个,但我还是放弃了它:我看不出连接阶段的意义。你不应该需要它。
-
顺便说一句,问题的布局很好。回答它所需的一切都在这里,除了提供编译器之外,我们几乎不需要干预来运行它并观看乐趣。
标签: c++ linked-list quicksort