《Thinking In Algorithm》03.数据结构之数组

数组Array: 数组的存储在计算机内存中是一片连续的区域。而且数组中每一项所占内存的大小是一定的。因此,我们只需要知道数组第一项所处的位置,就能简单的知道其他任何项所在的位置,而不需要依次去查找。所以数组在知道索引时的访问是很快的。

大O表示法 在讨论算法的优劣时,我们通常需要一个标尺来衡量。在计算机科学里,这个标尺就是:大O。
在比较两种算法时,我们不会说:A比B快2倍。为何?因为随着数据量的改变,算法运算耗时的增不是一样的,有的算法是线性的增加,有的是几何级数的增加。


我们看看数组相关算法的表示:
未排序数组的插入 对于未排序的数组,当我们要插入一个新的值时,我们要改变数组的长度,即在数组末尾加一个新的项,然后将值存入。所以它的耗时 T 是和数组现在的长度 N 无关的,我们可以认为这个耗时是一个常量 K。即:
T = K;
这里的常量的值是取决于机器运算的快慢,等。
线性搜索 线性搜索意思是从数组第一个项一直查到最后一个项。并找出我们需要的值。
在最理想的情况下,我们需要的项在第一个,所以耗时为K(运算常量),最坏的情况是在最后一个,耗时为K*N.所以平均下来,线性搜索耗时:
T = K * N / 2
因为常量K本身就是一个不确定的值,这里,我们就可以将2这个值忽略,将上式表达为:
T = K * N


Arrays are used to implement other data structures, such as heapshash tablesdequesqueuesstacksstrings, and VLists.

ArrayList is a dynamic array.有关于数组与动态数组的区别可看Array与ArrayList的区别(java)


与其他数据结构的比较

  Linked list Array Dynamic
array
Balanced
tree
Random access
list
Indexing Θ(n) Θ(1) Θ(1) Θ(log n) Θ(log n)
Insert/delete at beginning Θ(1) N/A Θ(n) Θ(log n) Θ(1)
Insert/delete at end Θ(n)
last element is unknown
Θ(1)
last element is known
N/A Θ(1) amortized Θ(log n) Θ(log n) updating
Insert/delete in middle search time +
Θ(1)[10][11][12]
N/A Θ(n) Θ(log n) Θ(log n) updating
Wasted space (average) Θ(n) 0 Θ(n)[13] Θ(n) Θ(n)

数组与链表的具体区别

  众所周知,在计算机中要对给定的数据集进行若干处理,首要任务是把数据集的一部分(当数据量非常大时,可能只能一部分一部分地读取数据到内存中来处理)或全部存储到内存中,然后再对内存中的数据进行各种处理。   

      例如,对于数据集S{1,2,3,4,5,6},要求S中元素的和,首先要把数据存储到内存中,然后再将内存中的数据相加。

      当内存空间中有足够大的连续空间时,可以把数据连续的存放在内存中,各种编程语言中的数组一般都是按这种方式存储的(也可能有例外),如图1(b);当内存中只有一些离散的可用空间时,想连续存储数据就非常困难了,这时能想到的一种解决方式是移动内存中的数据,把离散的空间聚集成连续的一块大空间,如图1(c)所示,这样做当然也可以,但是这种情况因为可能要移动别人的数据,所以会存在一些困难,移动的过程中也有可能会把一些别人的重要数据给丢失。另外一种,不影响别人的数据存储方式是把数据集中的数据分开离散地存储到这些不连续空间中,如图1(d)。这时为了能把数据集中的所有数据联系起来,需要在前一块数据的存储空间中记录下一块数据的地址,这样只要知道第一块内存空间的地址就能环环相扣地把数据集整体联系在一起了。C/C++中用指针实现的链表就是这种存储形式。


      由上可知,内存中的存储形式可以分为连续存储和离散存储两种。因此,数据的物理存储结构就有连续存储和离散存储两种,它们对应了我们通常所说的数组和链表。

      由于数组是连续存储的,在操作数组中的数据时就可以根据离首地址的偏移量直接存取相应位置上的数据,但是如果要在数据组中任意位置上插入一个元素,就需要先把后面的元素集体向后移一位为其空出存储空间。与之相反,链表是离散存储的,所以在插入一个数据时只要申请一片新空间,然后将其中的连接关系做一个修改就可以,但是显然在链表上查找一个数据时就要逐个遍历了。

      考虑以上的总结可见,数组和链表各有优缺点。在具体使用时要根据具体情况选择。当查找数据操作比较多时最好用数组;当对数据集中的数据进行添加或删除比较多时最好选择链表。


数组大小有没有限制

这一点,是下面一网友提问的,当时我也不是特别清楚,于是就到网上查了下,下面我就不翻译了。

第一个答案:

Nobody mentioned the limit on the size of the stack frame.

There are two places memory can be allocated:

  • On the heap (dynamically allocated memory).
    The size limit here is a combination of available hardware and the OS's ability to simulate space by using other devices to temporarily store unused data (i.e. move pages to hard disk).
  • On the stack (Locally declared variables).
    The size limit here is compiler defined (with possible hardware limits). If you read the compiler documentation you can often tweak this size.

Thus if you allocate an array dynamically (the limit is large and described in detail by other posts.

int* a1 = new int[SIZE];  // SIZE limited only by OS/Hardware

Alternatively if the array is allocated on the stack then you are limited by the size of the stack frame. N.B.vectors and other containers have a small presence in the stack but usually the bulk of the data will be on the heap.

int a2[SIZE]; // SIZE limited by COMPILER to the size of the stack frame

第二个答案:

There are two limits, both not enforced by C++ but rather by the hardware.

The first limit (should never be reached) is set by the restrictions of the size type used to describe an index in the array (and the size thereof). It is given by the maximum value the system's std::size_tcan take. This data type should always be the largest integer type of a system.

The other limit is a physical memory limit. The larger your objects in the array are, the sooner this limit is reached because memory is full. For example, a vector<int> of a given size n typically takes about four times as much memory as an array of type vector<char> (minus a small constant value). Therefore, a vector<char> may contain more items than a vector<int> before memory is full. The same counts for the native C-style arrays int[] and char[].

Additionally, this upper limit may be influenced by the type of allocator used to construct the vector because an allocator is free to manage memory any way it wants. A very odd but nontheless conceivable allocator could pool memory in such a way that identical instances of an object share resources. This way, you could insert a lot of identical objects into a container that would otherwise use up all the available memory.

Apart from that, C++ doesn't enforce any limits.


我的答案:

1》如果是new创建的话,那就是存储在堆中,溢不溢出跟硬件和系统通过其他设备模拟临时存储数据空间的能力有关(i.e.move pages to hard disk),理论上没有上限,但是integer大小有限制,所以没达到内存溢出前,integer就不够了。
2》如果你是通过int a[]这样来申请的,那么就是存储在栈上,这个跟编译器有关,一般不超过1m.如我实验了下,我的是g++,当1024*1024*511时不报错,1024*1024*512时就报错。



References:

http://en.wikipedia.org/wiki/Array_data_structure

http://www.cnblogs.com/lina1006/archive/2011/05/06/2039099.html

http://blog.sina.cn/dpool/blog/s/blog_5f54f0be0100q8ln.html

  • 5
    点赞
  • 27
    收藏
    觉得还不错? 一键收藏
  • 3
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值