• 算法必刷系列之栈、队列、哈希


    括号匹配

    遍历字符串,左括号入栈,右括号出栈判断是否匹配,遍历结束,判断栈是否为空

    public boolean isValid(String s) {
        Map<Character,Character> map = new HashMap<Character,Character>();
        Stack<Character> stack = new Stack<Character>();
        map.put('(',')');
        map.put('{','}');
        map.put('[',']');
        for(int i = 0; i<s.length();i++){
            Character c = s.charAt(i);
            if(map.containsKey(c)){
                stack.push(c);
            }else {
                if(stack.isEmpty()){
                    return false;
                }
                Character key = stack.pop();
                Character value = map.get(key);
                if(c!=value){
                    return false;
                }
            }
        }
        return stack.isEmpty();
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23

    最小栈

    创建两个栈,一个栈用来正常存储元素,另一个栈存储正常栈元素对应的最小元素

    class MinStack {
        private Deque<Integer> xStack;
        private Deque<Integer> minStack;
    
        public MinStack(){
            xStack = new LinkedList<Integer>();
            minStack = new LinkedList<Integer>();
            minStack.push(Integer.MAX_VALUE);
        }
    
        public void push(int val){
            xStack.push(val);
            minStack.push(val<minStack.peek()?val:minStack.peek());
        }
    
        public void pop(){
            xStack.pop();
            minStack.pop();
        }
    
        public int top(){
            return xStack.peek();
        }
    
        public int getMin(){
            return minStack.peek();
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28

    最大栈

    原理和最小栈相同

    class MaxStack {
        private Deque<Integer> xStack;
        private Deque<Integer> maxStack;
    
        public MinStack(){
            xStack = new LinkedList<Integer>();
            maxStack = new LinkedList<Integer>();
            maxStack.push(Integer.MIN_VALUE);
        }
    
        public void push(int val){
            xStack.push(val);
            maxStack.push(val>maxStack.peek()?val:maxStack.peek());
        }
    
        public void pop(){
            xStack.pop();
            maxStack.pop();
        }
    
        public int top(){
            return xStack.peek();
        }
    
        public int getMax(){
            return maxStack.peek();
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28

    基本计算器

    遍历字符串,记录当前数字前的运算符,如果为+或者-,则入栈当前数字或者相反数,如果为*或者/出栈一个元素与当前元素运算,将结果入栈,最后依次出栈进行加法运算,的到最终结果

    public int calculate(String s) {
        Stack<Integer> stack = new Stack<>();
        int num = 0;
        char pre = '+';
        for (int i = 0; i < s.length(); i++) {
            char c = s.charAt(i);
            if (Character.isDigit(c)) {
                num = num * 10 + c - '0';
            }
            if (!Character.isDigit(c) && s.charAt(i) != ' ' || i == s.length() - 1) {
                if (pre == '+' || pre == '-') {
                    stack.push(pre == '+' ? num : (-1) * num);
                } else {
                    int x = stack.pop();
                    if (pre == '*') {
                        x *= num;
                    } else {
                        x /= num;
                    }
                    stack.push(x);
                }
                pre = c;
                num = 0;
            }
        }
        int res = 0;
        while (!stack.isEmpty()) {
            res += stack.pop();
        }
        return res;
    } 
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31

    逆波兰表达式计算

    遍历字符数组,如果是数字,入栈,如果是运算符,出栈两个元素进行运算,结果入栈

    public int evalRPN(String[] tokens) {
            Stack<Integer> stack = new Stack<>();
            for(int i=0;i<tokens.length;i++){
    
                if(!Character.isDigit(tokens[i].charAt(0))&&tokens[i].length()==1){
                    char c = tokens[i].charAt(0);  
                    int x = stack.pop();              			    
                    int y = stack.pop();
                    int res=0;
                    if(c=='+'){
                        res = y+x;
                    }else if(c=='-'){
                        res = y-x;
                    }else if(c=='*'){
                        res = y*x;
                    }else if(c=='/'){
                        res = y/x;
                    }
                    stack.push(res);
                }else{
                    stack.push(Integer.parseInt(tokens[i]));
                }
            }
            return stack.pop();
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25

    队列

    两个栈实现队列

    一个栈作为元素入口,称为入口栈,一个栈作为元素出口,称为出口栈,入栈从入口栈压入元素,出栈从出口栈弹出元素,如果出口栈为空,将入口栈元素弹出,压入出口栈

    class MyQueue {
        Deque<Integer> inStack;
        Deque<Integer> outStack;
        
        public MyQueue() {
            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()){
                    int x = inStack.pop();
                    outStack.push(x);
                }
            }
            return outStack.pop();
        }
        
        public int peek() {
            if(outStack.isEmpty()){
                while(!inStack.isEmpty()){
                    int x = inStack.pop();
                    outStack.push(x);
                }
            }
            return outStack.peek();
        }
        
        public boolean empty() {
            return inStack.isEmpty()&&outStack.isEmpty();
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37

    两个队列实现栈

    每次从一个队列进入元素,进入后将另一个队列元素依次出队,进入到一个队列,之后交换两个队列

    class MyStack {
        Queue<Integer> queue1;
        Queue<Integer> queue2;
    
        public MyStack() {
            queue1 = new LinkedList<Integer>();
            queue2 = new LinkedList<Integer>();
        }
        
        void push(int x){
            queue2.offer(x);
            while(!queue1.isEmpty()){
                int num = queue1.poll();
                queue2.offer(num);
            }
            Queue<Integer> temp = queue1;
            queue1 = queue2;
            queue2 = temp;
        }
        int pop(){
            return queue1.poll();
        }
        int top(){
            return queue1.peek();
        }
        boolean empty(){
            return queue1.isEmpty();
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29

    LRU缓存

    创建双向链表存储元素,为双向链表加一个头节点,一个尾节点便于插入删除操作,创建一个map存储节点key,和value,实现O(1)时间找到节点。获取元素时,判断map中是否存在,如存在,先删除,然后再头部插入,最后返回。插入或者更新时,判断map中是否存在,存在则为更新,不存在则为插入。插入操作直接插入到头部,更新操作先删除,然后再头部插入。插入后,如果元素个数超出限制,从尾部删除

    class LRUCache {
        class DoubleLinkedNode{
            int key;
            int value;
            DoubleLinkedNode pre;
            DoubleLinkedNode next;
    
            public DoubleLinkedNode(){
    
            }
    
            public DoubleLinkedNode(int key, int value){
                this.key = key;
                this.value = value;
            }
        }
        Map<Integer,DoubleLinkedNode> map;
        DoubleLinkedNode head;
        DoubleLinkedNode tail;
        int size;
        int capacity;
        public LRUCache(int capacity) {
            map = new HashMap<>();
            head = new DoubleLinkedNode();
            tail = new DoubleLinkedNode();
            head.next = tail;
            tail.pre = head;
            size = 0;
            this.capacity = capacity;
        }
    
        public int get(int key) {
            if(map.containsKey(key)){
                DoubleLinkedNode node = map.get(key);
                remove(node);
                addToHead(node);
                return node.value;
            }
            return -1;
        }
    
        public void put(int key, int value) {
            if(map.containsKey(key)){
                DoubleLinkedNode node = map.get(key);
                node.value = value;
                remove(node);
                addToHead(node);
            }else{
                DoubleLinkedNode node = new DoubleLinkedNode(key, value);
                map.put(key, node);
                addToHead(node);
                size++;
                if(size>capacity){
                    int removeKey = removeFromTail();
                    map.remove(removeKey);
                    size--;
                }
            }
        }
    
        public void remove(DoubleLinkedNode node) {
            node.pre.next = node.next;
            node.next.pre = node.pre;
        }
    
        public void addToHead(DoubleLinkedNode node) {
            node.next = head.next;
            head.next.pre = node;
            node.pre = head;
            head.next = node;
        }
    
        public int removeFromTail() {
            DoubleLinkedNode pre = tail.pre;
            remove(tail.pre);
            return pre.key;
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78

    哈希

    两数之和

    使用HashMap,将双重循环变成一次遍历

    public int[] twoSum(int[] nums, int target) {
        Map<Integer,Integer> map = new HashMap<>();
        for(int i=0;i<nums.length;i++){
            if(map.containsKey(target-nums[i])){
                return new int[]{map.get(target-nums[i]),i};
            }else{
                map.put(nums[i],i);
            }
        }
        return null;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

    三数之和

    使用排序加双指针,代替三层循环的高复杂度,同时解决双层循环加哈希无法直接去重的问题

    public List<List<Integer>> threeSum(int[] nums) {
        List<List<Integer>> res = new ArrayList<>();
        int n = nums.length;
        Arrays.sort(nums);
        for (int first = 0; first < n; first++) {
            if (first > 0 && nums[first] == nums[first - 1]) {
                continue;
            }
            int third = n - 1;
            int target = -nums[first];
            for (int second = first + 1; second < n; second++) {
                if (second > first + 1 && nums[second] == nums[second - 1]) {
                    continue;
                }
                while (second < third && nums[second] + nums[third] > target) {
                    third--;
                }
                if (second == third) {
                    break;
                }
                if (nums[second] + nums[third] == target) {
                    List<Integer> list = new ArrayList<>();
                    list.add(nums[first]);
                    list.add(nums[second]);
                    list.add(nums[third]);
                    res.add(list);
                }
            }
        }
        return res;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
  • 相关阅读:
    C和指针 第15章 输入/输出函数 15.11 二进制I/O
    el-table 动态合并单元格和给某一行添加颜色
    Git 的概念以及相关操作
    netty包版本问题导致dubbo服务调用失败
    docker默认ip地址修改
    TiDB 悲观事务模式
    数字化助力生产制造管理:专项生产管理系统
    学习编程放飞梦想-太理程序设计竞赛团队总结2022
    NodeJs实战-待办列表(4)-解决待办事项中文乱码问题
    shell之sed
  • 原文地址:https://blog.csdn.net/m0_45362454/article/details/133897823