最常用、最基础的数据结构与算法

375 阅读4分钟

一、20个知识点

* 10个数据结构

数组、链表、栈、队列、散列表、二叉树、堆、跳表、图、Trie树

* 10个算法

递归、排序、二分查找、搜索、哈希算法、贪心算法、分治算法、回溯算法、动态规划、字符串匹配算法

二、基本概念:

什么是数据结构?什么是算法?

  • 数据结构就是一组数据的存储结构
  • 算法就是操作上数据的一组方法
  • 两者相辅相成,数据结构为算法服务,算法要作用在特定的数据结构之上

三、基础内容

3.1 数组

数组:数组是一种线性数据结构。他用一组连续的内存空间,来存储一组具有相同类型的数据。 关键词

  • 1、线性表(链表、队列、栈也是线性的,只有前后两个方向)
  • 2、连续的内存空间和相同类型的数据 利弊: 随机访问 低效的插入和删除

数组和链表的区别 数组支持随机访问,根据下标随机访问的时间复杂度为O(1) 数组插入、删除操作的平均时间复杂度是O(n)

警惕数组的访问越界问题 很多计算机病毒也正是利用到了代码中的数组越界可以访问非法地址的漏洞,来攻击系统,所以写代码的时候一定要警惕数组越界。

3.2 容器能否完全替代数组

  • ArrayList最大的优势就是可以将数组操作的细节封装起来,比如插入和删除是需要移动其他数据等。另外还支持动态扩容。
  • 数组本身的定义的时候需要预定注定的大小,因为需要分配连续的空间,我们申请大小为10的数组,当第11个数据想要插进来的时候,我们就需要重新申请一个更大的空间,将原来的数据复制过去,然后再将新的数据插入。
  • 如果使用ArrayList就完全不需要关系底层的扩容逻辑,他已经帮我们实现了。每次存储空间不够时会自动的扩容到原来的1.5倍。
  • 扩容也是涉及到内存申请和数据搬移的,所以最好事先指定合适大小的数据。事先指定大小可以省掉很多次内存申请和数据搬移操作。

上面是容器的优势,那么数组的用武之地在何处?

  • Java的ArrayList无法存储基本类型,比如int、long需要封装为Integer、Long 类而装箱和拆箱也是有一定的性能损耗的,所以如果特别关注性能、或者希望使用基本类型可以使用数组
  • 如果数组大小事先已知,并且对数据的操作非常简单,用不到ArrayList提供的大部分的方法,也可以直接使用数组。
  • 表示多维数组的时候,使用数组会更加直观。比如 Object[][] array;而用容器的话则需要这样定义:ArrayList > array。

总结

对于业务开发直接使用容器就好了,省时省力,比较性能损耗也就一丢丢,完全不会影响到系统的整体的性能,但是如果做底层的开发,比如开发网络框架,性能的优化就需要做到极致了。

3.3 为什么大多数编程语言中,数组要从 0 开始编号,而不是从 1 开始呢?

从数组存储的内存模型上来看,“下标”最确切的定义应该是“偏移(offset)”。前面也讲到,如果用 a 来表示数组的首地址,a[0]就是偏移为 0 的位置,也就是首地址,a[k]就表示偏移 k 个 type_size 的位置,所以计算 a[k]的内存地址只需要用这个公式:

a[k]_address = base_address + k * type_size

如果数组从 1 开始计数,那我们计算数组元素 a[k]的内存地址就会变为:

a[k]_address = base_address + (k-1)*type_size

对比这两个可以看出,从1开始编号对一次减法运算,对于CPU来说对执行一次减法指令。

3.4 总结

数组,可以说是最基础、最简单的数据结构了。数组用一块连续的内存空间,来存储相同类型的一组数据,最大的特点就是支持随机访问,但插入、删除操作也因此变得比较低效,平均情况时间复杂度为 O(n)。在平时的业务开发中,我们可以直接使用编程语言提供的容器类,但是,如果是特别底层的开发,直接使用数组可能会更合适。

3.4 反转链表