如何制作一个连续的链表?
How to make a contiguous linked list?
我有一个循环链表的自定义数据结构。它的用途非常小众,所以想知道有没有办法让每个节点都指向连续内存块中的一个地址,实现随机访问。该列表在初始填充后根本不会被修改 (inserted/deleted),因此一个连续的块不会阻碍任何我不认为的东西。
我怎样才能做到这一点?
此外,我不使用向量的原因是因为我正在基于指针重排对列表执行旋转。但是,我正在通过 O(n) 重新排列指针,而我相信如果我使用连续内存 space.
,我可以通过指针算法通过 O(1) 重新排列指针
为更清晰起见进行编辑:
我的“列表”是这样的
// barebones circular list structure
class List
{
private:
struct Node
{
Node* next;
int data;
};
// beginning of list
Node* start;
// end of list which POINTS to beginning as well
Node* end;
// number of list elements
int size;
public:
List()
{
start = NULL;
end = NULL;
size = 0;
}
~List();
// populates the list using size parameter and uses stdin for the values
void populate_list(const int &size);
// moves the starting pointer of a list, effectively rotating it
void move_start(int n, char d);
// print contents of list
void print();
};
正如我所说,列表的所有元素(节点)都是通过填充列表填充的。 population 的约定是从 1 开始按顺序排列的。因此,给定的大小为 5,列表填充为 {1, 2, 3, 4, 5}。然后,给定一个旋转列表的数量,我通过 move_start()
旋转
// moves starting node by n spaces in d direction
void List::move_start(int n, char d)
{
// left rotation procedure
if (d == 'L')
{
// move the start pointer the given rotation amount
for (int i = 0; i < n; i++)
start = start->next;
}
// right rotation procedure
else if (d == 'R')
{
// move the start pointer by the size of the list minux the given rotation amount
for (int i = 0; i < size-n; i++)
start = start->next;
}
return;
}
也许我的理解是错误的,但我相信我在 O(n) 处“旋转”,因为我在 start
指针的任何位置移动 1 到 size-1
次(。但是如果我如果要有一个连续的 Node
块,那么我可以简单地将 start
分配到一个新位置,而不是将其移动到那里 (O(1))。希望一切都解决了。
如您问题的评论中所述,有许多更好的方法可以满足您的要求。
但是,如果您的要求非常严格,您必须使用列表,您可以使用 placement new 来控制连续内存。但在使用之前了解使用 placement new 的所有复杂性。
下面是一个如何实现该目标的示例。
// buffer with required size
unsigned char buf[sizeof(int) * 4];
// placement new in buffer
int* pInt1 = new (buf) int(2);
int* pInt2 = new (buf + sizeof(int) * 1) int(4);
int* pInt3 = new (buf + sizeof(int) * 2) int(6);
int* pInt4 = new (buf + sizeof(int) * 3) int(8);
//Test
std::cout << pInt1 << " " << pInt2 << " " << pInt3 << " " << pInt4 << std::endl;
std::cout << *pInt1 << " " << *pInt2 << " " << *pInt3 << " " << *pInt4 << std::endl;
//Push into the list
std::list<int*> tList;
tList.push_back(pInt1);
tList.push_back(pInt2);
tList.push_back(pInt3);
tList.push_back(pInt4);
//Test
for (auto it = tList.begin(); it != tList.end(); ++it)
{
std::cout << *it << std::endl;
std::cout << **it << std::endl;
}
How can I achieve this?
您可以使用带有自定义分配器的标准列表容器。
However, I'm rearranging the pointers by O(n), whereas I believe I can rearrange the pointers by O(1) via pointer arithmetic if I use a contiguous memory space.
所有列表都可以使用 splice
操作在恒定时间内进行旋转(只要您不需要搜索相关的迭代器)。连续内存 space 不会提高此类操作的渐近复杂性。
根据您的描述,似乎简单地将 std::vector
与自定义迭代器一起使用到“第一个”元素是一种不错的数据结构选择。我没有看到使用链表的好处。向量和迭代器的结合本质上产生了一个循环缓冲区。
我有一个循环链表的自定义数据结构。它的用途非常小众,所以想知道有没有办法让每个节点都指向连续内存块中的一个地址,实现随机访问。该列表在初始填充后根本不会被修改 (inserted/deleted),因此一个连续的块不会阻碍任何我不认为的东西。
我怎样才能做到这一点?
此外,我不使用向量的原因是因为我正在基于指针重排对列表执行旋转。但是,我正在通过 O(n) 重新排列指针,而我相信如果我使用连续内存 space.
,我可以通过指针算法通过 O(1) 重新排列指针为更清晰起见进行编辑: 我的“列表”是这样的
// barebones circular list structure
class List
{
private:
struct Node
{
Node* next;
int data;
};
// beginning of list
Node* start;
// end of list which POINTS to beginning as well
Node* end;
// number of list elements
int size;
public:
List()
{
start = NULL;
end = NULL;
size = 0;
}
~List();
// populates the list using size parameter and uses stdin for the values
void populate_list(const int &size);
// moves the starting pointer of a list, effectively rotating it
void move_start(int n, char d);
// print contents of list
void print();
};
正如我所说,列表的所有元素(节点)都是通过填充列表填充的。 population 的约定是从 1 开始按顺序排列的。因此,给定的大小为 5,列表填充为 {1, 2, 3, 4, 5}。然后,给定一个旋转列表的数量,我通过 move_start()
// moves starting node by n spaces in d direction
void List::move_start(int n, char d)
{
// left rotation procedure
if (d == 'L')
{
// move the start pointer the given rotation amount
for (int i = 0; i < n; i++)
start = start->next;
}
// right rotation procedure
else if (d == 'R')
{
// move the start pointer by the size of the list minux the given rotation amount
for (int i = 0; i < size-n; i++)
start = start->next;
}
return;
}
也许我的理解是错误的,但我相信我在 O(n) 处“旋转”,因为我在 start
指针的任何位置移动 1 到 size-1
次(。但是如果我如果要有一个连续的 Node
块,那么我可以简单地将 start
分配到一个新位置,而不是将其移动到那里 (O(1))。希望一切都解决了。
如您问题的评论中所述,有许多更好的方法可以满足您的要求。
但是,如果您的要求非常严格,您必须使用列表,您可以使用 placement new 来控制连续内存。但在使用之前了解使用 placement new 的所有复杂性。
下面是一个如何实现该目标的示例。
// buffer with required size
unsigned char buf[sizeof(int) * 4];
// placement new in buffer
int* pInt1 = new (buf) int(2);
int* pInt2 = new (buf + sizeof(int) * 1) int(4);
int* pInt3 = new (buf + sizeof(int) * 2) int(6);
int* pInt4 = new (buf + sizeof(int) * 3) int(8);
//Test
std::cout << pInt1 << " " << pInt2 << " " << pInt3 << " " << pInt4 << std::endl;
std::cout << *pInt1 << " " << *pInt2 << " " << *pInt3 << " " << *pInt4 << std::endl;
//Push into the list
std::list<int*> tList;
tList.push_back(pInt1);
tList.push_back(pInt2);
tList.push_back(pInt3);
tList.push_back(pInt4);
//Test
for (auto it = tList.begin(); it != tList.end(); ++it)
{
std::cout << *it << std::endl;
std::cout << **it << std::endl;
}
How can I achieve this?
您可以使用带有自定义分配器的标准列表容器。
However, I'm rearranging the pointers by O(n), whereas I believe I can rearrange the pointers by O(1) via pointer arithmetic if I use a contiguous memory space.
所有列表都可以使用 splice
操作在恒定时间内进行旋转(只要您不需要搜索相关的迭代器)。连续内存 space 不会提高此类操作的渐近复杂性。
根据您的描述,似乎简单地将 std::vector
与自定义迭代器一起使用到“第一个”元素是一种不错的数据结构选择。我没有看到使用链表的好处。向量和迭代器的结合本质上产生了一个循环缓冲区。