最近在读霍罗维兹的《数据结构基础》(Fundamentals of Data Structures in C),本篇博客为阅读笔记和知识总结。(ARRAYS AND STRUCTURES)
目录
前言:
Ⅰ. 数组 - ARRAYS
0x00 抽象数据类型 - The Abstract Data Type
0x01 C语言中的数组
0x02 例子
Ⅱ. 动态分配数组 - DYNAMICALLY ALLOCATED ARRAYS
0x00 一维数组 - ONE-DIMENSIONAL ARRAYS
0x01 二维数组 - TWO-DIMENSIONAL ARRAYS
Ⅲ. 结构体和联合体
0x00 结构体
0x01 联合体(union)
0x02 内部运作结构 - Internal Implementation of Structures
0x03 自律性结构 - Self-Referential Structures
Ⅳ. 多项式 - POLYNOMIALS
0x00 抽象数据类型
0x01 多项式表示法
0x02 多项式加法
通常,数组通常被看作是 "一组连续地内存地址" 。
作为 ADT 的数组是 <索引,值> ,每个被定义的索引都有一个与之相关的值。
除了创建一个新数组外,大多数语言只为数组提供了两种标准操作:
① 检索一个值
② 储存一个值
ADT - 数组
object:一组
例如: 为一维。
为二维。
functions:
对于所有
Array Create(j, list) ::= return an array of j dimensions where list is a j-tuple
whose ith element is the size of the ith dimension. Items
are undefined.
Item Retrieve(A, i) ::= if (i ∈ index) return the item associated with index
value i in array A
else return error.
Array Store(A, i, x) ::= if (i ∈ index) return an array that is identical to
array A except the new pair has been inserted
else return error.
end Array
C语言中一维数组的声明:
int list[5], *plist[5];
内存分配:
C将 list[i] 解释为一个指向整数的指针。
观察下面声明之间的区别:
int* list1;
int list2[5];
变量 list1 和 list2 都是指向一个整数类型对象的指针。
list2 是一个指向 list2[0] 的指针,list2 + i 是一个指向 list2[i] 的指针。
因此, (list2 + i) 等于 &list2[i]
所以, *(list2 + i) 等于 list2[i]
[Program 2.1]
#define MAX_SIZE 100
float sum(float [], int);
float input[MAX_SIZE], answer;
int i;
void main(void)
{
for (i=0; i
当 sum 被调用时,input = &input[0] 被复制到一个临时位置,并于正是参数列表相关联起来。
当 list[i] 出现在赋值语句中 " = " 的右侧时,就会触发解引用,并返回 (list + i) 所指向的值。
如果 list[i] 出现在 " = " 的左侧,那么右侧产生的值将被存储在 (list + i) 处。
[一维数组寻址]
int one[] = {0, 1, 2, 3, 4};
打印出 this 的第 i 个元素的地址和在这个地址找到的值
void print1(int *ptr, int rows)
{
/* 使用指针打印出一个一维数组 */
int i;
printf("Address Contents\n");
for (i=0; i
如果用户向改变数组的大小,我们必须改变 MAX_SIZE 并重新编译程序。
为了解决这种问题,我们可以把这个 "决定" 推迟到运行时再去解决,当我们对所需的数组大小有一个很好地估计时再分配数组。
int i, n, *list;
printf("需要生成的数字数量");
scanf("%d", &n);
if ( n < 1 ) {
fprintf(stderr, "error! \n");
exit(EXIT_FAILURE);
}
MALLOC(list, n * sizeof(int));
二维数组可以理解为,它的每个元素都是一个一维数组。
举个例子:
int x[3][5];
三维数组可以理解为,它的每个元素本身是一个二维数组。
int** make2dArray(int rows, int cols)
{ /* create a two dimensional rows * cols array */
int **x, i;
/* get memory for row pointers */
MALLOC (x, rows * sizeof (*x));
/* get memory for each row */
for (i=0; i < rows; i++)
MALLOC (x[i], cols * sizeof (**x));
return x;
}
cf. x = (int **)malloc(rows*sizeof(*x));
void* calloc(elt_count, elt_size)
分配一个足够大的内存区域来容纳一个 elt_count 元素的数组,每个元素的大小为 elt_size,并且将内存区域设为0。
void* realloc(p, s)
将 p 所指向的内存块的大小重新分配,改为 s。
结构体 Structure(在许多其他编程语言中称为 record),是一个数据项的集合,其中每个项目都有类型和名称的标识。
结构体是一些值的集合,这些值称为成员变量。结构的每个成员以是不同类型的变量。
如果说数组是同一类型的变量集合,那么结构体就是各种各样变量的集合。因为结构体支持所有C数据类型,所以结构体内部也可以有数组存在。
举个栗子,如果要保存学生的信息(学号,姓名,年级),想将信息捆绑在一起,作为一个变量来管理会十分便利。像这样把多个数据类型捆绑在一起的,就叫做结构体。
声明结构体
上述举的例子(保存学生信息):
struct {
int id;
char name[26];
double gradePoints;
} student;
但是,对于有多个学生的情况,需要多个这样的变量。考虑到每次都是用 struct {} 语法来产生变量的方法未免过于繁琐,C语言允许将结构体当作一个数据类型来方便我们更好地使用。如下所示,
ps:tag 表示 结构体标签
struct tag {
type1 fieldName1;
...
typeN fieldNameN;
};
struct tag variable_identifier1;
struct tag variable_identifier2;
struct student{
int id;
char name[26];
float grade;
};
struct student xiaoming;
struct student xiaohong;
综上所述得出总结:结构体是用户自定义的类型 (user-defined type) 。
结构体初始化
下面将演示结构体变量声明后初始化的过程:
声明变量时允许指定初始值:
为了接近结构体的变量,我们可以使用 "点操作符(·)" 来获取它。值得一提的是,字符串复制时要使用 strcpy。
结构体与 typedef
当然,typedef 还可以作用于结构体。这样可以让结构体用起来更爽,而不用拖着 struct name 又臭又长的玩意来定义变量,使用方法如下:
之前举的学生结构体的例子,我们现在可以升级一下了:
使用 typedef 前
struct student {
int id;
char name[26];
float grade;
};
struct student xiaohong;
使用 typedef 后:
typedef struct {
int id;
char name[26];
float grade;
} stu;
stu s1;
[Program 2.4]
#define FALSE 0
#define TRUE 1
typedef struct {
char name[10];
int age;
float salary;
} humanBeing;
humanBeing person1, person2;
int humans_equal(human_being person1, human_being person2)
{
/* return TRUE if person1 and person2 are the same human being
otherwise return FALSE */
if (strcmp(person1.name, person2.name))
return FALSE;
if (person1.age != person2.age)
return FALSE;
if (person1.salary != person2.salary)
return FALSE;
return TRUE;
}
if (humans_equal(person1, person2))
printf("The two human beings are the same");
else
printf("The two human beings are not the same");
结构体嵌套
typedef struct {
int month;
int day;
int year;
} date;
typedef struct human_being {
char name[10];
int age;
float salary;
date dob;
};
person1.dob.month = 2;
person1.dob.day = 11;
person1.dob.year = 1944;
字段共享他们的内存空间,在任何时候只有一个字段的联合是有效的。
typedef struct sex-type {
enum tag_field {female, male} sex;
union {
int children;
int beard;
} u;
};
typedef struct human_being {
char name[10];
int age;
float salary;
date dob;
sex_type sex_info;
};
human_being person1, person2;
person1.sex_info.sex = male;
person1.sex_info.u.beard = FALSE;
person2.sex_info.sex = female;
person2.sex_info.u.children = 4;
大多数情况下,我们不需要关心C语言编译器究竟如何在内存中存储结构域。
一般来说,这些值将按照结构定义中指定的顺序使用递增的地址位置以相同的方式存储。
一个或多个组成部分是指向自身的指针。
自律性结构通常需要动态存储管理例程(malloc 和 free)来明确获取和释放内存。
typedef struct list {
char data;
list *link;
};
list item1, item2, item3;
item1.data = 'a';
item2.data = 'b';
item3.data = 'c';
item1.link = item2.link = item3.link = NULL;
item1.link = &item2;
item2.link = &item3;
数组不仅本身是数据结构,我们还可以用它来实现其他抽象的数据类型。
最简单和最常见的数据结构之一,顺序表和线性表。
例子:
一周七天(周一周二周三周四周五周六周日)
一副牌(A,2,3,4,5,6,7,8,9,10,J,Q,K)
建筑物的层数(地下室、停车场、大厅、第一层、第二层)
对顺序表的可能操作:
寻找某列表长度 n
从左到右(或从右到左)遍历列表中的元素。
从一个列表中检索第 项,
替换列表中的第 个位置的元素,
在一个列表中的第 个位置插入一个新元素,
之前的编号为 的元素变成编号为 的元素。
从一个列表的第 个位置删除一个元素,
之前的编号为 的元素变成编号为 的元素。
实现(表示顺序表的方法)
顺序映射
非顺序映射
多项式(viewed from a mathematical perspective)
从数学角度来看,多项式是项的综合,其中每个都是一个 ,
是一个变量, 是系数, 是指数。
举个例子:
多项式之与乘积的标准数学定义:
[ADT 2.2] ADT - 多项式
[Program 2.5] Initial version of padd function
/* d = a + b, where a, b, and d are polynomials */
d = Zero();
While(!IsZero(a) && ! IsZero(b)) do {
switch COMPARE(Lead_Exp(a), Lead_Exp(b)) {
case -1 : d = Attach(d, Coef(b, Lead_Exp(b)), Lead_Exp(b));
b = Remove(b, Lead_Exp(b));
break;
case 0 : sum = Coef(a, Lead_Exp(a)) + Coef(b, Lead_Exp(b));
if (sum) {
Attach(d, sum, Lead_Exp(a));
a = Remove(a, Lead_Exp(a));
b = Remove(b, Lead_Exp(b));
}
break;
case 1 : d = Attach(d, Coef(a, Lead_Exp(a)), Lead_Exp(a));
a = Remove(a, Lead_Exp(a));
}
}
insert any remaining terms of a or b into d
Representation
指数是按递减顺序唯一排列的。
包括多项式中的所有项:
#define MAX_DEGREE 101
typedef struct {
int degree;
float coef[MAX_DEGREE];
} polynomial;
令 是一个多项式类型的遍历,我们可以表示多项式 ,
通过设置 和 。
虽然这种表示方法带来了非常简单的算法,但它浪费了大量的空间。
例如,如果 ,或多项式是稀疏的(Sparse),
例子: 和
<稀疏表示 - Sparse Representation>
为了保留空间,我们只用一个全局数组来存储我们所有的多项式。
#define MAX_TERMS 100
typedef struct {
float coef;
int expon;
} polynomial;
polynomial terms[MAX_TERMS];
int avail = 0;
例子:
为了表示零多项式 ,设 .
[Program 2.7] : Function to add a new term
void attach(float coefficient, int exponent)
{
/* add a new term to the polynomial */
if (avail >= MAX_TERMS) {
fprintf(stderr, "Too many terms in the polynomial");
exit(1);
}
terms[avail].coef = coefficient;
terms[avail++].expon = exponent;
}
对 padd 的分析
时间复杂度为 ,其中 和 分别为 和 的项数。
参考资料:
Fundamentals of Data Structures in C
本章完。