引言
我想通过这个教程,使得读者不需要写太多代码,便能理解数据结构的概念和重要性。
很多算法是运行在特殊的数据结构之上的,可以说数据结构和算法是相辅相成的,有句出自某书的非常经典的话是这样说的:
Algorithms Plus Data Structures Equals Programs
即算法加数据结构等于程序。注意这里是程序,不是软件。软件还有工程性在其中。
但是我依然希望能够不讲太多的算法,因为“算法”太多,通过学习大量算法来学习数据结构的方式有些舍本逐末。
我会尽量从构造和设计的角度来阐述数据结构。
构造即我们手上有什么,怎么利用这些去制造我们需要的结构。
设计即我们的目的是什么,为了达成这个目的我们需要什么结构。
在内存里存储数据
在C语言中,我们要处理的数据,都存储在内存中,即便是硬盘中某个文件里存放的小说,要对其内容进行处理,我们依然需要将其加载到内存中,再接着进行后续的处理。
当我们在C语言里定义一个变量,诸如:
int o;
在这句代码之后,我们便可以操作这个变量。
我们可以往里面存放一个整数,那这个整数有多大呢?使用
printf("%d",sizeof(int));
这句代码,可以输出int型变量在其运行设备上的大小。如果你在你的笔记本电脑上编译运行,这个大小应该是4,即4bytes,换算成二进制位(bit)即4*8=32bits。
我们可以利用sizeof测出所有基本类型的大小。这样就有很多不同的盒子去放数据了。
内存里的数据看起来怎么样
上面我们创建了一个int型变量o。在内存里看起来是这样的:
当然现在什么都没有,因为在定义的时候并未初始化,我们也没放任何东西进去。现在存点东西进去:
o = 11;
这下变成这样了:
最后我们显示出这个数据存储单元在内存中的位置(变量地址):
printf("%d",&o);
现在我们什么都有了:
移动数据
先看看这样一个程序:
#include <stdio.h>
void showcopies(int copies)
{
printf("copies %d\n",&copies);
}
int main(void)
{
int o;
o = 11;
printf("original %d\n",&o);
showcopies(o);
return 0;
}
在这个程序里我们定义了一个函数,并且利用参数传递,将变量o“移动”到了showcopies这个函数中,并且“改名”叫做copies。
主函数main里输出了o这个变量的地址,showcopies里输出了移动后的o的地址。
最后的结果是什么样呢?可以试着运行看看。
我去泡杯咖啡喝……
如果你在程序里加入了输出变量o和变量copies的值的语句,你会更加肯定,被“移动”的只有变量o的值而已。
也就是说,当我们进行参数传递的时候,其实是在新的一块内存区域里开辟一个同样大小的空间,然后把作为参数传递的变量的内存空间里的数据复制到新的空间中。
一组数据
数组(Array)这个名字其实有些呆,因为光看中文名给人的感觉就是一组数,诸如(1,2,3)这样的,但事实上还可以是('a','b','c')。我倾向于把Array理解为一列数据或者一组数据。
#include <stdio.h>
int main(void)
{
int m[3];
int i;
for(i=0;i<3;i++) printf("%d\n",&m[i]);
return 0;
}
运行上面的程序,就可以看到(图画的我累,所以这里不画了=。=)这样的结果:
2686736
2686740
2686744
这三个地址便是数组m里三个依次相邻元素的地址。因为每个int变量占据4bytes,所以这三个int变量的地址是依次相差4(bytes)。数据里的数据,在内存里是连续存放的。
这样的好处在于访问会特别快,我们只需要知道第一个变量的地址,就能通过向后移动一定的位置来读取另一个变量。
正如我们在程序里使用的访问方式一样,[]括号里加入索引号,便能访问,在实际的程序运行中,这种直接访问的速度也非常快。
移动一组数据或自定义数据类型
当我们尝试在函数调用的时候传递数组(Array)或者自定义的数据类型(结构体)时,会出现一种例外。
因为这两类数据在内存里占用的空间可能会很大,所以全部复制到一个新空间里实在不是很划算的一件事。甚至多数时候我们不需要一个新的备份,只需要在原来的数据上修改就行。
所以对于这两类数据,C语言里使用指针(Pointer)来操作。
int m[3];
int *first_element_address;
first_element_address = &m[0];
printf("%d\n",*(first_element_address+1)==m[1]);
当调用函数时,会生成传递参数的指针变量,再将这个指针变量传递过去。
至于验证方法,可以利用移动数据一段中的程序来检验。作业1。
/* Cheatsheet for using pointer in C */
/* 生成指针 */
pointer = &variable;
/* 用指针读取指向变量的值,再与指向变量的值进行相等判断 */
*pointer == variable;
/* 当pointer指向一块连续数据区域时,例如数组 */
*(pointer+index) == array[index];
来,我们来实现一个可变数组
先看看C语言的数组,有两个明细的缺陷:
如果访问数组的时候下标(索引值)越界,会发生错误。
数组的大小是固定的。
因此我们要设计一个用起来更好的,比如拥有如下特性:
可以检查是否越界
可变大小
数组的数据在内存中皆是连续存放的,因此我们需要一个用于存放连续数据的区域。
作业2
参考
http://en.wikipedia.org/wiki/Algorithms_%2B_Data_Structures_%3D_Programs
《Linux C编程一站式学习》 宋劲杉