我是所有这些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。我正在尝试编写执行此操作的方法,但我无法想出任何结果。另外,这是我应该收到的清理工作的输出。
到目前为止,我的代码只是:inline void LList:: add_aa(const string _str)
{
cout << "\tp = " << std::hex << p << std::dec << '\n';
}
答案 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
应该如此。