简体   繁体   English

链表和文本文件读取

[英]linked list and reading from text file

I am a beginner to all this c++ programming, and I want to try one last time to get help. 我是所有这些c ++编程的初学者,我想最后一次尝试以获得帮助。 I have to create a method called add_aa( str ) that adds str in ascending alphabetical order. 我必须创建一个名为add_aa(str)的方法,该方法以字母升序添加str。 This involves using linked list and reading from a text file. 这涉及使用链表和从文本文件读取。 In the text file, the following is in it: aa hi dd hi. 在文本文件中,包含以下内容:aa hi dd hi。 that is all that is in the text file. 这就是文本文件中的全部内容。 in the main.cpp file here is a code that would out hi to str: 在main.cpp文件中,这里的代码对str的影响很大:

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

with this code, reading from the text file, hi would be outputted and assigned to str. 使用此代码,从文本文件读取,hi将被输出并分配给str。 I am trying to write the method that does this, but i cannot come up with anything. 我正在尝试编写执行此操作的方法,但是我什么都无法提出。 also, this is the output that i should receive to clear things up. 另外,这是我应该收到的输出,以清除问题。 在此处输入图片说明

my code so far is just: 到目前为止,我的代码只是:

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

oh my, we're into the C++ “linked list tutorial” that i said was impractical the last time you asked. 哦,我的,我们进入了C ++“链表教程”,我上次问过这是不切实际的。

but maybe i have time to write it. 但是也许我有时间写它。

i will make myself some tea first, and post this in small installments, one at a time. 我先给自己泡茶,然后分小批分批分发。


Basic singly linked list. 基本单链表。

A basic singly linked list consists of nodes , where each node contains a pointer to the next node in the list, eg called next . 基本的单链接列表由节点组成,其中每个节点都包含一个指向列表中下一个节点的指针,例如,称为next By convention next ==0 indicates the end of the list. 按照约定, next == 0表示列表的末尾。 So, let's create such a simple list, traverse it (here just displaying the contents), and clean up by deallocating the nodes: 因此,让我们创建一个简单的列表,遍历它(这里只是显示内容),并通过取消分配节点进行清理:

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

For the cleaning up at the end it's important to do things in the right order, so as to not access a deleted node. 对于最后的清理,以正确的顺序执行操作很重要,这样才能不访问已删除的节点。 Accessing a deleted node might work. 访问已删除的节点可能会起作用。 But then, due to Murphy's law, it will fail later on, at the point in time where it's absolutely most critical that things shoudl work. 但是随后,根据墨菲定律,它将在以后的某个时候失效,因为此时应该正常工作。

Output: 输出:

0 114 101 119 111 108 102 110 111 111 109

These are the ASCII character codes, including the string's terminating zero. 这些是ASCII字符代码,包括字符串的结尾零。

Note the order of the numbers! 注意数字的顺序! :-) :-)


Insertion in a singly linked list. 插入单链列表中。

A main advantage of a linked list, compared to direct use of an array, is that inserting a new node takes constant time. 与直接使用数组相比,链表的主要优点是插入新节点需要花费固定时间。 With the direct use of an array, an insertion of a new item has to move all items after it (or before), which takes time proportional to the array size. 在直接使用数组的情况下,插入新项必须将所有项移到其后(或之前),这花费的时间与数组大小成正比。 However, note that (1) the time to find the insertion point is generally forced to linear in list size, while it can be logarithmic for the array, and (2) by use of the technique called “insertion gap” or “cursor gap” insertion of an item can be constant time also for an array (at some slight costs). 但是,请注意,(1)找到插入点的时间通常被强制为列表大小的线性,而数组的长度可以是对数的;(2)使用称为“插入间隙”或“光标间隙”的技术对于数组,插入项目也可以是恒定时间(需要一些费用)。

In order to insert a number in ascending sorted position, with the basic list you need a pointer to the node before the insertion point, so that that node's next can be updated to point to the new node. 为了以升序排序插入数字,在基本列表中,您需要在插入点之前指向该节点的指针,以便可以将该节点的next更新为指向新节点。

Since with the basic list there is no such node before the first node, insertion at the start of the list becomes an ugly special case, as illustrated below: 由于基本列表在第一个节点之前没有这样的节点,因此在列表的开头插入将是一个非常特殊的情况,如下所示:

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

Output: 输出:

0 101 102 108 109 110 111 111 111 114 119

Note that due to the search for an insertion place, each insertion on average takes time proportional to the final length of the list, so that in total this is quadratic time, O(n 2 ). 请注意,由于要搜索插入位置,因此平均每次插入所花费的时间与列表的最终长度成正比,因此总的来说,这是二次时间O(n 2 )。 It's a simple insertion sort. 这是一种简单的插入排序。 I hope I did not introduce any bug! 希望我没有引入任何错误! :-) :-)


The ingenious pointer-to-pointer. 巧妙的指针对指针。

One way to reduce the mess for insertion at the start of a basic list, is to note that you really only need access to the next field of the node before the insertion point. 减少基本列表开头的插入混乱的一种方法是,您实际上只需要访问插入点之前的节点的next字段。

You do not need access to the complete node before the insertion point. 您不需要访问插入点之前的完整节点。

And so you can make do with just a pointer to the next pointer . 这样您就可以只用一个指针指向next指针 Ie a pointer to pointer . 指向指针的指针 And for the start of the list, the first pointer variable can serve nicely as if it was the next field in some node before that, so initially the pointer-to-pointer can just point to the first pointer (ahem), and then it can be moved on the next pointers, like this: 对于列表的开头,第first指针变量可以很好地发挥作用,就好像它是该节点之前的某个节点中的next字段一样,因此最初指向指针的指针可以仅指向第first指针(ahem),然后可以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_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;
    }
}

The output is still 输出仍然是

0 101 102 108 109 110 111 111 111 114 119

as it should be. 应该是。

At first I wrote that code with a rather long continuation condition directly in the while loop head. 首先,我直接在while循环头中编写了具有相当长的连续条件的代码。 But following the rule that one should name just about everything in sight, I moved part of that inside the loop, then with a break statement. 但是遵循一个规则,即应该命名几乎所有可见的事物,我将其中的一部分移入了循环,然后使用break语句。 So that's the reason for the break statement: to support the name next_node . 这就是break语句的原因:支持名称next_node

In C++ the pointer-to-pointer is sometimes represented as a reference-to-pointer. 在C ++中,指针指向有时表示为引用指向。 For example, a function to unlink a node from a basic list, might take a reference-to-pointer argument, instead of the C style pointer-to-pointer. 例如,从基本列表取消链接节点的函数可能会使用引用指向指针参数,而不是C样式的指针指向指针。 I find that a bit cleaner, when it is applicable. 我发现,在适用的情况下,它稍微干净一些。


Using a header node. 使用头节点。

An alternative to the pointer-to-pointer is to introduce a dummy node before the first real node. 指针对指针的替代方法是在第一个实节点之前引入虚拟节点。 Then even an empty list has one physical node, namely the dummy node. 这样,即使一个空列表也具有一个物理节点,即虚拟节点。 The dummy node is usually called a header node . 虚拟节点通常称为标头节点

After the compiler has finished with its translation to machine code and optimization, code that uses a header node may be identical to code that uses the pointer-to-pointer technique, except that the header node involves one more allocation, and takes a little more room than a single pointer variable. 编译器完成对机器代码的转换和优化后,使用头节点的代码可能与使用指针对指针技术的代码相同,除了头节点涉及更多的分配,并且需要更多的代码外房间比单个指针变量大。 But the difference is about how one thinks about the code, how one understands it. 但是不同之处在于,人们如何看待代码,如何理解代码。 Conceptually having a header node is very different from using the pointer-to-pointer technique – eg if you draw the list structure, then the list with header node looks manifestly different from the one without such a node. 从概念上讲,拥有标头节点与使用指针到指针技术有很大不同–例如,如果绘制列表结构,则带有标头节点的列表看起来与没有该节点的列表明显不同。

Anyway, code: 无论如何,代码:

#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* header_node = new node_t();

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

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

        // Insert at start of list, just like in the first program.
        node->next = p_before->next;    // After this `node` points to the new first node.
        p_before->next = node;          // Now ... also points to the new first node.
    }

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

    // Delete the list
    auto& first = header_node;          // Just a renaming for clarity.
    while( first != 0 )
    {
        node_t* const doomed = first;

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

And as before the output is still 和以前一样,输出仍然

0 101 102 108 109 110 111 111 111 114 119

as it should be. 应该是。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM