時々、次のインタビューの質問に出くわします:1つのアレイで3つのスタックを実装する方法は?もちろん、静的割り当ては解決策ではありません。
(時間ではなく)効率的なスペース。あなたは出来る:
1)アレイのエンドポイントから始まり、反対方向に成長する2つのスタックを定義します。
2)3番目のスタックを、真ん中から始まり、任意の方向に成長するものとして定義します。
3)プッシュ操作を再定義して、操作が他のスタックを上書きするときに、プッシュする前に中間スタック全体を反対方向にシフトするようにします。
最初の2つのスタックのスタックトップと、3番目のスタックの開始と終了を何らかの構造で保存する必要があります。
編集
上記の例をご覧ください。問題のヒューリスティックに応じて他の戦略を選択することもできますが、シフトは等スペースパーティションポリシーで行われます。
編集
@ruslikの提案に従って、middleスタックは、後続のプッシュのために交互のシーケンスを使用して実装できます。結果のスタック構造は次のようになります。
|エレム6 |エレム4 |エレム2 |エレム0 |エレム1 |エレム3 |エレム5 |
この場合、要素の数nを中間スタックに保存し、関数を使用する必要があります。
f[n_] := 1/4 ( (-1)^n (-1 + 2 n) + 1) + BS3
このスタックに使用する次の配列要素を知るため。
おそらくこれによりシフトが少なくなりますが、3つのスタックの実装は同種ではなく、不均一(ご存じのとおり)は特別な場合、バグの増加、コードの維持の困難につながります。
1つのスタックのすべての項目を配列の「端」にまとめようとする限り、3番目のスタック用のスペースが不足しています。
ただし、スタック要素を「点在させる」ことができます。最初のスタックの要素はインデックスi * 3
にあり、2番目のスタックの要素はインデックスi * 3 + 1
にあり、3番目のスタックの要素はインデックスi * 3 + 2
にあります(i
は整数)。
+----+----+----+----+----+----+----+----+----+----+----+----+----+..
| A1 : B1 : C1 | A2 : B2 : C2 | : B3 | C3 | : B4 : | :
+----+----+----+----+----+----+----+----+----+----+----+----+----+..
^ ^ ^
A´s top C´s top B´s top
もちろん、このスキームは、特にスタックのサイズが等しくない場合、スペースを無駄にします。上記のような任意の複雑なスキームを作成することもできますが、提起された質問に対するこれ以上の制約を知ることなく、ここで停止します。
更新:
非常に良い点を持っている以下のコメントのために、散在する必要はなく、次のようなはるかに単純なメモリレイアウトと比較するとパフォーマンスが低下する可能性があることを追加する必要があります。
+----+----+----+----+----+----+----+----+----+----+----+----+----+..
| A1 : A2 : : : | B1 : B2 : B3 : B4 : | C1 : C2 : C3 :
+----+----+----+----+----+----+----+----+----+----+----+----+----+..
^ ^ ^
A´s top B´s top C´s top
つまり、各スタックに独自の連続したメモリブロックを割り当てます。実際の質問が、各スタックを必要以上に制限しないために、固定メモリ量を可能な限り最大限に活用する方法である場合、私の答えはあまり役に立ちません。
その場合、私は @ belisarius 'answer を選択します:1つのスタックは、メモリ領域の「下部」端に移動し、「上方」に成長します。別のスタックはメモリ領域の「上端」に移動し、「下向き」に成長します。1つのスタックは中央にあり、任意の方向に成長しますが、他のスタックの1つに近づきすぎると移動できます。
3つのスタックすべてに対して単一のアリーナを維持します。スタックにプッシュされた各要素には、前の要素への逆方向ポインターがあります。各スタックの下部には、NULL/Noneへのポインターがあります。
アリーナは、空き領域内の次のアイテムへのポインターを維持します。 Pushは、この要素をそれぞれのスタックに追加し、空きスペースにないことをマークします。ポップすると、各スタックから要素が削除され、空きリストに追加されます。
このスケッチから、スタック内の要素には、データ用のリバースポインターとスペースが必要です。空き領域の要素には2つのポインターが必要なので、空き領域は二重にリンクされたリストとして実装されます。
3つのスタックを含むオブジェクトには、各スタックの先頭へのポインターと空きリストの先頭へのポインターが必要です。
このデータ構造は、すべてのスペースを使用し、一定の時間でプッシュおよびポップします。スタック内のすべてのデータ要素に対して1つのポインターのオーバーヘッドがあり、フリーリスト要素は最大(2つのポインター、1つのポインター+ 1つの要素)を使用します。
後で:pythonコードは次のようになります。ポインタとして整数インデックスを使用することに注意してください。
class StackContainer(object):
def __init__(self, stack_count=3, size=256):
self.stack_count = stack_count
self.stack_top = [None] * stack_count
self.size = size
# Create arena of doubly linked list
self.arena = [{'prev': x-1, 'next': x+1} for x in range(self.size)]
self.arena[0]['prev'] = None
self.arena[self.size-1]['next'] = None
self.arena_head = 0
def _allocate(self):
new_pos = self.arena_head
free = self.arena[new_pos]
next = free['next']
if next:
self.arena[next]['prev'] = None
self.arena_head = next
else:
self.arena_head = None
return new_pos
def _dump(self, stack_num):
assert 0 <= stack_num < self.stack_count
curr = self.stack_top[stack_num]
while curr is not None:
d = self.arena[curr]
print '\t', curr, d
curr = d['prev']
def _dump_all(self):
print '-' * 30
for i in range(self.stack_count):
print "Stack %d" % i
self._dump(i)
def _dump_arena(self):
print "Dump arena"
curr = self.arena_head
while curr is not None:
d = self.arena[curr]
print '\t', d
curr = d['next']
def Push(self, stack_num, value):
assert 0 <= stack_num < self.stack_count
# Find space in arena for new value, update pointers
new_pos = self._allocate()
# Put value-to-Push into a stack element
d = {'value': value, 'prev': self.stack_top[stack_num], 'pos': new_pos}
self.arena[new_pos] = d
self.stack_top[stack_num] = new_pos
def pop(self, stack_num):
assert 0 <= stack_num < self.stack_count
top = self.stack_top[stack_num]
d = self.arena[top]
assert d['pos'] == top
self.stack_top[stack_num] = d['prev']
arena_elem = {'prev': None, 'next': self.arena_head}
# Link the current head to the new head
head = self.arena[self.arena_head]
head['prev'] = top
# Set the curr_pos to be the new head
self.arena[top] = arena_elem
self.arena_head = top
return d['value']
if __== '__main__':
sc = StackContainer(3, 10)
sc._dump_arena()
sc.Push(0, 'First')
sc._dump_all()
sc.Push(0, 'Second')
sc.Push(0, 'Third')
sc._dump_all()
sc.Push(1, 'Fourth')
sc._dump_all()
print sc.pop(0)
sc._dump_all()
print sc.pop(1)
sc._dump_all()
この質問に対する解決策があります。次のプログラムは、配列(私の場合はStackNodeオブジェクトの配列)を最大限に活用しています。これについて何か質問があれば教えてください。 [ここではかなり遅れているので、コードを文書化することはありませんでした-知っておくべきです:)]
public class StackNode {
int value;
int prev;
StackNode(int value, int prev) {
this.value = value;
this.prev = prev;
}
}
public class StackMFromArray {
private StackNode[] stackNodes = null;
private static int CAPACITY = 10;
private int freeListTop = 0;
private int size = 0;
private int[] stackPointers = { -1, -1, -1 };
StackMFromArray() {
stackNodes = new StackNode[CAPACITY];
initFreeList();
}
private void initFreeList() {
for (int i = 0; i < CAPACITY; i++) {
stackNodes[i] = new StackNode(0, i + 1);
}
}
public void Push(int stackNum, int value) throws Exception {
int freeIndex;
int currentStackTop = stackPointers[stackNum - 1];
freeIndex = getFreeNodeIndex();
StackNode n = stackNodes[freeIndex];
n.prev = currentStackTop;
n.value = value;
stackPointers[stackNum - 1] = freeIndex;
}
public StackNode pop(int stackNum) throws Exception {
int currentStackTop = stackPointers[stackNum - 1];
if (currentStackTop == -1) {
throw new Exception("UNDERFLOW");
}
StackNode temp = stackNodes[currentStackTop];
stackPointers[stackNum - 1] = temp.prev;
freeStackNode(currentStackTop);
return temp;
}
private int getFreeNodeIndex() throws Exception {
int temp = freeListTop;
if (size >= CAPACITY)
throw new Exception("OVERFLOW");
freeListTop = stackNodes[temp].prev;
size++;
return temp;
}
private void freeStackNode(int index) {
stackNodes[index].prev = freeListTop;
freeListTop = index;
size--;
}
public static void main(String args[]) {
// Test Driver
StackMFromArray mulStack = new StackMFromArray();
try {
mulStack.Push(1, 11);
mulStack.Push(1, 12);
mulStack.Push(2, 21);
mulStack.Push(3, 31);
mulStack.Push(3, 32);
mulStack.Push(2, 22);
mulStack.Push(1, 13);
StackNode node = mulStack.pop(1);
node = mulStack.pop(1);
System.out.println(node.value);
mulStack.Push(1, 13);
} catch (Exception e) {
e.printStackTrace();
}
}
}
メモリ使用量が非常に効率的でない場合は簡単にするために、配列をリストノードに分割し、それらをすべてフリーノードのリストに追加し、必要に応じてフリーリストからノードを取得して、リンクリストとしてスタックを実装できます。ただし、このアプローチの3番目の数字には特別なものはありません。
[*]メモリを使用してポインタを格納できる低レベル言語、またはスタック要素がint
などの配列のインデックスを表すことができるタイプである場合。
以前の回答の変形:スタック#1は左から成長し、スタック#2は右から成長します。
スタック#3は中央にありますが、要素は左右に交互の順序で成長します。 Nがセンターインデックスの場合、スタックはN、N-1、N + 1、N-2、N + 2などのように成長します。単純な関数がスタックインデックスを配列インデックスに変換します。
このページには、この問題に対する多くの解決策がすでに記載されています。基本的な質問は、私見です:
各プッシュ/ポップ操作にはどれくらい時間がかかりますか?
どのくらいのスペースが使用されていますか?具体的には、3つのスタックにプッシュしてデータ構造のスペースを使い果たすことができる要素の最小数はどれくらいですか?
私の知る限り、このページにすでに投稿されている各ソリューションは、プッシュ/ポップの線形時間を要するか、線形のスペースが空のままでスペースが不足する可能性があります。
この投稿では、はるかに優れたパフォーマンスのソリューションを参照し、最も簡単なソリューションを紹介します。
ソリューション空間をより慎重に説明するために、次のようにデータ構造の2つの機能を参照します。
O(f(n))プッシュ/ポップの実行に償却時間を要し、3つのスタックが少なくともn-O(g(n))アイテムは(f、g)構造体と呼ばれます。 fとgは小さいほど良いです。このページにすでに投稿されているすべての構造体は、時間または空間(1、√n)構造を示します。
これはすべてに基づいています:
Michael L. FredmanおよびDeborah L. Goldsmith、「Three Stacks」、Journal of Algorithms、Volume 17、Issue 1、1994年7月、45〜70ページ
カリフォルニア大学サンディエゴ校のデボラルイーズゴールドスミス博士論文(1987年の電気工学/コンピューターサイエンス学科)、「3以上のスタックの効率的なメモリ管理」
表示されませんが、Sの(log n/log S、S)構造が表示されます。これは、(t、n1/t)tの構造(1、√n)構造の簡易バージョンを示します。
配列をサイズΘ(√n)のブロックに分割します。ブロックには1からΘ(√n)までの番号が付けられ、ブロックの番号は「アドレス」と呼ばれます。アドレスは、実際のアイテムの代わりに配列スロットに保存できます。特定のブロック内のアイテムは、O(√n)未満の数値で参照でき、そのような数値はインデックスと呼ばれます。インデックスも配列スロットに収まります。
最初のブロックはアドレスとインデックスを保存するために確保され、他のブロックはアドレスやインデックスを保存しません。最初のブロックはdirectoryと呼ばれます。ディレクトリ以外のブロックはすべて空になるか、3つのスタックのうちの1つだけの要素を保持します。つまり、異なるスタックの2つの要素を持つブロックはありません。さらに、すべてのスタックには、最大で1つのブロックが部分的にいっぱいになります。スタックに関連付けられている他のすべてのブロックは、完全にいっぱいまたは完全に空になります。
空のブロックがある限り、プッシュ操作はすべてのスタックに対して許可されます。ポップ操作は常に許可されます。プッシュ操作が失敗すると、データ構造がいっぱいになります。その時点で、スタックのいずれかの要素を含まないスロットの数は、最大でO(√n)です。プッシュされないスタックの2つの部分的に満たされたブロックと、1つのディレクトリブロックです。
すべてのブロックは、ブロックの前面に近い要素(低いインデックス)がスタックの下部に近づくように順序付けられます。
ディレクトリには次のものが含まれます。
3つのスタックの最上位にあるブロックの3つのアドレス、または特定のスタックにまだブロックがない場合は0
3つのスタックの最上位にある要素の3つのインデックス、または特定のスタックにアイテムがまだない場合は0。
完全または部分的に完全なブロックごとに、同じスタック内のそれより低いブロックのアドレス、またはスタック内の最下位ブロックの場合は0。
リーダーブロックと呼ばれるフリーブロックのアドレス、またはフリーブロックがない場合は0
空きブロックごとに、別の空きブロックのアドレス、または空きブロックがもうない場合は0
これらの最後の2つは、フリーリンクの単一リンクリストとして保存されたスタックを構成します。つまり、リーダーブロックから始まるフリーブロックのアドレスをたどると、0で終わるすべてのフリーブロックを通るパスが得られます。
アイテムをスタックにプッシュするには、ディレクトリを使用して、一番上のブロックとそのブロック内の一番上の要素を見つけます。そのブロックに空きがある場合は、そこにアイテムを置いて戻ります。
それ以外の場合は、リーダーブロックのアドレスをフリーブロックスタック内の次のフリーブロックのアドレスに変更して、フリーブロックのスタックをポップします。スタックのアドレスとインデックスを、それぞれポップしたばかりの空きブロックのアドレスと1に変更します。インデックス1でポップしたばかりのブロックにアイテムを追加し、戻ります。
すべての操作はO(1)時間です。ポップは対称的です。
配列を3つに分割し、最初のスタックの先頭を0、2番目のスタックの先頭をn/3、3番目のスタックの先頭をn-1にする必要があると思います。
プッシュ操作を実装します:
スペースが残っていないときにk/3と2 * k/3をシフトしているため、中間スタックをシフトした後、各スタックで使用可能なスペースが等しくなります。
最初のスタックがインデックス0になり、次に0 + 3 = 3、次に3 + 3 = 6 ...になったときに、そのような方法でスタックを領域に格納します。 2番目は、インデックス1、1 + 3 = 4、4 + 3 = 7 ...に入ります。 3番目のインデックスはインデックス2、2 + 3 = 5、5 + 3 = 8に入ります。したがって、最初のスタック要素をaでマークし、bで1つ、cでマークすると、a1 b1 c1 a2 b2 c2 a3 b3が得られます。 c3 ...
ギャップがある可能性がありますが、3要素のtopIndex配列に格納されているトップインデックスは常にわかっています。
解決策:2つのスタックの実装は簡単です。最初のスタックは最初から最後まで成長し、2番目のスタックは最後から最初まで成長します。配列に実際にスペースが残っていない限り、それらのいずれかのオーバーフローは発生しません。
3つのスタックの場合、以下が必要です。各ノードの親を維持するための補助配列。各スタックの現在のトップを保存する変数。これら2つを配置すると、すべてのスタックからのデータを元のアレイに散在させることができ、すべてのスタックに対してプッシュ/ポップ/サイズ操作を実行できます。
要素を挿入するときは、通常の配列のすべての要素の最後に挿入します。そのスタックの現在の最上部を新しい要素の親として(親の配列に)格納し、現在の最上部を新しい位置に更新します。
削除するとき、削除された要素のスタック配列にNULLを挿入し、そのスタックのスタックトップを親にリセットします。
配列がいっぱいになると、削除された要素に対応するいくつかの穴ができます。この時点で、配列を圧縮してすべての空き領域をまとめるか、新しい要素を挿入するときに空き領域を線形検索することができます。
詳細については、次のリンクを参照してください:-https://coderworld109.blogspot.in/2017/12/how-to-implement- 3-stacks-with-one-array.html
これが、単一の配列にあるN個のスタックのソリューションです。
いくつかの制約があります。配列のサイズは、スタックの数より小さくなりません。
私はソリューションで例外クラスStackExceptionをカスタマイズするために使用しました。プログラムを実行するための例外クラスを変更できます。
配列内の複数のスタックの場合、別の配列へのポインターを管理しました。
package com.practice.ds.stack;
import Java.util.Scanner;
import Java.util.logging.Logger;
/** Multiple stacks in a single array */
public class MultipleStack {
private static Logger logger = Logger.getLogger("MultipleStack");
private int[] array;
private int size = 10;
private int stackN = 1;
private int[] pointer;
public MultipleStack() {
this.array = new int[size];
this.pointer = new int[1];
}
public MultipleStack(int size, int stackN) throws StackException {
if (stackN > size)
throw new StackException("Input mismatch ! no of stacks can't be larger than size ");
this.size = size;
this.stackN = stackN;
init();
}
private void init() {
if (size <= 0) {
logger.info("Initialize size is " + size + " so assiginig defalt size ");
this.size = 10;
}
if (stackN < 1) {
logger.info("Initialize no of Stack is " + size + " so assiginig defalt");
this.stackN = 1;
}
this.array = new int[size];
this.pointer = new int[stackN];
initializePointer();
}
private void initializePointer() {
for (int i = 0; i < stackN; i++)
pointer[i] = (int)(i * Math.ceil(size / stackN) - 1);
}
public void Push(int item, int sn) throws StackException {
if (full(sn))
throw new StackException(sn + " is overflowed !");
int stkPointer = pointer[sn - 1];
array[++stkPointer] = item;
pointer[sn - 1] = stkPointer;
}
public void pop(int sn) throws StackException {
if (empty(sn))
throw new StackException(sn + " is underflow !");
int peek = peek(sn);
System.out.println(peek);
pointer[sn - 1] = --pointer[sn - 1];
}
public int peek(int sn) throws StackException {
authenticate(sn);
return array[pointer[sn - 1]];
}
public boolean empty(int sn) throws StackException {
authenticate(sn);
return pointer[sn - 1] == (int)(((sn - 1) * Math.ceil(size / stackN)) - 1);
}
public boolean full(int sn) throws StackException {
authenticate(sn);
return sn == stackN ? pointer[sn - 1] == size - 1 : pointer[sn - 1] == (int)((sn) * Math.ceil(size / stackN)) - 1;
}
private void authenticate(int sn) throws StackException {
if (sn > stackN || sn < 1)
throw new StackException("No such stack found");
}
public static void main(String[] args) {
try (Scanner scanner = new Scanner(System.in)) {
System.out.println("Define size of the stack");
int size = scanner.nextInt();
System.out.println("total number of stacks");
int stackN = scanner.nextInt();
MultipleStack stack = new MultipleStack(size, stackN);
boolean exit = false;
do {
System.out.println("1. Push");
System.out.println("2. Pop");
System.out.println("3. Exit");
System.out.println("Choice");
int choice = scanner.nextInt();
switch (choice) {
case 1:
try {
System.out.println("Item : ");
int item = scanner.nextInt();
System.out.println("Stack Number : ");
int stk = scanner.nextInt();
stack.Push(item, stk);
} catch (Exception e) {
e.printStackTrace();
}
break;
case 2:
try {
System.out.println("Stack Number : ");
int stk = scanner.nextInt();
stack.pop(stk);
} catch (Exception e) {
e.printStackTrace();
}
break;
case 3:
exit = true;
break;
default:
System.out.println("Invalid choice !");
break;
}
} while (!exit);
} catch (Exception e) {
e.printStackTrace();
}
}
}
これがC#での私の解決策です-
/* Program: Implement 3 stacks using a single array
*
* Date: 12/26/2015
*/
using System;
namespace CrackingTheCodingInterview
{
internal class Item
{
public object data;
public int prev;
}
/// <summary>
/// Class implementing 3 stacks using single array
/// </summary>
public class Stacks
{
/// <summary>
/// Pushing an element 'data' onto a stack 'i'
/// </summary>
public void Push(int i, object d)
{
i--;
if (available != null)
{
int ava = (int)available.DeleteHead();
elems[ava].data = d;
elems[ava].prev = top[i];
top[i] = ava;
}
else
{
Console.WriteLine("Array full. No more space to enter!");
return;
}
}
/// <summary>
/// Popping an element from stack 'i'
/// </summary>
public object Pop(int i)
{
i--;
if (top[i] != -1)
{
object popVal = elems[top[i]].data;
int prevTop = elems[top[i]].prev;
elems[top[i]].data = null;
elems[top[i]].prev = -1;
available.Insert(top[i]);
top[i] = prevTop;
return popVal;
}
else
{
Console.WriteLine("Stack: {0} empty!", i);
return null;
}
}
/// <summary>
/// Peeking top element of a stack
/// </summary>
public object Peek(int i)
{
i--;
if (top[i] != -1)
{
return elems[top[i]].data;
}
else
{
Console.WriteLine("Stack: {0} empty!", i);
return null;
}
}
/// <summary>
/// Constructor initializing array of Nodes of size 'n' and the ability to store 'k' stacks
/// </summary>
public Stacks(int n, int k)
{
elems = new Item[n];
top = new int[k];
for (int i = 0; i < k; i++)
{
top[i] = -1;
}
for (int i = 0; i < n; i++)
{
elems[i] = new Item();
elems[i].data = null;
elems[i].prev = -1;
}
available = new SinglyLinkedList();
for (int i = n - 1; i >= 0; i--)
{
available.Insert(i);
}
}
private Item[] elems;
private int[] top;
private SinglyLinkedList available;
}
internal class StacksArrayTest
{
static void Main()
{
Stacks s = new Stacks(10, 3);
s.Push(1, 'a');
s.Push(1, 'b');
s.Push(1, 'c');
Console.WriteLine("After pushing in stack 1");
Console.WriteLine("Top 1: {0}", s.Peek(1));
s.Push(2, 'd');
s.Push(2, 'e');
s.Push(2, 'f');
s.Push(2, 'g');
Console.WriteLine("After pushing in stack 2");
Console.WriteLine("Top 1: {0}", s.Peek(1));
Console.WriteLine("Top 2: {0}", s.Peek(2));
s.Pop(1);
s.Pop(2);
Console.WriteLine("After popping from stack 1 and 2");
Console.WriteLine("Top 1: {0}", s.Peek(1));
Console.WriteLine("Top 2: {0}", s.Peek(2));
s.Push(3, 'h');
s.Push(3, 'i');
s.Push(3, 'j');
s.Push(3, 'k');
s.Push(3, 'l');
Console.WriteLine("After pushing in stack 3");
Console.WriteLine("Top 3: {0}", s.Peek(3));
Console.ReadLine();
}
}
}
出力:
After pushing in stack 1
Top 1: c
After pushing in stack 2
Top 1: c
Top 2: g
After popping from stack 1 and 2
Top 1: b
Top 2: f
After pushing in stack 3
Top 3: l
私はそれをコーディングするためにこの投稿を参照します- http://codercareer.blogspot.com/2013/02/no-39-stacks-sharing-array.html
Python
class Stack:
def __init__(self):
self.pos_1 = 0
self.pos_2 = 1
self.pos_3 = 2
self.stack = [None, None, None]
def pop_1(self):
if self.pos_2 - 1 > 0:
to_ret = self.stack.pop(self.pos_1)
self.pos_2 -= 1
self.pos_3 -= 1
return to_ret
def Push_1(self, value):
self.stack.insert(self.pos_1, value)
self.pos_2 += 1
self.pos_3 += 1
return None
def pop_2(self):
if self.pos_2 - 1 < self.pos_3:
to_ret = self.stack.pop(self.pos_2)
self.pos_3 -= 1
return to_ret
def Push_2(self, value):
self.stack.insert(self.pos_2, value)
self.pos_3 += 1
return None
def pop_3(self):
if self.pos_3 - 1 > self.pos_2:
to_ret = self.stack.pop(self.pos_3)
return to_ret
def Push_3(self, value):
self.stack.insert(self.pos_3, value)
return None
if __== "__main__":
stack = Stack()
stack.Push_2(22)
stack.Push_1(1)
stack.Push_1(2)
print stack.pop_1()
print stack.pop_1()
print stack.pop_2()
prints: 2 1 22