有效地连接左侧的N个字符数组,C
假设我有N个要连接在一起的字符数组。每个字符数组存储在一个单独的结构中。为了访问stuct1中的char数组,我需要访问struct2,为了访问struct2,我需要访问struct3,等等(想象一个头在structN,尾在struct1的单链表) 我想连接每个结构中的每个字符数组,以便struct1中的字符数组首先出现,structN中的字符数组最后出现 例如,假设与struct1、struct2和struct3关联的字符数组的内容为“A”、“B”、“C”。我想得到结果字符数组“ABC”。但是,如上所述,要访问structX,我必须首先访问structX+1。因此,将左侧的这些字符数组连接起来会更加有效;我就不必一直检查所有的结构了 有没有一种方法可以在C中高效地实现这一点(例如strcat、snprintf等),或者我必须手动操作每个字符数组以获得我想要的(或者遍历列表,保存指向结构的指针,然后返回) 编辑(清晰度)有效地连接左侧的N个字符数组,C,c,string,concatenation,C,String,Concatenation,假设我有N个要连接在一起的字符数组。每个字符数组存储在一个单独的结构中。为了访问stuct1中的char数组,我需要访问struct2,为了访问struct2,我需要访问struct3,等等(想象一个头在structN,尾在struct1的单链表) 我想连接每个结构中的每个字符数组,以便struct1中的字符数组首先出现,structN中的字符数组最后出现 例如,假设与struct1、struct2和struct3关联的字符数组的内容为“A”、“B”、“C”。我想得到结果字符数组“ABC”。但是
假设我有一个单链表。每个元素都有一个字符数组。我想以相反的顺序连接字符数组。有没有一种方法可以做到这一点,而不必两次浏览列表?我知道运行时所有字符数组的最大大小,但我不知道它们各自的大小,直到我访问列表中的每个元素(当我访问元素X时,我知道存储在X中的字符数组的大小)以下是反向打印链接列表的代码,您应该能够针对concat问题修改它,通过传入char缓冲区参数
void ReversePrint(Node *head)
{
if (head == NULL)
return;
else if (head->next == NULL) {
printf("%d\n", head->data);
} else {
ReversePrint(head->next);
printf("%d\n", head->data);
}
}
诀窍是在执行实际工作之前进行递归调用,这样就可以开始处理最后一个节点,然后在展开时处理所有其他节点。然后,您可以避免左手连接问题,只需按照您通常希望的方式在末尾连接即可。我制作了一个简单的测试台,以检查哪种方法更有效:
- 在每次迭代中使用
realloc
- 在列表上迭代两次,第一次获取大小,第二次复制字符串
struct elem
{
char *str;
size_t size;
};
这是双链接列表的一个元素(我使用了这个列表的实现)
然后我以这种方式生成了一些字符串:
for(int j = 0; j < i; j ++)
{
char * str = malloc(SINGLE_STRING_LEN + 1);
memset(str, gimme_next_char(), SINGLE_STRING_LEN);
str[SINGLE_STRING_LEN] = '\0';
struct elem e;
e.str = str;
e.size = SINGLE_STRING_LEN;
dl_list_insert_at_tail(&l, (void *) &e);
}
(我假设sizeof(char)==1
)。正如您在每次迭代中看到的那样,
realloc
用于调整字符串大小。然后使用strcpy
将列表中的字符串追加到结果字符串中。基本上你得到了O(N*(M+realloc复杂度))
其中M
是strcpy
函数复杂度
第二种方法:
size_t concat_str_len = 1;
for_each_in_dl_list(struct elem, e, l)
concat_str_len += e->size;
char *concat_str = malloc(concat_str_len);
concat_str_len = 1;
for_each_in_dl_list(struct elem, e, l)
{
size_t new_len = concat_str_len + e->size;
strcpy(&concat_str[concat_str_len - 1], e->str);
concat_str_len = new_len;
}
在这里,我们首先获得整个最终字符串的大小,然后遍历列表以追加列表中的每个字符串。您将获得O(N+N*M+malloc复杂性)
必须释放动态分配的数据,但我不想将此代码粘贴到这里,因为它在本主题中毫无用处
我调用了第一个程序和第二个程序约20次,以计算平均执行时间:
Elems on the list | 105 | 305 | 505 | 705 | 905
First approach [us] | 9 | 25 | 42 | 54 | 67
Second approach [us] | 6 | 14 | 25 | 31 | 39
第二种方法相当快。我还可以看到,第一种方法的标准偏差要大得多(列表中905个元素的标准偏差要大6倍左右)。这可能是多次调用<代码> ReLoCu函数的原因,因为第一种方法更依赖于系统。 < P>如果你不介意做一些指针丑陋,我会考虑下面的代码。
struct Ptr {
char *alloc_;
char *start_;
};
struct Ptr concat(Node *head) {
Ptr ptr;
ptr.alloc_ = malloc(maxSizeOfCharArray);
ptr.start_ = ptr.alloc_ + maxSizeOfCharArray - 2;
*(ptr.start_ + 1) = 0;
while (head != NULL){
ptr.start_ -= strlen(head->str)
memcpy(ptr.start_, head->str, strlen(head->str));
head = head->next;
}
return ptr;
}
在所有这些之后,您负责释放所有内存。不知道它是否会比幼稚的解决方案更快
N
的最大值是多少?为什么不编写自己的函数将一个字符串前置到另一个字符串呢?确实有可能,但您必须确保复制到的缓冲区足够大,以适合这些对象的内容,或者在每次迭代时使用realloc
,这可能会很慢。用O(2N)(当你只分配一次内存时)和O(N)方法做一些基准测试,然后你会看到什么是更好的选择。在问题中展示你的结构。解释您尝试了什么。但是您没有说明或显示其他字段之一是否是char数组中字符串的长度(或数组中非字符串-无空终止符-数据的长度)。然而,就我的钱来说,你需要在列表上做这两件事,但与你可能需要做的复制相比,这是一个微不足道的成本。如果不在结构中记录数组的长度,则会给操作增加很多成本。当你创造它们时,你一定知道它们有多长;不要丢弃有价值的信息。不要忘了可以realloc()
收缩数组。这需要在链表的元素数量上呈线性的空间。这通常是不可接受的。中的空间,用于调用堆栈或存储缓冲区?调用堆栈上的空间。
struct Ptr {
char *alloc_;
char *start_;
};
struct Ptr concat(Node *head) {
Ptr ptr;
ptr.alloc_ = malloc(maxSizeOfCharArray);
ptr.start_ = ptr.alloc_ + maxSizeOfCharArray - 2;
*(ptr.start_ + 1) = 0;
while (head != NULL){
ptr.start_ -= strlen(head->str)
memcpy(ptr.start_, head->str, strlen(head->str));
head = head->next;
}
return ptr;
}