目次
1.スタック
1.1 コンセプト
スタック:固定端の要素の挿入と削除のみを許可する特別な線形リスト。データの挿入および削除操作が実行される一方の端をスタックの最上部と呼び、もう一方の端をスタックの最下部と呼び、スタック内の要素は先入れ後出しの原則に従います。
Push : スタックの挿入操作 (プッシュまたはプッシュとも呼ばれ、スタックの最上部にデータを挿入します); Pop : スタックの削除操作、スタックの最上部のデータを削除します。
1.2 スタックの使用
方法 | 説明する |
スタック() | 空のスタックを構築する |
そして押す(そしてそして) | e をスタックにプッシュし、e を返す |
そしてポップ() | スタックから最上位の要素をポップして返します。 |
E ピーク() | スタックの最上位要素を取得します |
int サイズ() | スタック内の有効な要素の数を取得します |
ブール値の空() | スタックが空かどうかを確認する |
public static void main(String[] args) { Stack<Integer> stack = new Stack(); stack.push(1); stack.push(2); stack.push(3); stack.push(4); stack.push(5); System.out.println(stack.size());//5 //获取栈顶元素 System.out.println(stack.peek());//5 System.out.println(stack); //[1, 2, 3, 4, 5] stack.pop();//出栈 5 System.out.println(stack.size());//4 System.out.println(stack); //[1, 2, 3, 4] System.out.println(stack.empty());//false }
1.3 スタックのシミュレーション実装
図からわかるように、Stack はVector を継承します。Vectorは動的シーケンス リストであるという点でArrayListに似ていますが、 Vectorがスレッド セーフである点が異なります。
public class MyStack { int[] elem; int usedSize; public MyStack(){ elem = new int[3]; } //スタックがフルかどうかを判断する private boolean CapacityIsFull(){ return usedSize == elem.length; } / / 十分な容量を確保 private void ensureCapacity(){ if(CapacityIsFull()){ elem = Arrays.copyOf(elem,elem.length*2); } } //スタックにプッシュ public int Push(int data){ ensureCapacity( ); elem [usedSize++] = data; return data; } //スタックの最上位要素を取得 public int Peak(){ if(usedSize == 0){ throw new StackNullEception("スタックが空のため先頭要素を取得できません"); } return elem[usedSize-1]; } //Pop public int Pop (){ int e = Peak(); usedSize--; return e ; } //スタックの長さを取得 public int size(){ return usedSize; } //スタックが空かどうかを判断 public boolean empty(){ return usedSize == 0; } }
1.4 スタックアプリケーションのシナリオ
1. 要素の順序を変更する
1. プッシュ シーケンスが 1、2、3、4 で、プッシュ プロセス中にスタックをポップできる場合、次の不可能なポップ シーケンスは () です。A: 1,4,3,2 B: 2,3,4,1 C: 3,1,4,2 D: 3,4,2,12. スタックの初期状態は空です。ここで、要素 1 、 2 、 3 、 4 、 5 、 A 、 B 、 C 、 D 、および E を順番にスタックにプッシュし、それらを順番にスタックからポップします。は順番は()です。A: 12345ABCDE B: EDCBA54321 C: ABCDE12345 D: 54321EDCBA
//再帰メソッド void printList(Node head){ if(head == null){ return; } printList(head.next); System.out.println(head.val+" "); } //ループメソッド void printList2( Node head){ if(head == null){ return; } Stack<LinkList.Node> stack = new Stack<>(); //リンクリスト内の要素(ノード)をスタックに入れる Node cur = head; while (cur !=null){ stack.push(cur); cur = cur.next; } //スタック内の要素がポップされます while (!stack.empty()){ System.out.print(stack.pop () .val+" "); } }
3. ブラケットのマッチング
コード
public boolean isValid(String s) {
Stack<Character> stack = new Stack<>();
for(int i = 0; i< s.length(); i++){
char ch = s.charAt(i);
if(ch == '(' || ch == '[' || ch == '{'){
stack.push(ch);
} それ以外 {
if(stack.empty()){
false を返します。
}
// スタックからスタックの先頭を取得します
char ch1 = stack.pop();
if((ch1 =='(' && ch == ')') || (ch1 == '[' && ch == ']') || (ch1 == '{' && ch == '}' )){
続く;
} それ以外 {
false を返します。
}
}
}
//スタックにはまだ左括弧が残っています
if(!stack.empty()){
false を返します。
}
true を返します。
}
4. 逆ポーランド語表現の評価
コード
public int evalRPN(String[] トークン) {
Stack<Integer> stack = new Stack();
for(String s:tokens){
if(!(s.equals("+")||s.equals("-")||s.equals("*")||s.equals("/"))){
stack.push(Integer.parseInt(s));
}それ以外{
int num2 = stack.pop();
int num1 = stack.pop();
スイッチ{
「+」の場合:
stack.push(num1+num2);
壊す;
場合 "-":
stack.push(num1-num2);
壊す;
場合 "*":
stack.push(num1*num2);
壊す;
場合 "/":
stack.push(num1/num2);
壊す;
}
}
}
戻りstack.pop();
}
コード
public boolean IsPopOrder (int[] PushV, int[] PopV) {
Stack<Integer> stack = new Stack<>();
//i は PushV をトラバースし、j は PopV をトラバースします
int i = 0、j = 0;
for(;i < PushV.length; i++){
//スタックにプッシュ
stack.push(pushV[i]);
// スタックの最上位要素を取得します
int pe = stack.peek();
//スタックの最上位要素をスタックからポップアウトする必要があるかどうかを決定します
while(pe == PopV[j]){
stack.pop();
j++;
//スタックは空です
if(stack.empty()){
壊す;
}
//後でスタックをポップする必要があるかどうかを決定します
pe = stack.peek();
}
}
// スタックにはまだ要素が残っています
if(!stack.empty()){
false を返します。
}
true を返します。
}
6. 最小スタック数
コード
クラス MinStack {
//通常のスタック
プライベート Stack<Integer> スタック;
//最小値スタック-》現在の通常スタックに最小値を格納します
private Stack<Integer> minStack;
public MinStack() {
stack = 新しい Stack<>();
minStack = 新しい Stack<>();
}
//スタックにプッシュ
public void Push(int val) {
if(stack.empty()){
stack.push(val);
myStack.push(選択);
}それ以外 {
stack.push(val);
int Peak = minStack.peek();
//minStack をスタックにプッシュするかどうかを決定します
if(val <= ピーク){
myStack.push(選択);
}
}
}
//ポップ
public void Pop() {
//通常のスタックは空です
if(stack.empty()){
戻る;
}
int po= stack.pop();
//minStack がスタックからポップアウトするかどうかを決定します
if(po == minStack.peek()){
myStack.pop();
}
}
// スタックの最上位要素を取得します
public int top() {
//通常のスタックは空です
if(stack.empty()){
-1 を返します。
}
戻りstack.peek();
}
// 現在のスタックの最小値を取得します
public int getMin() {
//最小値スタック-》空
if(minStack.empty()){
-1 を返します。
}
minStack.peek() を返します。
}
}
1.5 概念の区別
スタック、仮想マシン スタック、スタック フレームの違いは何ですか?
スタックはデータ構造であり、仮想マシンのスタックは JVM によって分割されたメモリの一部であり、スタック フレームはメソッドの呼び出し時に仮想マシンによってメソッドに割り当てられるメモリの一部です。
2. キュー
2.1 コンセプト
キュー:一方の端でデータを挿入し、もう一方の端でデータを削除することのみを許可する特殊な線形テーブルです。キューには先入れ先出しの特性があります。キューに入る:挿入操作が実行される端は末尾と呼ばれます/rear .キューの外:削除操作を実行する端はキューの先頭と呼ばれます。
2.2 キューの使用
方法 | 説明する |
ブール値オファー(E e) | 列 |
Epoll() | デキューして返す |
E ピーク() | head要素を取得する |
int サイズ() | キュー内の有効な長さの数を取得します |
ブール値は空です | キューが空かどうかを判断する |
Queueはインターフェイスであり、 LinkedList はQueueインターフェイスを実装しているため、 LinkedListオブジェクトはインスタンス化時にインスタンス化する必要があります。
public static void main(String[] args) { Queue<Integer> queue = new LinkedList<>(); //キューに入れます queue.offer(1); queue.offer(2); queue.offer(3); System . out.println(queue);//[1, 2, 3] //キューの先頭要素を取得 int t = queue.peek(); System.out.println(t);//1 //キューの外へ System.out.println(queue.poll());//1 System.out.println(queue);//[2, 3] //キューが空かどうかを判断する boolean bool = queue.isEmpty(); System .out.println(bool);//false }
2.3 キューのシミュレーション実装
/*双方向チェーンキュー*/ public class MyLinkqueue { static class ListNode{ int val; ListNode next; ListNode pre; public ListNode(int val){ this.val = val; } } ListNode first;//キューヘッド ListNode last ; //キューの末尾 int usedsize; //キュー内の要素の数 //キューを入力 public boolean offer(int data){ ListNode node = new ListNode(data); if(first == null){ //空のキュー first = ノード ; last = ノード; }else { last.next = ノード; node.pre = last; last = last.next; } usedsize++; return true; } //先頭要素を取得 public int Peak(){ if(usedsize == 0){ return -1; } return first.val; } // public int pollをデキューします(){ int x = Peak(); //要素は 1 つだけ if(first.next==null){ first = null; last = null; } first = first.next; first.pre = null; usedsize-- ; return x; } //キュー内の要素の数を取得します public int size(){ usedsize を返す; } //キューが空かどうかを判断します public boolean isEmpty(){ return usedsize == 0; } }
2.4 循環キュー
3. 両端キュー
両端キュー ( deque ) は、両端が操作の入力とキューからの取り出しを許可するキューを指します。これは、要素をキューの先頭から入力したりキューから取り出したりできるほか、キューの最後から要素を入力したりキューから取り出したりできることを示します。 。
Dequeはインターフェイスであり、使用する場合はLinkedListオブジェクトを作成する必要があります。
実際のプロジェクトではDequeインターフェースがよく使われており、スタックとキューの両方でこのインターフェースを使用できます。
Deque<Integer> stack = new ArrayDeque<>();// 両端キューの線形実装Deque<Integer> queue = new LinkedList<>();//両端キューの連鎖実装
4. 面接での質問
1.キューを使用してスタック リンクを実装する
コード
クラスMyStack {
プライベートキュー<整数> queue1 ;
プライベートキュー<整数> queue2 ;
public MyStack() {
queue1 = 新しい LinkedList<>();
queue2 = 新しい LinkedList<>();
}
//スタックにプッシュ
public void Push(int x) {
//スタックは空です
if (空()){
queue1.offer(x);
}それ以外 {
//空ではないキューに入れます
if(!queue1.isEmpty()){
queue1.offer(x);
}それ以外 {
queue2.offer(x);
}
}
}
//ポップ
public int Pop() {
//スタックは空です
if(空()){
-1 を返します。
}
if(!queue1.isEmpty()){
//queue1 のサイズ 1 の要素を queue2 に入れる
while (queue1.size()>1){
int x= queue1.poll();
queue2.offer(x);
}
// x をデキューして x を返す
int x = queue1.poll();
x を返します。
}それ以外 {
//キュー2のサイズ1の要素をキュー1に入れる
while (queue2.size()>1){
int x= queue2.poll();
queue1.offer(x);
}
// x をデキューして x を返す
int x = queue2.poll();
x を返します。
}
}
// スタックの最上位要素を取得します
public int top() {
//スタックは空です
if(空()){
-1 を返します。
}
if(!queue1.isEmpty()){
//queue1 のサイズ 1 の要素を queue2 に入れる
while (queue1.size()>1){
int x= queue1.poll();
queue2.offer(x);
}
//x を別のキューにデキューし、x を返します
int x = queue1.poll();
queue2.offer(x);
x を返します。
}それ以外 {
//キュー2のサイズ1の要素をキュー1に入れる
while (queue2.size()>1){
int x= queue2.poll();
queue1.offer(x);
}
//x を別のキューにデキューし、x を返します
int x = queue2.poll();
queue1.offer(x);
x を返します。
}
}
//スタックが空かどうかを判定する
public boolean empty() {
//両チームとも空です -> スタックは空です
return queue1.isEmpty() && queue2.isEmpty();
}
}
2. スタックを使用してキュー リンクを実装する
コード
クラスMyQueue {
private Stack<Integer> stack1;//エンキュー
private Stack<Integer> stack2;//出队
public MyQueue() {
stack1 = 新しい Stack<>();
sstack2 = 新しい Stack<>();
}
//キューに入る
public void Push(int x) {
stack1.push(x);
}
//デキュー
public int Pop() {
// キューは空です
if(空()){
-1 を返します。
}
// stack2 が空でないことを確認する
if(stack2.isEmpty()){
while (!stack1.isEmpty()){
stack2.push(stack1.pop());
}
}
戻りstack2.pop();
}
//キューの先頭要素を取得する
public int Peak() {
// キューは空です
if(空()){
-1 を返します。
}
// stack2 が空でないことを確認する
if(stack2.isEmpty()){
while (!stack1.isEmpty()){
stack2.push(stack1.pop());
}
}
戻り stack2.peek();
}
//キューが空かどうかを判断する
public boolean empty() {
//両方のスタックが空 -> キューが空
return stack1.isEmpty() && stack2.isEmpty();
}
}