一、线性结构
1.1 数组结构
1.2 栈结构
1.2.1 概述
栈也是一种非常常见的数据结构,并且在程序中的应用非常广泛,我们知道数组是一种线性结构,并且可以在数组的任意位置插入和删除数据,但是有时候,我们为了实现某些功能,必须对这种任意性加以限制.。而栈和队列就是比较常见的受限的线性结构,我们先来学习栈结构.
栈结构示意图
栈只能在一段添加和删除元素(后进先出),比如要想删除最底层的元素必须要先把上面两层删掉,往栈里面添加叫做进栈,删除元素叫做出栈。
面试题
题目的意思不是说一次性全部进栈,而是进栈和出栈可以交错进行,a选项5优先出栈说明在这之前6已经进栈,5出栈后4进栈又出栈,3进栈后出栈,然后栈底的6出栈,2和1依次进栈然后1出栈,最后2出栈,A合理,后面三个根据此思路推断C不合法
1.2.2 栈结构的实现
实现栈结构有两种比较常见的方式:
- 基于数组实现
- 基于链表实现
栈常见操作
十进制转二进制
逻辑
将100转化为十进制,按照栈逻辑如下,100/2=50余0,0压入栈底,然后50/2=25余0,0压入栈底,25/2=12余1,1压入栈底。依次类推,当商为0时停止,此时将压入栈的数按顺序取出,就是二进制结果
1.3 队列结构
1.3.1 概述
队列结构和栈结构一样也是一种受限的线性结构,特点为先进先出,只允许在表的前端进行删除操作(队头),在表的后端进行插入操作(队尾)。
生活中类似的队列结构,生活中类似队列的场景就是非常多了
- 比如在电影院,商场,甚至是厕所排队.
- 优先排队的人,优先处理. (买票,结账,WC)
1.3.2 队列类的创建
队列的实现和栈一样, 有两种方案:
- 基于数组实现
- 基于链表实现
队列常见操作
击鼓传花逻辑
逻辑:利用队列解决修改规则后的击鼓传花问题,将六个元素放入到队列里面,每个人都有一个固定的下标值。设置一个数字例如5,第一个人数1,然后放到队列尾部,第二个元素数2再排列到最后,以此类推数到5的时候将元素去除,然后再开始第二轮,直至剩下最后一个人即胜利
1.4 优先级队列
1.4.1 概述
1.4.2 优先级队列的封装
实现优先级队列相对队列主要有两方面需要考虑:
1.4 链表结构(单向链表)
1.4.1 数组与链表的区别
链表和数组一样,可以用于存储一系列的元素,但是链表和数组的实现机制完全不同。虽然数组是最常见的数据结构但是数组也有缺点,数组的创建通常需要申请一段连续的内存空间(一整块的内存),并且大小是固定的(大多数编程语言数组都是固定的),所以当当前数组不能满足容量需求时,需要扩容(一般情况下是申请一个更大的数组,比如2倍,然后将原数组中的元素复制过去),而且在数组开头或中间位置插入数据的成本很高,需要进行大量元素的位移。
1.4.2 链表的概念
链表结构相当于火车,火车头head指向第一个车厢,其中item表示数据,next指向下一个节点,也就是说,节点由元素和指向下一个节点的引用组成,最后一个节点包含数据和指向null的引用。如果链表一个节点都没有head就指向null。
1.4.3 链表结构的封装
链表要包含head属性和一个个节点,一个节点包含数据data和下一个节点的引用next。
封装LinkedList的类,用于表示我们的链表结构。 (和Java中的链表同名,不同Java中的这个类是一个双向链表,后面我们会讲解双向链表)在LinkedList类中有一 个Node类,用于封装每一 个节点上的信息.(和优先级队列的封装一 样)链表中我们保存两个属性,一个是链表的长度,一个是链表中第个节点。
1.4.4 链表的常见操作
基本上可以分为增删改查四大部分加上最后三个之前队列学过的方法。
append方法
向链表尾部追加数据可能有两种情况:
- 链表本身为空,新添加的数据时唯一的节点
- 链表不为空,需要向其他节点后面追加节点
第一步先创建这个添加的节点,添加节点时需要进行判断,如果添加的节点是第一个,就把head指向第一个节点(一开始head指向null),以此类推,如果在节点后面再添加节点,第一步还是创建节点,然后找到这个节点的上一个节点,把上一个节点的指针next指向新的节点(新创建的节点next默认指向null)
论如何找到最后一个节点?定义一个current指向第一个节点,如果他的next不为null就不是最后一个节点,以此类推来找
toString方法
该方法比较简单,主要是获取每一 个元素,还是从head开头,因为获取链表的任何元素都必须从第一个节点开头,循环遍历每一 个节点并且取出其中的element,拼接成字符串,将最终字符串返回
insert方法
插入可能性:插入position=0的位置(即插到第一个元素的位置)这时候head指向这个元素,这个元素的next指向原来的第一个元素。
情况2:position=其他,例如下图等于2,此时current指向第三个元素,还要定义一个prevIoUs指向current上一个元素,新元素就差劲插进这两个之间(代码图俩标记处异曲同工)
元素插入链表中时,先连接元素与链表的右侧,再连接链表与元素的左侧
get(position)方法
indexOf方法
定义index和current指向第一个元素,当对比后发现不是第一个时index+1指向下一个元素,current也指向下一个,一个个判断,满足要求时直接返回index,对比完后都不满足就return一个-1。
update方法
removeAt方法
情况一:position=0,删除第一个元素,然后只需要把this.head指向被删除元素的下一个元素就可以了。
情况二:position=其他,例如position=3,也要先找到被删除元素的上一个和下一个元素,将上一个元素指向被删除元素的下一个,定义prevIoUs和current分别指向上一个元素和下一个元素
remove方法
调用之前封装的removeAt方法和indexOf方法来完成这个函数的功能
isEmpty方法和size方法
1.5 双向链表
1.5.1 与单向链表对比
单向链表缺点
只能从头遍历到尾或者从尾遍历到头(一般从头到尾)也就是链表相连的过程是单向的,实现的原理是上一个链表中有一个指向下一个的引用。我们可以轻松的到达下一个节点但是回到前一个节点是很难的,但是,在实际开发中,经常会遇到需要回到上个节点的情况
双向链表
既可以从头遍历到尾,又可以从尾遍历到头
也就是链表相连的过程是双向的,那么它的实现原理是每个节点既有向前连接的引用,也有一个向后连接的引用,双向链表可以有效的解决单向链表中提到的问题.
双向链表缺点
- 每次在插入或删除某个节点时,需要处理四个引用,而不是两个,也就是实现起来要困难一 些
- 并且相当于单向链表,必然占用内存空间更大一些。
- 可以使用一个head和一个tail分别指向头部(第一个节点)和尾部(最后一个节点)
- 每个节点都由三部分组成:指向前一个节点的prev、数据data以及指向下一个节点的next
- 双向链表的第一个节点的prev是null
- 双向链表的最后的节点的next是null
1.5.2 双向链表常见操作
倒数第二个方法是从后往前遍历链表返回字符串,最后一个方法是从前往后遍历链表并返回字符串形式,与toString一样的效果。
append方法
三个String方法
insert方法
如果要把上面的元素插到第一个,需要把原来第一个节点prev指向新的节点,然后新的节点的next指向原来的第一个节点,然后把this.head指向新的节点
如果要把元素插入中间某个位置,找到这个位置后,要将新节点的prev和next分别指向上一个节点(current.prev)和下一个节点(current),然后上一个节点的next指向新节点,current的prev指向新节点。
get方法
以下方法和单向链表类似,但是还有更好的方法,可以将链表长度的一半与position比较,如果一半更大,说明position更靠前,从前面开始找,反之从后面开始找(index–)
upDate方法
和get方法一样也可以判断是从后往前好还是从前往后好
removeAt方法
若删除的是第一个节点,要把第一个节点的下一个节点的prev指向null同时把this.head指向第二个节点
remove方法
与单向链表一致
其他方法
获取第一个元素和最后一个元素的方法是比较常见的方法,比较简单,后期如果想要一些其他用得上的方法也可以自行封装进去。
原文地址:https://www.jb51.cc/wenti/3285630.html
版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。