Skip to content

Commit

Permalink
algorithm
Browse files Browse the repository at this point in the history
  • Loading branch information
lyonyang committed Nov 10, 2020
1 parent f9cd881 commit ab70352
Show file tree
Hide file tree
Showing 13 changed files with 1,892 additions and 0 deletions.
165 changes: 165 additions & 0 deletions 08-算法/02-数组、栈、队列.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,165 @@
本文为 `《91 天学算法》` 中的讲义, 因为是非开源项目, 所以这里无法给出链接, 作者 [lucifer](https://lucifer.ren/blog/2020/05/23/91-algo/)

后期本人会重写, 暂时引用, 特此声明

# 数组

数组存储一系列同一数据类型的值(虽然在JavaScript数组可以保存不同类型的值)

![https://www.runoob.com/wp-content/uploads/2015/06/goarray.png](https://www.runoob.com/wp-content/uploads/2015/06/goarray.png)

(图来自 [https://www.runoob.com/go/go-arrays.html](https://www.runoob.com/go/go-arrays.html)

那么为什么数组要存储相同类型的值呢?为什么有的语言就可以存储不同类型的值呢?

实际上存储相同的类型有两个原因:

1. 相同的类型大小可以是固定的,这样数组就可以随机访问了。试想数组第一项是4字节,第二项是8字节,第三项是6字节,我怎么随机访问?
2. 强类型语言要求指定数组的类型。

数组的一个特点就是**支持随机访问**,请务必记住这一点。当你需要支持随机访问的数据结构的话, 自然而然应该想到数组。

本质上数组是一段连续的地址空间,这个是和我们之后要讲的链表的本质差别。 虽然二者从逻辑上来看都是线性的数据结构。

需要注意的是虽然数据结构有很多,比如树,图,哈希表,但是这只是逻辑数据结构。而真正实现它们的还需要落实到具体的物理数据结构,即**数组和链表**

- 一个数组表示的是一系列的元素
- 数组(static array)的长度是固定的,一旦创建就不能改变(但是可以有dynamic array)
- 所有的元素需要是同一类型(个别的语言除外)
- 可以通过下标索引获取到所储存的元素(随机访问)。 比如 array[index]
- 下标可以是是 0 到 array.length - 1 的任意整数

## 数组的常见操作

1. 随机访问
```py
arr = [1,2,33]
arr[0] # 1
arr[2] # 33
```
2. 遍历
```py
for num in nums:
print(num)
```
3. 任意位置插入元素、删除元素
```py
arr = [1,2,33]
# 在索引2前插入一个5
arr.insert(2, 5)
print(arr) # [1,2,5,33]
```

我们不难发现, 插入2之后,新插入的元素之后的元素(最后一个元素)的索引发生了变化,从2变成了3,而其前面的元素没有影响。从平均上来看,数组插入元素和删除元素的时间复杂度为$O{N}$。

> 删除也是类似
基本上数组都支持这些方法。 虽然命名各有不同,但是都是上面四种操作的实现

- each()
- push(item)
- pop()
- insert(item, index)

**时间复杂度分析**

- 随机访问 -> O(1)
- 根据索引修改 -> O(1)
- 遍历数组 -> O(N)
- 插入数值到数组 -> O(N)
- 插入数值到数组最后 -> O(1)
- 从数组最后删除数值 -> O(1)

## 多维数组

当数组里的元素也是一个数组的时候,就可以形成多维数组
例子:

1. 用一个多维数组表示坐标 `array[y]`
2. 用一个多维数组来记录照片上每一个pixel的数值

力扣中有很多二维数组的题目,我一般称其为board。

## 题目推荐

### [380. 常数时间插入、删除和获取随机元素](https://leetcode-cn.com/problems/insert-delete-getrandom-o1/)

### [75. 颜色分类](https://leetcode-cn.com/problems/sort-colors/)

做题的时候经常会用到数组,常常会遍历数组,排序数组以及将元素插入或删除


# Stack

![](https://tva1.sinaimg.cn/large/007S8ZIlly1gfbikq9ipmj30cd0a73yp.jpg)

栈只允许新的内容从一个方向插入或删除,这个方向我们叫栈顶,而从其他位置获取内容是不被允许的

栈最显著的特征就是LIFO(Last In, First Out - 后进先出)

举个例子:
栈就像是一个放书本的抽屉,进栈的操作就好比是想抽屉里放一本书,新进去的书永远在最上层,而退栈则相当于从里往外拿书本,永远是从最上层开始拿,所以拿出来的永远是最后进去的哪一个

## 栈的常用操作

1. 进栈 - push - 将元素放置到栈顶
2. 退栈 - pop - 将栈顶元素弹出
3. 栈顶 - top - 得到栈顶元素的值
4. 是否空栈 - isEmpty - 判断栈内是否有元素

## 栈的常用操作时间复杂度

由于栈只在尾部操作就行了,我们用数组进行模拟的话,可以很容易达到O(1)的时间复杂度,很完美!

1. 进栈 - O(1)
2. 出栈 - O(1)

![](https://tva1.sinaimg.cn/large/007S8ZIlly1gfbil9jqqej30sd0fhdgz.jpg)

## 题目推荐

- [1381. 设计一个支持增量操作的栈](https://leetcode-cn.com/problems/design-a-stack-with-increment-operation/)
- [394. 字符串解码](https://leetcode-cn.com/problems/decode-string/)


# 队列

- 正如前面所说,队列是逻辑结构,底层可以用数组实现,也可以用链表实现,不同实现有不同的取舍。
- 队列也是数据结构的其中一种,和栈相反的是。队列是只允许在一端进行插入,在另一端进行删除的线性表。与栈一样,队列也是一种**受限的数据结构**
- 队列(Queue)是一种先进先出(FIFO - First In First Out)的数据结构

- 队列中,插入元素会发生在尾部而删除元素会发生在头部 - 先进先出原则

![](https://tva1.sinaimg.cn/large/007S8ZIlly1gfbilukn6tj30ng0a30t6.jpg)

## 队列的操作

- 插入 - 在队列的尾部添加元素
- 删除 - 在队列的头部删除元素
- 查看首个元素 - 返回队列头部的元素的值

## 队列操作的时间复杂度

跟栈一样,队列的操作都是有效率的 -> O(1)

- 插入 - O(1)
- 删除 - O(1)

我们知道直接用数组模拟队列的话, 在队头删除元素是无法达到O(1) 的复杂度的, 上面提到了由于存在调整数组的原因,时间复杂度为$O(N)$。因此我们需要一种别的方式,这种防方式就是下面要讲的Linked List

## 队列的实现(Linked List)

我们知道链表的删除操作,尤其是删除头节点的情况下,是很容易做到O(1)。那么我们是否可利用这一点来弥补上面说的删除无法达到O(1)?

> 删除非头节点可以做到O(1)么?什么情况下可以?
但是在链表末尾插入就不是O(1),而是O(n)了。别急, 我们只要维护一个遍历tail 存放当前链表的尾节点即可在O(1)的时间完成插入操作。

具体来说有两个指针 - 一个在头部 一个在尾部。

![](https://tva1.sinaimg.cn/large/007S8ZIlly1gfbjwbk20wj30jr0mtq5q.jpg)


## 更多

- [基础数据结构 by lucifer](https://github.com/azl397985856/leetcode/blob/master/thinkings/basic-data-structure.md)
Loading

0 comments on commit ab70352

Please sign in to comment.