1.スタックスタック
1.1スタック機能
- スタックは、線状構造であります
- 一端のみ添加元素から、要素は、同じ側(スタックの最上部)から除去することができます
- LIFO(ファーストアウト、LIFOで最終)
1.2アプリケーションスタック
- ユビキタスアンドゥ操作(元に戻します)
- プログラムを呼び出すシステムスタック
マッチング括弧( "{} []()[()]")
import java.util.Stack; class Solution { public boolean isValid(String s) { Stack<Character> stack = new Stack<>(); if(s == null) return false; for(int i = 0;i<s.length();i++) { char c = s.charAt(i); if(c == '(' || c == '[' || c=='{') { stack.push(c); } else { if(stack.isEmpty()) { return false; } if(c == ')' && stack.pop() != '(') return false; if(c == ']' && stack.pop() != '[') return false; if(c=='}' && stack.pop() != '{') return false; } } return stack.isEmpty(); } public static void main(String[] args) { System.out.println(new Solution().isValid("[]{}()")); System.out.println(new Solution().isValid("[()]{}()")); } }
1.3スタックの実装
:スタックの機能有していなければならない
1)描画、ボイドプッシュ(E、E)
2)スタックのうち、EのPOP()
3)上部要素に戻り、PEEK E()
)(4)スタックが空で、ブールのisEmptyであるか否かを判断します。
5))、スタック内の要素の数を返すINTのgetSize(スタックの定義をサポートするための汎用インターフェース
public interface Stack<E> { int getSize(); boolean isEmpty(); E peek(); E pop(); void push(E e);
ダイナミックアレイ・メモリ・スタック要素の定義において:
public class DynamicArray<E> { private E[] data; private int size; // 构造函数,传入数组的容量 public DynamicArray(int capacity) { data = (E[])new Object[capacity]; size = 0; } // 空构造,默认数组容量capacity=10 public DynamicArray() { this(10); } // 获取数组的容量 public int getCapacity(){ return data.length; } // 获取数组中元素个数 public int getSize(){ return size; } // 返回数组是否为空 public boolean isEmpty() { return size == 0; } // 向所有元素后添加一个新元素 public void addLast(E e) { // if(size == data.length) { // throw new IllegalArgumentException("AddLast failed. Array is full."); // } // // data[size] = e; // size++; add(size,e); } // 向所有元素前添加一个新元素 public void addFirst(E e) { add(0,e); } // 向指定位置插入一个新元素e public void add(int index,E e) { if(index<0 || index > size) { throw new IllegalArgumentException("Add failed. Require index >= 0 and index <= size."); } // 如果存储数据长度已超过数组容量,则扩容 if(size == data.length) resize(2*data.length); for(int i = size - 1;i>=index;i--) { data[i+1] = data[i]; } data[index] = e; size ++; } private void resize(int newCapacity) { E newData[] = (E[])new Object[newCapacity]; for(int i = 0;i<size;i++) { newData[i] = data[i]; } data = newData; } // 获取index索引位置的元素 public E get(int index) { if(index <0 || index >=size) { throw new IllegalArgumentException("Get failed. Index is illegal."); } return data[index]; } // 修改index索引位置的元素为e public void set(int index,E e) { if(index <0 || index >=size) { throw new IllegalArgumentException("Set failed. Index is illegal."); } data[index] = e; } // 查找数组中是否包含元素e public boolean contains(E e) { for(int i = 0;i<size;i++) { if (data[i].equals(e)) return true; } return false; } // 查找数组中元素e所在的索引,如果不存在元素e,则返回-1 public int find(E e) { for(int i = 0;i<size;i++) { if (data[i].equals(e)) return i; } return -1; } // 从数组中删除index索引位置的元素,返回删除的元素 public E remove(int index) { if(index<0 || index > size) { throw new IllegalArgumentException("Remove failed. Index is illegal."); } E ret = data[index]; for(int i = index + 1;i<size;i++) { data [i - 1] = data[i]; } size--; data[size] = null; // 如果存储数据的个数已小于容量的一半,则缩减容量 // 惰性,如果存储数据的个数已小于容量的1/4,则缩减一半容量 if(size==data.length/4) { resize(data.length/2); } return ret; } // 从数组中删除第一个元素,返回删除的元素 public E removeFirst() { return remove(0); } // 从数组中删除最后元素,返回删除的元素 public E removeLast() { return remove(size-1); } // 从数组删除元素e public void removeElement(E e) { int index = find(e); if(index != -1) remove(index); } @Override public String toString() { StringBuilder res = new StringBuilder(); res.append(String.format("Array: size = %d, capacity = %d \n", size,data.length)); res.append('['); for(int i = 0;i<size;i++) { res.append(data[i]); if(i!=size -1) res.append(","); } res.append(']'); return res.toString(); } public E getLast() { return get(size-1); } public E getFirst() { return get(0); } }
動的配列に基づいて、配列の実現スタック
public class ArrayStack<E> implements Stack<E> { DynamicArray<E> Array; public ArrayStack(int capacity) { Array = new DynamicArray<>(capacity); } public ArrayStack() { Array = new DynamicArray<>(); } @Override public int getSize() { return Array.getSize(); } @Override public boolean isEmpty() { return Array.isEmpty(); } @Override public void push(E e) { Array.addLast(e); } @Override public E peek() { return Array.getLast(); } @Override public E pop() { return Array.removeLast(); } @Override public String toString() { StringBuilder res = new StringBuilder(); res.append("Stack: "); res.append("["); for(int i = 0;i<Array.getSize();i++) { res.append(Array.get(i)); if(i != Array.getSize() - 1) res.append(","); } res.append("] top"); return res.toString(); } }
テスト
public class Main { public static void main(String[] args) { ArrayStack<Integer> stack = new ArrayStack<>(); for(int i = 0;i<5;i++) { stack.push(i); System.out.println(stack); // Stack: [0] top // Stack: [0,1] top // Stack: [0,1,2] top // Stack: [0,1,2,3] top // Stack: [0,1,2,3,4] top } stack.pop(); System.out.println(stack); // Stack: [0,1,2,3] top } }
スタックの複雑さの1.4分析
- ボイドプッシュ(E)// O(1)、(トリガ操作のサイズを変更してもよい)の複雑さを償却
- Eポップ()// O(1)、償却複雑さ(トリガ操作サイズを変更してもよいです)
- EのPEEK()// O(1)、最上位の要素に戻ります
- int型のgetSize()// O(1)
- ブールのisEmpty()// O(1)
2.キューキュー
2.1キューの機能
- FIFOのデータ構造
- 一方の端部(テール)から添加元素のみ、もう一方の端(最初のチーム)から要素を削除します
- 先入れ先出し(FIFO)
- 実生活では、広く使われている(キュー)
2.2アレイのキューを達成
基本的な機能
1)チームに、エンキュー(E E)を無効
2)デキュー、Eデキュー()
。3))キューエレメントの頭部、E getFrontを(取り外し
4)キューサイズを返し、(のgetSizeをINT)
。5)ブーリアン、キューが空であるか否かを判断しますisEmpty()定義のサポートジェネリック・キュー・インターフェース
public interface Queue<E> { int getSize(); boolean isEmpty(); void enqueue(E e); E getFront(); E dequeue(); }
アレイキューによって定義されるダイナミックアレイを使用
public class ArrayQueue<E> implements Queue<E> { private DynamicArray<E> array; public ArrayQueue(int capacity) { array = new DynamicArray<>(capacity); } public ArrayQueue() { array = new DynamicArray<>(); } @Override public int getSize() { return array.getSize(); } @Override public boolean isEmpty() { return array.isEmpty(); } @Override public void enqueue(E e) { array.addLast(e); } @Override public E dequeue(){ return array.removeFirst(); } @Override public E getFront() { return array.getFirst(); } @Override public String toString() { StringBuilder res = new StringBuilder(); res.append("Queue: "); res.append("front "+"["); for(int i = 0;i<array.getSize();i++) { res.append(array.get(i)); if(i != array.getSize() - 1) res.append(","); } res.append("] tail"); return res.toString(); } public static void main(String[] args) { ArrayQueue<Integer> queue = new ArrayQueue<>(); for(int i = 0;i<10;i++) { queue.enqueue(i); System.out.println(queue); if(i%3==2) { queue.dequeue(); System.out.println(queue); } } } }
2.3複雑分析キュー配列
- ボイドエンキュー(E)// O(1)、(トリガ操作のサイズを変更してもよい)の複雑さを償却
- そして、デキュー()// O(n)は、
- E getFront()要素に戻り// O(1)、最初のチーム
- int型のgetSize()// O(1)
- ブールのisEmpty()// O(1)
巡回キュー2.4
チームは常にキューを通過する必要があるアレイキューは、キューが高い複雑、前進するために、すべての要素をシフトし、その解決策は、円形のキューです。
- 鍵1:キューの記録ヘッドの前面を画定末尾位置とテール、エンキュー尾=(尾+ 1)%の長さ、フロントデキュー場合=(フロント+ 1)%の長さ
- キー2:キューが空になるとフロント==テール
- 3キー:キューがいっぱいになっている、追加の容量は時にフロント==(尾+ 1)%の長さが必要とされていることを示しています
実装プロセス:
ジェネリックインターフェイス
public interface Queue<E> { int getSize(); boolean isEmpty(); void enqueue(E e); E getFront(); E dequeue(); }
静的メンバ変数の定義は、動的循環キューを達成するために、アレイ、汎用インタフェースが含まれています
public class LoopQueue<E> implements Queue<E> { private E[] data; private int size; private int front,tail; public LoopQueue(int capacity) { data = (E[])new Object[capacity + 1];//循环队列要预留一个空间 front = 0; tail = 0; size = 0; } // 缺省构造 public LoopQueue() { this(10); } @Override public boolean isEmpty() { return front == tail; } @Override public int getSize() { return size; } public int getCapacity() { return data.length - 1; } @Override public void enqueue(E e) { // 如果队列已满,扩容 if(front == (tail + 1)%data.length) { resize(getCapacity()*2); } // 将元素添加到队尾 data[tail] = e; // tail 向后移一位,循环移位 tail = (tail + 1) % data.length; size++; } private void resize(int newCapacity) { E[] newData = (E[])new Object[newCapacity + 1]; for(int i =0 ; i<size;i++) { // 新队列的首位存储旧队列的front newData[i] = data[(i+front)%data.length]; } data = newData; front = 0; tail = size; } @Override public E dequeue() { if(isEmpty()) { throw new IllegalArgumentException("Dequeue failed. Queue is empty."); } // 取出队首元素,并将队首置空 E res = data[front]; data[front] = null; // front 向后移一位,循环 front = (front + 1)%data.length; size--; // 当队列元素等于容量的1/4时,缩小一半队列容量 if(size == getCapacity() / 4 && getCapacity() / 2 != 0){ resize(getCapacity() / 2); } return res; } @Override public E getFront() { if(isEmpty()) { throw new IllegalArgumentException("Queue is empty."); } return data[front]; } @Override public String toString() { StringBuilder res = new StringBuilder(); res.append(String.format("LoopQueue: size = %d , capacity = %d\n", size, getCapacity())); res.append("front "+"["); // 从队首开始循环,直到队尾结束 for(int i = front; i != tail ;i = (i+1)%data.length) { res.append(data[i]); // 未达到队尾时,添加间隔符 if((i+1)%data.length != tail) res.append(","); } res.append("] tail"); return res.toString(); } }
テスト
public static void main(String[] args) { LoopQueue<Integer> queue = new LoopQueue<>(); for(int i = 0;i<10;i++) { queue.enqueue(i); System.out.println(queue); if(i%3==2) { queue.dequeue(); System.out.println(queue); } } /* LoopQueue: size = 1 , capacity = 10 front [0] tail LoopQueue: size = 2 , capacity = 10 front [0,1] tail LoopQueue: size = 3 , capacity = 10 front [0,1,2] tail LoopQueue: size = 2 , capacity = 5 front [1,2] tail LoopQueue: size = 3 , capacity = 5 front [1,2,3] tail LoopQueue: size = 4 , capacity = 5 front [1,2,3,4] tail LoopQueue: size = 5 , capacity = 5 front [1,2,3,4,5] tail LoopQueue: size = 4 , capacity = 5 front [2,3,4,5] tail */ }
循環キューの時間複雑さの2.5分析
- ボイドエンキュー(E)// O(1)、(トリガ操作のサイズを変更してもよい)の複雑さを償却
- Eデキュー()// O(1)、償却複雑さ(トリガ操作サイズを変更してもよいです)
- E getFront()要素に戻り// O(1)、最初のチーム
- int型のgetSize()// O(1)
- ブールのisEmpty()// O(1)
3.円形のキューとキューの比較のアレイ
import java.util.Random;
public class Main {
public static double costTime(Queue<Integer> queue,int nCount) {
Random random = new Random();
long startTime = System.nanoTime();
for(int i = 0;i<nCount;i++) {
queue.enqueue(random.nextInt(Integer.MAX_VALUE));
}
for(int i = 0;i<nCount;i++) {
queue.dequeue();
}
long endTime = System.nanoTime();
return (endTime-startTime) / 1000000000.0 ;
}
public static void main(String[] args) {
int nCount = 100000;
ArrayQueue<Integer> arrayQueue = new ArrayQueue<>();
LoopQueue<Integer> loopQueue = new LoopQueue<>();
System.out.println("ArrayQueue:"+costTime(arrayQueue,nCount)); //ArrayQueue:5.000418012
System.out.println("LoopQueue:"+costTime(loopQueue,nCount)); // LoopQueue:0.022053394
}
}
4.まとめ
これは、メインのレッスンは二つの最も一般的なデータ構造、スタックとキューを学んだです。オペレーション、関数呼び出し、マッチングブラケットを元に戻し、ダイナミックアレイ・スタック関連機能によって実現することが容易である、FIFOキューは動的配列に基づいて線状構造である:線状構造がLIFOスタックであり、実用的なアプリケーションには、キューの関連する機能を実現するが、チームのキューの配列複雑性が比較的高く、従って、リサイクルの概念と、それがより効率的に円形キュー構造を実現することが可能であり、最終的な試験はまた、円形キューの効率を確認することができます。