【问题标题】:Arrays, Vectors & Linked Lists数组、向量和链表
【发布时间】:2013-04-23 16:37:30
【问题描述】:

我的主要问题是向量是如何工作的。

我知道链表是​​连接在一起的节点列表。这就是我在脑海中想象的样子。但我无法想象向量是什么样的。

我读到向量可以随机访问,就像数组一样。但什么是随机访问?计算机最终不会在内部搜索索引吗?我这样说是因为我可以通过运行 for 循环重载 [] 运算符以在链表中为我提供第 ith 元素。

还有一个数组是由什么组成的?我知道数组是如何工作的,但它背后是什么,它也是节点的集合吗?我问这个只是为了一般知识。

Here 是关于向量的问题。它看起来与用于链表的函数非常相似。

【问题讨论】:

  • @SLaks C++。这有关系吗?
  • 是的,这很重要。人们普遍同意链表到底是什么。数组和向量的情况并非如此,因为它可能因语言而异。
  • @BenjaminLindley 啊,我不知道。谢谢。

标签: c++ arrays data-structures vector linked-list


【解决方案1】:

C++ 中的vector 是一个本质上包装数组并在您向其中添加新元素时自动调整其大小的类。

数组本质上只是一块连续的内存,其中每个元素在内存中紧挨着放置。

随机访问基本上意味着通过索引访问元素,即获取列表中的第 5 个元素。数组(因此是vector)提供了高效的随机访问和元素的恒定时间查找。这意味着访问第 5 个元素与访问第 573 个元素一样快。

由于vector 包装了一个数组并将其数据作为数组在内部存储,因此元素查找的效率与数组的效率相同(基本上)。而且它不需要查找索引或类似的东西,因为它将数据保存在一个数组中。

【讨论】:

    【解决方案2】:

    通常一个向量有三个数据成员:

    • 指向数据开头的指针
    • 指向数据末尾的指针(或保存向量大小的整数)
    • 一个包含向量当前容量的整数(不用担心)

    “数据”是一个数组,所以最终向量看起来像一个指向包含一些额外信息的数组的指针。

    数组是内存中相邻对象的序列,因此每个对象都从比前一个对象的最后一个字节高一个字节的地址开始。

    计算机最终是否没有结束搜索索引 内部?

    不,RAM 硬件的部分功能是在不扫描内存的情况下获取指定地址的内容。这就是 RAM 与(例如)转鼓存储器的区别。

    在这种情况下,“随机访问”是指在最多固定时间内访问任何地址。

    对于向量和数组,ith 元素的地址可以通过将偏移量添加到第一个元素的地址来计算。以字节为单位的偏移量等于i 乘以单个元素的大小。对于链表,获取ith 元素的唯一方法是跟随next 指针i 次。因此,如果您确实为链表实现了 operator[],则执行 i 的值越大,执行所需的时间就越长,因此不会提供 随机 访问。

    【讨论】:

    • +1,IMO 最佳答案,因为它解决了计算机“在内部搜索索引”的问题,这是 OP 问题的核心部分。
    【解决方案3】:

    一个向量(至少在 C++ 中)表示一个连续的内存块,它保存着大小相等的对象。这允许它在恒定时间内计算任何给定项目的地址,例如:

    address = base_address + index * item_size;
    

    请注意,大部分内容都在幕后(可以这么说)——C++(就像之前的 C)根据计算某些指定大小的对象的地址来定义索引,所以实际上代码很可能只是看起来像:

    T &operator[](size_t index) { return base[index]; }
    

    ...编译器根据sizeof(T)处理生成代码以进行乘法运算。

    这意味着访问数组的任何部分都需要代码部分基本相同的机制。平心而论,这并不意味着所有访问都必然具有相同的速度。

    例如,数据的一部分可能在缓存中,其他部分在主内存中,还有其他部分可能在虚拟内存中,其中检索需要从磁盘驱动器读取数据。这可能很容易意味着一百万到一(或更多)的速度差异。

    然而,这仍然与链表形成对比,链表需要线性复杂度才能找到链表中任意项的地址。从那里,我们获得了关于如何存储数据的所有相同可能性(并且由于数据不连续,我们往往会获得较差的参考局部性,从而导致任何给定数据更有可能在一种较慢的存储形式)。

    【讨论】:

      【解决方案4】:

      数组是内存中的一个位置,以及它后面的一系列串行位置。

      考虑我是否有数组 int[] a = [1,2,3,4,5,6,7,8,9,10];

      如果我们查看内存中的这个数组,它可能看起来像这样 --

      15  null           00000000
          null           00000000
          null           00000000
          a[10]          00001011
          a[9]           00001010
      10  a[8]           00001001
          a[7]           00001000
          a[6]           00000111
          a[5]           00000110
          a[4]           00000101
      5   a[3]           00000100
          a[2]           00000011
          a[1]           00000010
          a[0]           00000001
          other stuff    00011101
      0   other stuff    01010101
      

      您可以在数组中随机访问,因为编译器正在为您找到您正在寻找的确切地址。

      例如在这种情况下,如果我运行该函数

      print(a*); //print the location in memory of a (the pointer to a)
      

      它会输出

      2
      

      现在当我要求 [2]

      print (a[2]) 
      

      编译器实际上在做的是这个

      print (&(a*+2) // print the values at the memory location 
                     // that is 2 plus the pointer to a
      

      这意味着它应该打印出内存位置4的值,即

      3
      

      当我们说一个数组可以随机访问时,这意味着访问的时间是恒定的,因为找到元素 a[100000] 和 a[1] 都需要相同的时间。

      1.) look-up a.
      2.) add the index to a. 
      3.) look-up the value at a + index. 
      

      现在向量只是一个数组的包装器。它添加了特殊功能并为数组定义“行为”。想象它像一个包含 3-4 条数据的结构,其中一些数据是指向特殊“向量函数”的指针。

      想象一下这样的数据。

      struct vect { 
          int* array; 
          int  arraysize; 
          int* functionPtr1;
          int* functionPtr2;
          int* functi....
      };
      

      我要避免在 c 中看起来如何的 gorp,因为我不是很好并且肯定会出错,但是在像 java 这样的面向对象语言中,我们的向量看起来像这样

      vect.array[1];
      int arg2 = vect.array[2]; 
      vect.functionPtr1(2); 
      int vectSize = vect.arraysize; 
      

      很简单。令人困惑的是,c++ 允许您在对象上定义 [] 运算符。当您执行此操作时,在 c++ 中的向量中。

      vect[2]; 
      

      它实际上只是这里 java 等价物的语法糖:

      vect.array[2]; 
      

      但是 tl;dr;这一切的版本是向量只是一个用于将函数包装在原始数组周围的对象。 C++ 提供了特殊的语法糖,因此您可以直接从对象指针寻址原始数组,而不仅仅是从对象数组成员,但是获取对象数组成员的工作仍在完成,只是抽象出来。

      【讨论】:

        【解决方案5】:

        没有特定于语言,这些术语通常意味着:

        • 数组是一个固定长度、非结构化的连续内存区域。通过计算与基地址的偏移量来访问位置。
        • 向量像数组一样是随机访问的,但知道其长度,因此可以动态更改。
        • 链表是完全动态且不连续的,因此它不是随机访问的。

        【讨论】:

        • 我认为您对向量的定义并不那么笼统,并且对几种语言非常具体(只有我能想到的一种)。
        • @BenjaminLindley:他对数组的定义也是如此。例如,在(至少某些版本的)BASIC 中,可以调整数组的大小。
        • @Jeffy Coffin 一旦分配,就无法在内存中调整大小和数组。可调整大小的数组是语法糖和某些语言的特性,但不是数组本身的属性
        • @gbtimmon:只有当你将数组定义为内存中的一个区域时,就像 stark 所做的那样。不需要这样定义。
        • @gbtimmon:即使假设您对数组的定义很窄:虽然不可能保证就地扩展数组,但在某些时候肯定是可能的(例如,这就是 C 包含 realloc 的很大一部分原因。
        猜你喜欢
        • 1970-01-01
        • 2018-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2022-01-10
        • 2013-10-03
        • 2010-09-13
        相关资源
        最近更新 更多