HOWTO · C++
在 C++ 中对链表进行排序
本编程教程演示了在 C++ 中对链表数据结构的排序操作的实现。
本页内容
要按升序排列单向链表,可以比较相邻节点的值,并交换顺序错误的值。链表指针保持不变,因此此实现排序的是节点中保存的值,而不是重新连接节点。下面的实现使用冒泡排序,并包含完整的驱动程序。
单向链表存储的内容
单向链表由一串节点组成。每个节点存储一个值,以及一个指向下一节点的指针;最后一个节点指向 nullptr。链表的 head 指针指向首节点。与数组不同,链表节点不必存放在连续的内存位置。要访问某个节点,需要从 head 开始,依次沿着 next 指针前进。
这种结构使得在头部插入只需常数时间,但要找到后续位置则必须遍历。因此,排序时需要沿着指针逐个访问节点。代码使用模板,因此可以存储不同类型的值,前提是该类型支持排序所需的比较和交换操作。
在 C++ 中构建一个简单的单向链表
每个节点在初始化时会同时设置其值和指针。链表负责管理自己的节点,并在析构函数中释放它们;同时禁用复制,以免两个链表意外删除同一批节点。insertAtHead 会将一个值添加到当前首节点之前,displayAll 则按遍历顺序打印各个值。
按升序排列链表
冒泡排序会多次遍历相邻节点对。如果当前值大于下一节点的值,std::swap 就会交换这两个值。每次遍历结束时,尚未归位的最大值会移动到右侧;last 标记边界,使下一轮跳过已排好序的后缀。如果某一轮没有发生交换,算法就会停止。空链表会立即返回,只有一个节点的链表则无需比较。
该排序在最坏情况下需要 O(n²) 时间,并使用 O(1) 额外空间。它保留节点之间的链接,但会改变各节点保存的值。T 必须支持 operator> 且可交换;此实现不会通过重新连接节点来排序。
编译并运行示例
将完整程序保存为 sort.cpp。驱动程序依次在头部插入 50、16 和 45,因此排序前遍历时会依次打印 45、16 和 50。随后程序会对链表排序并显示升序结果。
本节中的代码以及显示的标签或输出仍为英文。
#include <iostream>
#include <utility>
template <class T>
struct Node {
T data;
Node<T>* next;
explicit Node(const T& value) : data(value), next(nullptr) {}
};
template <class T>
class SinglyLinkedList {
private:
Node<T>* head;
public:
SinglyLinkedList() : head(nullptr) {}
SinglyLinkedList(const SinglyLinkedList&) = delete;
SinglyLinkedList& operator=(const SinglyLinkedList&) = delete;
~SinglyLinkedList() {
while (head != nullptr) {
Node<T>* next = head->next;
delete head;
head = next;
}
}
void insertAtHead(const T& value) {
Node<T>* node = new Node<T>(value);
node->next = head;
head = node;
}
void displayAll() const {
const Node<T>* current = head;
while (current != nullptr) {
std::cout << current->data << '\n';
current = current->next;
}
}
void sortLinkedList() {
if (head == nullptr) {
return;
}
Node<T>* last = nullptr;
bool swapped;
do {
swapped = false;
Node<T>* current = head;
while (current->next != last) {
if (current->data > current->next->data) {
using std::swap;
swap(current->data, current->next->data);
swapped = true;
}
current = current->next;
}
last = current;
} while (swapped);
}
};
int main() {
SinglyLinkedList<int> list;
list.insertAtHead(50);
list.insertAtHead(16);
list.insertAtHead(45);
std::cout << "Before sorting:\n";
list.displayAll();
list.sortLinkedList();
std::cout << "After sorting:\n";
list.displayAll();
}
已验证的运行结果如下。输出块中的 Before sorting: 和 After sorting: 标签保留英文;其中的数值展示了排序前后的链表内容。
Before sorting:
45
16
50
After sorting:
16
45
50
请在文件所在目录中使用 C++11 或更高版本的编译器进行编译和运行:
g++ -std=c++11 -Wall -Wextra -pedantic sort.cpp -o sort
./sort
显示操作会每行输出一个值,与上方输出一致。