棧和佇列常見題型(java版)
阿新 • • 發佈:2019-01-27
直接上乾貨。。。。。
棧和佇列常見題型:
- 實現棧和實現佇列。
- 兩個棧實現一個佇列。
- 設計棧,使得pop,push和min時間複雜度為O(1)。
- 滑動視窗的最大值。
- 棧的進出序列。
實現棧和實現佇列
主要包括:棧,佇列,迴圈佇列。
package com.sywyg;
/**
* 實現棧
* 陣列應該是Object型別的
* 注意top的設定影響出棧和獲取棧頂元素。
* size也可以用top代替
*/
class MyStack<E>{
// 棧元素個數
private int size;
// 棧頭
private int top;
// 陣列儲存元素
private Object[] stack = null;
public MyStack(int length){
stack = new Object[length];
top = 0;
}
public int size(){
return size;
}
// 進棧
public void push(E e){
if(size == stack.length){
try{
throw new Exception("棧已滿" );
}catch(Exception ex){
ex.printStackTrace();
}
}
stack[top++] = e;
size++;
}
// 出棧
public E pop(){
E e = null;
if(size == 0){
try{
throw new Exception("棧為空");
}catch(Exception ex){
ex.printStackTrace();
}
}
e = (E)stack[--top];
size--;
return e;
}
// 獲取棧頂元素
public E top(){
if(size == 0){
try{
throw new Exception("棧為空");
}catch(Exception e){
e.printStackTrace();
}
}
return (E)stack[top-1];
}
}
/**
* 建立佇列,這種佇列會造成假溢位
*/
class MyQueue<E>{
// 佇列長度
private int size;
// 隊頭
private int front;
// 隊尾
private int back;
private Object[] queue;
public MyQueue(int length){
queue = new Object[length];
size = 0;
front = 0;
back = 0;
}
public int size(){
return size;
}
// 進隊
public void enqueue(E e){
if(size == queue.length){
try{
throw new Exception("隊已滿");
}catch(Exception ex){
ex.printStackTrace();
}
}
queue[back++] = e;
size++;
}
// 出隊
public E dequeue(){
E e = null;
if(size == 0 || back == front){
try{
throw new Exception("隊為空");
}catch(Exception ex){
ex.printStackTrace();
}
}
e = (E)queue[front++];
size--;
return e;
}
// 返回隊頭
public E front(){
return (E)queue[front];
}
// 返回隊尾?
public E back(){
return (E)queue[back - 1];
}
}
/**
* 迴圈佇列,採用浪費一個位置(使用size可以保證全利用)
* 這裡不使用size標記佇列的長度,儘管這種方式很簡單
*/
class LoopQueue<E>{
// 隊頭
private int front;
// 隊尾
private int back;
private Object[] queue;
public LoopQueue(int length){
// 浪費一個
queue = new Object[length + 1];
front = 0;
back = 0;
}
public int size(){
return (back - front + queue.length)% queue.length;
}
// 進隊
public void enqueue(E e){
if((front - back + queue.length)% queue.length == 1){
try{
throw new Exception("隊已滿");
}catch(Exception ex){
ex.printStackTrace();
}
}
queue[back ++ % queue.length] = e;
}
// 出隊
public E dequeue(){
E e = null;
if(front == back){
try{
throw new Exception("隊為空");
}catch(Exception ex){
ex.printStackTrace();
}
}
e = (E)queue[front++ % queue.length];
return e;
}
// 返回隊頭
public E front(){
return (E)queue[front];
}
// 返回隊尾?
public E back(){
return (E)queue[(back - 1 + queue.length) % queue.length];
}
}
兩個棧實現一個佇列
思想:一個棧A只進,一個棧B只出,B為空則A元素進入B,再出棧。
package com.sywyg;
/**
* 兩個棧實現一個佇列。
* 這樣仍然會造成假溢位。
*
*
*/
public static class Solution<E>{
// 應該在構造器中賦值。
// stack1用來入隊
private Stack<E> stack1 = new Stack<E>();
// stack2用來出隊
private Stack<E> stack2 = new Stack<E>();
// 入隊
public void enqueue(E e){
stack1.push(e);
//System.out.println("此時stack1棧頂元素為:" + stack1.top());
}
// 出隊
public E dequeue(){
E e = null;
if(stack2.size() != 0){
e = stack2.pop();
}else if(stack1.size() != 0){
int length = stack1.size();
for(int i = 0;i<length;i++){
stack2.push(stack1.pop());
//System.out.println("此時stack2棧頂元素為:" + stack2.top());
}
e = stack2.pop();
}
return e;
}
}
- 測試:進進出出,進出。
- 如何用兩個佇列實現一個棧。
#設計棧,使得pop,push和min時間複雜度為O(1)
思想:額外的棧A存放當前最小值,每當進來的值a小於/等於該棧頂值b時,a需要入棧A;出棧時若棧A的棧頂和剛出棧的元素相等時,則A也出棧。
```java
package com.sywyg;
import java.util.Stack;
/**
* 設計棧,使得pop,push和min時間複雜度為O(1)。
* 或者使用一個每個最小值再包含一個計數標記
*/
public static class Solution<E>{
private Stack<E> stack,stackMin;
public Solution(){
stack = new Stack<E>();
stackMin = new Stack<E>();
}
// 入棧
public void push(E e){
stack.push(e);
if(stackMin.isEmpty()){
stackMin.push(e);
}else if(stackMin.peek() >= e){
stackMin.push(e);
}
}
// 出棧
public E pop(){
E e = stack.pop();
if(e == stackMin.peek()){
stackMin.pop();
}
}
// 返回最小
public E min(){
return stackMin.peek();
}
}
<div class="se-preview-section-delimiter"></div>
- 測試:小大大大小小,每次出棧判斷是否正確。
- 時間複雜度:O(1),空間複雜度O(n)
滑動視窗的最大值
給定一個數組,和滑動視窗的大小,計算每個視窗中的最大值。例如陣列{1,2,3,4,5},視窗大小為3。那麼共存在3個滑動視窗,它們的大小為:{3,4,5}。
思想:須用到上面的第2題和第3題,用兩個能夠O(1)時間內計算出最大值的棧實現佇列。先進入3個(視窗大小),然後再依次進1出1個。在統計最大值的時候比較兩個棧中的最大值即可,注意需要判斷棧是否為空。
package com.sywyg;
import java.util.Stack;
/**
* 滑動視窗的最大值。
* @author sywyg
* 測試
*/
public class Question4 {
public static void main(String[] args) {
int[] array = {2,3,4,2,6,5,2,1};
int[] max = solution(array, 3);
for (int i = 0; i < max.length; i++) {
System.out.println(max[i]);
}
}
public static int[] solution(int[] array,int size){
int[] max = new int[array.length - size + 1];
MyQueue<Integer> queue = new MyQueue<Integer>();
int i = 0;
for(i = 0; i<size; i++){
queue.enqueue(array[i]);
}
int j = 0;
// 先進一個
max[j++] = queue.stack1.max();
for(;i<array.length;i++){
queue.dequeue();
queue.enqueue(array[i]);
// 兩個棧中的最大值進行比較
if(queue.stack2.stackMax.size() == 0 || queue.stack1.max() >= queue.stack2.max()){
max[j++] = queue.stack1.max();
}else
max[j++] = queue.stack2.max();
}
return max;
}
/**
* 兩個棧實現佇列
*/
public static class MyQueue<E>{
// 應該在構造器中賦值。
// stack1用來入隊
public MyStack<E> stack1 = new MyStack<E>();
// stack2用來出隊
public MyStack<E> stack2 = new MyStack<E>();
// 入隊
public void enqueue(E e){
stack1.push(e);
//System.out.println("此時stack1棧頂元素為:" + stack1.top());
}
// 出隊
public E dequeue(){
E e = null;
if(stack2.size() != 0){
e = stack2.pop();
}else if(stack1.size() != 0){
int length = stack1.size();
for(int i = 0;i<length;i++){
stack2.push(stack1.pop());
//System.out.println("此時stack2棧頂元素為:" + stack2.top());
}
e = stack2.pop();
}
return e;
}
}
/**
* pop(),push(),max() 複雜度為O(1)
*/
public static class MyStack<E>{
public Stack<E> stack,stackMax;
public MyStack(){
stack = new Stack<E>();
stackMax = new Stack<E>();
}
// 入棧
public void push(E e){
stack.push(e);
if(stackMax.isEmpty()){
stackMax.push(e);
}else if((Integer)stackMax.peek() <= (Integer)e){
stackMax.push(e);
}
}
// 出棧
public E pop(){
E e = stack.pop();
if(e == stackMax.peek()){
stackMax.pop();
}
return e;
}
// 返回最大
public E max(){
return stackMax.peek();
}
public E peek(){
return stack.peek();
}
public int size(){
return stack.size();
}
}
}
<div class="se-preview-section-delimiter"></div>
- 測試:有大有小的輸入。
- 時間複雜度:O(n)
棧的進出序列
輸入進棧的順序判斷給出的出棧順序是否正確。
思想:先進棧,然後判斷是否和給出的順序相等。若不相等則繼續進棧判斷,直到進完;若相等則繼續出棧判斷,直到出完。
package com.sywyg;
import java.util.Stack;
/**
* 棧的進出序列
* @author sywyg
* @since 2015.7.25
* 測試:
*/
public class Question5 {
public static void main(String[] args) {
Question5 question = new Question5();
int[] array1 = {1,2,3,4,7};
int[] array2 = {3,1,2,5,0};
System.out.println(question.solution(array1, array2));
}
// 第一個引數為輸入順序,第二個引數為要判斷的輸出結果
public boolean solution(int[] array1,int[] array2){
// 健壯性判斷
if(array1 == null || array2 == null) return false;
if(array1.length != array2.length) return false;
Stack<Integer> stack = new Stack<Integer>();
int i = 0,j = 0;
for (; i < array1.length; i++) {
stack.push(array1[i]);
while(stack.size() != 0 && stack.peek() == array2[j]) {
stack.pop();
j++;
}
}
return j == i?true:false;
}
}
- 測試:給出正確的,給出錯誤的,給出不相等的陣列。
- 時間複雜度:O(n)