稳定的Quicksort与链接列表

时间:2014-04-25 05:46:58

标签: c++ linked-list quicksort

我正在尝试使用链接列表来快速实现快速排序,并且由于某种原因,此代码崩溃了。它似乎在崩溃之前进入分区中while循环的第二次迭代。

基本上,我使用头部作为枢轴点,而新的链接列表中的任何内容都少,并且任何大于枢轴的东西都会进入新的链接列表。这是递归调用的,然后在最后它们被组合在一起。

    class Node
    {
    public:
        Node* next;
        int key;
        int key2;

        Node()
        {
            next = NULL;
        }
    };

    void fillRandom(Node*& root, int length)
    {
       if(length != 0){
            root->key = rand()%10;
            root->next = new Node;
            fillRandom(root->next, length-1);
        }
    }

    void partitionLL(Node*& lessThan, Node*& greaterThan, Node*& root)
    {
        Node* lessThanTemp = lessThan; Node* greaterThanTemp = greaterThan;
        Node* temp = root;

        while(temp->next != NULL)
        {
            if(temp->key > root->key){
                greaterThanTemp = temp;
                greaterThanTemp->next = new Node;
                greaterThanTemp = greaterThanTemp->next;
            }else{
                lessThanTemp = temp;
                lessThanTemp->next = new Node;
                lessThanTemp = lessThanTemp->next;
            }
            temp = temp->next;
        }
    }

    void quickSort(Node* root)
    {
        if(root->next != NULL){//i.e. theres only two nodes in the list
            Node* lessThan = new Node; Node* greaterThan = new Node;
            partitionLL(lessThan, greaterThan, root);
            quickSort(lessThan);
            quickSort(greaterThan);
        }else{
            return;
        }
    }

    int main()
    {
        int length = 15;
        Node* root = new Node;

        fillRandom(root, length);
        quickSort(root);
    }

1 个答案:

答案 0 :(得分:1)

你的想法已经建立(建立两个链表)。它缺乏分离和重新附加代码。在处理链接列表时,第一个要记住的是罕见,您在进行重新安排时需要分配新节点。您使用您给出的节点。这就是他们的目标。您的所有算法应该只是重新安排指针。

以下就是这样做的。分配列表后,不需要新节点。除此之外的算法与您的想法相同:

  • 使用头节点作为透视值。将其从列表中删除
  • 在此过程中,将列表中的其余列表添加到两个列表中的一个列表中。请注意,从一个列表移动到另一个列表的代码会将节点添加到各自列表的结尾
  • 完成后,您自己拥有两个列表和一个寂寞的数据透视节点。终止两个列表并递归。
  • 一旦递归返回搜索到左侧列表的末尾。这是固定枢轴节点的地方,然后将右侧列表连接到该位置。

代码如下。我强烈建议在调试器中浏览它:

#include <iostream>
#include <iterator>
#include <algorithm>
#include <random>

struct Node
{
    Node* next;
    int key;
    int key2;

    Node( int key, int key2 )
        : key(key), key2(key2), next()
    {}
};

Node * createList(size_t N)
{
    std::random_device rd;
    std::mt19937 rng(rd());
    std::uniform_int_distribution<> dist(1,10);

    Node *root = nullptr;
    Node **pp = &root;
    for (int i=0; i<N; ++i)
    {
        *pp = new Node(dist(rng), i+1);
        pp = &(*pp)->next;
    }
    return root;
}

void freeList(Node *& root)
{
    while (root)
    {
        Node *tmp = root;
        root = tmp->next;
        delete tmp;
    }
}

void printList(const Node* root)
{
    for (;root;root = root->next)
        std::cout << root->key << '(' << root->key2 << ") ";
    std::cout << '\n';
}

// quicksort a linked list.
void quickSort(Node *&root)
{
    // trivial lists are just returned immediately
    if  (!root || !(root->next))
        return;

    Node *lhs = nullptr, **pplhs = &lhs;
    Node *rhs = nullptr, **pprhs = &rhs;
    Node *pvt = root;
    root = root->next;
    pvt->next = nullptr;

    while (root)
    {
        if (root->key < pvt->key)
        {
            *pplhs = root; // tack on lhs list end
            pplhs = &(*pplhs)->next;
        }
        else
        {
            *pprhs = root; // tack on rhs list end
            pprhs = &(*pprhs)->next;
        }
        root = root->next;
    }

    // terminate both list. note that the pivot is still
    //  unlinked, and will remain so until we merge
    *pplhs = *pprhs = nullptr;

    // invoke on sublists.
    quickSort(lhs);
    quickSort(rhs);

    // find end of lhs list, slip the pivot into  position, then 
    //  tack on the rhs list.
    while (*pplhs)
        pplhs = &(*pplhs)->next;
    *pplhs = pvt;
    pvt->next = rhs;

    // set final output
    root = lhs;
}

int main()
{
    Node *root = createList(20);
    printList(root);
    quickSort(root);
    printList(root);
    freeList(root);
    return 0;
}

输出(变化)

6(1) 7(2) 1(3) 10(4) 8(5) 10(6) 4(7) 7(8) 2(9) 9(10) 1(11) 8(12) 10(13) 8(14) 6(15) 9(16) 8(17) 2(18) 8(19) 9(20) 
1(3) 1(11) 2(9) 2(18) 4(7) 6(1) 6(15) 7(2) 7(8) 8(5) 8(12) 8(14) 8(17) 8(19) 9(10) 9(16) 9(20) 10(4) 10(6) 10(13) 

parens中的数字是原始列表中节点的原始顺序。请注意,我特意选择了一个小的随机池来进行选择,以便体验大量的等密钥,从而证明排序是稳定的; like-keys保留其原始列表排序。例如,原始列表中有五个8值。排序后,他们的序列是:

8(5) 8(12) 8(14) 8(17) 8(19)

这是故意的,并且通过确保我们将项目移动到拆分列表时我们总是在结束上添加它们来实现。

无论如何,我希望它有所帮助。