java中堆栈的基本操作_玩儿转队列和栈的基本操作及其应用:Java 版
隊列的基本操作
隊列入隊出隊實現
隊列是種先進先出的數據結構。
隊列的基本操作主要是入隊和出隊。
數據從隊尾進入隊列,從隊首出隊列。
下面來寫一個簡單的隊列:
public class MyQueue {
private List data;
private int pointer;
public MyQueue() {
data = new ArrayList<>();
pointer = 0;
}
public boolean isEmpty() {
return pointer >= data.size();
}
/**
* Get the front item from the queue.
*/
public int Front() {
return data.get(pointer);
}
/**
* Insert an element into the queue. Return true if the operation is successful.
*/
public boolean enQueue(int x) {
data.add(x);
return true;
}
/**
* Delete an element from the queue. Return true if the operation is successful.
*/
public boolean deQueue() {
if (isEmpty()) {
return false;
}
pointer++;
return true;
}
}
其中,pointer代表頭隊首第一個位置的數據。
當頭部有數據出隊時,對應的pointer指針會往后移動一位。
如果有數據入隊,會直接加在隊尾:
我們會發現,隨著隊首數據的數據的出隊,隊首指針之前的空間都會被浪費掉,而且隨著使用次數變多,空間浪費會越來越高。
為了重復使用這部分空間,避免浪費,我們可以設計一個隊首與隊尾相連接的隊列,重復利用空間。
循環隊列設計
如上圖所示,數據1 ~ 10依次入隊,隊首head在1處,隊尾tail在10處,如果將1出隊列,head頭指針將移動到2:
此時,有一個空位,我們還可以繼續入隊:
這樣利用空間,剛好可以把隊首浪費的空間利用上。
class MyCircularQueue {
private Integer[] data; //隊列數據
private Integer size; //隊列大小
private Integer head = -1; //頭指針
private Integer tail = -1; //尾指針
/**
* 初始化隊列
*/
public MyCircularQueue(int k) {
this.data = new Integer[k];
this.size = k;
}
/**
* 入隊操作
*/
public boolean enQueue(int value) {
if (isFull()) {
return false;
}
if (head == -1) {
head++;
}
tail++;
data[tail % size] = value;
return true;
}
/**
* 出隊操作
*/
public boolean deQueue() {
if (isEmpty()) {
return false;
}
if (head == tail % size) {
head = -1;
tail = -1;
} else {
head++;
}
return true;
}
/**
* 獲取隊首元素
*/
public int Front() {
if (isEmpty()) {
return -1;
}
return data[head];
}
/**
* 獲取隊尾元素
*/
public int Rear() {
if (isEmpty()) {
return -1;
}
return data[tail % size];
}
/**
* 判斷隊列是不是為空
*/
public boolean isEmpty() {
return tail == -1 && head == -1;
}
/**
* 檢查隊列是不是已經滿了
*/
public boolean isFull() {
return (tail % size - head) == size - 1 || (head - tail % size) == 1;
}
}
廣度優先搜索(BFS)及其實現
上面我們已經實現過基本的隊列已經如果優化隊列,下面我們來看一個隊列在 BFS(廣度優先搜索)算法中的應用。
首先我們來定義結點:
@Getter
@Setter
@EqualsAndHashCode
@NoArgsConstructor
class Node implements Serializable {
private static final long serialVersionUID = 3687337665231315466L;
String value;
Collection previews; //前置結點
Collection tails; //后置結點
public Node(String value, Collection previews, Collection tails) {
this.value = value;
}
public Node(String value) {
this.value = value;
}
}
之后,我們先用隊列來實現簡單的BFS:
int BFS(Node root, Node target) {
Queue queue = new LinkedBlockingQueue(); // store all nodes which are waiting to be processed
int step = 0; // number of steps neeeded from root to current node
queue.add(root);
while (!queue.isEmpty()) {
step++;
int size = queue.size();
for (int i = 0; i < size; i++) {
Node cur = queue.poll();
if (cur.equals(target)) {
return step;
}
if (cur.tails != null) {
for (Node node : cur.tails) {
queue.add(node);
}
}
}
}
return -1;
}
另外,考慮到圖結構中,像上面這樣訪問,可能會存在訪問重復結點的情況,所以,我們記錄下訪問過的結點,訪問過就直接跳過。
下面是改進算法:
/**
* 避免一個節點訪問兩次,單獨檢查訪問過的結點
*
* @param root
* @param target
* @return
*/
int BFS(Node root, Node target) {
Queue queue = new LinkedBlockingQueue(); // store all nodes which are waiting to be processed
Set userd = new HashSet<>(); // node that be used
int step = 0; // number of steps neeeded from root to current node
queue.add(root);
userd.add(root);
while (!queue.isEmpty()) {
step++;
int size = queue.size();
for (int i = 0; i < size; i++) {
Node cur = queue.poll();
if (cur.equals(target)) {
return step;
}
if (cur.tails != null) {
for (Node node : cur.tails) {
if (!userd.contains(node)) {
queue.add(node);
userd.add(node);
}
}
}
}
}
return -1;
}
Stack
與隊列相反,棧是種先入后出的結構。
下面我們來看下Java里面的棧基本入棧和出棧是如何實現的:
首先是入棧操作:
/**
* Pushes an item onto the top of this stack. This has exactly
* the same effect as:
*
* addElement(item)
*
* @param item the item to be pushed onto this stack.
* @return the item argument.
* @see java.util.Vector#addElement
*/
public E push(E item) {
addElement(item);
return item;
}
/**
* Adds the specified component to the end of this vector,
* increasing its size by one. The capacity of this vector is
* increased if its size becomes greater than its capacity.
*
*
This method is identical in functionality to the
* {@link #add(Object) add(E)}
* method (which is part of the {@link List} interface).
*
* @param obj the component to be added
*/
public synchronized void addElement(E obj) {
modCount++;
ensureCapacityHelper(elementCount + 1);
elementData[elementCount++] = obj;
}
入棧 操作即將數據插入數組尾部。ps:入棧之前會去進行容量檢查,如果不夠,會進行內部數組的擴容操作,會重新產生大容量數組,并將原來老數組拷貝到新數組,完成擴容。過程跟 ArrayList 的類似。
下面來看下出棧:
/**
* Removes the object at the top of this stack and returns that
* object as the value of this function.
*
* @return The object at the top of this stack (the last item
* of the Vector object).
* @throws EmptyStackException if this stack is empty.
*/
public synchronized E pop() {
E obj;
int len = size();
obj = peek();
removeElementAt(len - 1);
return obj;
}
public synchronized void removeElementAt(int index) {
modCount++;
if (index >= elementCount) {
throw new ArrayIndexOutOfBoundsException(index + " >= " +
elementCount);
}
else if (index < 0) {
throw new ArrayIndexOutOfBoundsException(index);
}
int j = elementCount - index - 1;
if (j > 0) {
System.arraycopy(elementData, index + 1, elementData, index, j);
}
elementCount--;
elementData[elementCount] = null; /* to let gc do its work */
}
這里,直接將數組尾部的數移除。
深度優先搜索(DFS)
首先來看DFS的簡單遞歸實現:
/*
* 基于遞歸實現 DFS
*/
boolean DFS(Node cur, Node target, Set visited) {
if (cur == target) {
return true;
}
if (cur.tails == null || cur.tails.size() < 1) {
return false;
}
for (Node n : cur.tails) {
visited.add(n);
if (DFS(n, target, visited)) {
return true;
}
}
return false;
}
基于遞歸的實現,系統會自動幫我們生成堆棧調用,但是如果遞歸的深度過高的話,終將造成堆棧溢出。這時候,我們就需要自己用Stack實現這一過程:
/**
* 基于 stack
*
* @param cur
* @param target
* @return
*/
boolean DFS(Node cur, Node target) {
Set visited = new HashSet<>();
Stack stack = new Stack();
stack.push(cur);
while (!stack.isEmpty()) {
Node temp = stack.pop();
if (temp == target) {
return true;
}
for (Node n : temp.tails) {
if (!visited.contains(n)) {
visited.add(n);
stack.push(n);
}
}
}
return false;
}
總結
以上是生活随笔為你收集整理的java中堆栈的基本操作_玩儿转队列和栈的基本操作及其应用:Java 版的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: reducebykeyandwindow
- 下一篇: 类库java_Java类库和常用类库介绍