数据结构与算法二

数据结构介绍


一、什么是数据结构

假如你现在在家里环顾一下你的四周,你可以看见用来放衣服的衣柜,用来存食物的冰箱,或者是用来放书的书架。相信你绝不会把衣服放到冰箱,把食物放到衣柜里保存吧!其实这就是数据结构。在计算机的世界里,不同的数据也是需要像你的衣服,食物一样找到合适的储存方法,一个程序员选择合适的数据结构加上合适的算法就可以写出优秀的程序了。

不同编程语言都有属于它们自己的数据类型,每种语言也都用数据结构来管理这些数据类型

二、数据结构:Arrays

1. Arrays介绍

数据被连续地储存到数组中,如下图的1,3,5,2,4。每个数据都占据一个房间并且每个所在的房间都有自己的房间号0,1,2,3,4(0代表第一个元素的房间号)。我们从数组开始学习数据结构的原因是数组可能拥有最简单的规则,并且它采用连续的储方式,易于我们理解。
在这里插入图片描述

2. 静态Arrays和动态Arrays

静态Arrays需要提前定好它的大小,也就是数组的大小被固定了。比如 int a[20] = {1,…,20}(C++)。如果我们需要在数组后面添加新元素就不能直接push一个新元素在数组a上,因为a的大小已经被固定了,这样我们就需要重新定义一个新的数组b[21]来装a和新的元素。但是像更高级的语言python或者Java Script的list,它们用动态Arrays的方式工作,内存是自动分配的,我们不需要自己分配内存。·动态Arrays不需要提前定好数组的大小,即用即扩展。需要注意的是虽然动态Arrays更加方便,但不一定就比静态Arrays好,因为底层语言如C++相比于更高级的语言python,它允许程序员自己控制内存的分配,这样就能够节省掉不必要的时间和内存的消耗,从而让代码运行的更快更有效率。

3. 操作Arrays的例子

现在有一个动态数组,我们可以对他进行如下的操作(不同语言写法不同):lookup,push,insert,delete等。lookup是根据房间号(index)查找数组内的某一个元素,它的复杂度是O(1),因为无论数组有多大,我们只需要将相应房间内的元素输出即可,无需遍历其他房间。push操作是将一个新的元素添加到数组的末端,同样这个操作与数组大小无关,复杂度是O(1)。Insert操作是将一个新的元素插入到数组的某一位置,由于新插入的元素会将插入位置后的所有元素向后位移一个房间,而最糟糕的情况就是向数组第一个位置前插入一个新的元素,这样其他所有元素都要向后搬一次家,这就导致了insert的复杂度是O(n)。同样delete操作和insert是相似的,delete指删除数组内某一个元素,由于删除掉的房间需要被后面的元素填满,那么delete的复杂度也是O(n)。

三、数据结构:Hash Tables

1. Hash tables介绍

假如一个水果商贩有许多个篮子,现在他把一个篮子贴上苹果的标签并放入100个苹果准备售卖,然后他又将另一个篮子贴上葡萄的标志并放入50个葡萄准备售卖。这个过程就和Hash tables的原理十分相似。如下图,在Hash tables中,keys就相当于我们例子中的葡萄和苹果(图片中换成了人名),接下来计算机使用一个函数(hash function)来为每一个key生成一串数字,随后利用这一串数字为每一个key分配独立的内存地址,最后将苹果或者葡萄的数量(图片中为人物代号)存入这个地址中。当我们需要寻找苹果蓝中苹果的数量的时候,计算机会再次使用hash函数解析“苹果”这个key并得到苹果在计算机内存中的地址,最后将这个地址中存储的100取出。

在这里插入图片描述
hash函数第一个特点是单向的,也就是只给出一个储存“苹果”的地址,没有人能够知道这个地址代表了“苹果”。第二个特点是hash函数对相同的key生成的那一串数字是相同的。

2. Hash tables的优点

hash tables的优点十分明显,相比于Arrays,它不采取连续储存的办法,所以针对Arrays的一些操作应用在hash tables上时,比如insert一个新元素,我们只需要hash函数为新的key分配内存后再将值存入此地址即可,时间复杂度为O(1)。同理,lookup,delete也都为O(1)。

3. Hash tables的缺点(Hash Collision)

由于hash函数生成的数字是有限长度的,当我们的需求超过这个极限的时候,hash函数就有可能将两个不同的对象映射到同一个内存地址上,如下图中的两个人John和Sandra。这个网址可以让你动手感受一下hash collision Open Hashing Visualization

解决hash collision的方法有很多,感兴趣的读者可以自行搜索维基百科,本文并不会详细讲解每一种方法。下图中右侧黑点处采用了separate chaining的方法解决了冲突的问题,相当于在一个地址上叠罗汉(运用linked list,后面会详细讨论),但当我们要搜索重叠区域的对象的时候会降低hash table的性能。

在这里插入图片描述
另一个hash tables的缺点是遍历所有keys是很慢的。

四、数据结构:Linked List(链表)

介绍linked list前我们先回顾一下数组和hash tables。对于一个放满的静态和动态数组,添加新元素需要额外分配新的内存空间(两倍),而且数组对sort和delete等操作的时间复杂度是O(n)。虽然hash tables没有这个问题,但是hash tables失去了数组有序存储的优点。这种情况下,linked list就有了它的用武之地。

singly linked list的概念如下图所示,每一个红色和绿色积木的组合叫做一个节点,红色积木用于储存数据,绿色积木用于指向下一个节点的位置(可以看成上一个绿色积木插在下一个红色积木的上面)。链表第一个节点叫做head,最后一个节点叫做tail,tail指向NULL(无)。如果你想在第二个节点后添加新的数据,那就从第一个head节点往后数两个节点,暂时从这个位置将链表断开,加入新的节点后再将他们在接在一起。可以看出,对于insert操作或delete操作,linked list的时间复杂度是O(n),因为我们需要从头开始数到我们的目标节点。你可能会问,既然arrays和linked list对于insert操作的时间复杂度都是O(n),linked list的优势在哪呢?要知道O(n)是我们考虑的最坏的情况,对于普遍情况,linked list和arrays的区别就是,arrays越往后insert元素他的操作越少,相反,linked list越往前insert元素操作越少,可以分析出,arrays的最坏情况(往第一个元素前插入新元素)是linked list的最优情况,也就是O(1)。这个网址能够让你手动感受linked list的工作原理动手尝试linked list

在这里插入图片描述
除了上面这种singly linked list,本文还要提到一种doubly linked list,它的结构如下图所示。doubly linked list与singly linked list的区别就是它能够从后向前搜寻。但是doubly linked list的缺点也显而易见,也就是需要更多的内存空间,所以相比于singly linked list执行insert或者delete操作时需要更多时间。

在这里插入图片描述

五、数据结构:Stacks和Queues(栈和队列)

1. Stacks和Queues介绍

Stacks是一种后进先出(LIFO)的数据结构,就好像洗好的盘子一个一个地摞在一起,那需要使用盘子的时候第一个被使用的盘子就是最后一个被清洗的盘子。浏览器历史记录也是使用了stacks。对于stacks,lookup操作的时间复杂度是O(n),对于pop或者push操作(去掉最上面的元素和添加一个新元素)时间复杂度是O(1)。

Queues是一种先进先出(FIFO)的数据结构,就像排队买东西,先买的人先走。对于队列,lookup操作同样也是O(n),添加新元素和退出第一个元素都是O(1)。

2. 如何搭建Stacks和Queues

对于Stacks,我们可以使用arrays和linked list来搭建,具体选择哪个根据前面提到的两种数据结构的特性来选择。
对于Queues,不要使用arrays来搭建,因为删除arrays第一个元素会导致其他元素位移。正确的选择是linked list。

六、数据结构:Trees(树)

1. Trees介绍

Trees数据结构如下图所示,linked list就是一种只有一条路线的trees结构。如果你搜索维基百科你会发现许多种不同的trees,不过本文只介绍绝大部分情况下最常见的几种类型。

在这里插入图片描述

2. Binary Trees

Binary Trees的规则很简单直接,每个节点只能拥有零个,一个或者两个节点,如下图所示。

在这里插入图片描述
Binary trees具体分为两种,Perfect Binary Tree和Complete Binary Tree(或者也可以叫做完全和几乎完全Binary Tree),如下图所示。两者的区别是完全Binary Trees更加有效率。完全Binary Trees有两个特点:1. 它的每一层都是上一层节点数的二倍。2. 某一层全部的节点数等于上面所有层的节点数的和加1。


我们可以用Binary Trees执行搜索任务(Binary Search Tree),举例来说现在有一个猜数游戏,你的朋友随机选择一个0到100以内的数,你每猜一次你的朋友都会告诉你猜大了还是猜小了直到你猜对为止。可以看出来,我们通常采取的策略就是每次都猜最中间的那个数字,这个猜法就像我们的Perfect Binary Tree。由于我们不会遍历所有的节点(0到100),所以它的时间复杂度要比O(n)要小得多,是O(log N)。所以对于Binary Search Tree,insert,lookup和delete操作的时间复杂度都是O(log N)。为什么我们不用hash tables的keys进行搜索呢?因为Binary Search Tree有特有的特征,也就是结构上的关系(类似于你电脑里一层一层的文件夹)。这个网址Binary Search Tree可以让你手动感受Binary Search Tree。

上面提到的Binary Search Tree的情况是均衡的情况,还有一种不均衡的Binary Search Tree,举例来说我们往一个Binary Search Tree添加新元素,新的元素一直走向Tree的最右侧就造成Tree的不均衡,这样在搜索的时候时间复杂度就变成了O(n),变多了。解决不均衡问题后面会详细讨论。

虽然Binary Search Tree看起来这么好(复杂度比O(N)小,有序,可变大小),但是他也有缺点,缺点就是他没有O(1)的操作。

3. AVL Trees和Red Black Trees

为了解决上述Binary Search Tree出现的不平衡的问题,利用使用这两种方法来重新平衡我们的Tree。它们是通过添加新元素后重新改编整个Tree的结构来实现平衡。这两种Trees相对来说比较复杂,通常使用library来调用它们。同样,读者有兴趣可以使用这个网站AVL Tree, 或者Red Black Tree动手实验一下。

4. Binary Heaps

(注:内存结构的heap,也就是堆,和数据结构的heap完全是两个东西,它们只是恰好名字一样而已,请读者不要带入另一个概念,不知道什么意思的读者可以忽略这句话。)

对于Binary Search Tree,一个节点的左右子节点是有优先级的,比如上面的猜数游戏的例子,当我们第一次猜50并且朋友告诉你猜大了的时候,你就不会取猜大于50的那组数了,但是对于binary heaps,它的结构和Binary Search Trees是一样的,区别是一个节点的左右两个子节点并无区别,也就是在猜数游戏中两个分岔路都会继续搜索,所以binary heaps的look up操作的时间复杂度是O(n)。你可能会问,这样岂不是更加浪费时间了吗,为什么要binary heaps呢?原因是binary heaps非常适合应用在需要比较的操作上。

Binary Heaps对于insert操作采取的是从右到左填充原则,也就是从左往右哪缺就把新元素补哪,这有什么好处呢?答案就是Binary Heaps不需要进行像Binary Search Trees一样进行平衡的操作。下面这个网址可以动手实践Binary Heaps,强烈建议试一下。

Binary Heaps实际应用的例子就是解决拥有优先级的queue问题,比如队列中有人是VIP,那么VIP就需要打破先进先出的原则被有限处理。Binary Heaps的缺点也十分明显,就是它的look up操作比较慢,是O(n)。

5. Trie (字典树)

就像名字表达的那样,Trie通常用来搜索文本,告诉使用者一段文本中有无一个单词或者单词的一部分。

在这里插入图片描述

七、数据结构:Graph(图)

1. Graph介绍

graph主要用于模拟现实生活的关系,举例来说,Facebook把它应用在社交关系网。如下图所示,每个数字叫做一个节点node,每个线叫做edge,nodes由edges连接起来。graph的优点正是可以很好地表现关系,缺点是很难扩展。

在这里插入图片描述

2. Graph的种类

第一种是nodes有无方向,有的graph可以有指向,有的是双向。
第二种是edges有无权重,权重可以告诉你两个nodes之间关系有多紧密(比如地图中两个点有多远等)。
第三种是graph有无闭环。


未完待续…

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值