我有一个字符串数据列表。 10,20,30是行号
10. string 1
20. string 2
30. string 3
如果用户输入" 23字符串数据"。 23是用户想要插入的行号。数据应该变成那样
10. string 1
20. string 2
23. string data
30. string 3
如果用户输入" 40字符串数据"。数据应该变成那样
10. string 1
20. string 2
23. string data
30. string 3
40. string data
我在C的数据结构中相对较新。 我应该使用哪种数据结构来高效存储此类数据?我目前的方向是实现动态数组或链表。但是,下面列出了我遇到的问题。
动态数组问题:
链表问题:
答案 0 :(得分:4)
让我们假设您的要求如下:
没有强大的实时性。(即,它不适用于高频交易或控制机器。)
它运行在相对现代的PC (RAM以GB为单位,CPU频率以GHz为单位)。特别是它不能在嵌入式系统上运行。
数据不超过几万行。
然后你几乎可以使用你喜欢的任何数据结构;它在内存或运行时行为方面都不重要。
例如,为了在链表中找到插入点,只需迭代该列表即可。在完成眨眼之前,PC足够快,可以迭代数万次。
或者只是分配一个包含100,000行的数组,每行包含80个字符。没问题。或者一百万行。仍然没问题。或者1000万行,仍然没有问题。你明白我的观点了吗? (在一个数组中,你需要一个标记来标记未使用的行。我会使用struct line { bool used; char text[80]; }
等。你也可以通过只有一个{{1来满足任意长行 - 并节省内存成员和动态分配,或将文本定义为块的链接列表。)
因此,选择归结为您最容易使用的内容。可能是数组。
答案 1 :(得分:3)
我会给出我能提出的两个解决方案,但这个问题可能是开放式的。
使用哈希表。键是行号。值为(string, pointer to next line's value)
。这使得随机和线性访问都很快。 修改:插入仍为O(n)
。它仅对访问时间有帮助,访问时间为O(1)
。第二种解决方案有O(1)
插入。
假设您没有非常间隔的行号:使用单链表L
来存储字符串。还要创建一个单独的数组P
,其中包含指向列表中每个k
个节点的指针。要访问第i
行,请检查P[floor(i/k)]
,跳转到L
指向的节点,然后向前跳i mod k
次以到达您的字符串。因此访问时间为O(k)
。插入时间为O(1)
。 n
字符串的空间使用情况为O(n + max{i}/k)
。
使这与C相关的一件事是,当然没有内置的哈希表!所以#2可能更容易实现。
答案 2 :(得分:2)
我知道你正在寻找一种专门的数据结构,但是如何使用简单的数据结构,但是 lazily 呢?您可以将新行附加到动态数组,然后在需要打印时对数组(使用qsort
)进行排序。
我认为这会更好,因为打印所有行可能比添加/插入行更少 。因此,您应该使添加行便宜(在这种情况下,O(1)摊销),并且打印可能更昂贵(在这种情况下,O( n log n ) )。这也使您的数据结构简单,并让C标准库处理复杂的部分。
你可以通过保留一个跟踪是否已知所有数据已被排序的标志来使这更好一点;那种方式反复打印(或者,假设你正在尝试编写一个BASIC解释器,反复运行)也会很便宜。如果你希望行按 按顺序输入,那么这样的标志也可能会有所帮助;然后添加每一行:
alreadySorted = alreadySorted && (new_line_number > last_line_number)
我会注意到,如果添加了重用现有行号的行,则未指定会发生什么。如果您希望替换旧行,则可以使用稳定排序调整此方法,然后迭代这些行以删除具有重复数字的行,仅保留最后一行。
(如果你想让qsort
在这种情况下保持稳定,而不是只为每一行存储一个字符串,你可以用它存储一些额外的元数据(任何单调增加的计数器都可以,例如当前时间) ,或者只是添加行时的总行数。)然后,您提供给qsort
的比较函数只需要使用该额外数据来解决重复行号的关系。)
这种方法的一个缺点是删除行不会很快或不会立即回收内存。但是,您尚未指定是否要求删除行;即使它是,它可能是一种罕见的操作(因此可能会更加节省时间或者更加节省空间效率)。
答案 3 :(得分:1)
此任务的最佳解决方案是使用字典数据类型。 当然,根据键的性质(行数),您可以通过适当的哈希表执行优化。
当然,c库没有字典的实现。但你可以创建自己的,基于红黑树。 Cormen很容易解释这种数据结构https://www.amazon.com/Introduction-Algorithms-3rd-MIT-Press/dp/0262033844
注意:如果您的收藏品尺寸较小或很少修改结构,那么您只需使用链接列表。
答案 4 :(得分:1)
我的建议是在需要时使用链表和插入排序,
以下是最初取自geeksforgeeks.org,
的代码我还没有测试过代码,这只是从网站上修改的代码。
/* C program for insertion sort on a linked list */
#include<stdio.h>
#include<stdlib.h>
/* Link list node */
struct node
{
int lineNumber;
char *str;
struct node* next;
}node;
// Function to insert a given node in a sorted linked list
void sortedInsert(struct node**, struct node*);
// function to sort a singly linked list using insertion sort
void insertionSort(struct node **head_ref)
{
// Initialize sorted linked list
struct node *sorted = NULL;
// Traverse the given linked list and insert every
// node to sorted
struct node *current = *head_ref;
while (current != NULL)
{
// Store next for next iteration
struct node *next = current->next;
// insert current in sorted linked list
sortedInsert(&sorted, current);
// Update current
current = next;
}
// Update head_ref to point to sorted linked list
*head_ref = sorted;
}
/* function to insert a new_node in a list. Note that this
function expects a pointer to head_ref as this can modify the
head of the input linked list (similar to push())*/
void sortedInsert(struct node** head_ref, struct node* new_node)
{
struct node* current;
/* Special case for the head end */
if (*head_ref == NULL || (*head_ref)->lineNumber >= new_node->lineNumber)
{
new_node->next = *head_ref;
*head_ref = new_node;
}
else
{
/* Locate the node before the point of insertion */
current = *head_ref;
while (current->next!=NULL &&
current->next->lineNumber < new_node->lineNumber)
{
current = current->next;
}
new_node->next = current->next;
current->next = new_node;
}
}
/* BELOW FUNCTIONS ARE JUST UTILITY TO TEST sortedInsert */
/* Function to print linked list */
void printList(struct node *head)
{
struct node *temp = head;
while(temp != NULL)
{
printf("%d %s \n", temp->lineNumber,temp->str);
temp = temp->next;
}
}
/* A utility function to insert a node at the beginning of linked list */
void push(struct node** head_ref, int new_data, char *line)
{
/* allocate node */
struct node* new_node = (struct node *)malloc(sizeof(struct node));
int len = strlen(line)+1;
/* put in the data */
new_node->lineNumber = new_data;
new_node->str = malloc(len);
strcpy(new_node->str,line);
new_node->str[len] = '\0';
/* link the old list off the new node */
new_node->next = (*head_ref);
/* move the head to point to the new node */
(*head_ref) = new_node;
}
// Driver program to test above functions
int main(int argc,char *argv[])
{
struct node *a = NULL;
push(&a, 5 , "TestLine");
push(&a, 1 , "SecondTest");
push(&a, 1 , "SecondTest");
push(&a, 3 , "SecondTest");
insertionSort(&a);
printf("\nLinked List after sorting \n");
printList(a);
return 0;
}
/* C program for insertion sort on a linked list */
#include<stdio.h>
#include<stdlib.h>
/* Link list node */
struct node
{
int lineNumber;
char *str;
struct node* next;
}node;
// Function to insert a given node in a sorted linked list
void sortedInsert(struct node**, struct node*);
// function to sort a singly linked list using insertion sort
void insertionSort(struct node **head_ref)
{
// Initialize sorted linked list
struct node *sorted = NULL;
// Traverse the given linked list and insert every
// node to sorted
struct node *current = *head_ref;
while (current != NULL)
{
// Store next for next iteration
struct node *next = current->next;
// insert current in sorted linked list
sortedInsert(&sorted, current);
// Update current
current = next;
}
// Update head_ref to point to sorted linked list
*head_ref = sorted;
}
/* function to insert a new_node in a list. Note that this
function expects a pointer to head_ref as this can modify the
head of the input linked list (similar to push())*/
void sortedInsert(struct node** head_ref, struct node* new_node)
{
struct node* current;
/* Special case for the head end */
if (*head_ref == NULL || (*head_ref)->lineNumber >= new_node->lineNumber)
{
new_node->next = *head_ref;
*head_ref = new_node;
}
else
{
/* Locate the node before the point of insertion */
current = *head_ref;
while (current->next!=NULL &&
current->next->lineNumber < new_node->lineNumber)
{
current = current->next;
}
new_node->next = current->next;
current->next = new_node;
}
}
/* BELOW FUNCTIONS ARE JUST UTILITY TO TEST sortedInsert */
/* Function to print linked list */
void printList(struct node *head)
{
struct node *temp = head;
while(temp != NULL)
{
printf("%d %s \n", temp->lineNumber,temp->str);
temp = temp->next;
}
}
/* A utility function to insert a node at the beginning of linked list */
void push(struct node** head_ref, int new_data, char *line)
{
/* allocate node */
struct node* new_node = (struct node *)malloc(sizeof(struct node));
int len = strlen(line)+1;
/* put in the data */
new_node->lineNumber = new_data;
new_node->str = malloc(len);
strcpy(new_node->str,line);
new_node->str[len] = '\0';
/* link the old list off the new node */
new_node->next = (*head_ref);
/* move the head to point to the new node */
(*head_ref) = new_node;
}
// Driver program to test above functions
int main(int argc,char *argv[])
{
struct node *a = NULL;
push(&a, 5 , "TestLine");
push(&a, 1 , "SecondTest");
push(&a, 1 , "SecondTest");
push(&a, 3 , "SecondTest");
insertionSort(&a);
printf("\nLinked List after sorting \n");
printList(a);
return 0;
}
答案 5 :(得分:0)
我建议你使用链表。
// Define your list like this
typedef struct node {
int line; // To hold the line number
char * data;
struct node * next;
} node_t;
// To insert
node_t* insert(node_t *head, const char * data, int line) // n is line from beginning
{
// Node to be inserted in given line
node_t *newNode;
// Allocating Memory
newNode = malloc(sizeof(node_t));
// Filling the Data to New Node
newNode->data = malloc(strlen(data)+1); // Allocate memory to store data
strcpy(newNode->data, data);
newNode->line = line;
newNode->next = NULL;
// It might be our First Node in Linked List
if(head == NULL) {
//Address of New Node Becomes our head
return (head = newNode);
}
// Node Might be inserted At Head
else if(line == 0) {
// Joining previous Linked List After new Node
newNode->next = head;
// Address of New Node Becomes our head
return (head = newNode);
}
// Inserting At the line next to line
else {
// Pointer to store intermediate address of node
// To be used in Traversing
node_t * current = head;
// Go through to insert at Nth line
while(current != NULL) {
node_t * next = current->next; //The next Node
if((line >= current->line && line < next->line) || (line >= current->line && NULL == next->line)) { // Test if we are at some point between current line and next line or if there is no next
// If we are, point newNode to the next node of current
newNode->next = current->next;
// Now point current towards our New Node
current->next = newNode;
// Return Head as soon as we have inserted our new node
return head;
}
current = next; // Point current to the next node to continue
}
}
}
如果保证行号始终更大,您还可以存储指向每个节点中行号最大的节点的指针。这将增加空间,但在n(0)时间内达到结果。