用字符串对模板问题中的链表进行排序
Sort linked list within template - issues with strings
我正在创建一个在插入上排序的链表。它是一个模板类,所以type是在运行时定义的。
问题是,我正在比较两个值,看看哪一个是最大的,然后我把这个值放在它应该在链表中的位置。这对于任何基于数学的类型来说都很好,但对于字符串来说就不太好了。它不按字母顺序排序。
这是我试图正常工作的代码,所以我可以比较两个字符串,找出哪一个是第一个字母顺序(但它是一个模板,所以它也必须为其他类型工作)
比较代码在这里
while ( data > iter->_data && iter->_next != 0) {
下面是完整的代码,我还包括了main()下面的一些代码
template <typename T>
class LinkedList;
template <class TNode>
class Iterator
{
friend class LinkedList<typename TNode::value_type>;
TNode* pNode;
Iterator(TNode* _pNode) : pNode(_pNode) {}
public:
void operator++() { pNode = pNode->_next; }
void operator++(int) { pNode = pNode->_next; }
bool operator!=(Iterator<TNode> rval) { return !(pNode == rval.pNode); }
bool operator==(Iterator<TNode> rval) { return (pNode == rval.pNode); }
typename TNode::value_type operator*() { return pNode->_data; }
Iterator<TNode> operator+(int _i)
{
Iterator<TNode> iter = *this;
for (int i = 0; i < _i; ++i)
{
if (iter.pNode) //If there's something to move onto...
++iter;
else
break;
}
return iter; //Return regardless of whether its valid...
}
};
template <typename T>
class Node
{
friend class LinkedList<T>;
friend class Iterator<Node<T> >;
Node() : _next(0) {}
Node(T data) : _data(data), _next(0) {}
Node(T data, Node<T>* next) : _data(data), _next(next) {}
Node(Node<T>* next) : _next(next) {}
T _data;
Node<T>* _next;
Node<T>* _prev;
public:
typedef T value_type;
};
template <typename T>
class LinkedList
{
Node<T>* first;
int count = 0;
public:
typedef Iterator<Node<T> > iterator;
typedef T value_type;
LinkedList() : first(0) {}
~LinkedList()
{
if (first)
{
Node<T> *iter = first;
while (iter != 0)
{
Node<T>* tmp = iter;
iter = iter->_next;
delete tmp;
}
}
}
bool LinkedList<T>::operator < (LinkedList<T> rhs);
bool LinkedList<T>::operator > (LinkedList<T> rhs);
void insert(T data)
{
if (first)
{
Node<T> *iter = first;
Node<T> *preIter = first;
while ( data > iter->_data && iter->_next != 0) {
preIter = iter;
iter = iter->_next;
}
if (iter == first) {
if (data < iter->_data) {
Node<T>* holder = first;
first = new Node<T>(data);
first->_next = holder;
holder->_prev = first;
first->_prev = 0;
}
else if (iter->_next != 0) {
Node<T>* newIter = new Node<T>(data);
newIter->_next = first->_next;
first->_prev = newIter;
newIter->_prev = first;
first->_next = newIter;
}
else {
first->_next = new Node<T>(data);
first->_next->_prev = first;
}
}
else if(preIter->_next != 0) {
Node<T>* newIter = new Node<T>(data);
preIter->_next = newIter;
newIter->_next = iter;
iter->_prev = newIter;
newIter->_prev = preIter;
}
else {
iter->_next = new Node<T>(data);
iter->_next->_prev = iter->_next;
}
}
};
iterator begin() { return iterator(first); }
iterator end() { return iterator(0); }
bool erase(iterator& _iNode) //True for success, vice versa
{
/* This is rather inneffecient. Maybe a better way to do this? */
/* Even then, it's *still* more effecient than a contiguous container */
if (_iNode.pNode == first)
{
first = first->_next;
delete _iNode.pNode;
return true;
}
else
{
for (Node<T>* iter = first; iter->_next; iter = iter->_next)
{
if (iter->_next == _iNode.pNode) //Find our node.
{
iter->_next = _iNode.pNode->_next;
delete _iNode.pNode;
return true;
}
}
}
return false;
}
};
对于下面的代码,最终排序结果不等于测试。它适用于整数测试(也包括在内),但不适用于字符串测试。我最后在开头的第一个A中使用了所有的大写字符…Z字符串测试,完整单词测试中的一些单词在第二个字符串测试中没有出现在正确的位置。一般来说,它是相当准确的,但不是完全按字母顺序排列的。
如何按字母顺序排序?
/** Test fundamental concept by storing and retrieving 0..9 */
BOOST_AUTO_TEST_CASE(ut_concept_0_to_9) {
vector<long> v{ 1, 2, 3, 4, 5, 6, 7, 8, 9 };
auto shuffle = v;
random_shuffle(shuffle.begin(), shuffle.end());
LinkedList<long> sl;
for (auto x : shuffle)
sl.insert(x);
auto it = sl.begin();
for (auto x : v) {
BOOST_CHECK_EQUAL(*it, x);
++it;
}
}
/** Test fundamental concept by storing and retrieving A..Z */
BOOST_AUTO_TEST_CASE(ut_concept_A_to_Z) {
string s = "Hello, world!";
LinkedList<string::value_type> sl;
for (auto ch : s)
sl.insert(ch);
sort(s.begin(), s.end());
auto it = sl.begin();
for (auto ch : s) {
BOOST_CHECK_EQUAL(*it, ch);
++it;
}
}
/** Test fundamental concept by storing and retrieving strings */
BOOST_AUTO_TEST_CASE(ut_concept_strings) {
vector<string> words{ "Sunna", "Mona", "Tiw", "Woden", "Thunor", "Frige", "Saturn" };
LinkedList<string> sl;
for (auto ch : words)
sl.insert(ch);
sort(words.begin(), words.end());
auto it = sl.begin();
for (auto word : words) {
BOOST_CHECK_EQUAL(*it, word);
++it;
}
}
EDIT: My Unique Solution
我最终发现我的问题不是一个纯粹的字母排序问题。它实际上在我的代码中。我错过了一种情况,我要把我想要排列的对象放在最后一个对象和倒数第二个对象之间。它应该经过最后一个物体。这段代码将被清理,因为有一堆区域需要清理,但这应该排序正确。我认为需要按字母顺序排序是错误的。
else if (iter->_next == NULL) {
if (iter->_data < data) {
iter->_next = new Node<T>(data);
iter->_next->_prev = iter->_next;
}
else {
Node<T>* newIter = new Node<T>(data);
preIter->_next = newIter;
newIter->_next = iter;
iter->_prev = newIter;
newIter->_prev = preIter;
}
}
}
我在下面标记了答案,因为我用它来解决我的问题,这也是按字母顺序的答案,这是我最初的问题。所以,原来的问题得到了回答,但我发现了另一个问题,使这个问题对我的情况毫无用处,但其他人可能需要这个问题的答案。
正如其他人所指出的,std::string
重载了操作符<
和>
来完成字典比较的工作。
但是,如果您的目标是进行字母排序,那么您需要重写模板类。但是,使用您已经编写的代码会使事情变得非常混乱,因此可以采用不同的方法。
如果允许使用std::list
,那么可以使用包装器类编写一个模板链表类,以自定义顺序存储项目。
首先,我们需要让模板不仅接受类型,而且接受比较函数,以告诉链表如何将项插入链表的标准。
不需要解释太多,下面是代码:#include <functional>
#include <algorithm>
#include <cctype>
#include <list>
#include <iterator>
#include <iostream>
#include <string>
template <typename T, typename Comparer = std::greater<T>>
class LinkedList
{
private:
std::list<T> m_list;
public:
void insert(const T& data)
{
Comparer comp;
typename std::list<T>::iterator it = m_list.begin();
while (it != m_list.end())
{
// call the > comparison function
if (comp(data, *it))
++it; // keep searching
else
{
// this is the spot
m_list.insert(it,data);
return;
}
}
// has to go on the end if not inserted
m_list.push_back(data);
}
// use this for demonstration purposes only
void displayMe()
{
std::copy(m_list.begin(), m_list.end(), std::ostream_iterator<T>(std::cout, "n"));
}
};
struct MyComparerForStrings
{
// case insensitive comparison
bool operator()(const std::string& s1, const std::string& s2)
{
std::string str1Cpy(s1);
std::string str2Cpy(s2);
// convert to lower case
std::transform(str1Cpy.begin(), str1Cpy.end(), str1Cpy.begin(), ::tolower);
std::transform(str2Cpy.begin(), str2Cpy.end(), str2Cpy.begin(), ::tolower);
// returns if first string is > second string
return (str1Cpy > str2Cpy);
}
};
int main()
{
LinkedList<std::string, MyComparerForStrings> sList;
sList.insert("abc");
sList.insert("zzz");
sList.insert("joe");
sList.insert("BOB");
sList.displayMe();
cout << "n";
LinkedList<int> intList;
intList.insert(10);
intList.insert(1);
intList.insert(3);
intList.displayMe();
}
我们做了什么?我们创建了一个模板类,它接受两个形参,一个类型和一个比较函数。
注意,对于T
类型,比较默认为std::greater
。例如,如果T
是int
,那么std::greater<int>
将对两个整数进行比较,看第一个是否大于第二个。上面的例子表明,如果类型的operator >
足够(对于int
,它是足够的),则使用单个参数以相同的方式实例化模板。
但是,出于我们的目的,我们希望按字母顺序(或不区分大小写)对std::string
进行排序。为此,我们提供一个自定义函数作为模板参数,该函数接受两个字符串,并按字母顺序对它们进行比较(指向这个SO问题:c++中不区分大小写的字符串比较)
现在,insert
函数使用迭代器,并使用比较函数搜索插入项的位置。当找到时,它只调用list::insert
函数,该函数完成所有工作。
下面是一个实例:http://coliru.stacked-crooked.com/a/ecb2e7957eb4fea8
- 对单向链表进行排序时出现运行时错误
- 为什么在排序链表上的这种合并实现总是将两个列表都设置为 NULL,而只有一个应该设置一个列表?
- 按升序对链表进行排序
- 我想在 c++ 中对单向链表进行冒泡排序,但我一直面临左值错误,无法解决它
- 合并链表上的排序
- 为什么链表的重新排序会失败?
- 合并两个排序链表时运行时出错
- 在 C++ 中合并两个排序链表
- 如何从几乎排序的链表中分离放错位置的元素?
- 使用C++中的链表对与原点的距离、x 和 y 坐标进行排序
- 使用运算符+将两个已排序的链表合并到位
- 合并 2 个排序的链表。必需的成员函数 bool Merge(List342 &list1):
- 双链表C++上的插入排序
- 在排序链表中插入结构并将其写入二进制文件
- C++:链表排序
- 链表排序不影响main(C++)中的列表项
- C++链表排序、拆分和打印问题
- 链表排序
- 自定义链表排序方法
- 链表排序方式的不同之处