背包、队列和栈的概念
背包、队列和栈都为一种数据类型(类)。
数据类型指的是一组值和一组对这些值的操作的集合。比如 String 类是一种数据类型,其值为保存的字符串,其操作即 length()、charAt(int index) 之类的方法。
背包、队列和栈的值为一组对象的集合(与数组类似),其操作即添加、删除或是访问集合中的对象。它们之间的差别在于删除或访问对象的顺序不同。
下面是三种数据类型的 API:
背包
背包是一种不支持从中删除元素的集合数据类型——它的作用就是帮助用例收集元素并迭代遍历所有收集到的元素。迭代的顺序不确定(或者说不要求确定)。
下面的用例借助背包来求一组数据的平均值和标准差。
背包的典型用例
public class Stats
{
public static void main(String[] args)
{
Bag<Double> numbers = new Bag<Double>();
while (!StdIn.isEmpty())
numbers.add(StdIn.readDouble());
int N = numbers.size();
double sum = 0.0;
for (double x : numbers)
sum += x;
double mean = sum/N;
sum = 0.0;
for (double x : numbers)
sum += (x - mean)*(x - mean);
double std = Math.sqrt(sum/(N-1));
System.out.printf("Mean: %.2f\n", mean);
System.out.printf("Std dev: %.2f\n", std);
}
}
使用方法
% java Stats
100
99
101
120
98
107
109
81
101
90
Mean: 100.60
Std dev: 10.51
队列
队列(全称先进先出队列)是一种基于先进先出(FIFO)策略的集合类型。所谓“基于先进先出(FIFO)策略”,也就是先进入队列的元素先出来。正如一群人在排队办理业务,先进入队列的人排在前面,其办理业务也最早,如图 1 所示。当用例迭代访问队列中的元素时,元素的处理顺序就是它们被添加到队列中的顺序。在应用程序中使用队列的主要原因是在用集合保存元素的同时保存它们的相对顺序:使它们入列顺序和出列顺序相同。
下面的 readInts() 方法为用例解决的问题是用例无需预先知道文件的大小即可将文件中的所有整数读入一个数组中。我们首先将所有的整数读入队列中,然后使用 Queue 的 size() 方法得到所需数组的大小,创建数组并将队列中的所有整数移动到数组中。队列之所以合适是因为它能够将整数按照文件中的顺序放入数组中(如果该顺序并不重要,也可以使用 Bag 对象)。
public static int[] readInts(String filePath) {
int[] a = null;
try {
File f = new File(filePath);
if (f.isFile() && f.exists()) {
String encoding = "UTF-8";
InputStreamReader reader = new InputStreamReader(new FileInputStream(f), encoding);
BufferedReader bufferedReader = new BufferedReader(reader);
String line = null;
Queue<Integer> q = new Queue<>();
// 读取文件中的整数,将其入列
while ((line = bufferedReader.readLine()) != null)
q.enqueue(Integer.parseInt(line.strip()));
bufferedReader.close();
reader.close();
int N = q.size();
a = new int[N];
// 从队列中取出整数,将其放入数组
for (int i = 0; i < N; i++)
a[i] = q.dequeue();
}
} catch (Exception e) {
System.out.println("读取文件内容出错");
e.printStackTrace();
}
return a;
}
栈
栈(全称下压栈/后进先出栈)是一种基于后进先出(LIFO)策略的集合类型,如图 2 所示。所谓“基于后进先出(LIFO)策略”,也就是后进入栈的元素先出来。正如你在网上冲浪时点击一个超链接,浏览器会显示一个新的页面(并将它压入一个栈),你不断点击超链接并访问新页面,但总是可以通过点击“回退”按钮重新访问以前的页面,返回到的页面(从栈中出来的页面)总是最近浏览(最近进入到栈中)的页面。
栈的另外一个典型用例是实现算术表达式求值。
实现
有两种常见的实现背包、队列和栈的方法,一种基于数组,一种基于链表。
总结自《算法(第四版)》1.3 背包、队列和栈