2016-03-18 110 views
0

我试图创建排序的链接列表=在创建它时对其进行排序。想法很简单,插入一个节点 - 并检查前一个是否较小,如果是,检查前一个等等,直到找到它的位置。我已经创建了这段代码。插入节点时排序链接列表

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

此函数检查列表为空,如果是,则它插入节点到头部和尾部(例如头=尾=有在列表中只有一个节点);

什么烦恼我是函数插入一个节点

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

如果列表是空的,它会调用insertNode(),如果节点的值比前一节点的值越小,所抓取的列表中找到它的现货。

只有当插入的第一个节点也是最小的节点时,此代码才有效。例如

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

此检查的节点是头,交换头新节点,使新节点焕然一新。

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

+3

首先赶上飞机坠毁在行动,看看它是什么,所有涉及的变量的值。如果它没有帮助,那么使用调试器逐行浏览代码,查看它的作用以及所有变量如何变化。 –

+0

“如果列表为空,则调用insertNode(),如果节点的值小于前一个节点的值。但是列表是空的。 –

+0

我使用的代码块和调试器指出,在我添加的代码行(我写的最后一个代码),我有点新到c/C++世界,没有使用过多的调试器,我想代码块调试器不是最好的。你能推荐一些好的吗? – Darlyn

回答

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/4xitp代码或github gist

+0

感谢您的建议,我会尝试使用分离功能重新创建它。 – Darlyn

+0

如果新值小于已经在列表中的所有值,那么'Node&FindLessThan(int value)'应该返回什么? – CiaPan

+0

枚举值'before_first'。你看过cpp.sh/4xitp上的运行代码吗? – ravenspoint

1

1.不能初始化结构内部成员:

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

2.(a)中的功能insertIt原型和insertNode是错误。您正在通过headtail,请使用传递参考。应如下所示:

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

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

2(B)当您在else部分节点,你应该设置你的新节点的nextprev指针NULL

tmp->prev=NULL; 
tmp->next=NULL; 

2( c)正如你已通过tail使用通过引用传递你在tail循环中所做的任何更改都反映在program.Hence使用临时指针ty pe Node

3.而且使用的是不good.Hence我会建议你改变it.This的设计是我实现链表:

main() 
{ 
    struct List Q; 
    Initialize_list(&Q); 
    Insert_it(&Q,12); 
} 
void Initialize_list(struct List *L) 
{ 
    L->head=NULL; 
    L->tail=NULL; 
} 
1

的问题是检查value < tail->prev->valuewhile环形头。这不检查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可以被拒绝。

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

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。不在循环中进行检查有一个较短和(在我看来)模式可读代码的好处。

2

当遍历列表,找到要插入一个新的节点位置,你就:

tail = tail -> prev; 

tail变量由参考过去了,那是你修改yout的tail成员List对象,从而破坏其一致性。

使用另一个名为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; 
    } 
} 
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"); 
}