一、ADT

  在介绍抽象数据类型的时候,先看看什么是数据类型,在Java中可能首先会想到像 int,double这样的词,这是Java中的基本数据类型,一个数据类型会涉及到两件事:

  ①、拥有特定特征的数据项

  ②、在数据上允许的操作

  比如Java中的int数据类型,它表示整数,取值范围为:-2147483648~2147483647,还能使用各种操作符,+、-、*、/ 等对其操作。数据类型允许的操作是它本身不可分离的部分,理解类型包括理解什么样的操作可以应用在该类型上。

  那么当年设计计算机语言的人,为什么会考虑到数据类型?

  我们先看这样一个例子,比如,大家都需要住房子,也都希望房子越大越好。但显然,没有钱,考虑房子没有意义。于是就出现了各种各样的商品房,有别墅的、复式的、错层的、单间的……甚至只有两平米的胶囊房间。这样做的意义是满足不同人的需要。

  同样,在计算机中,也存在相同的问题。计算1+1这样的表达式不需要开辟很大的存储空间,不需要适合小数甚至字符运算的内存空间。于是计算机的研究者们就考虑,要对数据进行分类,分出来多种数据类型。比如int,比如float。

  虽然不同的计算机有不同的硬件系统,但实际上高级语言编写者才不管程序运行在什么计算机上,他们的目的就是为了实现整形数字的运算,比如a+b等。他们才不关心整数在计算机内部是如何表示的,也不管CPU是如何计算的。于是我们就考虑,无论什么计算机、什么语言都会面临类似的整数运算,我们可以考虑将其抽象出来。抽象是抽取出事物具有的普遍性本质,是对事物的一个概括,是一种思考问题的方式。

  抽象数据类型(ADT)是指一个数学模型及定义在该模型上的一组操作。它仅取决于其逻辑特征,而与计算机内部如何表示和实现无关。比如刚才说得整型,各个计算机,不管大型机、小型机、PC、平板电脑甚至智能手机,都有“整型”类型,也需要整形运算,那么整型其实就是一个抽象数据类型。  

  更广泛一点的,比如栈和队列这两种数据结构,分别使用了数组和链表来实现,比如栈,对于使用者只需要知道pop()和push()方法或其它方法的存在以及如何使用即可,使用者不需要知道我们是使用的数组或是链表来实现的。

  ADT的思想可以作为我们设计工具的理念,比如我们需要存储数据,那么就从考虑需要在数据上实现的操作开始,需要存取最后一个数据项吗?还是第一个?还是特定值的项?还是特定位置的项?回答这些问题会引出ADT的定义,只有完整的定义了ADT后,才应该考虑实现的细节。

  这在我们Java语言中的接口设计理念是想通的。

二、栈概述

  数组、链表、树等数据结构适用于存储数据库应用中的数据记录,它们常常用于记录那些现实世界的对象和活动的数据,便与数据的访问:插入、删除和查找特定数据项

  而栈和队列更多的是作为程序员的工具来使用。他们主要作为构思算法的辅助工具,而不是完全的数据存储工具。这些数据结构的生命周期比那些数据库类型的结构要短很多。在程序操作执行期间它们才被创建,通常它们去执行某项特殊的任务,当任务完成后就被销毁

  栈和队列的访问是受限制的,即在特定时刻只有一个数据项可以被读取或删除

  栈和队列是比数组和其他数据结构更加抽象的结构,是站在更高的层面对数据进行组织和维护

  栈的主要机制可用数组来实现,也可以用链表来实现。优先级队列的内部实现可以用数组或者一种特别的树——堆来实现。

  先来了解栈的概念和实例,然后分别深入理解队列和优先级队列

  栈只允许访问一个数据项:即最后插入的数据。移除这个数据项后才能访问倒数第二个插入的数据项。它是一种“后进先出”的数据结构。

  栈最基本的操作是出栈(Pop)、入栈(Push),还有其他扩展操作,如查看栈顶元素,判断栈是否为空、是否已满,读取栈的大小等

1.1、数组编写栈

代码地址:data-002-stack StackWithArray

1.2、栈示例

栈通常用于解析某种类型的文本串。通常,文本串是用计算机语言写的代码行,而解析它们的程序就是编译器

用栈来实现分隔符匹配。如:a{b(c[d]e)f}是否正确。

分析:每次都是成对出现,分隔符匹配程序从字符串中不断地读取程序,每次读取一个字符,若发现它是左分隔符({、[、(),将它压入栈中。当读到一个右分隔符时()、]、}),弹出栈顶元素,并且查看它是否和该右分隔符匹配。如果它们不匹配,则程序报错。如果到最后一直存在着没有被匹配的分隔符,程序也报错。

代码地址:data-002-stack 中的测试代码 StackWithArrayTest,其中主要逻辑

    public boolean check() {
int length = input.length();
StackWithArray<Character> stackWithArray = new StackWithArray(length); for (int i = 0; i < length; i++) {
char ch = input.charAt(i);
switch (ch) {
case '{':
case '[':
case '(':
stackWithArray.push(ch);
continue;
case '}':
case ']':
case ')':
if (!stackWithArray.isEmpty()) {
Character character = stackWithArray.pop();//左侧
if ((character == '{' && ch != '}') ||
(character == '[' && ch != ']') ||
(character == '(' && ch != ')')) {
System.out.println("匹配出错,字符是" + ch + ",下标:" + i);
return false;
}
}
continue;
default:
continue; } }
if (!stackWithArray.isEmpty()) {
System.out.println("匹配出错,有未关闭的括号");
return false;
}
return true; }

1.3、java提供的栈结构

主要是类:java.util.Stack 。可参看实现。

三、队列概述

  栈是“后进先出”(LIFO,Last InFirst Out)的数据结构,与之相反,队列是“先进先出”(FIFO,First InFirst Out)的数据结构

  队列的作用就像售票口前的人们站成的一排一样:第一个进入队列的人将最先买到票,最后排队的人最后才能买到票

  在计算机操作系统或网路中,有各种队列在安静地工作着。打印作业在打印队列中等待打印。当敲击键盘时,也有一个存储键盘键入内容的队列,如果我们敲击了一个键,而计算机又暂时在做其他事情,敲击的内容不会丢失,它会排在队列中等待,直到计算机有时间来读取它,利用队列保证了键入内容在处理时其顺序不会改变

  栈的插入和删除数据项的命名方法很标准,成为push和pop,队列的方法至今也没有一个标准化的方法,插入可以称作put、add或enque等,删除可以叫作delete、get、remove或deque等

2.1、数组编写队列

  队列的数据项并不都是从数组的第一个下标开始,因为数据项在数组的下标越小代表其在队列中的排列越靠前,移除数据项只能从队头移除,然后队头指针后移,这样数组的前几个位置就会空出来。

  这与我们的直观感觉相反,因为我们排队买票时,队列总是向前移动,当前面的人买完票离开后,其他人都向前移动,在一般的设计中,队列并没有向前移动,因为那样做会使效率大打折扣,只需要使用指针来标记队头和队尾,队列发生变化时,移动指针就可以,而数据项的位置不变

  但是,这样的设计还存在着一个问题,随着队头元素不断地移除,数组前面空出的位置会越来越多,当队尾指针移到最后的位置时,即使队列没有满,也不能再插入新的数据项了。

  解决这种缺点的方法是环绕式处理,即让队尾指针回到数组的第一个位置。这就是循环队列(也成为缓冲环)。虽然在存储上是线形的,但是在逻辑上它是一个首尾衔接的环形

代码地址:data-002-stack QueueWithArray

  还有一种称为双端队列的数据结构,队列的每一端都可以进行插入和移除操作。

  其实双端队列是队列和栈的综合体。如果限制双端队列的一段只能插入,而另一端只能移除,就变成了平常意义上的队列;如果限制双端队列只能在一端进行插入和移除,就变成了栈

2.2、优先队列【数组实现】

  像普通队列一样,优先级队列有一个队头和一个队尾,并且也是从队头移除数据,从队尾插入数据,不同的是,在优先级队列中,数据项按关键字的值排序,数据项插入的时候会按照顺序插入到合适的位置

  除了可以快速访问优先级最高的数据项,优先级队列还应该可以实现相当快的插入,因此,优先级队列通常使用一种称为堆的数据结构来实现。在下例中,简便起见,我们仍然使用数组来实现

  在数据项个数比较少,或不太关心速度的情况下,用数组实现优先级队列还可以满足要求,如果数据项很多,或对速度要求很高,采用堆是更好的选择

  优先级队列的实现跟上面普通队列的实现有很大的区别。

  优先级队列的插入本来就需要移动元素来找到应该插入的位置,所以循环队列那种不需要移动元素的优势就不太明显了。在下例中,没有设置队头和队尾指针,而是使数组的第一个元素永远是队尾,数组的最后一个元素永远是队头,为什么不是相反的呢?因为队头有移除操作,所以将队头放在数组的末端,便于移除,如果放在首段,每次移除队头都需要将队列向前移动

代码地址:data-002-stack PriorityQueueWithArray

004-数据结构之ADT-栈与队列【数组方式实现】的更多相关文章

  1. SDUT-3334_数据结构实验之栈与队列七:出栈序列判定

    数据结构实验之栈与队列七:出栈序列判定 Time Limit: 30 ms Memory Limit: 1000 KiB Problem Description 给一个初始的入栈序列,其次序即为元素的 ...

  2. SDUT-2088_数据结构实验之栈与队列十一:refresh的停车场

    数据结构实验之栈与队列十一:refresh的停车场 Time Limit: 1000 ms Memory Limit: 65536 KiB Problem Description refresh最近发 ...

  3. SDUT-2449_数据结构实验之栈与队列十:走迷宫

    数据结构实验之栈与队列十:走迷宫 Time Limit: 1000 ms Memory Limit: 65536 KiB Problem Description 一个由n * m 个格子组成的迷宫,起 ...

  4. SDUT-1479_数据结构实验之栈与队列九:行编辑器

    数据结构实验之栈与队列九:行编辑器 Time Limit: 1000 ms Memory Limit: 65536 KiB Problem Description 一个简单的行编辑程序的功能是:接受用 ...

  5. SDUT-3335_数据结构实验之栈与队列八:栈的基本操作

    数据结构实验之栈与队列八:栈的基本操作 Time Limit: 1000 ms Memory Limit: 65536 KiB Problem Description 堆栈是一种基本的数据结构.堆栈具 ...

  6. SDUT-3332&3333_数据结构实验之栈与队列五:下一较大值

    数据结构实验之栈与队列六:下一较大值 Time Limit: 150 ms Memory Limit: 8000 KiB Problem Description 对于包含n(1<=n<=1 ...

  7. SDUT-2134_数据结构实验之栈与队列四:括号匹配

    数据结构实验之栈与队列四:括号匹配 Time Limit: 1000 ms Memory Limit: 65536 KiB Problem Description 给你一串字符,不超过50个字符,可能 ...

  8. SDUT-2133_数据结构实验之栈与队列三:后缀式求值

    数据结构实验之栈与队列三:后缀式求值 Time Limit: 1000 ms Memory Limit: 65536 KiB Problem Description 对于一个基于二元运算符的后缀表示式 ...

  9. SDUT-2132_数据结构实验之栈与队列二:一般算术表达式转换成后缀式

    数据结构实验之栈与队列二:一般算术表达式转换成后缀式 Time Limit: 1000 ms Memory Limit: 65536 KiB Problem Description 对于一个基于二元运 ...

  10. SDUT-2131_数据结构实验之栈与队列一:进制转换

    数据结构实验之栈与队列一:进制转换 Time Limit: 1000 ms Memory Limit: 65536 KiB Problem Description 输入一个十进制非负整数,将其转换成对 ...

随机推荐

  1. jquery 第四章

    1.回顾 节点.append(内容) 节点.prepend(内容) 节点.remove() 节点.attr("属性","值") 节点.css("样式& ...

  2. 办公用品管理系统VB——库存数量导出EXCEL,SaveEXCEL

    办公用品管理系统VB——库存数量导出EXCEL,SaveEXCEL 总体来说,VB的EXCEL导出效率还是蛮低的,就是一个小型化的办公用品管理软件,不再优化了. 时间紧迫,就没有从头到尾的用C#编写, ...

  3. 图书管理系统 (c语言实现) (全部代码)

    直接上代码不解释 #include <stdio.h> #include <stdlib.h> #include <string.h> #define AVAILA ...

  4. jdbc和odbc

    JDBC简介 JDBC(Java Data Base Connectivity,java数据库连接)是一种用于执行SQL语句的Java API,它是Java十三个规范之一.可以为多种关系数据库提供统一 ...

  5. vue处理异步请求

    vue 处理异步请求 项目中需要 先调一个接口去取到人员编号,再去调另一个借口,人员编号作为参数才能去请求数据 用setTimeout 其实也可以,先new了一个promise对象 ,把请求放在里面, ...

  6. luogu P3952 时间复杂度 模拟

    题目链接 luogu P3952 时间复杂度 题解 直接模拟即可 注意不要直接return 我真是naive ...... 代码 #include<map> #include<sta ...

  7. 前端性能优化 —— 添加Expires头

    简要:添加Expires头能有效的利用浏览器的缓存能力来改善页面的性能,能在后续的页面中有效避免很多不必要的Http请求,WEB服务器使用Expires头来告诉Web客户端它可以使用一个组件的当前副本 ...

  8. 08_ for 练习 _ sumOf7

    <!DOCTYPE html> <html> <head> <meta charset="utf-8" /> <title&g ...

  9. (76)Wangdao.com第十四天_JavaScript 正则表达式对象 RegExp

    RegExp Regular Expression,正则表达式是一种表达    文本模式(字符串结构)  的式子. 常常用来按照“给定模式”匹配文本.比如,正则表达式给出一个 Email 地址的模式, ...

  10. Web版记账本开发记录(二)开发过程遇到的问题小结1 对数据库的区间查询

    问题1 对数据库的区间查询 如功能显示,想要按照年份和月份查询相应的记录,就要使用区间查询 对应的代码如下 servlet层的ChaXun java.sql.Date sDate = new java ...