链表和从文本文件中读取

时间:2013-02-21 03:04:21

标签: c++ linked-list

我是所有这些c ++编程的初学者,我想最后一次尝试获得帮助。我必须创建一个名为add_aa(str)的方法,该方法按升序字母顺序添加str。这涉及使用链表和从文本文件中读取。在文本文件中,包含以下内容:aa hi dd hi。这就是文本文件中的所有内容。在main.cpp文件中这里是一个代码,可以打开str:

if( cmds.first == "aa" )            // add alphabetically
    {
        lst.add_aa( cmds.second );
    }

使用此代码,从文本文件中读取,hi将被输出并分配给str。我正在尝试编写执行此操作的方法,但我无法想出任何结果。另外,这是我应该收到的清理工作的输出。 enter image description here

到目前为止,我的代码只是:

inline void LList:: add_aa(const string _str)
{
cout << "\tp = " << std::hex << p << std::dec << '\n';
}

1 个答案:

答案 0 :(得分:7)

哦,我的,我们进入C ++“链表教程”,我说上次你问的时候是不切实际的。

但也许我有时间写它。

我会先给自己喝点茶,然后分一小段地发布,一次一个。

<小时/>

基本单链表。

基本单链表由节点组成,其中每个节点包含指向列表中下一个节点的指针,例如:叫next。按惯例next == 0表示列表的结尾。所以,让我们创建一个这样一个简单的列表,遍历它(这里只显示内容),并通过释放节点进行清理:

#include <iostream>
using namespace std;

struct node_t
{
    node_t*     next;
    int         value;

    node_t( int const v = 0 )
        : next( 0 ), value( v )
    {}
};

int main()
{
    char const blah[] = "moonflower";

    node_t*     first   = 0;

    // Create the list.
    for( auto ch : blah )
    {
        node_t* node = new node_t( ch );

        node->next = first;     // After this `node` points to the new first node.
        first = node;           // Now `first` also points to the new first node.
    }

    // Display it
    for( node_t* p = first;  p != 0;  p = p->next )
    {
        cout << p->value << " ";
    }
    cout << endl;

    // Delete the list
    while( first != 0 )
    {
        node_t* const doomed = first;

        first = first->next;
        delete doomed;
    }
}

为了最后清理,以正确的顺序执行操作非常重要,这样才能访问已删除的节点。访问已删除的节点可能有效。但是,由于墨菲定律,它将在以后失败,在这个时刻,事情总是最重要的。

输出:

0 114 101 119 111 108 102 110 111 111 109

这些是ASCII字符代码,包括字符串的终止零。

注意数字的顺序! : - )

<小时/>

插入单链表。

与直接使用数组相比,链表的主要优点是插入新节点需要恒定的时间。通过直接使用数组,插入新项目必须在其之后(或之前)移动所有项目,这需要与数组大小成比例的时间。但是,请注意(1)找到插入点的时间通常在列表大小中被强制为线性,而对于数组则可以是对数的,以及(2)使用称为“插入间隙”或“光标间隙”的技术“插入一个项目也可以是一个阵列的恒定时间(以一些微小的成本)。

为了在升序排序位置插入数字,使用基本列表,您需要一个指向插入点之前的节点的指针,以便该节点的next可以更新为指向新节点。

由于基本列表在第一个节点之前没有这样的节点,所以列表开头的插入变成了一个丑陋的特殊情况,如下所示:

#include <iostream>
using namespace std;

struct node_t
{
    node_t*     next;
    int         value;

    node_t( int const v = 0 )
        : next( 0 ), value( v )
    {}
};

int main()
{
    char const blah[] = "moonflower";

    node_t*     first   = 0;

    // Create the list.
    for( auto ch : blah )
    {
        node_t* node = new node_t( ch );

        // Find the insertion point.
        node_t* p = first;
        node_t* p_node_before = 0;
        while( p != 0 && p->value < ch )
        {
            p_node_before = p;
            p = p->next;
        }

        if( p_node_before == 0 )
        {
            // Insert at start of list, just like in the first program.
            node->next = first;     // After this `node` points to the new first node.
            first = node;           // Now `first` also points to the new first node.
        }
        else
        {
            // Insert within the list or at the end of the list.
            node->next = p_node_before->next;
            p_node_before->next = node;
        }
    }

    // Display it
    for( node_t* p = first;  p != 0;  p = p->next )
    {
        cout << p->value << " ";
    }
    cout << endl;

    // Delete the list
    while( first != 0 )
    {
        node_t* const doomed = first;

        first = first->next;
        delete doomed;
    }
}

输出:

0 101 102 108 109 110 111 111 111 114 119

请注意,由于搜索插入位置,每次插入平均需要与列表的最终长度成比例的时间,因此总共这是二次时间,O(n 2 ) 。这是一个简单的插入排序。我希望我没有介绍任何错误! : - )

<小时/>

巧妙的指向指针。

减少基本列表开头插入的混乱的一种方法是注意,您实际上只需要在插入点之前访问节点的next字段。

您不需要在插入点之前访问完整节点。

因此,您只需使用指向next指针指针即可。即指向指针的指针。并且对于列表的开头,first指针变量可以很好地服务,就好像它之前的某个节点中的next字段一样,所以最初指向指针的指针只能指向{ {1}}指针(ahem),然后它可以在first指针上移动,如下所示:

next

输出仍然是

0 101 102 108 109 110 111 111 111 114 119

应该如此。

首先,我直接在#include <iostream> using namespace std; struct node_t { node_t* next; int value; node_t( int const v = 0 ) : next( 0 ), value( v ) {} }; int main() { char const blah[] = "moonflower"; node_t* first = 0; // Create the list. for( auto ch : blah ) { node_t* node = new node_t( ch ); // Find the insertion point. node_t** p_next_before = &first; while( *p_next_before != 0 ) { node_t* const next_node = *p_next_before; if( next_node->value >= ch ) { break; } p_next_before = &next_node->next; } // Insert at start of list, just like in the first program. node->next = *p_next_before; // After this `node` points to the new first node. *p_next_before = node; // Now ... also points to the new first node. } // Display it for( node_t* p = first; p != 0; p = p->next ) { cout << p->value << " "; } cout << endl; // Delete the list while( first != 0 ) { node_t* const doomed = first; first = first->next; delete doomed; } } 循环头中编写了具有相当长的延续条件的代码。但遵循规则,人们应该只是看到所有的东西,我在循环中移动了部分内容,然后使用while语句。这就是break声明的原因:支持名称break

在C ++中,指针指针有时表示为指针引用。例如,取消链接节点与基本列表的函数可能采用引用指针参数,而不是C样式指针指针。当它适用时,我发现它有点清洁。

<小时/>

使用标题节点。

指向指针的替代方法是在第一个真实节点之前引入虚拟节点。然后,即使空列表也有一个物理节点,即虚节点。虚节点通常称为标头节点

编译器完成其转换为机器代码和优化后,使用头节点的代码可能与使用指针到指针技术的代码相同,除了头节点涉及一个以上的分配,并采取比单个指针变量多一点空间。但不同之处在于人们如何看待代码,如何理解代码。概念上具有标题节点与使用指针到指针技术非常不同 - 例如,如果您绘制列表结构,那么带有标题节点的列表看起来明显不同于没有这样节点的列表。

无论如何,代码:

next_node

和以前一样,输出仍然是

0 101 102 108 109 110 111 111 111 114 119

应该如此。