【十四】队列的特别实现--利用双栈

1、简述

代码复用是工程开发中经常用到的,本系列文章也一直致力于使用代码复用,但是要保证所实现的数据结构与算法在各方面都能达到“产品级别的代码”,即向外保持一致的接口,屏蔽内部实现的细节,优化内部实现算法的时间复杂度以及空间复杂度!

前面已经使用四种方法实现了队列,有两种使用了代码复用,但时间复杂度方面却不理想,因此又使用了两种不使用代码复用的方法,解决了时间复杂度方面的问题,但是编写起来难免比代码复用麻烦,本篇文章就巧妙的利用栈来实现队列,它的好处在于,既使用了代码复用,又有较好的时间复杂度与空间复杂度!

2、实现原理

这里写图片描述

如上图所示,利用两个栈,一个用来做入队操作,一个用来做出队操作!

3、实现思路

  •  准备两个栈用于实现队列:inStack和outStack
  • 当有新元素入队时:将其压入inStack中
  • 当需要出队时:
    • 当outStack为空时:
      • 将inStack中的元素逐一弹出并压入outStack中
      • 将outStack的栈顶元素弹出
    • 当outStack不为空时:
      • 直接将outStack的栈顶元素弹出

4、部分源码

/*
  代码复用,使用两个栈完成队列的功能
  一个只用于元素的进栈操作,一个只用于元素的出栈操作
  正好实现了队列的先进先出
*/
typedef struct _struct_stackqueue
{
  LinkStack *inStack;
  LinkStack *outStack;
}TStackQueue;

/*
  创建一个队列
  如果成功,返回一个队列指针,否则返回NULL
*/
StackQueue *StackQueue_Create() //O(1) ~ O(n)
{
  //分配内存空间,保存两个栈的指针
  TStackQueue *tsqueue = (TStackQueue*)malloc(sizeof(TStackQueue));
  if(tsqueue != NULL)
  {
    //创建两个栈
    tsqueue->inStack = LinkStack_Create();
    tsqueue->outStack = LinkStack_Create();

    //如果失败,要销毁所有内存空间
    if((tsqueue->inStack == NULL) || (tsqueue->outStack == NULL))
    {
      LinkStack_Destroy(tsqueue->inStack);
      LinkStack_Destroy(tsqueue->outStack);

      free(tsqueue);
      tsqueue = NULL;
    }
  }
  return tsqueue;
}

/*
  在队尾添加一个元素
  如果成功,返回1,失败返回0
*/
int StackQueue_Append(StackQueue *queue, StackQueueNode *item) //O(1)
{
  int iret = 1;
  TStackQueue *tsqueue = (TStackQueue*)queue;
  iret = iret && (tsqueue != NULL) && (item != NULL);
  //队列中添加元素,只需要向输入栈中直接压入元素即可
  if(iret)
  {
    iret = LinkStack_Push(tsqueue->inStack,item);
  }
  return iret;
}


/*
  在队头移出一个元素
  如果成功,返回被移出的元素指针,否则,返回NULL
*/
StackQueueNode *StackQueue_Retrieve(StackQueue *queue) //O(1)
{
  StackQueueNode *ret = NULL;
  TStackQueue *tsqueue = (TStackQueue*)queue;

  if(tsqueue != NULL)
  {
    /*
      如果输出栈为空,那么把当前输入栈中的所有元素全部出栈,并压入输出栈中
      然后从输出栈中弹出元素,作为队列的出队元素,直到输出栈中元素再次为空时,
      再把输出栈中的数据弹出并压入输出栈
    */
    if(LinkStack_Size(tsqueue->outStack) <= 0)
    {
      while(LinkStack_Size(tsqueue->inStack))
      {
        LinkStack_Push(tsqueue->outStack,LinkStack_Pop(tsqueue->inStack));
      }
    }
    //从输出栈中弹出元素,作为队列的出队元素
    ret = LinkStack_Pop(tsqueue->outStack);
  }
  return ret;
}

5、小结

  • 组合使用两个栈的“后进先出”可以实现队列的“先进先出”
  • 两个栈实现队列的方法复用栈数据结构,实现过程简单且高效
  • 两个栈实现的队列其操作的平均时间复杂度能够达到O(1)

6、完整源码下载

文件名:stackqueue-1.0.tar.gz
链接: http://pan.baidu.com/s/1i3ELK13 密码: y5tk

文件名:stackqueue-1.1.tar.gz
链接: http://pan.baidu.com/s/1o6DVGHc 密码: akdm
说明:修复了由linklist.c中的List_Get()函数所引起的BUG,详情见链接!

编译步骤:
0.1 解压缩:tar -zxvf stackqueue-1.0.tar.gz
0.2 进入目录:./configure
0.3 生成Seqlist:make
0.4 运行程序:./StackQueue

你可能感兴趣的:(数据结构与算法)