直通BAT面试算法总结——栈和队列

题一:栈的逆序

/**
 * 题干:
 * 实现一个栈的逆序,但是只能用递归函数和这个栈本身的pop操作来实现,而不能自己申请另外的数据结构。
 * 给定一个整数数组A即为给定的栈,同时给定它的大小n,请返回逆序后的栈。
 * 测试样例:
 * [4,3,2,1],4
 * 返回:[1,2,3,4]
 * 
 * 
 * 解析:首先要实现两种操作(两种递归)
 * 第一是要能递归的弹出返回栈底getPopButtom
 * 第二是递归将栈底逆序入栈:reverse
 * 
 *
 */
class Solution{
        public  void solve(Stack stack){

            if(stack.empty())
                return;

            T t=getPopButtom(stack);

            solve(stack);//递归

            stack.push(t);//

        }

        public  T getPopButtom(Stack stack){
            T head = stack.pop();
            if (stack.empty()){
                return head;//代表头结点弹出以后就是空栈了,而这个时候的头结点就是栈底;
            } else {
                T last=getPopButtom(stack);
                stack.push(head);

                return last;
            }

        }
    }

题二: 双栈排序

/**
 * 题干:请编写一个程序,按升序对栈进行排序(即最大元素位于栈顶),要求最多只能使用一个额外的栈存放临时数据,但不得将元素复制到别的数据结构中。
 * 测试样例:
 * [1,2,3,4,5]
 * 返回:[5,4,3,2,1]
 * 
 * 解析:就是利用一个辅助栈,将当前栈的栈顶压入help栈中的合适位置,
 * 在过程中可能还需要将help中大于当前栈顶的元素压入stack栈中,当然之后再逆序压回去就行。
 *
 */
 class Solution{
        public  Stack<Integer> solve(Stack<Integer> stack){

            if (stack.size()<2)
                return stack;
            Stack<Integer> help=new Stack<Integer>();

            sort(stack, help);

            return help;

        }


        public  void sort(Stack<Integer> stack,Stack<Integer> help){


            Integer top=stack.pop();

            Integer next_top=stack.peek();
            Integer helpTop=null;
             try {
                 next_top = stack.pop();
            } catch (EmptyStackException e) {
                next_top=null;//原本的空栈弹出或者获取的时候是要抛出异常的,在这里将异常捕获并将next_top赋值为null
            }



            //在help栈中找到当前stack的top位置,过程中吧大于当前top的help中的元素压入stack中
            while (helpTop!=null && top<helpTop) {
                //吧大于当前top的help中的元素压入stack中
                stack.push(helpTop);

                helpTop=help.pop();
            }
            //把之前help中压入进来的元素挨个弹出并且压入help栈中。
            //这里就需要使用一个next_top指针指示着stack上一次的top的下一个元素是什么
            while(top!=next_top){

                help.push(top);

                top=stack.pop();
            }

            //如果stack中还有元素那么继续递归将stack中的栈顶压入到help中
            if(stack.size()>0)
                sort(stack, help);


        }
    }

题三:滑动窗口


/**
 * 题干:
 * 有一个整型数组 arr 和一个大小为 w 的窗口从数组的最左边滑到最右边,窗口每次向右边滑一个位置。 返回一个长度为n-w+1的数组res,res[i]表示每一种窗口状态下的最大值。 以数组为[4,3,5,4,3,3,6,7],w=3为例。因为第一个窗口[4,3,5]的最大值为5,第二个窗口[3,5,4]的最大值为5,第三个窗口[5,4,3]的最大值为5。第四个窗口[4,3,3]的最大值为4。第五个窗口[3,3,6]的最大值为6。第六个窗口[3,6,7]的最大值为7。所以最终返回[5,5,5,4,6,7]。
 * 给定整形数组arr及它的大小n,同时给定w,请返回res数组。保证w小于等于n,同时保证数组大小小于等于500。
 * 测试样例:
 * [4,3,5,4,3,3,6,7],8,3
 * 返回:[5,5,5,4,6,7]
 * 
 * 解析:使用一个双端队列(LinkList)记录走过的元素下标,规则是:
 * 如果队列为空直接将当前元素的下标从队尾入队,
 * 如果当前元素的值小于队尾下标所对应的的元素那么直接将当前下标从对位入队,
 * 如果当前下标对应元素的值大于等于队尾元素所对应的数组元素,那么队尾出队,循环操作直到小于时再入队
 * 
 *还有一个问题就是什么时候队列中的值过期?
 *如果当前元素的下标i减去队头j的差d大于窗口值那么队头过期; 
 *
 * 
 *
 */

class Solution{
        public int[] solve(int[] arr, int n, int w) {  
            if(arr == null || arr.length == 0 || arr.length < w){  
                return null;  
            }  
            //辅助双端队列
            LinkedList queue = new LinkedList(); 
            //最终返回值
            int [] result = new int[arr.length - w + 1];

            int index = 0;  
            for( int i = 0; i < arr.length ; i ++){
                //循环:队尾下标对应的元素小于数组中的当前元素就弹出队尾
                while( !queue.isEmpty() && arr[queue.peekLast()] <= arr[i]){  
                    queue.pollLast();  
                }  
                //当前下标入队
                queue.addLast(i);

                //判断队首元素是否起作用-->十分重要  
                if(queue.peekFirst() == i - w){  
                    queue.pollFirst();  
                }  
                //向result中加入元素  
                if( i >= w - 1){  
                    result[index ++] = arr[queue.peekFirst()];  
                }  
            }  
            return result;  
        }  


    }

题四:数组变maxTree


/**
 * 题干: 对于一个没有重复元素的整数数组,请用其中元素构造一棵MaxTree,MaxTree定义为一棵二叉树,其中的节点与数
 * 组元素一一对应,同时对于MaxTree的每棵子树,它的根的元素值为子树的最大值。现有一建树方法,对于数组中的
 * 每个元素,其在树中的父亲为数组中它左边比它大的第一个数和右边比它大的第一个数中更小的一个。若两边都不存
 * 在比它大的数,那么它就是树根。请设计O(n)的算法实现这个方法。给定一个无重复元素的数组nums和它的大小n,请返
 * 回一个数组,其中每个元素为原数组中对应位置元素在树中的父亲节点的编号,若为根则值为-1。
 * 例如:输入数字{3,1,4,2}
 * 返回数组:{2,0,-1,2}
 * 
 * 解析:
 * 
 *
 */


class Solution{
         // 使用一个栈 保存下标,来对比元素  
        // 刚开始的话,将所有父结点的小标初始化为 -1
        public  int[] solve(int[] nums ,int n){
             if(nums == null || n==0) return null;  
                int[] result = new int[n];  
                Stack st = new Stack<>();  

                //初始状态下,所有元素的父结点都为 null,对应下标为-1  
                Arrays.fill(result,-1);  

                // 循环处理每一个元素  
                // 需要一个额外的栈来辅助获取两个阶段的最大值(两个阶段指左边最大值和右边最大值)  
                // 每次判断一个元素分三种情况  
                //  1. 栈为空,也就是初始状态,直接将元素下标入栈(此时该元素的父结点下标为默认值 -1)  
                //  2. 栈不为空,同时即将进栈的元素比栈顶元素小,那么直接将元素进栈同时更新当前元素的  
                //     父结点下标为i(设定父结点为左边第一个比该元素大的,右侧第一个比该元素大的第3步说明)  
                //  3. 栈不为空,同时即将进栈的元素比栈顶元素大,那么需要将栈顶元素出栈,然后比较  
                //     需要分情况:  
                //     1) 栈顶元素对应父结点下标为null, 说明栈顶元素左侧没有最大值,此时需要更新  
                //        栈顶元素对应父结点下标为当前进栈元素的下标 i,然后将栈顶元素pop 出栈  
                //     2) 栈顶元素对应的父结点下标对应数组中元素比,i 位置对应数组元素大,由maxTree 的  
                //        定义,我们必须保存左侧和右侧相对较小的那个作为最终结果,所以也更新元素下标为i  
                //        然后将栈顶元素 pop 出栈  
                //     3) 如果不在上面两种情况范围内,直接将栈顶元素出栈,然后继续循环执行 3 步骤  
                for(int i=0;iwhile(!st.isEmpty() && nums[st.peek()]// 如果即将《被pop 元素》的左侧最大值为null 或者其左侧最大值比右侧(nums[i])最大值大  
                        // 需要先更新该元素的父结点为右侧结点下标  
                        if(result[st.peek()] == -1 || nums[result[st.peek()]]>nums[i]){  
                            result[st.peek()] = i;//更新成右侧最大值  
                            System.out.println("更新右侧");  
                        }  
                        // 不管更新没更新栈顶元素最终的父结点下标,接下来都需要把其pop 出栈  
                        System.out.println("出栈"+st.pop());  
                    }  

                    // 经过上面循环,判断一下栈是否为空了  
                    if(!st.isEmpty()){  
                        //如果不空,更新当前元素左侧第一个比它大的父结点为栈顶元素  
                        System.out.println("栈顶元素"+st.peek()+"作为result数组中"+i);  
                        result[i] = st.peek();  
                    }  
                    //不管栈是否为空,最终都要将当前元素进栈  
                    System.out.println("入栈"+i);  
                    st.push(i);  
                }  
                return result; 
        }
    }

假设我们要将[3、1、2、5、4]变为maxTree,以下是解析:
直通BAT面试算法总结——栈和队列_第1张图片
直通BAT面试算法总结——栈和队列_第2张图片
直通BAT面试算法总结——栈和队列_第3张图片
直通BAT面试算法总结——栈和队列_第4张图片

最终返回的数组是[3,2,0,-1,3]

你可能感兴趣的:(Java)