用字符串对模板问题中的链表进行排序

Sort linked list within template - issues with strings

本文关键字:链表 排序 问题 字符串      更新时间:2023-10-16

我正在创建一个在插入上排序的链表。它是一个模板类,所以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。例如,如果Tint,那么std::greater<int>将对两个整数进行比较,看第一个是否大于第二个。上面的例子表明,如果类型的operator >足够(对于int,它是足够的),则使用单个参数以相同的方式实例化模板。

但是,出于我们的目的,我们希望按字母顺序(或不区分大小写)对std::string进行排序。为此,我们提供一个自定义函数作为模板参数,该函数接受两个字符串,并按字母顺序对它们进行比较(指向这个SO问题:c++中不区分大小写的字符串比较)

现在,insert函数使用迭代器,并使用比较函数搜索插入项的位置。当找到时,它只调用list::insert函数,该函数完成所有工作。

这绝不是世界上最好的例子。但是你应该能理解我的意思。

下面是一个实例:http://coliru.stacked-crooked.com/a/ecb2e7957eb4fea8