高薪·进阶之数据结构篇

824 阅读9分钟

从我们接触开发开始,算法 + 数据结构 = 程序 都是我们认同的。很多开发岗位都要求深入了解数据 结构,院校的计算机专业中,数据结构都是必修课程。在面试中,很多面试官会提到,例如:二叉树,红黑树,或者“医院的看病人挂号排队该怎么处理”。

什么是数据结构?

数据结构是计算机存储、组织数据的方式。

数据是程序的核心要素,不管我们是在写商城类的应用,还是社交类的应用,亦或股票类的应用,我们都需要处理其数据,如商品数据、消息数据、股票价格等等。对于特定的数据结构(比如数组),有些操作效率很高(读某个数组元素),有些操作的效率很低(删除某个数组元素)。程序员的目标是为当前的问题选择最优的数据结构。

数据结构的种类

计算机发展至今,有很多数据的组织方式,而我们常用的差不多是下面的一种之一:

  • 数组(Array)
  • 栈(Stack)
  • 队列(Queue)
  • 堆积(Heap)
  • 图(Graph)
  • 链表(Link List)
  • 树(Tree)
  • 哈希表(Hash Table)

数组

是由相同类型的元素(element)的集合所组成的数据结构,分配一块连续的内存来存储。利用元素的索引(index)可以计算出该元素对应的存储地址

数组根据维度区分,有两种数组:

  • 一维数组
  • 多维数组(数组的元素为数组)

数组的操作

  • insert:在某个 index 插入元素
  • delete:在 C 中根据索引删除,在其他高级语言 (OC, Swift, Java) 直接删除元素
  • get:通过索引查找数组内部的元素
  • size:可以直接获取数组中元素的个数

由于数组是连续性的内存,所以作删除和插入时会导致内存数据的移动,对现在的计算速度来说并不算什么,单在编写中还是尽量避免。

常见的面试题

栈和队列

栈和队列都是动态集合,且在其上的 delete 操作所移除的元素是预先设定的。栈实现的是后进先出(last-in, first-out, LIFO),删除的总是最近插入的元素,而队列则是先进先出(first-in, first-out, FIFO),总是存在时间最长的被移除。

栈的应用最为熟知的 Ctrl + Z ,而队列的操作于我们生活中排队买票、结账的场景一样。

栈和队列的基本操作

栈:

  • push
  • pop
  • isEmpty
  • top

队列:

  • Enqueue:入队
  • Dequeue:出队
  • isEmpty
  • top

常见的面试题

堆(heap)是计算机科学中一种特别的树状结构,其特性是:“给定堆中任意节点P和C,若P是C的母节点,那么P的值会小于等于(或大于等于)C的值”

也就是说,若母节点的值恒小于等于子节点的值,此堆称为最小堆(min heap);反之,若母节点的值恒大于等于子节点的值,此堆称为最大堆(max heap)。在堆中最顶端的那一个节点,称作根节点(root node),根节点本身没有母节点(parent node)

对于操作系统,任务调动程序的工作是频繁的,而每个任务需要时间/资源/权重有长短,对于这些不同操作任务来说,队列/栈等按一定顺序的数据结构已经不适应了,而的出现可以解决这个问题。

堆的性质

堆的实现通过构造二叉堆(binary heap),实为二叉树的一种;由于其应用的普遍性,当不加限定时,均指该数据结构的这种实现。这种数据结构具有以下性质。

  • 任意节点小于(或大于)它的所有后裔,最小元(或最大元)在堆的根上(堆序性)。
  • 堆总是一棵完全树。即除了最底层,其他层的节点都被元素填满,且最底层尽可能地从左到右填入。

将根节点最大的堆叫做最大堆或大根堆,根节点最小的堆叫做最小堆或小根堆。常见的堆有二叉堆、斐波那契堆等。

堆的基本操作

  • build:构建堆积
  • insert:向堆中插入一个新的元素
  • update:将新元素提升使其符合堆的性质
  • get:获取当前堆顶元素的值
  • delete:删除堆顶元素
  • heapify:使删除堆顶元素的堆再次成为堆

应用

  • 堆排序
  • 事件模拟

链表

链表是线性结构,用指针链接数据的方式串联数据,但数据并不会按线性顺序存储。与数组不同的是,链表插入或删除数据的时候不需要移动数据,存储的数据也不是连续的内存,其充分利用计算机内存空间,实现灵活的内存动态管理。 链表没有随机读取的优点,而且空间开销比较大。 链表有一下几种常用的类型:

  • 单向链表
  • 双向链表
  • 循环链表

单链表

链表中最简单的一种是单向链表,它包含两个域,一个信息域和一个指针域。这个链接指向列表中的下一个节点,而最后一个节点则指向一个空值

双链表

双向链表相比与单链表的优势在于它同时支持高效的正向及反向遍历,并且可以方便的在链表尾部删除结点(单链表可以方便的在尾部插入结点,但不支持高效的表尾删除操作)

循环链表

与单链表一样,只是最后的节点指向的是首节点

链表的基本操作

  • InsertAtEnd
  • InsertAthead
  • Delete
  • DeleteAtHead
  • Search
  • isEmpty

常见的面试题

在计算机科学中,树 (Tree) 是一种抽象数据类型(ADT)或是实作这种抽象数据类型的数据结构,用来模拟具有树状结构性质的数据集合.它是由n(n>0)个有限节点组成一个具有层次关系的集合。把它叫做“树”是因为它看起来像一棵倒挂的树,也就是说它是根朝上,而叶朝下的。

600px-Treedatastructure.png

树的特点

  • 每个节点都只有有限个子节点或无子节点
  • 没有父节点的节点称为根节点
  • 每一个非根节点有且只有一个父节点
  • 除了根节点外,每个子节点可以分为多个不相交的子树
  • 树里面没有 环路(cycle)

simple-tree

树的分类

  • N叉树
  • 平衡树
  • 二叉树和二叉查找树(比较常用)
  • 红黑树

常见的面试题

哈希表

哈希(Hash) 将某个对象变换为唯一标识符,该标识符通常用一个短的随机字母和数字组成的字符串来代表。哈希可以用来实现各种数据结构,其中最常用的就是 哈希表 (hash table)

哈希表也称为 散列表,是根据 **键(Key)**而直接访问在内存存储位置的数据结构。也就是说,它通过计算一个关于键值的函数,将所需查询的数据映射到表中一个位置来访问记录,这加快了查找速度。这个映射函数称做散列函数,存放记录的数组称做散列表。 其性能取决于3个指标

  • 哈希函数
  • 哈希表的大小
  • 哈希冲突的解决方法

构建哈希函数

  • **直接定址法:**取关键字或关键字的某个线性函数值为散列地址。即 hash(k)=khash(k)=ak+b,其中 a,b 为常数(这种散列函数叫做自身函数)
  • **数字分析法:**假设关键字是以r为基的数,并且哈希表中可能出现的关键字都是事先知道的,则可取关键字的若干数位组成哈希地址
  • **平方取中法:**取关键字平方后的中间几位为哈希地址。通常在选定哈希函数时不一定能知道关键字的全部情况,取其中的哪几位也不一定合适,而一个数平方后的中间几位数和数的每一位都相关,由此使随机分布的关键字得到的哈希地址也是随机的。取的位数由表长决定
  • **折叠法:**将关键字分割成位数相同的几部分(最后一部分的位数可以不同),然后取这几部分的叠加和(舍去进位)作为哈希地址
  • **除余法:**取关键字被某个不大于散列表表长 m 的数 p 除后所得的余数为散列地址。即 hash(k)=k mod p, p <= m。不仅可以对关键字直接取模,也可在折叠法、平方取中法等运算之后取模。对 p 的选择很重要,一般取素数或 m,若 p 选择不好,容易产生冲突。

冲突处理

hash_table.png

常见面试题

图(graph)由多个节点(vertex)构成,节点之间阔以互相连接组成一个网络。(x, y)表示一条边(edge),它表示节点 x 与 y 相连。边可能会有权值(weight/cost)

图实例

图可以分为:有向图、无向图,编程语言中可能有:邻接矩阵、邻接表 对于图的搜索算法,则是:

  • 深度优先搜索(Depth First Search)
  • 广度优先搜索(Breadth First Search)

常见的面试题