数据结构:可变数组->链表

本文深入探讨了从变长数组到链表的数据组织方式转变,阐述了这种变化如何逐步降低存储数据的空间复杂度和时间复杂度。文章详细介绍了链表的概念、特性、不同类型以及与变长数组的对比,旨在帮助读者理解数据结构选择的重要性及其对程序性能的影响。

变长数组(variable-length array), 简称 VLA
C语言中,直到C99标准出现之前,声明数组时在方括号内只能使用整数常量表达式。
而C99做了很大改进,允许数组的[ ]中的值是整形变量或是整形表达式。这就解释了下面的情况:
int n;
scanf ("%d", &n);
int array[n];
虽然n确实是需要运行时动态确定的变量,但是在C99中,以这种变量作为数组大小的形式已经是允许的了。
这样的数组就被称之为“变长数组”。
注意:变长数组是指用整型变量或表达式声明或定义的数组,而不是说数组的长度会随时变化,变长数组在其生存期内的长度同样是固定的。

可变数组(Resizeable Array)

数组的大小可“长大”,具体实现是在前期指定数组的大小,并在后期重新定义数组大小。使数组能在原大小的基础上“长大”,原来空间定义的数据不改变地拷贝到新定义的空间,并释放原空间,多出的未利用的空间即为新增空间。可变数组主要用于事先不明确所需数组的大小,需要在程序的中后段手工扩充的具体问题中。

为实现可变的功能必须满足以下条件:

1.可变大小

2.可得到数组的大小

3.可访问数组其中的单元

Interface(接口,封装一定功能的集合)

Array array_creat(int init_size);

void array_free(Array *a);

int array_size(const Array *a);

int *array_at(Array *a,int index);

void array_inflate(Array *a,int more_size);

需要建一个项目,包括array.h和array.c文件,具体步骤省略。

可变数组的缺陷:数组每次长大都需要申请新的内存空间,并需要拷贝原数组数据并释放它。

                                                1.拷贝的时间呈线性增加

                                                2.在内存受限的情况下(如单片机的内存空间),明明有足够的内存空间却不能再申请新的空间了。

//在一个大小为2n+Block_size的内存空间中,当前的数组空间大小是n,n-Block_size是之前数组现已被释放的空间,2Block_size是未利用的内存空间。

//对于下一个数组的创建所需空间是n+Block_size,n-Block_size和2Block_size都不够大,虽然他们相加正好是n+Block_size,但因为不连续所以不能创建。

对于第一个缺陷可以用memcpy函数来解决,

函数名: memcpy
功  能: 从源source中拷贝n个字节到目标destin中
用  法: void *memcpy(void *destin, void *source, unsigned n);
程序例:

#include <stdio.h>
#include <string.h>
int main(void)
{
   char src[] = "*****";
   char dest[] = "0123456709";
   char *ptr;
   printf("destination before memcpy: %s\n", dest);
   ptr = memcpy(dest, src, strlen(src)); //这里用一个变量ptr来测试memcpy是否正常复制成功
   if (ptr)
      printf("destination after memcpy:  %s\n", dest);
   else
      printf("memcpy failed\n");
   return 0;
}

链表(Linked list)

链表是一种物理存储单元上非连续、非顺序的存储结构
(相对于数组和可变数组而言,数组的存储方式是在开创的一片连续空间中存入数据,所以其物理存储单元是连续且顺序的)
 
数据元素的逻辑顺序是通过链表中的 指针 链接次序实现的。链表由一系列结点(链表中每一个元素)组成,结点可以在运行时动态生成。
每个结点包括两个部分:一 .存储数据元素 的数据域,
                                            二 .是存储下一个结点地址的 指针 域。
由于不必须按顺序存储,链表在插入的时候可以达到O(1)的复杂度,比线性表顺序表快得多,但是查找一个节点或者访问特定编号的节点则需要O(n)的时间,而线性表和顺序表相应的时间复杂度分别是O(logn)和O(1)。

链表最明显的好处就是,常规数组排列关联项目的方式可能不同于这些数据项目在记忆体磁盘上顺序,数据的存取往往要在不同的排列顺序中转换。链表允许插入和移除表上任意位置上的节点,但是不允许随机存取

链表有很多种不同的类型:单向链表双向链表以及循环链表

链表的提出主要在于顺序存储中的插入和删除的时间复杂度是线性时间的,而链表的操作则可以是常数时间的复杂度。

 

OTHERS:

1、链接存储方法
链接方式存储的线性表简称为链表(Linked List)。
链表的具体存储表示为:
① 用一组任意的存储单元来存放线性表的结点(这组存储单元既可以是连续的,也可以是不连续的)
② 链表中结点的逻辑次序和物理次序不一定相同。为了能正确表示结点间的逻辑关系,在存储每个结点值的同时,还必须存储指示其后继结点的地址(或位置)信息(称为指针(pointer)或链(link))
注意:链式存储是最常用的存储方式之一,它不仅可用来表示线性表,而且可用来表示各种非线性的数据结构。

2、链表的结点结构
┌——┬——┐
 | data | next  |
└——┴——┘
数据域---存放数据元素
指针域---存放下一个结点地址
注意:
①链表通过每个结点的链域将线性表的n个结点按其逻辑顺序链接在一起的。
②每个结点只有一个链域的链表称为单链表(Single Linked List)。

3、头指针head和终端结点指针域的表示
单链表中每个结点的存储地址是存放在其前趋结点next域中,而开始结点无前趋,故应设头指针head指向开始结点。
注意:链表由头指针唯一确定,单链表可以用头指针的名字来命名。
终端结点无后继,故终端结点的指针域为空,即NULL。

4、单链表的一般图示法
由于我们常常只注重结点间的逻辑顺序,不关心每个结点的实际位置,可以用箭头来表示链域中的指针

 

线性表

线性表是一种基本的顺序存储数据结构,线性表都是以队列字符串等特殊线性表的形式来使用的

在实现线性表数据元素的存储方面,一般可用顺序存储结构和链式存储结构两种方法。链式存储结构包含在线性链表中,另外栈、队列和串也是线性表的特殊情况,又称为受限的线性结构。

线性表中所有的元素所占的存储空间是连续的,线性表中常用的有链表和顺序表,顺序表所占的存储空间必须连续,链表没有这个要求,连续指的是存储空间的连续


从变长数组到链表,数据组织方式即数据结构的改变,逐步降低了存储数据的空间复杂度和时间复杂度。
C语言代码转换成为Java语言代码 #include<stdio.h> #include <string.h> #include <stdlib.h> #include <iostream> //非终结符 typedef struct noterminal { char ch; char* FirstVt; //对应firstvt集 char* Lastvt; //对应lastvt集 char* guize[10]; //对应产生式 int size = 0; //产生式个数 struct noterminal* next; }noterminal; //终结符 typedef struct terminal { int number; char ch; char* patable; //优先关系表 struct terminal* next; }terminal; //头指针 noterminal* h_noter; terminal* h_ter; noterminal* check_noterminal(char ch, noterminal* h_noter); //检查非终结符是否已拥有 terminal* check_terminal(char ch, terminal* h_ter); //检查终结符是否已拥有 bool in_char(char* string, char ch); //检查字符串string中是否有字符ch int combin(char* ch, char* bech); //连接字符串ch和bech于ch void read_rule(char* rulefile, noterminal* h_noter, terminal* h_ter); //产生式的读取,非终结符必须为大写字母,$表示空 void print_rule(noterminal* h_noter, terminal* h_ter); //产生式输出 void free_list(noterminal* h_noter, terminal* h_ter); //释放链表空间 void make_firstvt(noterminal* h_noter, terminal* h_ter); //计算FirstVt集 void make_lastvt(noterminal* h_noter, terminal* h_ter); //计算lastvt集 void make_patable(noterminal* h_noter, terminal* h_ter); //构造优先关系表 void error_treat(); //错误处理 int sf_analyse(noterminal* h_noter, terminal* h_ter, char* st); //算符优先分析 char guiyue(char* st, noterminal* h_noter); //归约处理 int main() { char rulefile[100] = "D:\\规则4.txt"; //初始化 h_noter = (noterminal*)malloc(sizeof(noterminal)); h_noter->next = NULL; h_noter->FirstVt = NULL; h_noter->Lastvt = NULL; h_noter->size = 0; h_ter = (terminal*)malloc(sizeof(terminal)); h_ter->next = NULL; //读取文法 read_rule(rulefile, h_noter, h_ter); //输出文法 print_rule(h_noter, h_ter); //求firstvt集 make_firstvt(h_noter, h_ter); //求lastvt集 make_lastvt(h_noter, h_ter); //求算法优先表 make_patable(h_noter, h_ter); //对输入符号串进行分析 char st[100]; int h = 1; while (h) { printf("\n\n\t请输入要分析的字符串:"); scanf_s("%s", st, 99); int sign = sf_analyse(h_noter, h_ter, st); if (sign == 0) printf("\t非法输入!\n\n"); else printf("\t合法输入!\n\n"); printf("\t是否继续输入(0退出,1继续):"); scanf_s("%d", &h); } free_list(h_noter, h_ter); return 1; } noterminal* check_noterminal(char ch, noterminal* h_noter) { int sign = 0; noterminal* h_n = h_noter; while (h_n->next != NULL) { h_n = h_n->next; if (h_n->ch == ch) { sign = 1; break; } }; if (sign == 0) return NULL; else return h_n; } terminal* check_terminal(char ch, terminal* h_ter) { int sign = 0; terminal* h_n = h_ter; while (h_n->next != NULL) { h_n = h_n->next; if (h_n->ch == ch) { sign = 1; break; } }; if (sign == 0) return NULL; else return h_n; } bool in_char(char* string, char ch) { int coutn = strlen(string); int i = 0; for (; i < coutn; i++) { if (string[i] == ch) break; } return coutn == i ? false : true; } int combin(char* ch, char* bech) { int cont = strlen(ch); int sign = 0; for (int i = 0; bech[i] != 0; i++) { if (bech[i] != '$' && !in_char(ch, bech[i])) { ch[cont++] = bech[i]; sign = 1; } } return sign; } void free_list(noterminal* h_noter, terminal* h_ter) { noterminal* noter = h_noter; terminal* ter = h_ter; while (h_noter != NULL) { h_noter = h_noter->next; free(noter); noter = h_noter; } while (h_ter != NULL) { h_ter = h_ter->next; free(ter); ter = h_ter; } } void read_rule(char* rulefile, noterminal* h_noter, terminal* h_ter) { FILE* yuan = NULL; noterminal* noter, * r_noter = h_noter; terminal* ter, * r_ter = h_ter; //非终结符加入'#' ter = (terminal*)malloc(sizeof(terminal)); ter->ch = '#'; ter->number = 0; ter->next = NULL; r_ter->next = ter; r_ter = ter; char ch; int size = 1; //终结符个数 if (fopen_s(&yuan, rulefile, "r") != 0) { printf("规则 文件打开失败!"); exit(1); } while ((ch = fgetc(yuan)) != -1) { if (64 < ch && ch < 91) { noterminal* h_n = check_noterminal(ch, h_noter); //增加非终结符结点 if (h_n == NULL) { noter = (noterminal*)malloc(sizeof(noterminal)); noter->ch = ch; noter->next = NULL; noter->FirstVt = NULL; noter->Lastvt = NULL; noter->size = 0; r_noter->next = noter; r_noter = noter; } else { noter = h_n; } //读取产生式 char chsh[10]; for (int i = 0; i < 2; i++) ch = fgetc(yuan); int quit = 0; while ((ch = fgetc(yuan))) { if (ch == '|' || ch == '\n' || ch == -1) { //存储产生式 chsh[quit] = '\0'; quit = 0; noter->guize[noter->size++] = (char*)malloc(sizeof(char[10])); int s = 0; do { noter->guize[noter->size - 1][s] = chsh[s]; } while (chsh[s++] != '\0'); if (ch == '\n' || ch == -1) { break; } } else { chsh[quit++] = ch; //增加终结符 if (ch < 65 || ch>90) { terminal* h_t = check_terminal(ch, h_ter); if (h_t == NULL) { ter = (terminal*)malloc(sizeof(terminal)); ter->ch = ch; ter->next = NULL; ter->number = size; r_ter->next = ter; r_ter = ter; size++; } } } } } else { printf("产生式错误"); } } //初始化优先关系表 ter = h_ter; while (ter->next != NULL) { ter = ter->next; ter->patable = (char*)malloc(size + 1); for (int i = 0; i < size; i++) ter->patable[i] = ' '; ter->patable[size] = 0; } fclose(yuan); } void print_rule(noterminal* h_noter, terminal* h_ter) { printf("\t产生式:\n"); noterminal* noter = h_noter; int i = 0; while (noter->next != NULL) { noter = noter->next; for (int j = 0; j < noter->size; j++) { printf("\t%-10d %-10c %-10s\n", ++i, noter->ch, noter->guize[j]); } } printf("\n\t非终结符:\n\t"); noter = h_noter; while (noter->next != NULL) { noter = noter->next; printf("%-10c", noter->ch); } printf("\n\n\t终结符:\n\t"); terminal* ter = h_ter; while (ter->next != NULL) { ter = ter->next; printf("%-10c", ter->ch); } printf("\n\n\n"); } void make_firstvt(noterminal* h_noter, terminal* h_ter) { int sign = 0; char ch; noterminal* noter = h_noter; do { noter = h_noter; sign = 0; //循环终结符 while (noter->next != NULL) { noter = noter->next; //初始化 if (noter->FirstVt == NULL) { noter->FirstVt = (char*)malloc(sizeof(char[20])); for (int i = 0; i < 20; i++) noter->FirstVt[i] = 0; } //循环每个终结符的产生式 for (int i = 0; i < noter->size; i++) { ch = noter->guize[i][0]; //为终结符 if (ch < 63 || ch>90) { if (!in_char(noter->FirstVt, ch)) { noter->FirstVt[strlen(noter->FirstVt)] = ch; sign = 1; } } //为非终结符 else { noterminal* r_noter = check_noterminal(ch, h_noter); if (r_noter->FirstVt != NULL) { sign += combin(noter->FirstVt, r_noter->FirstVt); } ch = noter->guize[i][1]; if (ch < 63 || ch>90) { if (!in_char(noter->FirstVt, ch) && ch != 0) { noter->FirstVt[strlen(noter->FirstVt)] = ch; sign = 1; } } else { error_treat(); } } } } } while (sign); //输出firstvt noter = h_noter; printf("\tFIRSTVT集:\n\n"); while (noter->next != NULL) { noter = noter->next; printf("\t%-10c", noter->ch); printf("firstvt=%-10s\n", noter->FirstVt); } printf("\n\n"); } void make_lastvt(noterminal* h_noter, terminal* h_ter) { int sign = 0; char ch; noterminal* noter = h_noter; do { noter = h_noter; sign = 0; //循环终结符 while (noter->next != NULL) { noter = noter->next; //初始化 if (noter->Lastvt == NULL) { noter->Lastvt = (char*)malloc(sizeof(char[20])); for (int i = 0; i < 20; i++) noter->Lastvt[i] = 0; } //循环每个终结符的产生式 for (int i = 0; i < noter->size; i++) { ch = noter->guize[i][strlen(noter->guize[i]) - 1]; //为终结符 if (ch < 63 || ch>90) { if (!in_char(noter->Lastvt, ch)) { noter->Lastvt[strlen(noter->Lastvt)] = ch; sign = 1; } } //为非终结符 else { noterminal* r_noter = check_noterminal(ch, h_noter); if (r_noter->Lastvt != NULL) { sign += combin(noter->Lastvt, r_noter->Lastvt); } if (strlen(noter->guize[i]) == 1) continue; ch = noter->guize[i][strlen(noter->guize[i]) - 2]; if (ch < 63 || ch>90) { if (!in_char(noter->Lastvt, ch)) { noter->Lastvt[strlen(noter->Lastvt)] = ch; sign = 1; } } else { error_treat(); } } } } } while (sign); //输出Lastvt noter = h_noter; printf("\tLASTVT集:\n\n"); while (noter->next != NULL) { noter = noter->next; printf("\t%-10c", noter->ch); printf("lastvt=%-10s\n", noter->Lastvt); } printf("\n\n"); } void make_patable(noterminal* h_noter, terminal* h_ter) { noterminal* noter = h_noter, * r_noter; terminal* ter1, * ter2; while (noter->next != NULL) { noter = noter->next; for (int i = 0; i < noter->size; i++) { for (int j = 0; j < strlen(noter->guize[i]) - 1; j++) { if (noter->guize[i][j] < 63 || noter->guize[i][j]>90) { ter1 = check_terminal(noter->guize[i][j], h_ter); if (noter->guize[i][j + 1] < 63 || noter->guize[i][j + 1]>90) { ter2 = check_terminal(noter->guize[i][j + 1], h_ter); if (ter1->patable[ter2->number] != ' ') error_treat(); ter1->patable[ter2->number] = '='; } else { r_noter = check_noterminal(noter->guize[i][j + 1], h_noter); for (int q = 0; q < strlen(r_noter->FirstVt); q++) { ter2 = check_terminal(r_noter->FirstVt[q], h_ter); if (ter1->patable[ter2->number] != ' ') error_treat(); ter1->patable[ter2->number] = '<'; } if (j < strlen(noter->guize[i]) - 2 && (noter->guize[i][j + 2] < 63 || noter->guize[i][j + 2]>90)) { ter2 = check_terminal(noter->guize[i][j + 2], h_ter); if (ter1->patable[ter2->number] != ' ') error_treat(); ter1->patable[ter2->number] = '='; } } } else { if (noter->guize[i][j + 1] < 63 || noter->guize[i][j + 1]>90) { ter1 = check_terminal(noter->guize[i][j + 1], h_ter); r_noter = check_noterminal(noter->guize[i][j], h_noter); for (int q = 0; q < strlen(r_noter->Lastvt); q++) { ter2 = check_terminal(r_noter->Lastvt[q], h_ter); if (ter2->patable[ter1->number] != ' ') error_treat(); ter2->patable[ter1->number] = '>'; } } else { error_treat(); } } } } } //初始化'#'关系表 noter = h_noter->next; ter1 = h_ter->next; for (int e = 0; e < strlen(noter->FirstVt); e++) { ter2 = check_terminal(noter->FirstVt[e], h_ter); ter1->patable[ter2->number] = '<'; } for (int e = 0; e < strlen(noter->Lastvt); e++) { ter2 = check_terminal(noter->Lastvt[e], h_ter); ter2->patable[ter1->number] = '>'; } ter1->patable[0] = '='; //输出优先关系表 printf("\t优先关系表:\n\n\t%-10c", ' '); terminal* ter = h_ter; while (ter->next != NULL) { ter = ter->next; printf("%-10c", ter->ch); } ter = h_ter; int size = strlen(ter->next->patable); while (ter->next != NULL) { ter = ter->next; printf("\n\n\t"); printf("%-10c", ter->ch); for (int w = 0; w < size; w++) { printf("%-10c", ter->patable[w]); } } printf("\n\n\n"); } void error_treat() { printf("\t-----------------此文法不是算符优先文法-----------------\n\n\n"); free_list(h_noter, h_ter); exit(-1); } int sf_analyse(noterminal* h_noter, terminal* h_ter, char* st) { printf("\t分析栈 关系 输入串 动作\n"); char anal[16] = { 0 }; //分析栈 int topa = 0; anal[topa++] = '#'; char rela[16] = { 0 }; //关系栈 int topr = 0; rela[topr++] = '#'; st[strlen(st) + 1] = 0; //输入串 st[strlen(st)] = '#'; int tops = 0; char a; terminal* ter1, * ter2; while (true) { //取出输入串字符 a = st[tops]; //st[tops++] = ' '; int j; //取出分析栈第一个终结符 if (anal[topa - 1] < 63 || anal[topa - 1]>90) j = topa - 1; else j = topa - 2; //动作判断和处理 ter1 = check_terminal(anal[j], h_ter); ter2 = check_terminal(a, h_ter); if (ter2 == NULL) { rela[topr++] = '?'; rela[topr++] = a; printf("\t%-15s %-15s %-15s 无优先关系\n", anal, rela, st); return 0; } char re = ter1->patable[ter2->number]; //接受 if (a == '#' && anal[j] == '#') { rela[topr++] = '='; rela[topr++] = '#'; printf("\t%-15s %-15s %-15s 接受\n", anal, rela, st); return 1; } //小于或等于-移进 if (re == '<' || re == '=') { rela[topr++] = re; rela[topr++] = a; printf("\t%-15s %-15s %-15s 移进\n", anal, rela, st); anal[topa++] = a; st[tops++] = ' '; } //大于-归约 else if (re == '>') { rela[topr++] = re; rela[topr++] = a; printf("\t%-15s %-15s %-15s ", anal, rela, st); //归约串提取 char gy[10] = { 0 }; char w; int p = 1; //关系栈变数 terminal* m1, * m2; do { w = anal[j]; p++; if (anal[j - 1] < 63 || anal[j - 1] > 90)j--; else j -= 2; m1 = check_terminal(anal[j], h_ter); m2 = check_terminal(w, h_ter); } while (m1->patable[m2->number] != '<'); for (int e = j + 1; e < topa; e++) { gy[e - j - 1] = anal[e]; anal[e] = 0; } //归约 char n = guiyue(gy, h_noter); if (n == 0) { printf("无匹配产生式\n"); return 0; } topa = j + 1; anal[topa++] = n; for (int r = 0; r < 2 * p; r++)rela[(topr--) - 1] = 0; printf("归约\n"); } //无关系 else { rela[topr++] = '?'; rela[topr++] = a; printf("\t%-15s %-15s %-15s 无优先关系\n", anal, rela, st); return 0; } } } char guiyue(char* st, noterminal* h_noter) { noterminal* noter = h_noter; while (noter->next != NULL) { noter = noter->next; for (int i = 0; i < noter->size; i++) { int le = strlen(noter->guize[i]); char a; int e = 0; for (; e <= le; e++) { a = noter->guize[i][e]; if (a > 62 && a < 91) { if (st[e] > 62 && st[e] < 91)continue; else break; } else { if (st[e] == a)continue; else break; } } if (e == le + 1) return noter->ch; } } return 0; }
05-28
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值