写给你的数据结构教程(第一天)

引言

我想通过这个教程,使得读者不需要写太多代码,便能理解数据结构的概念和重要性。

很多算法是运行在特殊的数据结构之上的,可以说数据结构和算法是相辅相成的,有句出自某书的非常经典的话是这样说的:

Algorithms Plus Data Structures Equals Programs

即算法加数据结构等于程序。注意这里是程序,不是软件。软件还有工程性在其中。

但是我依然希望能够不讲太多的算法,因为“算法”太多,通过学习大量算法来学习数据结构的方式有些舍本逐末。

我会尽量从构造设计的角度来阐述数据结构。

构造即我们手上有什么,怎么利用这些去制造我们需要的结构。

设计即我们的目的是什么,为了达成这个目的我们需要什么结构。

在内存里存储数据

在C语言中,我们要处理的数据,都存储在内存中,即便是硬盘中某个文件里存放的小说,要对其内容进行处理,我们依然需要将其加载到内存中,再接着进行后续的处理。

当我们在C语言里定义一个变量,诸如:

int o;

在这句代码之后,我们便可以操作这个变量。

我们可以往里面存放一个整数,那这个整数有多大呢?使用

printf("%d",sizeof(int));

这句代码,可以输出int型变量在其运行设备上的大小。如果你在你的笔记本电脑上编译运行,这个大小应该是4,即4bytes,换算成二进制位(bit)即4*8=32bits。

我们可以利用sizeof测出所有基本类型的大小。这样就有很多不同的盒子去放数据了。

内存里的数据看起来怎么样

上面我们创建了一个int型变量o。在内存里看起来是这样的:

174155_RyQ9_998619.png

当然现在什么都没有,因为在定义的时候并未初始化,我们也没放任何东西进去。现在存点东西进去:

o = 11;

这下变成这样了:

174740_So7x_998619.png

最后我们显示出这个数据存储单元在内存中的位置(变量地址):

printf("%d",&o);

现在我们什么都有了:

174919_5ETk_998619.png

移动数据

先看看这样一个程序:

#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的值而已。

180922_2hHT_998619.png

也就是说,当我们进行参数传递的时候,其实是在新的一块内存区域里开辟一个同样大小的空间,然后把作为参数传递的变量的内存空间里的数据复制到新的空间中。

一组数据

数组(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语言的数组,有两个明细的缺陷:

  1. 如果访问数组的时候下标(索引值)越界,会发生错误。

  2. 数组的大小是固定的。

因此我们要设计一个用起来更好的,比如拥有如下特性:

  1. 可以检查是否越界

  2. 可变大小

数组的数据在内存中皆是连续存放的,因此我们需要一个用于存放连续数据的区域。

作业2


参考

  1. http://en.wikipedia.org/wiki/Algorithms_%2B_Data_Structures_%3D_Programs

  2. 《Linux C编程一站式学习》 宋劲杉


转载于:https://my.oschina.net/sooshian/blog/221101

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值