在模板中对链接列表进行排序 - 字符串问题

时间:2015-06-06 01:29:51

标签: c++ string sorting templates

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

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

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

比较代码在这里

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

我在下面给出了答案,因为我用它来解决我的问题,这也是字母顺序的答案,这是我原来的问题。所以,最初的问题得到了解答,但我发现另一个问题导致我的这个问题对我来说无用,但其他人可能需要回答这个问题。

1 个答案:

答案 0 :(得分:1)

正如其他人所指出的那样,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();
}

那我们做了什么?我们创建了一个模板类,它接受两个参数,一个类型和一个比较函数。

请注意,比较默认为类型std::greater的{​​{1}}。例如,如果TT,那么int将对两个整数进行比较,以查看第一项是否大于第二项。上面的示例显示,如果类型的std::greater<int>足够(对于operator >,则使用单个参数来实例化模板)。

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

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

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

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