【问题标题】:Sort linked list within template - issues with strings对模板中的链表进行排序 - 字符串问题
【发布时间】:2015-08-21 02:00:42
【问题描述】:

我正在创建一个在插入时排序的链表。它是一个模板类,所以类型是在运行时定义的。

问题是,我正在比较两个值以查看哪个值最大,然后我将该值放在链表中应该存在的位置。这对于任何基于数学的类型都很好用,但它不适用于字符串。它不按字母顺序排序。

这是我正在尝试正常工作的代码,因此我可以比较两个字符串并找出哪个字符串按字母顺序排在第一位(但它是一个模板,因此它也必须适用于其他类型)

对比代码在这里

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;
    }
}

编辑:我的独特解决方案

我最终发现我的问题不是纯粹的字母排序问题。它实际上在我的代码中。我错过了一种情况,即我将尝试放置的对象放在最后一个对象和倒数第二个对象之间。它应该已经过了最后一个对象。这段代码将被清理,因为有很多区域需要清理,但这应该正确排序。我错了需要按字母排序。

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;
        }
    }
}

我在下面标记了答案,因为我用它来解决我的问题,它也是按字母顺序排列的答案,这是我最初的问题。因此,最初的问题得到了回答,但我发现另一个问题对我来说对我来说毫无用处,但其他人可能需要这个问题的答案。

【问题讨论】:

    标签: c++ string sorting templates


    【解决方案1】:

    正如其他人指出的那样,std::string 重载了运算符 &lt;&gt; 来完成字典比较的工作。

    但是,如果您的目标是进行字母排序,那么您需要重写您的模板类。但是使用您已经编写的代码会变得非常混乱,因此可以采用不同的方法。

    如果允许您使用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&lt;int&gt; 将比较两个整数,看第一项是否大于第二项。上面的示例显示,如果类型的 operator &gt; 足够(对于 int 而言,它是足够的),则使用单个参数以相同的方式实例化模板。

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

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

    这绝不是世界上最好的例子。不过你应该能明白。

    这是一个活生生的例子:http://coliru.stacked-crooked.com/a/ecb2e7957eb4fea8

    【讨论】:

    • 非常感谢。这将使我走上我认为的正确道路。从这一点开始,我只需要弄清楚如何将其变成方形列表。最初我打算以不同的方式来做,但我现在必须做一些思考来尝试找出最好的方法。我不认为我的教授会希望我更改他的单元测试代码,而且我认为这不会起作用,但是您给了我一些好的思考。非常感谢您到目前为止的时间。想了一会儿我再汇报,哈哈。
    • 我也刚刚阅读了您上面的其他评论。老师将用他自己的新副本替换他给我们的所有单元测试,以确保我们不会更改任何一个。如果真的不能按照他想要的方式完成,我觉得这很令人沮丧。
    • 我实现了你的一个版本,但将它包含在我的模板类中(我不知道我应该做什么)。我基本上只是为 compare() 创建了一个方法。我使用 boost 将任何内容转换为字符串,得到的结果与最初得到的结果相同。因此,我将这两个对象放入一个向量中,并对向量进行排序——然后我知道这对于单元测试应该产生相同的结果。如果在 first() 中应该是更少的对象,我返回 true,否则返回 false。这产生了相同的结果。最后,我的逻辑是错误的。我错过了 _next 为空的情况。
    猜你喜欢
    • 2018-09-13
    • 1970-01-01
    • 2016-08-28
    • 2020-09-09
    • 1970-01-01
    • 1970-01-01
    • 2011-02-04
    • 1970-01-01
    • 2015-03-26
    相关资源
    最近更新 更多