这里写目录标题
一、箱子排序
考虑用一个链表保存一个班级学生的清单。我们简化节点的数据为:学生姓名和学生成绩。考虑到成绩范围是有限值,如果我们采用冒泡排序等方式,其时间复杂度都为O( n 2 n^{2} n2) 。这里我们使用桶排序可以得到对大量数据的更快速排序。桶排序的基本思想是选举与数据范围一致的个数的桶,然后将数据分别放在桶中,最后从桶中将数据重新放回容器中。
使用链表实现的桶排序,其过程如下:
1、逐步删除输入链表的节点,把删除的节点分配到相应的箱子里。
2、把每一个箱子中的链表收集并链接起来。
1、学生记录结构
struct StudentRecord
{
int m_iScore;
std::string *m_pName;
bool operator!=(const StudentRecord& other)
{
return (*m_pName != *other.m_pName);
}
};
ostream& operator<<(ostream& os, const StudentRecord& record)
{
os << *record.m_pName << " " << record.m_iScore << endl;
return os;
}
2、实现
void binSort(chain<StudentRecord>& records, int range)
{
vector<chain<StudentRecord>> bins(range + 1);
while (!records.empty())
{
auto record = records.get(0);
records.erase(0);
bins[record.m_iScore].insert(0, record);
}
for (auto iter = bins.rbegin(); iter != bins.rend(); iter++)
{
auto bin = *iter;
while (!bin.empty())
{
auto record = bin.get(0);
bin.erase(0);
records.insert(0, record);
}
}
}
在第一个while循环中,我们每次都从原链表获取并删除头结点,在桶中头结点前插入,因此时间复杂度为O(n);在第二个for循环中,操作相似,但是循环执行的次数需要包含桶的个数,因此时间复杂度为O(n+range)。因此上述排序方式总的时间复杂度为O(n+range)。
3、作为链表模板类方法实现
仔细观察上述代码,不难发现,我们其实并不需要真正的将链表节点删除。它们实际上是从原始链表中移动到桶中,再回到原链表中。因此,如果我们将桶排序作为模板方法,可以直接把各桶收尾节点相连而不需移动节点,也不必再new和delete节点,那么其效率将会有很大提升。其思路如图:
template<class T>
inline void chain<T>::binSort(int range)
{
chainNode<T>** bottom, ** top;
bottom = new chainNode<T>*[range + 1];
top = new chainNode<T>*[range + 1];
for (int i = 0; i < range + 1; ++i)
{
bottom[i] = nullptr;
}
for (; first != nullptr; first = first->next)
{
int binIndex = first->element;
if (bottom[binIndex] == nullptr)
{
bottom[binIndex] = top[binIndex] = first;
}
else
{
top[binIndex]->next = first;
top[binIndex] = top[binIndex]->next;
}
}
chainNode<T>* lastTop = nullptr;
for (int i = 0; i <= range; ++i)
{
if (bottom[i] != nullptr)
{
if (first == nullptr)
{
first = bottom[i];
}
else
{
lastTop->next = bottom[i];
}
lastTop = top[i];
}