web-dev-qa-db-ja.com

C#でスタックの一番上にないスタックアイテムを削除する方法

残念ながら、アイテムはスタックから "pop"によってのみ削除できます。スタックには「remove」メソッドなどはありませんが、スタック(はい、スタックが必要です!)があり、そこから要素を削除する必要があります。

これを行うトリックはありますか?

32
Enyra

一番上にないアイテムを削除する必要がある場合は、スタック以外のものが必要です。

リストからスタックを独自に実装してみてください。次に、独自のプッシュ関数とポップ関数(リストで追加および削除)、および独自の特別なPopFromTheMiddle関数を実装します。

例えば

public class ItsAlmostAStack<T>
{
    private List<T> items = new List<T>();

    public void Push(T item)
    {
        items.Add(item);
    }
    public T Pop()
    {
        if (items.Count > 0)
        {
            T temp = items[items.Count - 1];
            items.RemoveAt(items.Count - 1);
            return temp;
        }
        else
            return default(T);
    }
    public void Remove(int itemAtPosition)
    {
        items.RemoveAt(itemAtPosition);
    }
}
49
Binary Worrier

別のコンテナの使用を検討してください。多分LinkedList。その後、使用できます

AddFirst 
 AddLast 
 RemoveLast 
 RemoveFirst

スタックからポップ/プッシュのように使用できます

削除する

リストの中央からノードを削除するには

11

LinkedList を使用できます

リストベースの削除は、効率が低下する可能性があります。参照による削除では、リストベースのスタックはO(N) search and O(N) resizing。LinkedList search is O(N)であり、削除はO(1)です。インデックスによる削除の場合、LinkedListにはO(N)トラバーサルとO(1)削除が必要です、リストにはO(1)トラバーサル(インデックス化のため))とO(N)サイズ変更による削除が含まれます。

効率性に加えて、LinkedListの実装により、標準ライブラリに留まり、コードをより柔軟に開いて、記述を少なくすることができます。

これは、ポップ、プッシュ、および削除を処理できるはずです

    public class FIFOStack<T> : LinkedList<T>
    {
        public T Pop()
        {
            T first = First();
            RemoveFirst();
            return first;
        }

        public void Push(T object)
        {
            AddFirst(object);
        }

        //Remove(T object) implemented in LinkedList
   }
7
Benjamin Brown

おそらく拡張メソッドが機能しますが、実際には別のデータ構造が完全に必要であると思います。

public static T Remove<T>( this Stack<T> stack, T element )
{
     T obj = stack.Pop();
     if (obj.Equals(element))
     {
         return obj;
     }
     else
     {
        T toReturn = stack.Remove( element );
        stack.Push(obj);
        return toReturn;
     }
}
5
tvanfosson

真のスタックでは、これは一方向にしか実行できません-

必要なものがなくなるまですべてのアイテムをポップしてから、適切な順序でスタックに戻します。

ただし、これはあまり効率的ではありません。

本当にどこからでも削除したい場合は、List、LinkedList、またはその他のコレクションから疑似スタックを構築することをお勧めします。これにより、これを簡単に実行できるようになります。

4
Reed Copsey
   Stack temp = new Stack();
   object x, y;
   While ((x = myStack.Pop()) != ObjectImSearchingFor)
       temp.Push(x);
   object found = x;
   While ((y = temp.Pop()) != null)
      myStack.Push(y);
3
Charles Bretana

私が厄介な状況で使用するトリックは、スタック内のアイテムに「非推奨」フラグを追加することです。アイテムを「削除」したい場合は、そのフラグを立てるだけです(そして、オブジェクトが取得したリソースをクリーンアップします)。次に、アイテムをPop()するときに、フラグが立っているかどうかを確認し、非推奨のアイテムが見つかるまでループで再度ポップします。

do 
{  
   obj = mQueue.Pop();  
} while (obj.deprecated);  

独自のアイテム数を管理して、キューに残っている「実際の」アイテムの数を知ることができます。マルチスレッドソリューションで必要な場合は、明らかにロックを使用する必要があります。

フローが一定のキュー-アイテムがプッシュおよびポップされるキュー-この方法で処理する方がはるかに効率的であり、最も速く取得できます(アイテムを中央から削除するためにO(1)を支払う) )そして保持されるオブジェクトが小さい場合、メモリに関しては、アイテムが妥当なペースで流れるかどうかはほとんど関係ありません。

3
Amit Bens

じゃあスタックじゃない?スタックはLAST in FIRST out。カスタムのものを書くか、他のものを選択する必要があります。

2
aJ.

Stack <>のコンストラクターは、IEnumerable <>をパラメーターとして受け取ります。したがって、次のことを実行できます。

myStack = new Stack<item>( myStack.Where(i => i != objectToRemove).Reverse() );

これは、多くの点で非パフォーマンスです。

1
Phil Carson

リストを使用して、いくつかの拡張メソッドを追加しました。

public static IThing Pop(this List<IThing> list)
{
  if (list == null || list.Count == 0) return default(IThing);

  // get last item to return
  var thing = list[list.Count - 1];
  // remove last item
  list.RemoveAt(list.Count-1);

  return thing;
}

public static IThing Peek(this List<IThing> list)
{
  if (list == null || list.Count == 0) return default(IThing);

  // get last item to return
  return list[list.Count - 1];
}

public static void Remove(this List<IThing> list, IThing thing)
{
  if (list == null || list.Count == 0) return;
  if (!list.Contains(thing)) return;

  list.Remove(thing); // only removes the first it finds
}

public static void Insert(this List<IThing> list, int index, IThing thing)
{
  if (list == null || index > list.Count || index < 0) return;

  list.Insert(index, thing);
}
0
user2825546

hmmmm ......私は前の2つの答えに同意しますが、自分のやり方をハッキングする場合は、必要な要素に到達するまですべての要素をポップして保存し、それらすべてを再度プッシュします

はい、醜い、パフォーマンスが悪い、おそらく奇妙なコードで、理由を説明する長いコメントが必要ですが、それは可能です...

0
webclimber

私はこの質問に出くわしました。私のコードでは、独自の拡張メソッドを作成しました:

public static class StackExtensions
{
    public static void Remove<T>(this Stack<T> myStack, ICollection<T> elementsToRemove)
    {
        var reversedStack = new Stack<T>();

        while(myStack.Count > 0)
        {
            var topItem = myStack.Pop();
            if (!elementsToRemove.Contains(topItem))
            {
                reversedStack.Push(topItem);
            }
        }

        while(reversedStack.Count > 0)
        {
            myStack.Push(reversedStack.Pop());
        }           
    }
}

そして、私はこのように私のメソッドを呼び出します:

var removedReportNumbers = 
selectedReportNumbersInSession.Except(selectedReportNumbersList).ToList();

selectedReportNumbersInSession.Remove(removedReportNumbers);