算法 队列和栈

一、队列

1.1 栈的性质

  • 先进先出

1.2 优先级队列(堆结构)

1.3 双端队列

双端队列的首部和尾部都可以亚压入元素

1.4 宽度优先遍历

二、栈

2.1 栈的性质

  • 先进后出
  • 栈的基本操作都是o(1)的操作

2.2 栈的基本操作

  • pop操作
  • top或者peek操作
  • push操作
  • size操作

2.3 深度度优先遍历(DFS)

三、案例

3.1 实现一个特殊的栈,在基本栈的功能上,再实现返回栈中最小元素的操作getMin

  • 要求:pop,push,getMin的时间复杂度都是o(1)

  • 思路:
    两个栈:一个保存数据,一个保存当前栈中的最小值

public class statck {
    public static void main(String[] args) {
        int[] array = new int[]{4,3,4,2,5,3};
        stackTest(array);
    }
    public static void stackTest(int[] array){
        Stack<Integer> data = new Stack<Integer>();
        Stack<Integer> Min = new Stack<Integer>();
        for (int i = 0; i < array.length; i++) {
            if(Min.isEmpty() && data.isEmpty()){
                Min.push(array[i]);
                data.push(array[i]);
            }else {
                if(array[i] > Min.peek()){
                    Min.push(Min.peek());
                }else {
                    Min.push(array[i]);
                }
                data.push(array[i]);
            }
        }
        System.out.println("每一步栈的最小值:");
        while (!Min.isEmpty()){
            System.out.print(Min.pop() + " ");
        }
        System.out.println();
        System.out.println("data栈:");
        while (!data.isEmpty()){
            System.out.print(data.pop() + " ");
        }
    }
}

3.2 两个栈模拟队列,支持队列的基本操作

注意:

  • 数据要一次性的从statck1倒到statck2,其间不允许新的数据加入模拟的队列,不然会出错。
public class statck {
    static Stack<Object> objects1 = new Stack<>();
    static Stack<Object> objects2 = new Stack<>();
    public static void main(String[] args) {
        int[] array = new int[]{4,3,4,2,5,3};
        add(array);
        pop(array);
        peek(array);
        pop(array);
    }
    public static void add(int[] array){
        for (int i = 0; i < array.length; i++) {
            objects1.push(array[i]);
        }
        for (int i = 0; i < array.length; i++) {
            objects2.push(objects1.pop());
        }
    }
    public static void pop(int[] array){
        System.out.print("模拟元素出队列:" + " ");
         if(!objects2.isEmpty()){
            System.out.print(objects2.pop() + " ");
        }
    }
    public static void peek(int[] array){
        System.out.println();
       if(!objects2.isEmpty()){
           System.out.print("栈顶元素是: " + objects2.peek() + " ");
       }
        System.out.println();
    }
}

3.3 实现栈的逆序,但是只能用递归函数和这个栈的本身的操作来实现,而不能自己申请另外的数据结构。

  • 思路:
  • 递归取出栈底的元素,然后入栈。使用递归实现。
public class statck {
    public static void main(String[] args) {
        Stack stack = new Stack();
        stack.push(3);
        stack.push(2);
        stack.push(1);
        reserve(stack);
        while (!stack.isEmpty()){
            System.out.print(stack.pop() + " ");
        }
    }
    //取栈底的值
    public static int getBottom(Stack<Integer> stack) {
        Integer pop = stack.pop();
        if(stack.isEmpty()){
            return pop;
        }else {
            int bottom = getBottom(stack);
            stack.push(pop);
            return bottom;
        }
    }
    //逆序栈
    public static void reserve(Stack<Integer> stack){
        if(stack.isEmpty()){
            return;
        }else{
            int bottom = getBottom(stack);
            reserve(stack);
            stack.push(bottom);
        }
    }
}

3.4 将栈中的元素按照从大到小排序,只允许额外申请一个栈

注意点:

  • 第二个栈为空时要跳出循环
  • 每步从栈2拿出来的元素记得放回栈1或者栈2
public class statck {
    public static void main(String[] args) {
        Stack stack = new Stack();
        stack.push(3);
        stack.push(2);
        stack.push(6);
        stack.push(1);
        stack.push(4);
        compare(stack);
    }
    public  static void compare(Stack<Integer> integers1){
        int mid = 0;
        Stack<Integer> integers2 = new Stack<>();
        if(!integers1.isEmpty()){
            integers2.push(integers1.pop());
        }
        while (!integers1.isEmpty() && integers2.size() >= 0){
            Integer pop1 = integers1.pop();
            mid = pop1;
            Integer pop2 = integers2.pop();
            int count = 0;
            while (mid > pop2){
                integers1.push(pop2);
                if(integers2.isEmpty()){
                    break;
                }
                if(!integers2.isEmpty()) {
                    pop2 = integers2.pop();
                }
                count ++;
            }
            if(mid <= pop2) {
                integers2.push(pop2);
            }
            integers2.push(mid);
            for (int i = 0; i < count; i++) {
                integers2.push(integers1.pop());
            }
        }
        while (!integers2.isEmpty()){
            integers1.push(integers2.pop());
        }
        while (!integers1.isEmpty()){
            System.out.print(integers1.pop() + " ");
        }
    }
    //取栈底的值
}

3.5 有一个整形数组arr和一个大小为w的窗口从数组的最左边滑到最右边,窗口每次向右边滑一个位置,返回一个长度为n-w+1的数据res,res[i]表示每一种窗口下的最大值。

最优解可以做到时间复杂度为:o(N)

思路:

  • 使用双端队列
  • 初始化
  1. 0放到双端队列中
  2. 依次从数组中取一个数进入双端队列中,从双端队列的最顶端的数依次比较,>比顶端的数所对应的数组元素大,移除顶端的数,再取顶端的数比较,直到小于等于顶端的数,将该数的下标添加进去。
  3. 一直重复步骤2,直到数组的小标到达w-1。

思路:
初始化完成之后:

  • 滑动窗口,判断顶端的数据是否符合所在的窗口范围,在,保留顶端数据,不在,移除顶端数据,像初始化那添加数据,把最顶端的数据保留在一个score数组,该数据记录每次窗口的最大值。
  • 重复上一步骤,直到窗口滑动结束。

public class DQueueTest {
    public static void main(String[] args) {
        int[] ints = new int[]{1,2,3,4,5,6,7,8,9};
        getMax(ints, 3);
    }
    //初始化双端队列,双端队列中存储的是元素的下标,新来的数把比他小的数全踢出去,后来的小的数保留
    public static Deque getInitialMax(int[] array, int length) {
        Deque dQueue = new LinkedList();
        if (length > array.length) {
            return null;
        }
        dQueue.push(0);
        int i = 1;
        while (i < length) {
            if (!dQueue.isEmpty() && array[i] >= array[(int) dQueue.peekFirst()]) {
                dQueue.pop();
            } else {
                dQueue.push(i);
                i++;
            }
        }
        Iterator iterator = dQueue.iterator();
        System.out.println();
        return dQueue;
    }
    public static void getMax(int[] array, int length){
        Deque initialMax = getInitialMax(array, length);
        int[] score = new int[array.length - length + 1];
        int key = 1;
        score[0] = array[(int)initialMax.peekLast()];
        int start = length;
        while (start < array.length){
            //先判断头部的数有效没
            if(!initialMax.isEmpty() && (int)initialMax.peekLast() >= start-length + 1){
            }else {
                initialMax.removeLast();
            }
            //接着将数据放到合适的位置
            while (!initialMax.isEmpty() && array[start] >= (int)initialMax.peekFirst()){
                initialMax.removeFirst();
            }
            initialMax.push(array[start]);
            score[key++] = (int)initialMax.peekLast();
            start ++ ;
        }
        for (int i = 0; i < score.length; i++) {
            System.out.print(score[i] + " ");
        }
    }
}

3.6 给定一个没有重复元素的数组arr,写出生成这个数组的MaxTree的函数,要求如果数组长度为N,则时间复杂度为o(N),额外空间复杂度为o(N)。* *

  • MaxTree是一颗二叉树,数组的每一个值对应一个二叉树节点。
  • 包括MaxTree树在内且在其中的每一颗子树上,值最大的节点都是树的头。

思路:
对于数组中的每一个元素,找左边比他大的第一个元素,再找右边比他大的第一个元素,两个元素中最小的数作为其父节点即可。

public class BuileTree {
    public static void main(String[] args) {
        Node[] node = new Node[7];
        node[0] = new Node("8");
        node[1] = new Node("4");
        node[2] = new Node("3");
        node[3] = new Node("5");
        node[4] = new Node("7");
        node[5] = new Node("9");
        node[6] = new Node("2");
        Node[] lnode = new Node[7];
        Node[] rnode = new Node[7];
        Deque deque = new LinkedList<>();
        deque.push(node[0]);
        for (int i = 1; i < node.length; i++) {
            while (!deque.isEmpty() && Integer.parseInt(node[i].getValue()) > Integer.parseInt(((Node)deque.peekFirst()).getValue())){
                deque.pollFirst();
            }
            if(deque.isEmpty()){
            }else {
                lnode[i] = (Node)deque.peekFirst();
            }
            deque.push(node[i]);
        }
        Deque deque1 = new LinkedList<>();
        for (int i = node.length - 1; i >= 0; i--) {
            while (!deque1.isEmpty() && Integer.parseInt(node[i].getValue()) > Integer.parseInt(((Node)deque1.peekFirst()).getValue())){
                deque1.pollFirst();
            }
            if(deque1.isEmpty()){
            }else {
                rnode[i] = (Node)deque1.peekFirst();
            }
            deque1.push(node[i]);
        }
        for (int i = 0; i < node.length; i++) {
            if (lnode[i] == null && rnode[i] != null) {
                if (rnode[i].getlNode() == null) {
                    rnode[i].setlNode(node[i]);
                } else {
                    rnode[i].setrNode(node[i]);
                }
            }
            else if (rnode[i] == null && lnode[i] != null) {
                if (lnode[i].getlNode() == null) {
                    lnode[i].setlNode(node[i]);
                } else {
                    lnode[i].setrNode(node[i]);
                }
            }
            else if(rnode[i] == null && lnode[i] == null){}
            else if(lnode[i] != null && rnode[i] != null){
                if (Integer.parseInt(lnode[i].getValue()) > Integer.parseInt(rnode[i].getValue())) {
                    if (rnode[i].getlNode() == null) {
                        rnode[i].setlNode(node[i]);
                    } else {
                        rnode[i].setrNode(node[i]);
                    }
                } else {
                    if (lnode[i].getlNode() == null) {
                        lnode[i].setlNode(node[i]);
                    } else {
                        lnode[i].setrNode(node[i]);
                    }
                }
            }
        }
        Node node1 = (Node) deque.pollLast();
        sortNode(node1);
    }
    public static void sortNode(Node node){
        if(node != null){
            System.out.print(node.getValue() + " ");
        }
        if(node.getlNode() != null){
            sortNode(node.getlNode());
        }
        if(node.getrNode() != null){
            sortNode(node.getrNode());
        }
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值