棧與佇列(Stack and Queue)
阿新 • • 發佈:2018-12-13
1.定義
棧:後進先出(LIFO-last in first out):最後插入的元素最先出來。
佇列:先進先出(FIFO-first in first out):最先插入的元素最先出來。
2.用陣列實現棧和佇列
實現棧:
由於陣列大小未知,如果每次插入元素都擴充套件一次資料(每次擴充套件都意味著構建一個新陣列,然後把舊陣列複製給新陣列),那麼效能消耗相當嚴重。
這裡使用貪心演算法,陣列每次被填滿後,加入下一個元素時,把陣列拓展成現有陣列的兩倍大小。
每次移除元素時,檢測陣列空餘空間有多少。當數組裡的元素個數只有整個陣列大小的四分之一時,陣列減半。
為什麼不是當數組裡的元素個數只有整個陣列大小的四分之一時,陣列減半?考慮以下情況:陣列有4個元素,陣列大小為4個元素空間。此時,加一個元素,陣列拓展成8個空間;再減一個元素,陣列縮小為4個空間;如此迴圈,效能消耗嚴重。
具體程式碼(Java):
public ResizingArrayStackOfStrings() { s=new String[1];
int N = 0; } pubilc void Push(String item) { //如果下一個加入元素超出陣列容量,拓展陣列 if(N == s.length) Resize(2 * s.length); s[N++] = item; } private void Resize(int capacity) { String[] copy = new String[capacity];//將舊陣列元素複製給新陣列 for(int i=0; i<N; i++) copy[i] = s[i]; s = copy; } public String Pop() { String item = s[--N]; s[N] = null; //剩餘元素只佔陣列四分之一空間時,陣列減半 if(N>0 && N=s.length/4) Resize(s.length/2); return item; }
效果如下圖:
實現佇列
與棧類似:
陣列每次被填滿後,加入下一個元素時,把陣列拓展成現有陣列的兩倍大小。
每次移除元素時,檢測陣列空餘空間有多少。當數組裡的元素個數只有整個陣列大小的四分之一時,陣列減半。
不同之處在於:
由於是先進先出,移除是從佇列的最前端開始的。所以佇列資料是儲存在陣列的中間部分。令佇列資料的尾端資料ID為Num,首端資料ID為HeadIndex,則Num - HeadIndex為佇列資料元素個數。
當佇列資料元素個數為整個陣列空間的四分之一時,陣列減半,且佇列資料左移至陣列最左端。即Num-=HeadIndex;HeadIndex=0;
圖中,HeadIndex=2;Num=5;
具體程式碼:
.h: UCLASS() class ALGORITHM_API AStackAndQueuesExerciseTwo : public AActor { GENERATED_BODY() public: // Sets default values for this actor's properties AStackAndQueuesExerciseTwo(); // Called every frame virtual void Tick(float DeltaTime) override; //輸入 void Enqueue(int Input); //重構陣列(拓展或縮小) void Resize(int Capacity); //輸出且移除 int Dequeue(); //佇列裡沒元素了? bool IsEmpty(); protected: // Called when the game starts or when spawned virtual void BeginPlay() override; public: private: //記錄陣列中有多少個Int int Num; //佇列陣列 TArray<int> MyIntArray; //記錄下一個移除的資料ID int HeadIndex; }; .cpp: AStackAndQueuesExerciseTwo::AStackAndQueuesExerciseTwo() { // Set this actor to call Tick() every frame. You can turn this off to improve performance if you don't need it. PrimaryActorTick.bCanEverTick = true; //一開始陣列沒成員 Num = 0; HeadIndex = 0; //陣列中有一個假元素 MyIntArray.Add(0); } // Called when the game starts or when spawned void AStackAndQueuesExerciseTwo::BeginPlay() { Super::BeginPlay(); //測試 Enqueue(1); Enqueue(2); Enqueue(3); Enqueue(4); Enqueue(5); Dequeue(); Dequeue(); Dequeue(); //佇列陣列成員 for (int i = HeadIndex; i < Num; i++) { UKismetSystemLibrary::PrintString(this, "i: " + FString::FromInt(i) + " End: " + FString::FromInt(MyIntArray[i])); } //佇列陣列的容量 UKismetSystemLibrary::PrintString(this, "MyIntArray.Num(): " + FString::FromInt(MyIntArray.Num())); } // Called every frame void AStackAndQueuesExerciseTwo::Tick(float DeltaTime) { Super::Tick(DeltaTime); } void AStackAndQueuesExerciseTwo::Enqueue(int Input) { //如果佇列陣列已滿,拓展陣列 if (Num == MyIntArray.Num()) { Resize(2 * MyIntArray.Num()); } //拓展或者陣列有空位時,新增元素 if (Num < MyIntArray.Num()) { MyIntArray[Num] = Input; } Num++; } void AStackAndQueuesExerciseTwo::Resize(const int Capacity) { //int a[] = new int[Capacity]; TArray<int> Copy; //新增數個假元素填充陣列 for (int i = 0; i < Capacity; i++) { Copy.Add(0); } //將佇列陣列賦值給Copy陣列,如果是縮小陣列,則把佇列陣列左移,節省空間 for (int i = HeadIndex; i < Num; i++) { Copy[i - HeadIndex] = MyIntArray[i]; } MyIntArray = Copy; } int AStackAndQueuesExerciseTwo::Dequeue() { //判斷陣列是否為空 if (IsEmpty()) { UKismetSystemLibrary::PrintString(this, "No Element Exist!!!"); return 0; } else { UKismetSystemLibrary::PrintString(this, "Dequeue: " + FString::FromInt(MyIntArray[HeadIndex])); } HeadIndex++; //如果移除元素後,所剩元素為陣列空間的四分之一,則陣列減半 if ((Num - HeadIndex) != 0 && (Num - HeadIndex) == (MyIntArray.Num() / 4)) { Resize(MyIntArray.Num() / 2); //移除空間後,佇列陣列左移,節省空間 Num -= HeadIndex; HeadIndex = 0; return MyIntArray[HeadIndex]; } else { return MyIntArray[HeadIndex - 1]; } } //如果下一個要移除的資料不存在,則為空陣列 bool AStackAndQueuesExerciseTwo::IsEmpty() { return HeadIndex >= Num; }