目录
栈和队列基本理解
1. 栈:是一个先进后出的线性表,只在栈顶(表尾)进行增加和删除的操作,栈底 为表头。一般有顺序栈和链栈。
1.1.顺序栈和链式栈在进行插入删除操作的时间复杂度
顺序栈:头插:O(N),头删O(N);尾插:O(1),尾删O(1)
链式栈:头插:O(1),头删O(1);尾插:O(1),尾删O(N)
2. 队列:特征是先进先出,在表的一头进行插入操作,在另一头进行删除操作,可以用链表的形式来实现。
顺序队列:头插:O(N) , 尾删:O(1);尾插:O(1),头删 O(N)
1.验证栈序列(出栈序列判断)——leetcode
题目:
给定 pushed 和 popped 两个序列,每个序列中的值都不重复,当他们的入栈序列已知,判断给定的出栈序列是否正确。
输入:入栈: [1,2,3,4,5],出栈:[3,5,4,2,1]
输出:true
入1,2,3,出3,入4,5,出5,4,2,1
思路:
- 当 入栈序列为空 或 出栈序列为空 或 出栈序列和入栈序列的长度不相等 时,返回false。
- 定义两个数组,inOrder[] 为入栈序列的数组,outOrder[] 为出栈序列的数组;
- 定义两个指针i,j,i 指向入栈序列,j 指向出栈序列;
- 定义一个栈,当stack.isEmpty() || stack.peek() != outOrder[j](即栈为空或栈顶元素不等于当下待比较的出栈序列数组下标为j的元素,则 i++;继续入栈,栈顶元素改变;若相同,则栈顶元素pop操作,变为下一个,i不变,j++;即j的下一个元素进行比较。
- 最后,若i== j (即全部遍历完)并且 stack.isEmpty() 为真,则说明出栈序列正确;
代码1:
public static boolean validateStackSequences(int[] inOrder, int[] outOrder) {
if (inOrder == null || outOrder == null || inOrder.length != outOrder.length) {
return false;
}
int i = 0;// i 遍历inOrder序列
int j = 0;// j遍历outOrder 序列
Stack<Integer> stack = new Stack<>();
if (inOrder.length == 0 && outOrder.length == 0) {
return true;
}
// 避免数组越界 如果长度为0,则下一步push(0) 出错
stack.push(inOrder[i++]);// 栈入第一个元素
// 当最后一个inOrder 的元素遍历后,i还自增了一下,
// 所以在下一个while循环中,i不符合,会提前退出
// i一定先比j遍历完,所以为了不让循环提前退出,则i<= inOrder.length;
while (j < outOrder.length && i <= inOrder.length) {
//避免栈里的元素全部出栈,空指针报错
if (stack.isEmpty() || stack.peek() != outOrder[j]) {
// 出现错误:当出栈序列出现错误时,将会满足以上if语句的条件,则会执行if中的语句
// 当栈顶元素和出栈序列中下标为j的元素不相等的时候,栈中会一直入元素,当入到下标为inOrder.length的时候,就会出现数组越界异常
if (i < inOrder.length) {
stack.push(inOrder[i]);
// 如果栈为空,或者入栈的当前元素和出栈的当前元素不相同,则i++;
}
i++; // i++不能放在以上if语句中,否则当以上if语句进不去的时候,i不能自增(即当i遍历完);while循环不能退出。
}
if (stack.peek() == outOrder[j]) {
stack.pop();
j++;
// 如果相同,则j++;所有元素都出去了,所以j = 5(假设为5个元素),
// 从0开始,当stack.peek() == outOrder[0]时,j再自增,j=1,
// 当最后一个元素outOrder[4]后,j=5,退出循环
}
}
if (i == j && stack.isEmpty()) {
return true;
} else {
return false;
}
}
代码2:
public boolean InOutOfStack(int[] pushed, int[] popped) {
if(pushed.length == popped.length){
Stack<Integer> stack = new Stack<>();
int i = 0;
int j = 0;
while(i<pushed.length && j<popped.length){
while(i<pushed.length && (stack.empty()||stack.peek()!=popped[j])){
stack.push(pushed[i]);
i++;
}
while(j<popped.length&&!stack.empty()&&stack.peek() == popped[j]){
stack.pop();
j++;
}
}
return stack.empty();
}
return false;
}
2.两个队列模拟一个栈
思路:
- 定义两个队列,让序列先入queue1,在出队列,剩下一个元素,将其他元素再入queue2,将剩下的元素poll()出来。
- queue2 中的操作同上
代码1:
public static void twoQueueToOneStack(int[] arr) {
Queue<Integer> queue1;
Queue<Integer> queue2;
queue1 = new LinkedList<Integer>();
queue2 = new LinkedList<Integer>();
for (int i = 0; i < arr.length; i++) {
queue1.offer(arr[i]);// 将元素入到一个队列中
}
int total = arr.length;// 元素总大小
int count;
while (total > 0) { // 不能将条件置为total != 0;total可能为负数,所以设为>0,为保险
count = total;
while (count > 1) { // 当元素的个数大于一时,队列2入队列1中的元素
queue2.offer(queue1.peek());
queue1.poll();// 更新queue1,每次queue2 获取一个值后,queue1 相应的删除一个值
count--;
}
if(!queue1.isEmpty()) { // 必须判断该队列是否为空,若为空,则打印出来的值为null
System.out.print(queue1.peek()+" ");
total--;// 总数-1;
queue1.poll();// queue 更新,置为null
}
count = total;
while (count > 1) {
queue1.offer(queue2.peek());
queue2.poll();
count--;
}
if(!queue2.isEmpty()) {
System.out.print(queue2.peek()+" ");
queue2.poll();
total--;
}
}
System.out.println("");
}
代码2:
Queue<Integer> queue1;
Queue<Integer> queue2;
public pratice() { // 需要一个一个的输入value,并一个一个的打印栈内的元素
// 队列是一个典型的先进先出的容器,Java中LinkedList提供了方法以支持队列的行为,并且它实现了Queue接口,
// 因此LinkedList可以用作Queue的一种实现。
queue1 = new LinkedList<Integer>();
queue2 = new LinkedList<Integer>();
}
public void push(int value){ //
queue1.offer(value);
}
public Integer pop(){
if(queue1.size() == 1){
return queue1.poll();
// poll:将首个元素从队列中弹出,如果队列是空的,就返回null
//peek:查看首个元素,不会移除首个元素,如果队列是空的就返回null
//element:查看首个元素,不会移除首个元素,如果队列是空的就抛出异常
}else{
while(queue1.size() > 1){
queue2.offer(queue1.poll());
}
int res = queue1.poll(); // 最后一个元素
while(!queue2.isEmpty()){
queue1.offer(queue2.poll());
}
return res;
}
}
public int top(){
if(queue1.size() == 1 ){
return queue1.peek();
}else{
while(queue1.size() > 1){
queue2.offer(queue1.poll());
}
int res = queue1.poll();
while (!queue2.isEmpty()){
queue1.offer(queue2.poll());
}
queue1.offer(res);
return res;
}
}
public boolean isEmpty(){
return queue1.isEmpty();
}
3.两个栈模拟一个队列
思路:
- 先准备一个栈,让序列先入栈,如先入 1 2 3 4 5 ,再让这个序列出原来的栈,为5 4 3 2 1.进入辅助的栈,入栈序列是 5 4 3 2 1,在进行出栈时就变成了1 2 3 4 5
- 即实现了队列的特征:先进先出
代码1:
twoStackToOneQueue(int[] arr){
if(arr == null || arr.length == 1){
return;
}
Stack<Integer> stack1 = new Stack<>();
Stack<Integer> stack2 = new Stack<>();
// 将arr数据依次入栈1
for(int i = 0;i<arr.length;i++){
stack1.push(arr[i]);
}
// 将栈1数据依次入栈2
while(!stack1.isEmpty()){
stack2.push(stack1.peek());
stack1.pop();
}
//将栈2数据依次打印出来
while(!stack2.isEmpty()){
System.out.println(stack2.pop());
}
}
public static void main(String[] args) {
int[] brr = {1,2,3,4,5};
twoStackToOneQueue(brr);
}
}
代码2:
Stack<Integer> stack1 = new Stack<>();
Stack<Integer> stack2 = new Stack<>();
public void enQueue(int value){
stack1.push(value);
}
public int outQueue(){
if(stack2.isEmpty()){
while(!stack1.isEmpty()){
stack1.pop();// 栈1 元素先pop出来;
stack2.push(stack1.pop()); // 进入栈2
}
}
return stack2.pop();
}
3段代码的测试结果
测试代码:
public static void main(String[] args) {
int[] brr = {1,2,3,4,5}; // 测试三个题中的代码1
int[] arr = {4,3,2,1,5};
System.out.println(validateStackSequences(brr,arr));// 验证栈序列 true
twoStackToOneQueue(brr); // 两个栈模拟一个队列
twoQueueToOneStack(brr); // 两个队列模拟一个栈
pratice queue1 = new pratice();
queue1.push(1); //代码2:两个队列模拟一个栈
queue1.push(2);
queue1.push(3);
queue1.push(4);
System.out.print(queue1.pop() + " ");
System.out.print(queue1.pop()+" ");
System.out.print(queue1.pop()+" ");
System.out.println(queue1.pop()+" ");
pratice stack = new pratice();
stack.push(1);// 代码2: 两个栈模拟一个队列
stack.push(2);
System.out.print(stack.pop()+" ");
System.out.println(stack.pop()+" ");
System.out.println(InOutOfStack(brr,arr)); // true
}
}
得到结果为: