找回密码
 立即注册
首页 业界区 业界 算法大全(2)栈和队列

算法大全(2)栈和队列

挽幽 2025-5-29 15:04:24
  声明,本文所有9道算法题目,覆盖了基本上所有常见的栈/队列问题,全都用C#实现,并测试通过,代码下载:StackAndQueue.zip
目录:
1.设计含min函数的栈,要求min、push和pop的时间复杂度都是o(1)。
2.设计含min函数的栈的另解
3.用两个栈实现队列
4.用两个队列实现栈 
5.栈的push、pop序列是否一致
6.递归反转一个栈,要求不得重新申请一个同样的栈,空间复杂度o(1)
7.给栈排个序
8..如何用一个数组实现两个栈
9..如何用一个数组实现三个栈
 
1.设计含min函数的栈,要求min、push和pop的时间复杂度都是o(1)。
  算法思想:需要设计一个辅助栈,用来存储当前栈中元素的最小值。网上有人说存储当前栈中元素的最小值的所在位置,虽然能节省空间,这其实是不对的,因为我在调用Min函数的时候,只能得到位置,还要对存储元素的栈不断的pop,才能得到最小值——时间复杂度o(1)。
  所以,还是在辅助栈中存储元素吧。
  此外,还要额外注意Push操作,第一个元素不用比较,自动成为最小值入栈。其它元素每次都要和栈顶元素比较,小的那个放到栈顶。 
  1. public class NewStack
  2. {
  3.     private Stack dataStack;
  4.     private Stack mindataStack;
  5.     public NewStack()
  6.     {
  7.         dataStack = new Stack();
  8.         mindataStack = new Stack();
  9.     }
  10.     public void Push(int element)
  11.     {
  12.         dataStack.Push(element);
  13.         if (mindataStack.Count == 0)
  14.             mindataStack.Push(element);
  15.         else if (element <= (int)mindataStack.Peek())
  16.             mindataStack.Push(element);
  17.         else //(element > mindataStack.Peek)
  18.             mindataStack.Push(mindataStack.Peek());
  19.     }
  20.    
  21.     public int Pop()
  22.     {
  23.         if (dataStack.Count == 0)
  24.             throw new Exception("The stack is empty");
  25.         
  26.         mindataStack.Pop();
  27.         return (int)dataStack.Pop();
  28.     }
  29.     public int Min()
  30.     {
  31.         if (dataStack.Count == 0)
  32.             throw new Exception("The stack is empty");
  33.         
  34.         return (int)mindataStack.Peek();
  35.     }
  36. }
复制代码
  
2.设计含min函数的栈的另解
  话说,和青菜脸呆久了,就沾染了上海小市民意识,再加上原本我就很抠门儿,于是对于上一题目,我把一个栈当成两个用,就是说,每次push,先入站当前元素,然后入栈当前栈中最小元素;pop则每次弹出2个元素。
  算法代码如下所示(这里最小元素位于当前元素之上,为了下次比较方便):
  1. public class NewStack
  2. {
  3.     private Stack stack;
  4.     public NewStack()
  5.     {
  6.         stack = new Stack();
  7.     }
  8.     public void Push(int element)
  9.     {
  10.         if (stack.Count == 0)
  11.         {
  12.             stack.Push(element);
  13.             stack.Push(element);
  14.         }
  15.         else if (element <= (int)stack.Peek())
  16.         {
  17.             stack.Push(element);
  18.             stack.Push(element);
  19.         }
  20.         else //(element > stack.Peek)
  21.         {
  22.             object min = stack.Peek();
  23.             stack.Push(element);
  24.             stack.Push(min);            
  25.         }
  26.     }
  27.     public int Pop()
  28.     {
  29.         if (stack.Count == 0)
  30.             throw new Exception("The stack is empty");
  31.         stack.Pop();
  32.         return (int)stack.Pop();
  33.     }
  34.     public int Min()
  35.     {
  36.         if (stack.Count == 0)
  37.             throw new Exception("The stack is empty");
  38.         return (int)stack.Peek();
  39.     }
  40. }  
复制代码
  之所以说我这个算法比较叩门,是因为我只使用了一个栈,空间复杂度o(N),节省了一半的空间(算法1的空间复杂度o(2N))。
 
 
3.用两个栈实现队列
  实现队列,就要实现它的3个方法:Enqueue(入队)、Dequeue(出队)和Peek(队头)。
1)stack1存的是每次进来的元素,所以Enqueue就是把进来的元素push到stack1中。
2)而对于Dequeue,一开始stack2是空的,所以我们把stack1中的元素全都pop到stack2中,这样stack2的栈顶就是队头。只要stack2不为空,那么每次出队,就相当于stack2的pop。
3)接下来,每入队一个元素,仍然push到stack1中。每出队一个元素,如果stack2不为空,就从stack2中pop一个元素;如果stack2为空,就重复上面的操作——把stack1中的元素全都pop到stack2中。
4)Peek操作,类似于Dequeue,只是不需要出队,所以我们调用stack2的Peek操作。当然,如果stack2为空,就把stack1中的元素全都pop到stack2中。
5)注意边界的处理,如果stack2和stack1都为空,才等于队列为空,此时不能进行Peek和Dequeue操作。
  按照上述分析,算法实现如下:
  1. public class NewQueue
  2. {
  3.     private Stack stack1;
  4.     private Stack stack2;
  5.     public NewQueue()
  6.     {
  7.         stack1 = new Stack();
  8.         stack2 = new Stack();
  9.     }
  10.     public void Enqueue(int element)
  11.     {
  12.         stack1.Push(element);
  13.     }
  14.     public int Dequeue()
  15.     {
  16.         if (stack2.Count == 0)
  17.         {
  18.             if (stack1.Count == 0)
  19.                 throw new Exception("The queue is empty");
  20.             else
  21.                 while (stack1.Count > 0)
  22.                     stack2.Push(stack1.Pop());
  23.         }
  24.         return (int)stack2.Pop();
  25.     }
  26.     public int Peek()
  27.     {
  28.         if (stack2.Count == 0)
  29.         {
  30.             if (stack1.Count == 0)
  31.                 throw new Exception("The queue is empty");
  32.             else
  33.                 while (stack1.Count > 0)
  34.                     stack2.Push(stack1.Pop());
  35.         }
  36.         return (int)stack2.Peek();
  37.     }
复制代码
 
4.用两个队列实现栈
  这个嘛,就要queue1和queue2轮流存储数据了。这个“轮流”发生在Pop和Peek的时候,假设此时我们把所有数据存在queue1中(此时queue2为空),我们把queue1的n-1个元素放到queue2中,queue中最后一个元素就是我们想要pop的元素,此时queue2存有n-1个元素(queue1为空)。
  至于Peek,则是每次转移n个数据,再转移最后一个元素的时候,将其计下并返回。
  那么Push的操作,则需要判断当前queue1和queue2哪个为空,将新元素放到不为空的队列中。
  1. public class NewStack
  2. {
  3.     private Queue queue1;
  4.     private Queue queue2;
  5.     public NewStack()
  6.     {
  7.         queue1 = new Queue();
  8.         queue2 = new Queue();
  9.     }
  10.     public void Push(int element)
  11.     {
  12.         if (queue1.Count == 0)
  13.             queue2.Enqueue(element);
  14.         else
  15.             queue1.Enqueue(element);
  16.     }
  17.     public int Pop()
  18.     {
  19.         if (queue1.Count == 0 && queue2.Count == 0)
  20.             throw new Exception("The stack is empty");
  21.         if (queue1.Count > 0)
  22.         {
  23.             while (queue1.Count > 1)
  24.             {
  25.                 queue2.Enqueue(queue1.Dequeue());
  26.             }
  27.             //还剩一个
  28.             return (int)queue1.Dequeue();
  29.         }
  30.         else  //queue2.Count > 0
  31.         {
  32.             while (queue2.Count > 1)
  33.             {
  34.                 queue1.Enqueue(queue2.Dequeue());
  35.             }
  36.             //还剩一个
  37.             return (int)queue2.Dequeue();
  38.         }
  39.     }
  40.     public int Peek()
  41.     {
  42.         if (queue1.Count == 0 && queue2.Count == 0)
  43.             throw new Exception("The stack is empty");
  44.         int result = 0;
  45.         if (queue1.Count > 0)
  46.         {
  47.             while (queue1.Count > 1)
  48.             {
  49.                 queue2.Enqueue(queue1.Dequeue());
  50.             }
  51.             //还剩一个
  52.             result = (int)queue1.Dequeue();
  53.             queue2.Enqueue(result);
  54.         }
  55.         else  //queue2.Count > 0
  56.         {
  57.             while (queue2.Count > 1)
  58.             {
  59.                 queue1.Enqueue(queue2.Dequeue());
  60.             }
  61.             //还剩一个
  62.             result = (int)queue2.Dequeue();
  63.             queue1.Enqueue(result);
  64.         }
  65.         return result;
  66.     }
复制代码
 
5.栈的push、pop序列是否一致
  输入两个整数序列。其中一个序列表示栈的push顺序,判断另一个序列有没有可能是对应的pop顺序。为了简单起见,我们假设push序列的任意两个整数都是不相等的。
  比如输入的push序列是1、2、3、4、5,那么4、5、3、2、1就有可能是一个pop系列。因为可以有如下的push和pop序列:push 1,push 2,push 3,push 4,pop,push 5,pop,pop,pop,pop,这样得到的pop序列就是4、5、3、2、1。但序列4、3、5、1、2就不可能是push序列1、2、3、4、5的pop序列。
 
  网上的若干算法都太复杂了,现提出包氏算法如下:
  先for循环把arr1中的元素入栈,并在每次遍历时,检索arr2中可以pop的元素。如果循环结束,而stack中还有元素,就说明arr2序列不是pop序列。
static bool JudgeSequenceIsPossible(int[] arr1, int[] arr2)
{
    Stack stack = new Stack();

    for (int i = 0, j = 0; i < arr1.Length; i++)
    {
        stack.Push(arr1);

        while (stack.Count > 0 && (int)stack.Peek() == arr2[j])
        {
            stack.Pop();
            j++;
        }
    }

    return stack.Count == 0;
}

 
 
6.递归反转一个栈,要求不得重新申请一个同样的栈,空间复杂度o(1)
  算法思想:汉诺塔的思想,非常复杂,玩过九连环的人都想得通的
  1. static void ReverseStack(ref Stack stack)
  2. {
  3.     if (stack.Count == 0)
  4.         return;
  5.     object top = stack.Pop();
  6.     ReverseStack(ref stack);
  7.     if (stack.Count == 0)
  8.     {
  9.         stack.Push(top);
  10.         return;
  11.     }
  12.     object top2 = stack.Pop();
  13.     ReverseStack(ref stack);
  14.     stack.Push(top);
  15.     ReverseStack(ref stack);
  16.     stack.Push(top2);        
  17. }
复制代码
  1.  
复制代码
7.给栈排个序
  本题目是上一题目的延伸
  1. static void Sort(ref Stack stack)
  2. {
  3.     if (stack.Count == 0)
  4.         return;
  5.     object top = stack.Pop();
  6.     Sort(ref stack);
  7.     if (stack.Count == 0)
  8.     {
  9.         stack.Push(top);
  10.         return;
  11.     }
  12.     object top2 = stack.Pop();
  13.     if ((int)top > (int)top2)
  14.     {
  15.         stack.Push(top);
  16.         Sort(ref stack);
  17.         stack.Push(top2);
  18.     }
  19.     else
  20.     {
  21.         stack.Push(top2);
  22.         Sort(ref stack);
  23.         stack.Push(top);
  24.     }
  25. }
复制代码
 
 
8..如何用一个数组实现两个栈
  继续我所提倡的抠门儿思想,也不枉我和青菜脸相交一场。
  网上流传着两种方法:
  方法1  采用交叉索引的方法
一号栈所占数组索引为0, 2, 4, 6, 8......(K*2)  
二号栈所占数组索引为1,3,5,7,9 ......(K*2 + 1) 
  算法实现如下:
  1. public class NewStack
  2. {
  3.     object[] arr;
  4.     int top1;
  5.     int top2;
  6.     public NewStack(int capticy)
  7.     {
  8.         arr = new object[capticy];
  9.         top1 = -1;
  10.         top2 = -2;
  11.     }
  12.     public void Push(int type, object element)
  13.     {
  14.         if (type == 1)
  15.         {
  16.             if (top1 + 2 >= arr.Length)
  17.                 throw new Exception("The stack is full");
  18.             else
  19.             {
  20.                 top1 += 2;
  21.                 arr[top1] = element;
  22.             }
  23.         }
  24.         else //type==2
  25.         {
  26.             if (top2 + 2 >= arr.Length)
  27.                 throw new Exception("The stack is full");
  28.             else
  29.             {
  30.                 top2 += 2;
  31.                 arr[top2] = element;
  32.             }
  33.         }
  34.     }
  35.     public object Pop(int type)
  36.     {
  37.         object obj = null;
  38.         if (type == 1)
  39.         {
  40.             if (top1 == -1)
  41.                 throw new Exception("The stack is empty");
  42.             else
  43.             {
  44.                 obj = arr[top1];
  45.                 arr[top1] = null;
  46.                 top1 -= 2;
  47.             }
  48.         }
  49.         else //type == 2
  50.         {
  51.             if (top2 == -2)
  52.                 throw new Exception("The stack is empty");
  53.             else
  54.             {
  55.                 obj = arr[top2];
  56.                 arr[top2] = null;
  57.                 top2 -= 2;
  58.             }
  59.         }
  60.         return obj;
  61.     }
  62.     public object Peek(int type)
  63.     {
  64.         if (type == 1)
  65.         {
  66.             if (top1 == -1)
  67.                 throw new Exception("The stack is empty");
  68.             return arr[top1];
  69.         }
  70.         else //type == 2
  71.         {
  72.             if (top2 == -2)
  73.                 throw new Exception("The stack is empty");
  74.             return arr[top2];
  75.         }
  76.     }
  77. }
复制代码
  
  方法2:
第一个栈A:从最左向右增长
第二个栈B:从最右向左增长 
  代码实现如下:
  1. public class NewStack
  2. {
  3.     object[] arr;
  4.     int top1;
  5.     int top2;
  6.     public NewStack(int capticy)
  7.     {
  8.         arr = new object[capticy];
  9.         top1 = 0;
  10.         top2 = capticy;
  11.     }
  12.     public void Push(int type, object element)
  13.     {
  14.         if (top1 == top2)
  15.             throw new Exception("The stack is full");
  16.         if (type == 1)
  17.         {
  18.             arr[top1] = element;
  19.             top1++;
  20.         }
  21.         else //type==2
  22.         {
  23.             top2--;
  24.             arr[top2] = element;
  25.         }            
  26.     }
  27.     public object Pop(int type)
  28.     {
  29.         object obj = null;
  30.         if (type == 1)
  31.         {
  32.             if (top1 == 0)
  33.                 throw new Exception("The stack is empty");
  34.             else
  35.             {
  36.                 top1--;
  37.                 obj = arr[top1];
  38.                 arr[top1] = null;
  39.             }
  40.         }
  41.         else //type == 2
  42.         {
  43.             if (top2 == arr.Length)
  44.                 throw new Exception("The stack is empty");
  45.             else
  46.             {
  47.                 obj = arr[top2];
  48.                 arr[top2] = null;
  49.                 top2++;
  50.             }
  51.         }
  52.         return obj;
  53.     }
  54.     public object Peek(int type)
  55.     {
  56.         if (type == 1)
  57.         {
  58.             if (top1 == 0)
  59.                 throw new Exception("The stack is empty");
  60.             return arr[top1 - 1];
  61.         }
  62.         else //type == 2
  63.         {
  64.             if (top2 == arr.Length)
  65.                 throw new Exception("The stack is empty");
  66.             return arr[top2];
  67.         }
  68.     }
  69. }
复制代码
  综合比较上述两种算法,我们发现,算法1实现的两个栈,每个都只有n/2个空间大小;而算法2实现的两个栈,如果其中一个很小,另一个则可以很大,它们的和为常数n。
 
9..如何用一个数组实现三个栈
  最后,让我们把抠门儿进行到底,相信看完本文,你已经从物质和精神上都升级为一个抠门儿主义者。
  如果还使用交叉索引的办法,每个栈都只有N/3个空间。
  让我们只好使用上个题目的第2个方法,不过这只能容纳2个栈,我们还需要一个位置存放第3个栈,不如考虑数组中间的位置——第3个栈的增长规律可以如下:
第1个入栈C的元素进mid处
第2个入栈C的元素进mid+1处
第3个入栈C的元素进mid-1处
第4个入栈C的元素进mid+2处
  这个方法的好处是, 每个栈都有接近N/3个空间。
  1. public class NewStack
  2. {
  3.     object[] arr;
  4.     int top1;
  5.     int top2;
  6.     int top3_left;
  7.     int top3_right;
  8.     bool isLeft;
  9.     public NewStack(int capticy)
  10.     {
  11.         arr = new object[capticy];
  12.         top1 = 0;
  13.         top2 = capticy;
  14.         isLeft = true;
  15.         top3_left = capticy / 2;
  16.         top3_right = top3_left + 1;
  17.     }
  18.     public void Push(int type, object element)
  19.     {
  20.         if (type == 1)
  21.         {
  22.             if (top1 == top3_left + 1)
  23.                 throw new Exception("The stack is full");
  24.             arr[top1] = element;
  25.             top1++;
  26.         }
  27.         else if (type == 2)
  28.         {
  29.             if (top2 == top3_right)
  30.                 throw new Exception("The stack is full");
  31.             top2--;
  32.             arr[top2] = element;
  33.         }
  34.         else //type==3
  35.         {
  36.             if (isLeft)
  37.             {
  38.                 if (top1 == top3_left + 1)
  39.                     throw new Exception("The stack is full");
  40.                 arr[top3_left] = element;
  41.                 top3_left--;
  42.             }
  43.             else
  44.             {
  45.                 if (top2 == top3_right)
  46.                     throw new Exception("The stack is full");
  47.                 arr[top3_right] = element;
  48.                 top3_right++;
  49.             }
  50.             isLeft = !isLeft;
  51.         }
  52.     }
  53.     public object Pop(int type)
  54.     {
  55.         object obj = null;
  56.         if (type == 1)
  57.         {
  58.             if (top1 == 0)
  59.                 throw new Exception("The stack is empty");
  60.             else
  61.             {
  62.                 top1--;
  63.                 obj = arr[top1];
  64.                 arr[top1] = null;
  65.             }
  66.         }
  67.         else if (type == 2)
  68.         {
  69.             if (top2 == arr.Length)
  70.                 throw new Exception("The stack is empty");
  71.             else
  72.             {
  73.                 obj = arr[top2];
  74.                 arr[top2] = null;
  75.                 top2++;
  76.             }
  77.         }
  78.         else //type==3
  79.         {
  80.             if (top3_right == top3_left + 1)
  81.                 throw new Exception("The stack is empty");
  82.             if (isLeft)
  83.             {
  84.                 top3_left++;
  85.                 obj = arr[top3_left];
  86.                 arr[top3_left] = null;
  87.             }
  88.             else
  89.             {
  90.                 top3_right--;
  91.                 obj = arr[top3_right];
  92.                 arr[top3_right] = null;
  93.             }
  94.             isLeft = !isLeft;
  95.         }
  96.         return obj;
  97.     }
  98.     public object Peek(int type)
  99.     {
  100.         if (type == 1)
  101.         {
  102.             if (top1 == 0)
  103.                 throw new Exception("The stack is empty");
  104.             return arr[top1 - 1];
  105.         }
  106.         else if (type == 2)
  107.         {
  108.             if (top2 == arr.Length)
  109.                 throw new Exception("The stack is empty");
  110.             return arr[top2];
  111.         }
  112.         else //type==3
  113.         {
  114.             if (top3_right == top3_left + 1)
  115.                 throw new Exception("The stack is empty");
  116.             if (isLeft)
  117.                 return arr[top3_left + 1];
  118.             else
  119.                 return arr[top3_right - 1];
  120.         }
  121.     }
  122. }
复制代码
 

来源:程序园用户自行投稿发布,如果侵权,请联系站长删除
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!
您需要登录后才可以回帖 登录 | 立即注册