力扣刷题记录_栈和队列(自学)

栈与队列

  • 1、用栈实现队列(力扣232)
  • 2、用队列实现栈(力扣225)
  • 3、有效的括号(力扣20)
  • 4、删除字符串中的所有相邻重复项(力扣1047)
  • 5、逆波兰表达式求值(力扣150)
  • 6、滑动窗口最大值(力扣239)
  • 7、前 K 个高频元素(力扣347)

1、用栈实现队列(力扣232)

        //输入栈
        Deque<Integer> instack;
        //输出栈
        Deque<Integer> outstack;

        public MyQueue() {
            //使用linkedlist来实现
            instack = new LinkedList<Integer>();
            outstack = new LinkedList<Integer>();
        }

        public void push(int x) {
            instack.push(x);
        }

        public int pop() {
            if(outstack.isEmpty()){
                //将输入栈元素全部放入输出栈,此时输出栈的栈顶即为队列的对头
                while(!instack.isEmpty()){
                    outstack.push(instack.pop());
                }
            }
            return outstack.pop();
        }

        public int peek() {
            if(outstack.isEmpty()){
                while(!instack.isEmpty()){
                    outstack.push(instack.pop());
                }
            }
            return outstack.peek();
        }

        public boolean empty() {
            if(instack.isEmpty() && outstack.isEmpty()){
                return true;
            }
            return false;
        }

    }

在java里,栈和队列基于linkedlist实现,分别提供了针对于栈和队列操作的不同方法。
用栈来实现队列,关键点在往出取元素时。输入栈作为输入使用,输入栈的栈底对应队列头,想要输出栈底元素,必须将栈底元素挪动到栈顶,因此要将输入栈元素全部移动到输出栈中

2、用队列实现栈(力扣225)

//1.使用两个队列
    class MyStack {
        //创建两个队列
        Queue<Integer> queue1;
        Queue<Integer> queue2;

        public MyStack() {
            queue1 = new LinkedList<Integer>();
            queue2 = new LinkedList<Integer>();
        }

        public void push(int x) {
            queue2.offer(x);
            while(!queue1.isEmpty()){
                queue2.offer(queue1.poll());
            }
            //交换两队列,保证queue2是一个空队列,实现最后加入的元素在队列头部
            Queue<Integer> temp = queue1;
            queue1 = queue2;
            queue2 = temp;
        }

        public int pop() {
            //返回元素并删除
            return queue1.poll();
        }

        public int top() {
            //返回元素不删除
            return queue1.peek();
        }

        public boolean empty() {
            //因为每次加入元素之后将queue2赋给queue1,所以判断queue1即可
            return queue1.isEmpty();
        }
    }

用队列实现栈,关键点在出栈时。要保证队列头是最后一次加入的元素。因此每一次添加元素时,都要保证所加元素在一个队列的头部;

//使用一个队列
    class MyStack {

        Queue<Integer> queue;

        public MyStack() {
            queue = new LinkedList<Integer>();
        }

        public void push(int x) {
            queue.offer(x);
            //每添加一个新元素,保证此元素在队列的头部
            int n = queue.size();
            for(int i = 0;i < n - 1;i ++){
                queue.offer(queue.poll());
            }
        }

        public int pop() {
            return queue.poll();
        }

        public int top() {
            return queue.peek();
        }

        public boolean empty() {
            return queue.isEmpty();
        }
    }

3、有效的括号(力扣20)

//1.自己想的
    public boolean isValid(String s) {

        Map<Character, Character> map = new HashMap<>();
        map.put(')', '(');
        map.put('}', '{');
        map.put(']', '[');

        Deque<Character> deque = new LinkedList<>();
        char[] arr = s.toCharArray();
        int len = arr.length;

        if (len % 2 != 0) {
            return false;
        }

        for (int i = 0; i < len; i++) {
            if (!deque.isEmpty() && map.get(arr[i]) == deque.peek()) {
                deque.pop();
            } else {
                deque.push(arr[i]);
            }
        }

        return deque.isEmpty();

    }
//2.不利用map
    public boolean isValid2(String s) {
        Deque<Character> stack = new LinkedList<>();
        for(int i = 0;i < s.length();i ++){
            char c = s.charAt(i);
            //如果当前字符为左括号,将对应的右括号压入栈中
            if(c == '('){
                stack.push(')');
            }else if(c == '{'){
                stack.push('}');
            }else if(c == '['){
                stack.push(']');
                //如果当前字符为右括号,如果栈为空或是不匹配
            }else if(stack.isEmpty() || stack.peek() != c){
                return false;
            }else{
                //当前字符为右括号,并且刚好匹配
                stack.pop();
            }
        }
        return stack.isEmpty();
    }

4、删除字符串中的所有相邻重复项(力扣1047)

//1.自己想的
    public String removeDuplicates(String s) {

        //或者使用ArrayDeque stack = new ArrayDeque<>();
        //针对于添加/删除两端,ArrayDeque似乎更高效
        Deque<Character> stack = new LinkedList<>();
        int len = s.length();
        for(int i = 0;i < len;i ++){
            char ch = s.charAt(i);
            if(!stack.isEmpty() && stack.peek() == ch){
                stack.pop();
            }else{
                stack.push(ch);
            }
        }

        StringBuilder sb = new StringBuilder();
        while(!stack.isEmpty()){
            sb.append(stack.pop());
        }
        sb.reverse();
        return sb.toString();

    }
//2.使用StringBuilder实现栈
    public String removeDuplicates2(String s) {

        //使用StringBuilder实现栈,append()对应push(),charAt()对应peek(),deleteCharAt()对应pop()
        StringBuilder ans = new StringBuilder();
        int len = s.length();
        //用于指向ans的最后一个元素,便于取出比较和删除
        int index = -1;
        for(int i = 0;i < len;i ++){
            char ch = s.charAt(i);
            if(index >= 0 && ch == ans.charAt(index)){
                ans.deleteCharAt(index);
                index --;
            }else{
                ans.append(ch);
                index ++;
            }
        }

        return ans.toString();

    }
//3.双指针,以abba为例走一遍就明白了
    public String removeDuplicates3(String s) {
        char[] arr = s.toCharArray();
        int fast = 0;
        int slow = 0;
        while(fast < arr.length){
            arr[slow] = arr[fast];
            if(slow > 0 && arr[slow] == arr[slow - 1]){
                slow --;
            }else{
                slow ++;
            }
            fast ++;
        }

        return new String(arr,0,slow);
    }

5、逆波兰表达式求值(力扣150)

//1.栈
    public int evalRPN1(String[] tokens) {
        Deque<Integer> stack = new LinkedList<>();
        int len = tokens.length;
        for(int i = 0;i < len;i ++){
            String s = tokens[i];
            if(!("+".equals(s) || "-".equals(s) || "*".equals(s) || "/".equals(s))){
                stack.push(Integer.parseInt(s));
            }else{
                int a = stack.pop();
                int b = stack.pop();
                if("+".equals(s)) stack.push(b + a);
                if("-".equals(s)) stack.push(b - a);
                if("*".equals(s)) stack.push(b * a);
                if("/".equals(s)) stack.push(b / a);
            }
        }
        return stack.pop();
    }
//2.用数组模拟栈
    public int evalRPN2(String[] tokens) {
        int len = tokens.length;
        int[] arr = new int[len];
        int index = -1;
        for(int i = 0;i < len;i ++){
            String s = tokens[i];
            if(!("+".equals(s) || "-".equals(s) || "*".equals(s) || "/".equals(s))){
                index ++;
                arr[index] = Integer.parseInt(s);
            }else{
                int a = arr[index--];
                int b = arr[index--];
                switch(s){
                    case "+":
                        arr[++index] = a + b;
                        break;
                    case "-":
                        arr[++index] = b - a;
                        break;
                    case "*":
                        arr[++index] = b * a;
                        break;
                    case "/":
                        arr[++index] = b / a;
                        break;
                }
            }
        }
        return arr[index];
    }

6、滑动窗口最大值(力扣239)

//1.单调队列
    /*
    * 维护一个单调队列,队列里包含当前窗口中的元素,且从队列头是从大到小,
    * 每当滑动窗口向右移动,向返回数组中添加当前队列中的最大值
    * */
    public int[] maxSlidingWindow1(int[] nums, int k) {
        Deque<Integer> deque = new LinkedList<>();
        int len = nums.length + 1 - k;
        int[] ans = new int[len];

        int index = 0;
        for (int i = 0; i < nums.length; i++) {
            //维护队列是单调队列
            while (!deque.isEmpty() && nums[i] > deque.getLast()) {
                deque.removeLast();
            }
            deque.add(nums[i]);
            //如果第一次达到k个元素,将队列中的最大值加入返回数组,不涉及元素的移除
            if (i == k - 1) {
                ans[index++] = deque.peek();
            }
            //如果不是第一次达到k个元素,涉及元素的移除
            if (i >= k) {
                //如果当前队列头元素刚好等于要移除的元素,则将队列头出队
                if (!deque.isEmpty() && nums[i - k] == deque.peek()) {
                    deque.poll();
                }
                ans[index++] = deque.peek();
            }
        }

        return ans;
    }

7、前 K 个高频元素(力扣347)

时间复杂度O(nlogk),空间复杂度O(n)

//1.小顶堆
    public int[] topKFrequent(int[] nums, int k) {
        //使用map统计数组中各个数字出现的次数
        Map<Integer, Integer> map = new HashMap<>();
        for (int i = 0; i < nums.length; i++) {
            map.put(nums[i], map.getOrDefault(nums[i], 0) + 1);
        }

        //建立一个小顶堆,用来记录数组中出现频次前k的数字信息
        //小顶堆的排序需要按照map中value的大小,但是小顶堆中实际存放的是map中的key,
        //所以需要重写compare方法,按照value的值进行比较
        PriorityQueue<Integer> minHeap = new PriorityQueue<>(new Comparator<Integer>() {
            @Override
            public int compare(Integer a, Integer b) {
                return map.get(a) - map.get(b);
            }
        });

        //遍历map中的key
        for (Integer key : map.keySet()) {
            //如果堆中元素的个数小于k,则持续加入
            if (minHeap.size() < k) {
                minHeap.add(key);
            } else {
                //否则的话,判断将要加入元素对应的value值是否大于堆顶元素对应的value值
                //如果是,则堆顶元素出,新元素入
                if (map.get(key) > map.get(minHeap.peek())) {
                    minHeap.poll();
                    minHeap.add(key);
                }
            }
        }

        int[] ans = new int[k];
        int index = 0;
        //最终小顶堆中的k个数即为出现频次最高的前k个数
        for (Integer i : minHeap) {
            ans[index++] = i;
        }

        return ans;
    }

你可能感兴趣的