两种在一个数组中设计K堆栈的方法

122 阅读4分钟

在这篇文章中,我们介绍了两种在一个数组中设计K堆栈的方法。挑战在于如何有效地存储元素,以使用数组中的所有可用空间,并保持堆栈操作的时间复杂性。

目录:

  1. 简单的方法
  2. 高效的方法

前提是:堆栈数组

让我们开始在一个数组中实现K堆栈。

简单的方法

在这个方法中,我们将创建一定大小的数组,并将该数组分成k个部分,用于存储k个堆栈的元素。

为了实现这一点,首先我们将创建两个大小为n的数组arr ,用于存储值

top 大小为k的数组用于存储堆栈的顶部位置

我们将用-1索引初始化Arr的所有元素,其中的-1被认为是空索引

并用堆栈的起始元素初始化顶层的元素。

例如:
arr 是大小为9(n=9) 的数组,它将存储元素的值 。

top 大小为 的数组,它将存储堆栈的顶部位置。3(k=3)

栈的范围将是[(stack_no-1)*n/k, stack_no*n/k-1]

那么:

top[3]={0, 3, 6}
range of stack 1 = [0, 2]
range of stack 2 = [3, 5]
range of stack 1 = [6, 8]

如果第i个索引的值表示相应堆栈的顶部元素,其值将被存储在arr[i]
中:

arr={-1, -1, -1, -1}
top = {0, 2}

如果我们在栈1中输入1,在栈2中输入2,那么

arr = {1, -1, 2, -1}
top = {0, 2}
top[0] = 0 = top of 1st stack
top[1] = 2 = top of 2nd stack

现在,如果我们在第一栈中加入3,在第二栈中加入4 ,那么:

arr = {1, 3, 2, 4}
top = {1, 3}
top[0] = 1 = top of 1st stack
top[1] = 3 = top of 2nd stack

在堆栈中插入

  • 对于插入,我们将使用push 函数
  • 为了在堆栈中插入元素,首先我们将检查堆栈是否已满,如果是,我们将简单地打印堆栈已满
  • 如果不是,则在堆栈的顶部添加值。

从堆栈中移出值

  • 为了从堆栈中移出值,我们将使用pop 函数
  • 为了从堆栈中移除元素,首先我们将检查堆栈是否为空,如果是,我们将简单地打印堆栈为空
  • 否则,我们将从堆栈中移除值

push(int value, int stack_no)

  • 如果top[stack_no](stack_no*n)/k和arr[top[stack_no]]-1,那么堆栈是空的[堆栈顶部等于堆栈的第一个索引,在arr的同一索引中的值是-1],所以我们可以在堆栈中加入插入元素

  • 如果top[stack_no]+1<((stack_no+1)*n)/k,那么顶部元素旁边的索引是空的,我们可以在那里加值。所以我们首先将top[stack_no]增加1,然后我们将在arr[top[stack_no]]插入值

  • 如果top[stack_no]>=((stack_no+1)*n)/k,那么我们将打印堆栈已满,因为((stack_no+1)*n)/k是堆栈的第一个元素,它在当前堆栈旁边

pop(int stack_no)

  • 如果top[stack_no](stack_no*n)/k和arr[top[stack_no]]-1意味着栈顶等于栈的第一个索引,并且arr的相同索引的值是-1,因此栈是空的,所以我们将直接打印栈是空的,因为栈中没有元素存在
  • 如果top[stack_no]==(stack_no*n)/k并且arr[top[stack_no]]!=-1意味着栈顶是栈的第一个索引,并且在当前栈的起始索引中有一些值,因此我们将返回栈顶的值并且在当前栈的起始索引的arr中存储-1。
  • 如果top[stack_no]中的索引大于第一个索引,那么我们将返回索引top[stack_no]的arr中的值,在arr[index]的位置上存储-1,并将栈的一侧减少1。

代码

#include<iostream>
using namespace std;

class kstack{
    int n, k;
    int *arr, *top;
    public:
    kstack(int n, int k){
        this->n = n; this->k = k;
        arr = new int[n];
        top = new int[k];
        for(int i=0; i<n; i++) arr[i]=-1;
        for(int i=0; i<k; i++) top[i] = (i*n)/k;
    }

    void push(int value, int stack_no){
        stack_no--;
        if(top[stack_no]==(stack_no*n)/k and arr[top[stack_no]]==-1) arr[top[stack_no]]=value;
        else if(top[stack_no]+1<((stack_no+1)*n)/k and arr[top[stack_no]+1]==-1) top[stack_no]++, arr[top[stack_no]]=value;
        else cout<<"Stack no "<<stack_no+1<<" is Full, can't enter "<<value<<endl;
    }

    int pop(int stack_no){
        int temp=-1;
        stack_no--;
        if(top[stack_no]==(stack_no*n)/k and arr[top[stack_no]]==-1) cout<<"Stack no "<<stack_no+1<<" is Empty"<<endl;
        else if(top[stack_no]==(stack_no*n)/k and arr[top[stack_no]]!=-1) temp=arr[top[stack_no]], arr[top[stack_no]]=-1;
        else temp=arr[top[stack_no]], arr[top[stack_no]]=-1, top[stack_no]--;
        return temp;
    }

    ~kstack(){
        delete [] arr;
        delete [] top;
    }
};

void solve(){
    // Stack with k=2 and n=4
    kstack k1(4, 2);

    // Adding elements to stack 1
    k1.push(1, 1);
    k1.push(2, 1);
    k1.push(3, 1);

    // Adding elements to stack 2
    k1.push(4, 2);
    k1.push(5, 2);
    k1.push(6, 2);

    // Removing elements from Stack 1
    cout<<k1.pop(1)<<endl;
    cout<<k1.pop(1)<<endl;
    cout<<k1.pop(1)<<endl;

    // Removing elements from Stack 2
    cout<<k1.pop(2)<<endl;
    cout<<k1.pop(2)<<endl;
    cout<<k1.pop(2)<<endl;
}

int main(){
    solve();
    return 0;
}

输出

Stack no 1 is Full, can't enter 3
Stack no 2 is Full, can't enter 6
2
1
Stack no 1 is Empty
-1
5
4
Stack no 2 is Empty
-1

时间复杂度

  • O(1) [所有的操作(pop和push)都在O(1)时间内执行] 。

空间复杂度

  • O(N) [因为我们使用两个数组,一个大小为n,另一个大小为k(k<=n),所以是O(N)] 。

高效的方法

在这个方法中,我们将创建三个数组:

  • 数组arr ,用于存储堆栈元素
  • 数组top ,用于存储堆栈的顶部索引
  • next 用于存储指向下一个空闲空间的指针,也用于存储该堆栈顶部元素下面的元素索引

例子:让arr的大小为n
,那么对于一个索引0 <= i < n,
如果arr[i]将存储该元素的值,而next[i]将存储堆栈中该元素下面的索引。

但是如果arr[i]是空的或者没有分配给任何一个堆栈,那么next[i]将存储在索引i之后的下一个空闲空间。arr

我们将创建一个名为free 的变量来存储最小索引的空位。

我们将在类中实现这一点,并将完整的操作分给函数。

我们将创建大小为n的k的数组,并将所有元素初始化为-1。

n大小的下一个数组,用索引初始化每个元素,如果是下一个元素,则用-1初始化最后一个元素,用0初始化空位
例如:
n=4,k=2 ,则 空位=0
top[2]={-1,-1}
next[4]={1,2,3,-1}。
isEmpty(int stack_no)。

  • 这个函数将检查堆栈是否为空。

  • 如果堆栈的顶部是-1,那么堆栈就是空的,因为我们已经声明所有的元素都是-1。

isFull(int stack_no)

  • 这个函数将检查堆栈是否已满
  • 如果free是-1,那么堆栈是满的,因为free是最小的索引,如果free是-1,意味着free存储的是下一个数组的最后一个元素。

push(int value, int stack_no)

这个函数将被用来在堆栈中插入元素

  • 首先检查堆栈是否已满,然后打印堆栈已满。
  • 否则
    • 将free的值存储在temp变量中,在arr的索引temp中我们将存储我们的值。
    • 使用free = next[temp]将下一个空闲位置存储在free中。
    • 存储temp,它是当前元素的索引,也是堆栈中的顶层元素,在顶层数组中。

pop(int stack_no)

这个函数将被用来从一个特定的堆栈中移除元素

  • 首先我们将检查堆栈是否为空,如果是,我们将直接打印堆栈为空
  • 否则
    • 在temp中存储顶部元素的索引
    • 使用next[temp]更新top与第二个top元素的索引
    • 使用free变量用下一个空闲位置来更新temp
    • 用temp作为索引更新free,temp刚刚得到free。
    • 从arr中返回索引temp中的值

代码

#include<iostream>
using namespace std;

class kstack{
    int n, k, free;
    int *arr, *top, *next;
    public:
    kstack(int n, int k){
        this->n = n;
        this->k = k;
        this->arr = new int[n];
        this->top = new int[k];
        for(int i=0; i<k; i++) top[i]=-1;
        this->next = new int[n];
        for(int i=0; i<n-1; i++) next[i]=i+1;
        next[n-1]=-1;
        this->free = 0;
    }

    bool isFull(){
        if(free==-1) return true;
        else return false;
    }

    bool isEmpty(int stack_no){
        if(top[stack_no]==-1) return true;
        else return false;
    }

    void push(int value, int stack_no){
        stack_no-=1;
        if(isFull()){
            cout<<"Stack no "<<stack_no+1<<" is Full, can't enter "<<value<<endl;
            return;
        }
        int temp = free;
        free = next[temp];
        next[temp] = top[stack_no];
        top[stack_no] = temp;
        arr[temp] = value;
    }

    int pop(int stack_no){
        stack_no-=1;
        if(isEmpty(stack_no)){
            cout<<"Stack no "<<stack_no+1<<" is Empty"<<endl;
            return -1;
        }
        int temp = top[stack_no];
        top[stack_no] = next[temp];
        next[temp] = free;
        free = temp;
        return arr[temp];
    }

    ~kstack(){
        delete [] arr;
        delete [] top;
    }
};

void solve(){
    // Stack with k=2 and n=4
    kstack k1(4, 2);

    // Adding elements to stack 1
    k1.push(1, 1);
    k1.push(2, 1);
    k1.push(3, 1);

    // Adding elements to stack 2
    k1.push(4, 2);
    k1.push(5, 2);
    k1.push(6, 2);

    // Removing elements from Stack 1
    cout<<k1.pop(1)<<endl;
    cout<<k1.pop(1)<<endl;
    cout<<k1.pop(1)<<endl;

    // Removing elements from Stack 2
    cout<<k1.pop(2)<<endl;
    cout<<k1.pop(2)<<endl;
    cout<<k1.pop(2)<<endl;
}

int main(){
    solve();
    return 0;
}

输出

Stack no 2 is Full, can't enter 5
Stack no 2 is Full, can't enter 6
3
2
1
4
Stack no 2 is Empty
-1
Stack no 2 is Empty
-1

时间复杂度

  • O(1) [所有的操作(pop和push)都在O(1)时间内完成] 。

空间复杂度

  • O(N) [因为我们使用三个数组,2个大小为n,1个大小为k(k<=n)]因此,O(N)] 。

通过OpenGenus的这篇文章,你一定对如何在一个数组中设计和实现K个堆栈有了完整的认识。