(面试题)数组与链表的区别,队列和堆的区别

278 阅读4分钟

这是蔚来一面考察的我们需要熟知的数据结构,今天通过这篇文章我们来深入了解这几种数据结构。

数组与链表的区别

要知道这两者的区别首先我们要先了解它们的特性

数组

数组是有序的,线性的

  1. 线性(Linear)

    数组是线性数据结构的一种,意味着数据元素按顺序排列,每个元素可以通过索引(或位置)来访问。索引通常从0开始,因此第一个元素的索引是0,第二个元素的索引是1,依此类推。

  2. 有序性(Ordered)

    数组中的元素是有序的,这意味着元素在数组中的位置是固定的,且元素之间的相对顺序是已知的。这种有序性使得数组支持快速的随机访问。

  • 随机访问:由于数组是线性且有序的,支持常数时间复杂度的随机访问,即可以在O(1)时间内访问任意位置的元素。
  • 内存连续性:数组在内存中是连续存储的,这使得访问数组元素相对较快,因为CPU缓存可以更有效地处理连续内存访问。

链表

链表是离散的,还可以是无序的

  1. 无序性(Unorderedness)

    链表本身并不强制要求元素有序。链表中的元素顺序取决于它们被插入链表的顺序,除非进行了特定的排序操作。因此,从这个角度看,链表可以是无序的。但是,链表也完全有能力存储有序的元素,只要我们在插入时保持元素的顺序。

  2. 离散性(Discreteness)

    “离散”一词在数据结构的上下文中不是非常标准。然而,如果我们从内存布局的角度来看,链表中的元素确实是离散的,因为每个元素(通常称为节点)在内存中不是连续存储的。每个节点包含数据部分和指向下一个节点的指针(在单链表中),这使得链表能够灵活地动态增长和缩小。

  • 动态大小:链表可以动态地增长和缩小,不需要像数组那样在创建时指定固定大小。
  • 非连续内存:链表中的节点在内存中不是连续存储的,这使得链表在插入和删除操作方面具有灵活性,但这些操作通常比数组慢,因为需要调整指针。
  • 顺序访问:与数组支持快速随机访问不同,链表通常只能顺序访问元素,从头节点开始,通过指针逐个访问后续节点。这使得链表的随机访问时间复杂度较高,通常是O(n)。
  • 多样性:链表有多种变体,如单链表、双链表、循环链表等,每种变体都有其特定的用途和特性。

区别

  1. 数组的内存是连续的,链表的内存是离散的
  2. 数组可以通过下标来访问任何元素,链表只能通过指针来访问元素
  3. 数组的插入和删除操作效率低,链表的插入和删除操作效率高
  4. 数组的查找操作效率高O(1),链表的查找操作效率低O(n)

队列和堆的区别

其实这两者没什么关系,可能是面试官单纯想看我们熟不熟悉这两种数据结构,下面我们来了解一下这两者的特性

队列

队列是一种遵循先进先出原则的数据结构,尾部删除,头部添加,队列中还有一种特殊结构叫作双端队列,顾名思义就是两端都能进出。

堆:堆是完全二叉树,也就是说除了底部,其它每个节点都有两个度,也就是每个父节点都会有两个子节点,堆中有两种特殊结构,叫作大顶堆和小顶堆

  • 大顶堆 : 每个父节点都大于它的两个子节点
  • 小顶堆 : 每个父节点都小于它的两个子节点

我们来做一个练习,假设有一个大顶堆数组heap = [10, 8, 7, 5, 3, 2,1],我们要往里面植入数字9,但是要是这个数组依然保持大顶堆的数据结构

function insert(value,heap){
    heap.push(value)
    swim(heap.length - 1,heap)
}

function swim(index,heap){
    while(index>0){
        const parentIndex = Math.floor((index - 1) / 2)
        if(heap[parentIndex] <= heap[index]){
            [heap[parentIndex],heap[index]] = [heap[index],heap[parentIndex]]

        }
        else{
            break
        }
        index = parentIndex;

    }
}
let heap = [10, 8, 7, 5, 3, 2, 1];
insert(9, heap);
console.log(heap); // 输出最终的最大堆