Data Structures and Algorithms Week8: Stacks, Queues and Graphs

2375 字
12 分钟
Data Structures and Algorithms Week8: Stacks, Queues and Graphs

Stacks and Queues#

Content#

  • Stacks
  • Abstract data types
  • Queues
  • Priority queues
  • Stacks and queues in the Java collections framework

Stacks#

  • A stack is an object that contains a list of elements, like an ArrayList.
  • However the methods of a stack allow us to add elements only to the top of the list, and to remove only the element that is at the top of the list.


  • 4 operations on stack

Stack feature:Last in first out#

  • A Stack is a “Last In First Out” (LIFO) structure.
  • Real world examples
    • A stack of plates waiting to be washed
    • A stack of cards in a patience game (see practical instructions).
    • Other Games such as Connect 4, Towers of Hanoi, etc.
    • Many examples in computer science – e.g. stack of method calls.

Implementing a stack using an Arraylist#

public class StringStack {
private ArrayList<String> list = new ArrayList<String>();
public void push(String s) {
list.add(s);
}
public String pop() {
assert list.size() != 0;
return list.remove(list.size()-1);
}
public String peek() {
assert list.size() != 0;
return list.get(list.size()-1);
}
public boolean isEmpty() {
return list.isEmpty();
}
}

What did we do#

  • We took an ArrayList, which has more than 25 methods, and used it to create a StringStack, that can perform only 4.
  • Everything we can do with a StringStack we can also do with an ArrayList.
  • There are many things we can do with an ArrayList but cannot do with the StringStack.
  • So what was the point of the stack? Is it wasteful?

Why would we do that#

  • There are many cases in which we want to treat data as being on a stack.
  • In these cases there are advantages in hiding all methods except those that treat the data as being on a stack.
    • It makes the program easier to understand, reason about, and modify.
    • It means that we can replace one implementation of a stack with another.

A better way to describe the procedure of program

Abstraction#

  • In computer science Abstraction is the hiding of irrelevant detail.
  • The fact that our StringStack uses an ArrayList to store data is irrelevant. We do not need to know that the stack is implemented in this way, and we do not want anyone to write code that is dependent on that particular implementation.

Hiding is better than showing more detail for you to worry about

Abstract data types#

  • A stack is an example of an Abstract Data Type (ADT).
  • An ADT is defined by a set of operations that can be performed on its data.
  • The ADT hides all details of how those operations are implemented, and how data are stored.
  • The operations that can be performed ADT define an interface to the ADT.

Stack exercise#

  • If s is an initially empty stack
  • state the values of the variables a and b after the following code fragment is run:
s.push("Red");
s.push("Orange");
s.push("Blue");
s.pop();
s.push("Yellow");
s.pop();
String a = s.pop();
s.push("Brown");
s.pop();
String b = s.pop();

Queues#

  • A queue is an ADT which maintains a list of data
  • The methods available allow us to add data only to the back of the queue, and to remove data only from the front of the queue.
  • A queue is FIFO (First In First Out) structure

  • 4 operations on queue

Implementing a queue using an Arraylist#

public class StringQueue {
private ArrayList<String> list = new ArrayList<String>();
public void add(String s) {
list.add(s);
}
public String remove() {
assert list.size() != 0;
return list.remove(0);
}
public String peek() {
assert list.size() != 0;
return list.get(0);
}
public boolean isEmpty() {
return list.isEmpty();
}
}

Queue Exercise#

  • If the q refers to a queue that is initially empty
  • state the values of the variables a and b after the following code fragment is run
q.add("Diamond");
q.add("Tobby");
q.remove();
q.add("Garnet");
q.add("Emeric");
q.remove();
String a = q.remove();
q.add("Sapphire");
String b = q.remove();

Priority queues#

  • A priority queue implements the same methods as an ordinary queue, but it works in BIFO (Best In First Out) manner.
  • Each element in the queue is allocated a priority, and the remove() method always removes the element with the highest priority.

  • For example suppose that a priority queue contains Strings, and that when two Strings are compared the one that is lowest in alphabetical order has highest priority.
  • The code
pq.add("Jam");
pq.add("Ham");
pq.add("Spam");
String best = pq.remove();

Results in best pointing to the String “Ham”.

Implementing a priority queue#

  • A simple implementation would use an ArrayList and would be like the queue implementation shown earlier except that the add method is implemented as follows:
public void add(String s) {
int i = 0;
int size = list.size();
while (i < size && s.compareTo(list.get(i)) > 0) {
i++;
}
list.add(i, s);
}
  • The add method would be O(n)

  • A more efficient implementation uses a data structure known as a heap (not the same as the heap). The add method is then O(log n)

  • We shall not cover heaps in this module.

Double-ended queue (DEQUE)#

  • A deque (pronounced “deck”) is like a queue, except that elements can be added or removed from either end.

Stacks and queues in the java collections framework#

  • The Java collections framework contains an interface Queue, which extends the Collection interface.
  • The Queue interface contains the methods that we have already mentioned when describing queues, plus a few others.
  • For example in addition to the remove() method, there is an poll() method. The difference between the two is that, if the queue is empty, remove() throws an exception whereas poll() returns a null value.
flowchart BT A["<span>&lt;&lt;interface&gt;&gt;<br><i>Queue</i></span>"] --> B["<span>&lt;&lt;interface&gt;&gt;<br><i>Collection</i></span>"]

  • Similarly the Queue interface provides add() and offer() methods, the difference being that add() has a void return type and throws an exception if the capacity of the queue is exceeded, whereas offer returns a Boolean value – false if the capacity was exceeded, and true otherwise.
  • The Queue interface is implemented by, among others, the LinkedList and ArrayDeque classes
flowchart BT A["<span>&lt;&lt;interface&gt;&gt;<br><i>Queue</i></span>"] --> B["<span>&lt;&lt;interface&gt;&gt;<br><i>Collection</i></span>"]

  • The Java collections framework does not contain a Stack interface. There is a Stack class, but its use is not advised. It is better to use the ArrayDeque class
  • There is a class PriorityQueue that implements the Queue interface and where the remove(), poll(), and peek() methods return the highest-priority element.

Simplified summary of some java collections framework classes#

classDiagram class Collection { <<interface>> size() isEmpty() } class Queue { <<interface>> add() offer() remove() poll() } class LinkedList class Deque { <<interface>> } class ArrayDeque Collection <|-- Queue Queue <|.. LinkedList Queue <|-- Deque Deque <|-- ArrayDeque

public class LinkedList<E> extends AbstractSequentialList<E> implements List<E>, DequeE>, Cloneable, Serializable {
transient int size;
transient LinkedList.Node<E> first;
transient LinkedList.Node<E> last;
private static final long serialVersionUID = 876323262645176354L;
public LinkedList() {
this.size = 0;
}
public LinkedList(@NotNull @Flow(sourcelsContainer = true, targetsContainer = true) Collection<? extends E> c) {
this();
this.addAll(c);
}

Summary#

  • A stack is a LIFO (Last-In, First-Out) structure.
  • A queue is a FIFO (First-In, First-Out) structure.
  • A priority queue is BIFO (Best-In, First-Out) structure.
  • A deque behaves like a queue except that data can be added or removed from either end.
  • Abstraction is the deliberate concealment of irrelevant detail.
  • An Abstract Data Type is defined by the operations that it allows you to perform on its data. The implementation of these methods, and the means by which data are stored, are deliberately concealed.
  • The Java collections framework contains classes and interfaces representing stacks, queues, priority queues, and deques.

Graphs#

Content#

  • Basic graph definitions
  • Implementation of graph data structures
  • Traversals and applications

Graphs: Nodes and Edges#

  • Network-like data structures can be represented using graphs.
  • A graph consists of a set of nodes (also known as vertices) together with a set of edges (also known as arcs) connecting them
  • Some graphs may have loops (a loop is an edge connecting a node to itself)

Graphs: Directed and Undirected#

  • Graphs can be directed (e.g. a street map with one way streets)
  • or undirected (e.g. street map where all streets can be traversed in any direction)

Example: Chengdu Subway#

Graphs: Data Storage#

  • As well as the nodes and edge structure, graphs typically have additional data stored.
  • This data can be at nodes and/or edges,
  • For example: in the Chengdu Subway graph:
    • nodes have names of stations
    • edges are labeled with the line(s) between those stations

Implementing Graphs on a Computer#

  • There are two common ways of representing graph data:
    • adjacency matrices
    • adjacency lists

Adjacency Matrix#

  • This uses a two-dimensional array of booleans(note this is a static data structure)
  • The array shows which edges are connected to which other edges.


  • Please draw the adjacency matrix for following graph


Directed Graph:

Straightforward to program:

boolean[][] edgeMatrix = new boolean[n][n];

An edge exists from node i to node j when edgeMatrix[i][j] is true

Adjacency Matrices#

  • Access to edge information is fast, due to use of arrays.
  • Storage of node data
    • use an additional array of information, one piece of information per node
  • Storage of edge data
    • use an additional 2D array of information, one piece of information per edge

Adjacency Matrices features#

  • Space usage:
    • For a graph with n\textcolor{orange}n nodes, it can have from 0\textcolor{orange}0 to n2\textcolor{orange}{n^2} edges (including loops and counting two-way edges twice), so the space taken up in memory to store the graph using an adjacency matrix is the space required to store n2\textcolor{orange}{n^2} boolean values.
  • If a graph is sparse (significantly fewer edges than n2n^2), this can be wasteful of space. There is a more efficient way of representing sparse graphs

Adjacency Lists#

  • For each node is stored a list of nodes that it connects to (note this is more of a dynamic data structure)

e.g. for a graph with n nodes labeled with integers, could use an array of length n, each pointing to the node’s adjacency list


  • Please draw the adjacency list for following graph

Adjacency Lists: integer labels#

  • e.g. if the nodes are labeled by integers, an adjacency lists structure could be of type
ArrayList<Integer>[]
  • An edge exists from node i to node j when j is a node in the list adjacencyLists[i]
ArrayList<Interger> adjacencyLists[]= new ArrayList[n];

Adjacency Lists: non-integer labels#

  • More generally, if there is a type Node for the nodes of the graph, an adjacency list structure could be
ArrayList<Node> adjacencyLists = new ArrayList<Node>();
  • where each Node would contain its own adjacency list of type ArrayList<Node>.
  • In this case, edge exists from node n to node m when m is a node in the adjacency list belonging to node n.

Class Teacher {
…………
public ArrayList<Teacher> adjacencyLists = new ArrayList<Teacher>();
}
…………
Teacher l,j,ja,jak;
l.adjacencyLists.add(j);

Adjacency Lists features#

  • Access to edge information is not as fast as an adjacency matrix, because the relevant list has to be traversed.
  • However, the amount of memory required to store the adjacency lists can be less, as it is proportional to how many edges are in the graph.

More edges more space

Traversals#

  • A traversal is a way of systematically going through all the data in a structure, visiting each piece of data precisely once.
  • Traversals can be used for various purposes, including searching.
  • Two common types of graph traversal:
    • Depth-first traversal
    • Breadth-first traversal

3 traversal methods for binary tree

Depth-First Traversal#

  • Start at some node S
    • If there is a node that is adjacent to S that you have not yet visited then visit it
    • If there is an unvisited node adjacent to the one you just visited, then visit that.


  • Keep on doing this until you come to a “dead end”, that is to say that you reach a node that either has no adjacent nodes at all, or where all the adjacent nodes have already been visited.
  • When you get to a dead end, “backtrack” along the path that you just followed until you come to a node that has an unvisited adjacent node, and repeat the process from there.

Depth-First Traversal: Another Example#

This traversal:
A,B,E,F,G,C,D,H,I\textcolor{orange}{A, B, E, F, G, C, D, H, I}
Different depth-first traversals are also possible, for example:
A,B,E,F,G,H,I,C,D\textcolor{orange}{A, B, E, F, G, H, I, C, D}

Depth-First Traversal: Notes#

  • DFT can be implemented on directed graphs (in that case, the edge directions must be respected) or on undirected graphs (every edge goes in both directions)
  • DFT can be implemented using recursion, or using a stack of nodes (not covered in this lecture)
  • In either case, a record needs to be kept of the set of nodes that have been visited
    • this set could be represented as an array of booleans, for example, one boolean for each node.

Depth-First Traversal pseudocode (recursive version)#

dft(g,n) {
visit(n)
mark n as visited
for each node x adjacent to n {
if x is unvisited then
dft(g,x)
}
}

Notes:

  • We assume that all nodes are originally marked as unvisited.

  • You don’t need to worry about what the visit method does (if you are worried, imagine that it just prints out the name of the node being visited).

Breadth-First Traversal#

  • Start at some point S
  • Repeat
    • Visit all the nodes adjacent to S
    • Visit all the nodes adjacent to the ones you just visited (unless you have already visited them)
    • And so on


Explores nodes in a pattern that “radiates out” from the start point.

Breadth-First Traversal Example#

  • Starting at the root node, visit all its neighbours before visiting all their neighbours, and so on.

One possible order of nodes visited using this method:
A,B,C,D,E,F,G,H,I\textcolor{orange}{A, B, C, D, E, F, G, H, I}

Note: as B was visited before C and D, B’s neighbors need to be visited before those of C and D.

Breadth-First Traversal: Notes#

  • BFT can also be implemented on directed graphs (respect the edge directions) or on undirected graphs (every edge goes in both directions)
  • BFT is usually implemented using a queue of nodes (the queue holds nodes waiting to be visited). The queue is used in the same way the stack is used for DFT. [Code not covered.]
  • Similarly as for DFT, a record needs to be kept of which nodes have been visited (e.g. in an array of booleans).

Using BFT/DFT to test connectivity#

  • Informally, an undirected graph is connected if you can draw it ALL without taking your pen off the paper (you may have to retrace some parts of it!)

Exercise#

  • To test the connectivity of an undirected* graph:

Algorithm:
Start from any node, do a BFT or DFT from that node, and test afterwards whether all the nodes were visited.

* It’s more complicated if the graph is directed.

Why?

Checklist#

  • What you want to be capable of by the end of this week:
    • know basic graph terminology
    • know suitable storage structures for graph data
    • perform both depth-first and breadth-first traversals of graphs
    • describe uses for graphs and traversals

支持与分享

如果这篇文章对你有帮助,欢迎分享给更多人或赞助支持!

赞助
Data Structures and Algorithms Week8: Stacks, Queues and Graphs
https://firefly.anka2.top/posts/obu/level5/semester2/dsa/week8/lecture/
作者
🐦‍🔥不死鸟Anka
发布于
2026-04-27
许可协议
CC BY-NC-SA 4.0

评论区

Profile Image of the Author
A-n-k-a
Over the Frontier / Into the Front
看这里~
合作翻译官绝赞招募中!
音乐
封面

音乐

暂未播放

0:00 0:00
暂无歌词
分类
标签
站点统计
文章
59
分类
6
标签
20
总字数
550,118
运行时长
0
最后活动
0 天前

目录