插入节点时对链表进行排序

时间:2016-03-18 13:23:53

标签: c++ algorithm sorting linked-list

我正在尝试创建已排序的链接列表=在创建它时对其进行排序。想法很简单,插入一个节点 - 并检查先前是否较小,如果是,请检查之前的情况,依此类推,直到找到它的位置。我创建了这段代码。

struct Node{
    Node *prev;
    Node *next;
    int value;
};

struct List{
    Node *head = nullptr;
    Node *tail = nullptr;
};

在这里,我创建了一个节点,以及list的“holder”,引用了列表的第一个和最后一个项目。

void insertNode(Node *&head,Node *&tail, int value ){
    Node *tmp = new Node;
    tmp -> prev = nullptr;
    tmp -> next = nullptr;
    tmp -> value = value;
    head = tmp;
    tail = tmp;
}

此函数检查list是否为空,如果是,则将节点插入head和tail(例如head = tail =列表中只有一个节点);

让我烦恼的是插入节点的功能

void insertIt(Node *&head , Node *&tail , int value){
    if( head == nullptr){
        insertNode(head,tail,value);
    }
    else{
        Node *tmp = new Node;
        tmp -> value = value;

        if( value < tail -> value){    
            while(value < tail -> prev -> value){                
                tail = tail -> prev;
                if( tail -> prev == nullptr){                    
                    tmp -> next = head;
                    tmp -> prev = nullptr;
                    head -> prev = tmp;
                    head = tmp;
                    return;
                }
            }
            tail -> prev -> next = tmp;
            tmp -> prev =  tail -> prev;
            tmp -> next = tail;
            tail -> prev = tmp;
        }else{    
            tmp -> next = nullptr;
            tmp ->prev = tail;
            tail -> next = tmp;
            tail = tmp;
        }
    }
}

如果list为空,则调用insertNode(),如果节点的值小于上一个节点的值,则会抓取列表以查找其位置。

此片段代码仅在插入的第一个节点也是最小节点时才有效。 e.g

insertIt(list.head , list.tail , -1);
insertIt(list.head , list.tail , 0);
insertIt(list.head , list.tail , 7);
insertIt(list.head , list.tail , 1);
insertIt(list.head , list.tail , 2);
insertIt(list head , list.tail , 2);

工作,如果我打印列表,它是很好的排序。但

insertIt(list.head , list.tail , -2);
insertIt(list.head , list.tail , -1);
insertIt(list.head , list.tail , 7);
insertIt(list.head , list.tail , 1);
insertIt(list.head , list.tail , 2);
insertIt(list.head , list.tail , 2);

第一个节点不是最小的节点,它会使程序崩溃。我以为我正在将值与nullptr进行比较,所以我添加了你可以在insertIt()函数中看到的那段代码,那就是

if( tail -> prev == nullptr){
    tmp -> next = head;
    tmp -> prev = nullptr;
    head -> prev = tmp;
    head = tmp;
    return;
}

这将检查节点是否为头,并与新节点交换头,使新节点成为新节点。

为什么会崩溃代码?我没有找到合理的答案。另外,我怎样才能改进我的“算法”以使其更有效?

5 个答案:

答案 0 :(得分:2)

您想要做两件事:在列表中找到新节点所在的位置并在一个位置插入新节点。所以,编写两个函数,一个执行每个任务。然后,您可以在集成之前单独测试和调试它们。这将更加直截了当。进一步的推荐:在实现功能之前,为每个功能编写单元测试。

/** Find node with largest value less than given 
    Assumes sorted list exist.  If empty, throws exception
*/
Node & FindLessThan( int value );

/** Inset new node after given with value */
InsertAfter( Node& n, int value );

如果列表为空,有一个插入第一个节点的函数也很方便

/** Insert first node with value
    @return true if list empty */
bool InsertFirstNode( int value );

关键是你应该隐藏所有可以测试的函数中的指针,这样你就可以编写一个可以理解的主线,它将首次运行:

if( ! InsertFirstNode( value ) )
   InsertAfter( FindLessThan( value ), value );

由于您使用的是C ++,因此请将列表作为类和函数成员。

实施细节:您必须担心特殊情况:新值在头部之前或尾部之后。所以我建议使用枚举来处理这些。

/** Special cases for placing a new node */
enum class eFind
{
    list_empty,         // the list was empty
    before_first,       // the new node goes before the first node in list
    before_node,        // the new node goes before the specified node
    after_last,         // the new node goes after the last node in the list
}; 
/** Find node with smallest value greater than given

    @param[out] place eFind enumeration, one of list_empty,before_first,before_node,after_last
    @param[in] value being inserted

    @return n node before which value should be placed

    Assumes sorted list exist.
*/
Node * FindSmallestGreaterThan( eFind & place, int value )

使用InsertBefore而不是InsertAfter也更容易(更少的代码)。您可以在cpp.sh/4xitpgithub gist

看到代码

答案 1 :(得分:2)

当遍历列表以找到插入新节点的位置时,您可以执行以下操作:

  tail = tail -> prev;

tail变量由引用传递,即修改yout tail对象的List成员,从而破坏其一致性。< / p>

使用另一个名为currentposition的临时变量来遍历列表,除非您在末尾添加新节点,否则不要修改tail清单。

编辑示例方法

struct Node {
    Node(int val);

    Node *prev;
    Node *next;
    int value;
};

struct List{
    List() : head(nullptr), tail(nullptr) {}
    void insert(int value);

    Node *head;
    Node *tail;
};

Node::Node(int val) :
    value(val), next(nullptr), prev(nullptr)
{
}

void List::insert(int value) {
    Node *tmp = new Node(value);

    if(head == nullptr) {
        head = tmp;
        tail = tmp;
        return;
    }

    Node *pos;  // find the node greater or equal to 'value'
    for(pos = head; pos && pos->value < value; pos = pos->next)
        ;

    if(pos) {    // appropriate pos found - insert before
        tmp->next = pos;
        tmp->prev = pos->prev;
        tmp->next->prev = tmp;
        if(tmp->prev)       // there is some predecessor
            tmp->prev->next = tmp;
        else
            head = tmp;     // making a new first node
    } else {     // pos not found - append at the end
        tmp->next = nullptr;
        tmp->prev = tail;
        tail->next = tmp;
        tail = tmp;
    }
}

答案 2 :(得分:1)

1。您无法初始化结构内的成员:

struct List
{
    Node *head;
    Node *tail;
};

2.(a)函数原型insertItinsertNode是错误的。您使用传递引用传递headtail它应该如下:

void insertIt(Node * head ,Node * tail ,int value)

void insertNode(Node * head,Node * tail,int value)

2.(b)else部分创建节点时,应将新节点的nextprev指针设置为{{ 1}}:

NULL

2.(c)正如您通过引用传递tmp->prev=NULL; tmp->next=NULL; 时所做的任何更改,而tail上的循环都反映在program.Hence中使用临时指针类型为tail

3。此外,您使用的设计并不好。我建议您更改它。这是我对链表的实现:

Node

答案 3 :(得分:1)

问题是value < tail->prev->value循环头中的while检查。这不会检查tail->prev != nullptr是否为真。对于head == tailvalue < head->value的情况,这是一个问题。如果head != tail,您的代码确实有效,因为第一次评估value < tail->prev->value时,tail->prev != nullptr为真,并且案例head->next == tail将被循环体中的代码捕获。 正确的检查将是tail->prev != nullptr && value < tail->prev->value。首先检查tail->prev是否可以解除反对。

然后,在完成tail->prev == nullptr循环后(由于新条件),您可能会以while结束。检查可以移出循环,导致以下代码:

while (tail->prev != nullptr && value < tail->prev->value) {
    tail = tail->prev;
}
if (tail->prev == nullptr) {
    // Prepend node to the list
    return;
}
// Insert node in front of tail

编辑:您仍然可以检查循环中的条件tail->prev == nullptr;循环之后的检查只对捕获案例head == tail && value < head->value有用。不在循环中进行检查具有更短和(在我看来)模式可读代码的好处。

答案 4 :(得分:0)

这可能是您正在寻找的代码;-)您可以在VS2013中按原样运行它。它将插入函数简化为几个if语句。并且可以通过使用头部和头部的终端元件进一步简化。尾。

我希望这会有所帮助: - )

struct Node
{
    int value; Node *prev, *next;
};

struct DoublyLinkedSortedList
{
    Node *head = nullptr, *tail = nullptr;

    void insert(int value)
    {
        // Find first node bigger then the new element, or get to the end of the list
        Node* node = head;
        while (node && node->value <= value) { node = node->next; }

        // Once found, insert your new element before the currently pointed node, or at the end of the list
        node = new Node{ value, node?node->prev:tail, node };
        if (node->prev) node->prev->next = node; else head = node;
        if (node->next) node->next->prev = node; else tail = node;
    }
};

#include <climits>
#include <iostream>

using namespace std;

int main()
{
    cout << "This is a DoublyLinkedList test." << endl << endl;

    // test the list
    DoublyLinkedSortedList list;
    list.insert(234);
    list.insert(INT_MIN);
    list.insert(17);
    list.insert(1);
    list.insert(INT_MAX);
    list.insert(-34);
    list.insert(3);
    list.insert(INT_MAX);
    list.insert(INT_MIN);
    list.insert(9);
    list.insert(7);

    // print nodes in order;
    cout << "This are the contents of the linked list front to back" << endl << endl;
    for (Node* curr = list.head; curr != nullptr; curr = curr->next) { cout << curr->value << "; "; }
    cout << endl << endl << "This are the contents of the linked list back to front" << endl << endl;
    for (Node* curr = list.tail; curr != nullptr; curr = curr->prev) { cout << curr->value << "; "; }
    cout << endl << endl;

    system("pause");
}