力扣解题汇总(中等)_JAVA

数学 > 数组 > 链表 > 字符串 > 哈希表 > 双指针 > 递归 > 栈 > 队列 > 树

数组_中等

209_长度最小的子数组

class Solution {
    public int minSubArrayLen(int target, int[] nums) {
        //滑动串口,(其实还是双指针)
        //拿符合的子串的长度与后面的符合的子串长度做对比,找出长度最小的那个
        //不会漏掉任何一个符合的子串
        int start = 0;
        int end = 0;
        int sum = 0;
        int minLen = nums.length+1;
        while(end<nums.length&&start<nums.length){
            //end和start变化后与minlen的记录的原子性操作很重要
            sum += nums[end];
            while (sum >= target) {
                minLen = Math.min(minLen,end-start+1);
                sum -= nums[start++];
            }
            end++;
        }
        return minLen == nums.length+1? 0:minLen;
    }
}

59. 螺旋矩阵 II

跳转锚点
class Solution {
    public int[][] generateMatrix(int n) {
        int[][] matrix = new int[n][n];
        int edgeLen = 0;//本圈距离边缘的距离
        int i = 0;//行数
        int j = 0;//列数
        int num = 1;//填入的数字
        //按层模拟,我这种有点像是模拟和按层模拟的结合
        //感觉还是'拿了橘子跑啊'的思路好一点,不断收缩left\right\bottom\top
        for (; edgeLen < Math.ceil(((double)n)/2) ; edgeLen++) {
            while (j<n-edgeLen) {
                matrix[i][j] = num;
                j++;
                num++;
            }
            j--;
            i++;
            while (i<n-edgeLen) {
                matrix[i][j] = num;
                i++;
                num++;
            }
            i--;
            j--;
            while (j>=0+edgeLen) {
                matrix[i][j] = num;
                j--;
                num++;
            }
            j++;
            i--;
            while (i>=0+edgeLen+1) {
                matrix[i][j] = num;
                i--;
                num++;
            }
            i++;
            j++;

        }
        return matrix;
    }
}

238_除自身以外数组的乘积

class Solution {
    public int[] productExceptSelf(int[] nums) {
        //暴力解法是O(n2)
        //不能用除法,就不能乘一块再除
        //ans不过就是左前缀乘积和右前缀乘积
        //ansL[i] = ans[i-1] * nums[i-1]
        //ansR[i] = ans[i+1] * nums[i+1]
        //ans[i] = ansL[i] * ansR[i]
        //左遍历一次,右遍历一次
        //时间复杂度O(n),空间复杂度O(n),额外用了一个数组
        int[] ansL = new int[nums.length];
        int[] ansR = new int[nums.length];
        ansL[0] = ansR[nums.length-1] = 1;
        for (int i = 1; i < nums.length; i++) {
            ansL[i] = ansL[i - 1] * nums[i - 1];
        }
        for (int j = nums.length - 2; j > -1; j--) {
            ansR[j] = ansR[j + 1] * nums[j + 1];
        }
        for (int i = 0; i < nums.length; i++) {
            ansR[i] = ansL[i] * ansR[i];
        }
        return ansR;
    }
}

56_合并区间

class Solution {
    public int[][] merge(int[][] intervals) {
        // 排序
        Arrays.sort(intervals, new Comparator<int[]>() {
            public int compare(int[] o1,int[] o2){
                return o1[0] - o2[0];
            }
        });
        List<int[]> mergeIntervals = new ArrayList<>();
        //int[]不是基本数据类型,可以做泛型
        mergeIntervals.add(intervals[0]);
        // 遍历剩余区间
        for (int i = 1; i < intervals.length; i++) {
            int[] lastInterval = mergeIntervals.get(mergeIntervals.size() - 1); 
            //最后一个合并区间
            if (intervals[i][0] <= lastInterval[1]) {
                // 重叠,更新结束位置
                lastInterval[1] = Math.max(lastInterval[1], intervals[i][1]);
            } else {
                // 不重叠,添加新区间
                mergeIntervals.add(intervals[i]);
            }
        }
        return mergeIntervals.toArray(new int[0][]);
        //转换为int[][],因为默认是object[]
        //int[]是object的子类,但不是object[]的子类
        //为什么int[0][]必须有这个零
        //如果传入的数组长度 等于或大于 List 的元素数量(merged.size()),
        //直接将 List 的元素填充到这个数组中,并返回它。
        //如果传入的数组长度 小于 List 的元素数量,会忽略传入的数组,
        //创建一个新数组(大小为 List 的元素数量),然后填充并返回。
        //用new int[merged.size()][]也可以
    }
}

189_轮转数组

class Solution {
    public void rotate(int[] nums, int k) {
        //尾部的k个元素会被移动到最前面
        //其余元素向后移动k
        //当热如果k大于nums.length,应该是移动了k mod n个位置多于一圈,算不到一圈的余量
        reverse(nums, 0, nums.length - 1);
        reverse(nums, 0, k % nums.length - 1);
        reverse(nums, k % nums.length, nums.length - 1);
    }
    public void reverse(int[] nums, int start, int end){
        while (start < end) {
            int temp = nums[start];
            nums[start] = nums[end];
            nums[end] = temp;
            start++;
            end--;
        }
    }
}

链表_中等

707_设计链表

class MyNode {
    Integer val;
    MyNode next;
    public MyNode(Integer val){
        // 注意这里
        //坑死我了,我淦
        //必须加this指明是成员变量,不加this优先表示的是成员变量val自己给自己传值
        this.val = val;
        next = null;
    }
    public MyNode(Integer val,MyNode ptr){
        this.val = val;
        next = ptr;
    }
}
class MyLinkedList {
    //头节点的定义将会省很多麻烦,很重要
    MyNode head;
    //MyNode ptr; ptr的声明不能放在这,将会在堆中存储,当多个线程调用一个new MyLinkedList就会重复覆盖引用

    public MyLinkedList() {
        head = new MyNode(0);
    }
    
    public int get(int index) {
        MyNode ptr;//在方法中声明就是局部变量,在各线程的栈中存储
        ptr = head;
        for (int i = 0; i < index+1; i++) {
            ptr = ptr.next;
            if(ptr==null){
                return -1;
            }
        }
        return ptr.val;
    }
    
    public void addAtHead(int val) {
        MyNode ptr;
        ptr = new MyNode(val);
        ptr.next = head.next;
        head.next = ptr;
    }
    
    public void addAtTail(int val) {
        MyNode ptr;
        ptr = head;
        while (ptr !=null && ptr.next != null) {
            ptr = ptr.next;
        }
        ptr.next = new MyNode(val);
    }
    
    public void addAtIndex(int index, int val) {
        MyNode ptr;
        ptr = head;
        for (int i = 0; i < index; i++) {//获取idx-1位置的节点
            ptr = ptr.next;
            if(ptr == null){
                return;
            }
        }
        ptr.next = new MyNode(val,ptr.next);


        
    }
    
    public void deleteAtIndex(int index) {
        MyNode ptr;
        ptr = head;
        for (int i = 0; i < index; i++) {//获取idx-1位置的节点
            ptr = ptr.next;
            if(ptr == null){
                return;
            }
        }
        //ptr.next才是索引的那个节点,必须保证不能为null
        if(ptr == null || ptr.next == null) return;
        ptr.next = ptr.next.next;
    }
}

/**
 * Your MyLinkedList object will be instantiated and called as such:
 * MyLinkedList obj = new MyLinkedList();
 * int param_1 = obj.get(index);
 * obj.addAtHead(val);
 * obj.addAtTail(val);
 * obj.addAtIndex(index,val);
 * obj.deleteAtIndex(index);
 */

24_两两交换链表中的节点

/**
 * Definition for singly-linked list.
 * public class ListNode {
 *     int val;
 *     ListNode next;
 *     ListNode() {}
 *     ListNode(int val) { this.val = val; }
 *     ListNode(int val, ListNode next) { this.val = val; this.next = next; }
 * }
 */
class Solution {
    public ListNode swapPairs(ListNode head) {
        int len = 0;
        ListNode newHead = new ListNode(0);
        newHead.next = head;
        ListNode ptr = newHead;
        while (ptr.next !=null && ptr.next.next !=null) {
            //链表的题的精髓就是每一轮一个固定的变量代表一个固定的节点
            //这样思路比较清晰
            //然后随着轮,迭代变量代表的节点
            ListNode front = ptr.next;
            ListNode back = ptr.next.next;
            ptr.next = back;
            ListNode temp = front.next;
            front.next = back.next;
            back.next = front;
            ptr = front;
        }
        return newHead.next;
    }
}

19_删除链表的倒数第 N 个结点

记录链表长度

/**
 * Definition for singly-linked list.
 * public class ListNode {
 *     int val;
 *     ListNode next;
 *     ListNode() {}
 *     ListNode(int val) { this.val = val; }
 *     ListNode(int val, ListNode next) { this.val = val; this.next = next; }
 * }
 */
class Solution {
    public ListNode removeNthFromEnd(ListNode head, int n) {
        //特殊情况[],[1]
        //还是先建立一个头节点作为空节点
        //做链表的题,一定要节点化,思路就清晰
        if (head == null) {
            return head;
        }
        ListNode newHead = new ListNode(0);
        newHead.next = head;
        ListNode ptr = head;
        Integer len = 0;
        while (ptr!=null) {
            ptr = ptr.next;
            len++;
        }
        ptr = newHead;
        for (int i = 0; i < len-n+1-1; i++) {
            ptr = ptr.next;
        }
        //ptr现在是要被删除的节点前面那个
        ListNode front = ptr;
        ListNode delNode = ptr.next;
        ListNode back = ptr.next.next;
        front.next = back;
        return newHead.next;
    }
    //还有一种双指针的方法
}

基于递归或栈的方法

class Solution {
    public ListNode removeNthFromEnd(ListNode head, int n) {
        //栈,记录退栈的次数
        //还是要在头部定义一个新节点,能解决很多空指针的问题
        ListNode newHead = new ListNode(0);
        newHead.next = head;
        Deque<ListNode> stack = new LinkedList<>();
        ListNode ptr = newHead;
        while (ptr != null) {
            stack.push(ptr);
            ptr = ptr.next;
        }
        for (int i = 0; i < n; i++) {
            stack.pop();
        }
        ptr = stack.peek();
        ptr.next = ptr.next.next;
        return newHead.next;
    }
}

142_环形链表 II

/**
 * Definition for singly-linked list.
 * class ListNode {
 *     int val;
 *     ListNode next;
 *     ListNode(int x) {
 *         val = x;
 *         next = null;
 *     }
 * }
 */
public class Solution {
    public ListNode detectCycle(ListNode head) {
        //这道题就是如何找链表的头节点,芜湖
        //先说结论,用快慢指针
        //当快慢指针相遇后,快指针回到头节点,这回快慢指针都只走一步,再次相遇时
        //那个节点就是头节点
        //设从头节点到环入口的距离为a
        //从环入口到相遇点的距离为b
        //从相遇点回到环入口的距离为c
        //那么从开始走到第一次相遇,快指针走的步数是慢节点的两倍
        //则,得:2(a+b)=a+b+c+b
        //得,a=c
        //所以,快指针再从头开始走到环入口(a的距离)就和慢指针相遇(c的距离)
        ListNode fastPtr = head;
        ListNode slowPtr = head;
        while (true) {
            if (fastPtr ==null || fastPtr.next ==null) {
                return null;
            }
            fastPtr = fastPtr.next.next;
            slowPtr = slowPtr.next;
            if (fastPtr == slowPtr) {
                fastPtr = head;
                break;
            }
        }
        while (true) {
            if (fastPtr == slowPtr) {
                return fastPtr;
            }
            fastPtr = fastPtr.next;
            slowPtr = slowPtr.next;
        }
        //其他比较慢的方法就是,空间复杂度高的方法就是哈希了
    }
}

138_随机链表的复制

/*
// Definition for a Node.
class Node {
    int val;
    Node next;
    Node random;

    public Node(int val) {
        this.val = val;
        this.next = null;
        this.random = null;
    }
}
*/

class Solution {
    public Node copyRandomList(Node head) {
        if (head == null) {
            return null;
        }
        Node ptr = head;
        while (ptr != null) {
            Node newNode = new Node(0);
            //对复制节点赋值
            newNode.val = ptr.val;
            //复制节点保存原节点的next节点
            newNode.next = ptr.next;
            ptr.next = newNode;
            //指向下个节点
            ptr = newNode.next;
        }

        ptr = head;
        // 随机指向变为深拷贝
        while (ptr != null) {
            if (ptr.random !=null) {
                ptr.next.random = ptr.random.next;
            }
            else{
                ptr.next.random = null;
            }
            ptr = ptr.next.next;
        }

        // 分开原链表和新链表
        ptr = head;
        Node newHead = ptr.next;
        Node newPtr = newHead;
        while (ptr !=null) {
            ptr.next = ptr.next.next;
            if (newPtr.next != null) {
                newPtr.next = newPtr.next.next;
            }
            ptr = ptr.next;
            newPtr = newPtr.next;            
        }

        return newHead;


    }
}

2_两数相加

/**
 * Definition for singly-linked list.
 * public class ListNode {
 *     int val;
 *     ListNode next;
 *     ListNode() {}
 *     ListNode(int val) { this.val = val; }
 *     ListNode(int val, ListNode next) { this.val = val; this.next = next; }
 * }
 */
class Solution {
    public ListNode addTwoNumbers(ListNode l1, ListNode l2) {
        ListNode ptrL1 = l1;
        ListNode ptrL2 = l2;
        ListNode newHead = new ListNode(0);
        ListNode newPtr = newHead;
        int l1Num = 0;
        int l2Num = 0;
        int newNum = 0;
        int carryFlag = 0;
        // 只要两个相加的链表或进位不为0就要一直创建新节点承接值
        while (ptrL1 != null || ptrL2 != null || carryFlag != 0) {
            l1Num = ptrL1 == null? 0 : ptrL1.val;
            l2Num = ptrL2 == null? 0 : ptrL2.val;
            newNum = (l1Num + l2Num + carryFlag) % 10;
            newPtr.next = new ListNode(newNum);
            carryFlag = (l1Num + l2Num + carryFlag) / 10;
            newPtr = newPtr.next;
            if (ptrL1 != null) {
                ptrL1 = ptrL1.next;
            }
            if (ptrL2 != null) {
                ptrL2 = ptrL2.next;
            }
        }
        return newHead.next;
    }
}

146_LRU 缓存

class LRUCache {
    class DLinkedNode {
        int key;
        int value;
        DLinkedNode prev;
        DLinkedNode next;

        public DLinkedNode() {
        }

        public DLinkedNode(int _key, int _value) {
            key = _key;
            value = _value;
        }
    }

    private Map<Integer, DLinkedNode> cache = new HashMap<Integer, DLinkedNode>();
    private int size;
    private int capacity;
    private DLinkedNode head, tail;

    public LRUCache(int capacity) {
        this.size = 0;
        this.capacity = capacity;
        // 使用伪头部和伪尾部节点
        head = new DLinkedNode();
        tail = new DLinkedNode();
        head.next = tail;
        tail.prev = head;
    }

    public int get(int key) {
        DLinkedNode node = cache.get(key);
        if (node == null) {
            return -1;
        }
        // 如果 key 存在,先通过哈希表定位,再移到头部
        moveToHead(node);
        return node.value;
    }

    public void put(int key, int value) {
        DLinkedNode node = cache.get(key);
        if (node == null) {
            // 如果 key 不存在,创建一个新的节点
            DLinkedNode newNode = new DLinkedNode(key, value);
            // 添加进哈希表
            cache.put(key, newNode);
            // 添加至双向链表的头部
            addToHead(newNode);
            ++size;
            if (size > capacity) {
                // 如果超出容量,删除双向链表的尾部节点
                DLinkedNode tail = removeTail();
                // 删除哈希表中对应的项
                cache.remove(tail.key);
                --size;
            }
        } else {
            // 如果 key 存在,先通过哈希表定位,再修改 value,并移到头部
            node.value = value;
            moveToHead(node);
        }
    }

    private void addToHead(DLinkedNode node) {
        node.prev = head;
        node.next = head.next;
        head.next.prev = node;
        head.next = node;
    }

    private void removeNode(DLinkedNode node) {
        node.prev.next = node.next;
        node.next.prev = node.prev;
    }

    private void moveToHead(DLinkedNode node) {
        removeNode(node);
        addToHead(node);
    }

    private DLinkedNode removeTail() {
        DLinkedNode res = tail.prev;
        removeNode(res);
        return res;
    }
}

直接用LinkedHashMap

import java.util.LinkedHashMap;
import java.util.Map;

class LRUCache extends LinkedHashMap<Integer, Integer> {
    private final int capacity;

    // 构造函数
    public LRUCache(int capacity) {
        // initialCapacity: 初始容量
        // loadFactor: 加载因子
        // accessOrder: true 表示按访问顺序排序
        super(capacity, 0.75f, true);
        this.capacity = capacity;
    }

    // 获取元素
    public int get(int key) {
        return super.getOrDefault(key, -1); // 如果不存在返回 -1
    }

    // 插入或更新元素
    public void put(int key, int value) {
        super.put(key, value);
    }

    // 重写 removeEldestEntry 方法,控制缓存大小
    @Override
    protected boolean removeEldestEntry(Map.Entry<Integer, Integer> eldest) {
        // 当元素数量超过容量时,移除最老的元素(最近最少使用的)
        return size() > capacity;
    }
}

字符串_中等

151_反转字符串中的单词

class Solution {
    public String reverseWords(String s) {
        //string是一种特殊的不可变的数组,所以如果某个函数的传参是数组.string可以作为形参
        StringBuffer stringBuffer = new StringBuffer(s).reverse();
        //保证在处理每一个词的时候都有上升沿和下降沿
        stringBuffer.insert(0, ' ');
        stringBuffer.append(' ');
        int left = 0;
        int right = 0;
        char leftChar;
        char rightChar;
        for (int i = 1; i < stringBuffer.length(); i++) {
            if (stringBuffer.charAt(i-1) == ' ' &&
            stringBuffer.charAt(i) != ' ') {
                left = i;
            }
            if (stringBuffer.charAt(i-1) != ' ' &&
            stringBuffer.charAt(i) == ' ') {
                right = i-1;
                while (left<right) {
                    leftChar = stringBuffer.charAt(left);
                    rightChar = stringBuffer.charAt(right);
                    stringBuffer.setCharAt(left, rightChar);
                    stringBuffer.setCharAt(right, leftChar);
                    left++;
                    right--;
                }
            }
            if (stringBuffer.charAt(i-1) == ' ' &&
            stringBuffer.charAt(i) == ' ') {
                stringBuffer.deleteCharAt(i);
                i--;
            }
        }
        return stringBuffer.toString().trim();
    }
    // public void createObject() {
    //     // 在方法中创建一个对象
    //     MyClass obj = new MyClass();
    //     System.out.println("Object created: " + obj);
    // }
    //局部的对象被清理是引用MyClass的变量obj存在栈中,方法运行完,obj被销毁,没有引用MyClass()
    //的变量,这个对象就被清理
}

438_找到字符串中所有字母异位词

class Solution {
    public List<Integer> findAnagrams(String s, String p) {
        //用两个数组分别存储s滑动窗口内元素的数量,和p中的各元素的数量
        //这里是用数组还是hashMap
        //hashMap的初始容量为16,考虑到a-z字符在hashMap中存储还是比较均匀的
        //当hashMap中键值对大于16*0.75才会扩容到32
        //所以如果滑动窗口内不同元素或p中的不同元素大于16*0.75,肯定是数组好,因为hashMap肯定要遍历空桶
        //键值对小于16*0.75(存储还是比较均匀的)大概率hashMap更好一些
        //折中一下,还是用数组吧
        //每次并不需要比较所有的s滑动窗口内元素的数量和p中的各元素的数量
        //用一个differ差异数量来记录差异
        //单独处理p串长于s串的情况
        if (s.length() < p.length()) {
            return new ArrayList<Integer>();
        }
        List<Integer> list = new ArrayList<>();
        int[] count = new int[26];
        int differ = 0;
        for (int i = 0; i < p.length(); i++) {
            --count[p.charAt(i) - 'a'];
            ++count[s.charAt(i) - 'a'];
        }
        for (int i = 0; i < count.length; i++) {
            if(count[i] != 0){
                differ++;
            }
        }
        if (differ == 0) {
            list.add(0);
        }
        int preSub, aftSub, preAdd, aftAdd;
        for (int i = 0; i < s.length() - p.length(); i++) {
            if (count[s.charAt(i) - 'a'] == 0) {
                preSub = 0;
            }
            else{
                preSub = 1;
            }
            if(count[s.charAt(i + p.length()) - 'a'] == 0){
                preAdd = 0;
            }
            else{
                preAdd = 1;
            }
            count[s.charAt(i) - 'a'] -= 1;
            count[s.charAt(i + p.length()) - 'a'] += 1;
            if (count[s.charAt(i) - 'a'] == 0) {
                aftSub = 0;
            }
            else{
                aftSub = 1;
            }
            if(count[s.charAt(i + p.length()) - 'a'] == 0){
                aftAdd = 0;
            }
            else{
                aftAdd = 1;
            }
            if (preSub == 0 && aftSub == 1) {
                differ++;
            }
            else if (preSub == 1 && aftSub == 0) {
                differ--;
            }
            if (preAdd == 0 && aftAdd == 1) {
                differ++;
            }
            else if(preAdd == 1 && aftAdd == 0){
                differ--;
            }
            if (differ == 0) {
                list.add(i + 1);
            }

        }
        return list;

    }
}

哈希表

454_四数相加 II

import java.util.Map.Entry;
import java.util.Map.Entry;class Solution {
    public int fourSumCount(int[] nums1, int[] nums2, int[] nums3, int[] nums4) {
        //数的相加,用哈希就可以保存数值,就不用遍历了,省时间复杂度
        //但是会增加空间复杂度
        //AB数组为一组,CD数组为一组
        //将AB组合的所有组合添加在一个哈希中,CD同样
        //建为数组,值为出现的次数
        Map<Integer,Integer> abHashMap = new HashMap<>();
        Map<Integer,Integer> cdHashMap = new HashMap<>();
        int count = 0;
        for (int i = 0; i < nums1.length; i++) {
            for (int j = 0; j < nums2.length; j++) {
                //getOrDefault如何哈希中存在这个键,返回对应值
                //没有,返回默认值
                abHashMap.put(nums1[i]+nums2[j], 
                abHashMap.getOrDefault(nums1[i]+nums2[j], 0)+1);
            }
        }
        for (int i = 0; i < nums3.length; i++) {
            for (int j = 0; j < nums4.length; j++) {
                cdHashMap.put(nums3[i]+nums4[j],
                cdHashMap.getOrDefault(nums3[i]+nums4[j],0)+1);
            }
        }
        for (Entry<Integer,Integer> entrySet : abHashMap.entrySet()) {
            if (cdHashMap.containsKey(0-entrySet.getKey())) {
                count +=entrySet.getValue()*cdHashMap.get(0-entrySet.getKey());
            }
        }
        return count;
    }
}

49_字母异位词分组

import java.util.Map.Entry;
import java.util.Map.Entry;class Solution {
    public List<List<String>> groupAnagrams(String[] strs) {
        List<List<String>> out = new LinkedList<>();
        Map<String,List<String>> hashMap = new HashMap<>();
        //踩坑,数组其实继承的是object类,所以数组的toString方法没重写过,是默认输出
        //类名@哈希值
        for(String str : strs){
            char[] charArray = str.toCharArray();
            Arrays.sort(charArray);
            String key = new String(charArray);
            List<String> value = hashMap.getOrDefault(key, new LinkedList<String>());
            value.add(str);
            hashMap.put(key, value);
        }
        for(Entry<String,List<String>> entry : hashMap.entrySet()){
            out.add(entry.getValue());
        }
        return out;

    }
}

128_最长连续序列

class Solution {
    public int longestConsecutive(int[] nums) {
        //时间复杂度O(n)意味不能用排序
        //哈希表
        Set<Integer> set = new HashSet<>();
        for (int num : nums) {
            set.add(num);
        }
       // 遍历每一个数,当这个数不是连续数列的头数就跳过,在头数的内循环中会遍历这个数
       //所以总的来说是O(n)
        int maxLen = 0;
       for(Integer num : set){//用set遍历会快一些,因为set去重了
            if (set.contains(num - 1)) {
                continue;
            }
            int currentMaxLen = 1;
            int currentNum = num;
            while (set.contains(++currentNum)) {
                currentMaxLen++;
                maxLen = Math.max(currentMaxLen, maxLen);
            }
            maxLen = Math.max(currentMaxLen, maxLen);
        }
       return maxLen;
    }
}

双指针

15_三数之和

class Solution {
    public List<List<Integer>> threeSum(int[] nums) {
        //排序+双指针
        //这道题也可以用哈希,但是在空间复杂度上要高一些
        Arrays.sort(nums);
        List<List<Integer>> list = new LinkedList<>();

        //固定外层循环,内层循环一个指向固定外层索引的下一个,一个指向最后一位
        //三个条件
        //i != j、i != k 且 j != k
        //nums[i] + nums[j] + nums[k] == 0
        //答案中不可以包含重复的三元组。
        
        for (int i = 0; i < nums.length-2; i++) {
            if (i>0 && nums[i]==nums[i-1]) {
                continue;
            }
            int left = i+1;
            int right = nums.length-1;
            
            while (left<right) {
                if (nums[i]+nums[left]+nums[right]>0) {
                    right--;
                }
                else if(nums[i]+nums[left]+nums[right]<0){
                    left++;
                }
                else{
                    List<Integer> group = new LinkedList<>();
                    group.add(nums[i]);
                    group.add(nums[left]);
                    group.add(nums[right]);
                    list.add(group);
                    left++;
                    right--;
                    while (left<right&& nums[left]==nums[left-1]) {
                        left++;                        
                    }
                    while (left<right&& nums[right]==nums[right+1]) {
                        right--;
                    }
                }
            }
        }
        return list;
        
    }
}

18_四数之和

class Solution {
    public List<List<Integer>> fourSum(int[] nums, int target) {
        List<List<Integer>> list = new LinkedList<>();
        int left;
        int right;
        Arrays.sort(nums);
        //延续三数之和的思路
        //我还以为有什么高明的解法呢,结果还是排序+双指针,时间复杂度为O(n^3)
        for (int i = 0; i < nums.length-3; i++) {
            //防止前后两数相等
            while (i>0 && i < nums.length-3 && nums[i-1]==nums[i]) {
                i++;
            }
            for (int j = i+1; j < nums.length-2; j++) {
                //防止前后两个数相等
                while (j>i+1 && j < nums.length-2 && nums[j-1]==nums[j]) {
                    j++;
                }
                left = j+1;
                right = nums.length-1;
                while (left<right) {
                    //群众里有坏人,相加后用int会溢出
                    long sum = (long)nums[i]+nums[j]+nums[left]+nums[right];
                    if (sum>target) {
                        right--;
                    }
                    else if(sum<target){
                        left++;
                    }
                    else{
                        List<Integer> group = new LinkedList<>();
                        group.add(nums[i]);
                        group.add(nums[j]);
                        group.add(nums[left]);
                        group.add(nums[right]);
                        list.add(group);
                        left++;
                        right--;
                        //这里很关键
                        //为什么之前不等于的时候,前后相等不需要再移动指针?
                        //这主要是防止将相同数值元组添加到列表中,
                        //前面没有添加操作,不需要移动,让他慢慢移动就行了
                        while (left<right && nums[left] == nums[left-1]) {
                            left++;
                        }
                        while (left<right && nums[right] == nums[right+1]) {
                            right--;
                        }
                    }
                }
            }
        }
        return list;
    }
}

5_最长回文子串

class Solution {
    public String longestPalindrome(String s) {
        if (s == null || s.length() == 0 || s.length() == 1) {
            return s;
        }
        int len = s.length();
        int maxSonLen = 0;
        int maxSonLenL = 0;//最长子串左边界
        int maxSonLenR =0;//最长子串右边界
        //先找重叠扩散,找adsda这种的
        for (int i = 0; i < len; i++) {
            int l = i;
            int r = i;
            while (l >= 0 && r <= len-1) {
                if (s.charAt(l) != s.charAt(r)) {
                    break;
                }
                if (maxSonLen < (r-l+1)) {
                    maxSonLen = r-l+1;
                    maxSonLenL = l;
                    maxSonLenR = r;
                }
                l--;
                r++;
            }
        }
        //再找不重叠扩散,找baab这种
        for (int i = 0; i < len; i++) {
            int l = i;
            int r = i+1;
            while (l >= 0 && r <= len-1) {
                if (s.charAt(l) != s.charAt(r)) {
                    break;
                }
                if (maxSonLen < (r-l+1)) {
                    maxSonLen = r-l+1;
                    maxSonLenL = l;
                    maxSonLenR = r;
                }
                l--;
                r++;
            }
        }
        StringBuilder sb = new StringBuilder();
        sb.append(s, maxSonLenL, maxSonLenR + 1);
        return sb.toString();
    }
}

11_盛最多水的容器

class Solution {
    public int maxArea(int[] height) {
        //开始头尾指针作为边界
        //如果开始h[l]和h[r],h[l]更小,那么现在无论h[r]怎么移动,当前都是以
        //h[l]为边界的最大值了,所以移动i这个指针继续寻找
        //area = min(h[l],h[r])*(r-l)
        int l = 0;
        int r = height.length-1;
        int maxArea = Math.min(height[l], height[r]) * (r-l);
        while (l < r) {
            maxArea = Math.max(maxArea, (Math.min(height[l], height[r]) * (r-l)));
            if (height[l] < height[r]) {
                    l++;
            } else {
                    r--;
            }
        }
        return maxArea;
    }
}

3_无重复字符的最长子串

class Solution {
    public int lengthOfLongestSubstring(String s) {
        Map<Character, Integer> hashMap= new HashMap<>();
        int maxLen = 0;
        int conCurrentLen = 0;
        Integer idx = -1;
        // 右指针探索,左指针根据情况紧缩
        for (int i = 0; i < s.length(); i++) {
            if (hashMap.containsKey(s.charAt(i))) {
                idx = hashMap.get(s.charAt(i)) > idx ? 
                hashMap.get(s.charAt(i)) : idx;

                conCurrentLen = i - idx;
            }
            else{
                conCurrentLen++;
            }
            maxLen = maxLen > conCurrentLen ? maxLen : conCurrentLen;
            hashMap.put(s.charAt(i), i);
        }
        return maxLen > conCurrentLen ? maxLen : conCurrentLen;
    }
}

75_颜色分类

class Solution {
    public void sortColors(int[] nums) {
        //两个关键点
        //双指针可以实现1次遍历
        //指针p0交换0,指针p1交换1
        //当p0<p1时,p0交换出去的肯定是1,所以需要用指针p1把这个1交换过来
        //所以当指针p0交换0时,指针p0和指针p1都要往后移
        int p0 = 0;
        int p1 = 0;
        int temp;
        for (int i = 0; i < nums.length; i++) {
            if (nums[i] == 0) {
                temp = nums[i];
                nums[i] = nums[p0];
                nums[p0] = temp;
                if (p0 < p1) {
                    temp = nums[i];
                    nums[i] = nums[p1];
                    nums[p1] = temp;
                }
                p0++;
                p1++;
            }
            else if(nums[i] == 1){
            temp = nums[i];
            nums[i] = nums[p1];
            nums[p1] = temp;
            p1++;
            }
        }
    }
}

287_ 寻找重复数

class Solution {
    public int findDuplicate(int[] nums) {
        //这题必须记忆一点,根据题目的限制,必定成环,且环的入口的下标就是重复数
        int slow = 0;
        int fast = 0;
        do{
            slow = nums[slow];
            fast = nums[nums[fast]];
        }while(slow !=fast);
        //相遇了
        slow = 0;
        while (slow != fast) {
            slow = nums[slow];
            fast = nums[fast];
        }
        return slow;
    }
    //自环不会影响此题的解答,初始在idx0处,而题目指出数值在[1,n],idx0处不会自环
    //而其他只有一个的数值自环的话,路径根本到不了那里
}

堆_中等

215_数组中的第K个最大元素

手搓最小堆的方法

class Solution {
    public int findKthLargest(int[] nums, int k) {
        //快速排序的选择方法,时间复杂度O(n),空间复杂度O(logn)
        //最小堆,时间O(n + klogn),空间O(logn)
        //和最小栈区分开来
        int heapSize = nums.length;
        buildMaxHeap(nums, heapSize);
        for (int i = nums.length - 1; i >= nums.length - k + 1; --i) {
            swap(nums, 0, i);
            --heapSize;
            maxHeapify(nums, 0, heapSize);
        }
        return nums[0];
    }
    public void buildMaxHeap(int[] a,int heapSize){
        for (int i = heapSize / 2 - 1; i >= 0; i--) {
            maxHeapify(a, i, heapSize);
        }
    }
    public void maxHeapify(int[] a,int i, int heapSize){
        int l = i * 2 + 1, r = i * 2 + 2,largest = i;
        if (l < heapSize && a[l] > a[largest]) {
            largest = l;
        }
        if (r < heapSize && a[r] > a[largest]) {
            largest = r;
        }
        if (largest != i) {
            swap(a, i, largest);
            maxHeapify(a, largest, heapSize);
        }
    }
    public void swap(int[] a, int i, int j){
        int temp = a[i];
        a[i] = a[j];
        a[j] = temp;
    }

}

使用java定义的最小堆PriorityQueue

import java.util.*;
class Solution {
    public int findKthLargest(int[] nums, int k) {
        // 使用一个含有 k 个元素的最小堆,PriorityQueue 底层是动态数组,为了防止数组扩容产生消耗,可以先指定数组的长度
        PriorityQueue<Integer> minHeap = new PriorityQueue<>(k, Comparator.comparingInt(a -> a));
        //最大堆
        // PriorityQueue<Integer> maxHeap = new PriorityQueue<>(k, Comparator.comparingInt(a -> -a));
        for (int i = 0; i < k; i++) {
            minHeap.offer(nums[i]);
        }
        int n = nums.length;
        for (int i = k; i < n; i++) {
            if(nums[i] > minHeap.peek()){
                minHeap.poll();
                minHeap.offer(nums[i]);
            }
        }
        return minHeap.peek();
    }   
}

347_前 K 个高频元素

import java.util.Map.Entry;
import java.util.Map.Entry;class Solution {
    public int[] topKFrequent(int[] nums, int k) {
        Map<Integer,Integer> occurrences = new HashMap<>();
        for(int num : nums){
            occurrences.put(num, occurrences.getOrDefault(num, 0)+1);
        }
        //小顶堆数据结构(优先队列)
        PriorityQueue<int[]> queue = new PriorityQueue<>(new Comparator<int[]>() {
            public int compare(int[] m, int[] n){
                return m[1]-n[1];
            }
        });
        for (Entry<Integer,Integer> entry : occurrences.entrySet()) {
            int num = entry.getKey() , count = entry.getValue();
            if (queue.size() == k){
                if (queue.peek()[1] < count){
                    queue.poll();
                    queue.offer(new int[]{num,count});
                }
            }
            else{
                queue.offer(new int[]{num,count});
            }
        }
        int[] ret = new int[k];
        for (int i = 0; i < k; i++) {
            ret[i] = queue.poll()[0];
        }
        return ret;
    }
}

更优雅的解法

import java.util.Map.Entry;
import java.util.Map.Entry;class Solution {
    public int[] topKFrequent(int[] nums, int k) {
        //哈希 + 最小堆
        Map<Integer,Integer> map = new HashMap<>();
        //小顶堆
        PriorityQueue<Entry<Integer,Integer>> minHeap =
         new PriorityQueue<>((a,b) -> a.getValue() - b.getValue());
        for (int i = 0; i < nums.length; i++) {
            Integer count = map.getOrDefault(nums[i], 0);
            map.put(nums[i], ++count);
        }
        for (Entry<Integer,Integer> entrySet : map.entrySet()) {
            if (minHeap.size() == k) {
                if (entrySet.getValue() > minHeap.peek().getValue()) {
                    minHeap.poll();
                    minHeap.offer(entrySet);
                }
            }
            else{
                minHeap.offer(entrySet);
            }
        }
        int[] res = new int[k];
        for (int i = 0; i < k; i++) {
            res[i] = minHeap.poll().getKey();
        }
        return res;
    }
}

树_中等

102_二叉树的层序遍历

/**
 * Definition for a binary tree node.
 * public class TreeNode {
 *     int val;
 *     TreeNode left;
 *     TreeNode right;
 *     TreeNode() {}
 *     TreeNode(int val) { this.val = val; }
 *     TreeNode(int val, TreeNode left, TreeNode right) {
 *         this.val = val;
 *         this.left = left;
 *         this.right = right;
 *     }
 * }
 */
class Solution {
    public List<List<Integer>> levelOrder(TreeNode root) {
        List<List<Integer>> list = new LinkedList<>();
        //一定要考虑空树的情况
        if (root==null) {
            return list;
        }
        //额外使用的空间是队列queue和listOneLayer存储每层数值
        //将每层的节点存入队列
        //取节点的时候也是一次批量取节点
        Queue<TreeNode> queue = new LinkedList<>();
        queue.offer(root);
        while (!queue.isEmpty()) {
            List<Integer> listOneLayer = new LinkedList<>();
            int len = queue.size();
            while (len>0) {
                TreeNode treeNode = queue.poll();
                listOneLayer.add(treeNode.val);
                if (treeNode.left!=null) {
                    queue.offer(treeNode.left);
                }
                if (treeNode.right!=null) {
                    queue.offer(treeNode.right);
                }
                len--;
            }
            list.add(listOneLayer);
        }
        return list;
    }
}

107_二叉树的层序遍历 II

/**
 * Definition for a binary tree node.
 * public class TreeNode {
 *     int val;
 *     TreeNode left;
 *     TreeNode right;
 *     TreeNode() {}
 *     TreeNode(int val) { this.val = val; }
 *     TreeNode(int val, TreeNode left, TreeNode right) {
 *         this.val = val;
 *         this.left = left;
 *         this.right = right;
 *     }
 * }
 */
class Solution {
    public List<List<Integer>> levelOrderBottom(TreeNode root) {
        //层序遍历,广度优先搜素,用队列的思想
        //与二叉树的层序遍历I的区别只是添加到list的顺序变了
        Queue<TreeNode> queue = new LinkedList<>();
        List<List<Integer>> list = new LinkedList<>();
        queue.offer(root);
        
        if (root==null) {
            return list;
        }
        while (!queue.isEmpty()) {

            int lenOneLayer = queue.size();
            List<Integer> listOneLayer = new LinkedList<>();
            for (int i = 0; i < lenOneLayer; i++) {
                TreeNode ptr = queue.poll();
                if (ptr.left != null) {
                    queue.offer(ptr.left);
                }
                if (ptr.right != null) {
                    queue.offer(ptr.right);
                }
                listOneLayer.add(ptr.val);
            }
            list.add(0,listOneLayer);
            
        }
        return list;
    }
}

199_二叉树的右视图

/**
 * Definition for a binary tree node.
 * public class TreeNode {
 *     int val;
 *     TreeNode left;
 *     TreeNode right;
 *     TreeNode() {}
 *     TreeNode(int val) { this.val = val; }
 *     TreeNode(int val, TreeNode left, TreeNode right) {
 *         this.val = val;
 *         this.left = left;
 *         this.right = right;
 *     }
 * }
 */
class Solution {
    public List<Integer> rightSideView(TreeNode root) {
        //就是每一层最右边那个
        //深度优先搜索不能知道每个节点是哪层的,
        //只有广度优先搜索可以
        Deque<TreeNode> deque = new LinkedList<>();
        List<Integer> list = new LinkedList<>();
        if (root == null) {
            return list;
        }
        TreeNode ptr = root;
        deque.offer(root);
        while (!deque.isEmpty()) {
            int count = deque.size();//计数每层节点数
            while (count>0) {
                ptr = deque.poll();
                if (count==1) {
                    list.add(ptr.val);
                }
                if (ptr!=null&&ptr.left!=null) {
                    deque.offer(ptr.left);
                }
                if (ptr!=null&&ptr.right!=null) {
                    deque.offer(ptr.right);
                }
                count--;
            }
        }
        return list;
    }
}

98_验证二叉搜索树

/**
 * Definition for a binary tree node.
 * public class TreeNode {
 *     int val;
 *     TreeNode left;
 *     TreeNode right;
 *     TreeNode() {}
 *     TreeNode(int val) { this.val = val; }
 *     TreeNode(int val, TreeNode left, TreeNode right) {
 *         this.val = val;
 *         this.left = left;
 *         this.right = right;
 *     }
 * }
 */
class Solution {
    //直接用中序遍历,如果是递增的,那么一定是二叉搜索树
    //中序遍历要么用栈结构实现,要么用递归这种底层栈帧实现
    long currentVal = -Long.MAX_VALUE;
    public boolean isValidBST(TreeNode root) {
        TreeNode ptr = root;
        return recursion(ptr);
    }
    public boolean recursion(TreeNode ptr){
        if (ptr == null) {
            return true;
        }
        if (!recursion(ptr.left)) {
            return false;
        }
        if ((long)ptr.val <= currentVal) {
            return false;
        }
        currentVal = ptr.val;
        if (!recursion(ptr.right)) {
            return false;
        }
        return true;
    }
}

矩阵

73_矩阵置零

class Solution {
    public void setZeroes(int[][] matrix) {
        int m = matrix.length, n = matrix[0].length;
        boolean flagCol0 = false, flagRow0 = false;
        for (int i = 0; i < m; i++) {
            if (matrix[i][0] == 0) {
                flagCol0 = true;
            }
        }
        for (int j = 0; j < n; j++) {
            if (matrix[0][j] == 0) {
                flagRow0 = true;
            }
        }
        for (int i = 1; i < m; i++) {
            for (int j = 1; j < n; j++) {
                if (matrix[i][j] == 0) {
                    matrix[i][0] = matrix[0][j] = 0;
                }
            }
        }
        for (int i = 1; i < m; i++) {
            for (int j = 1; j < n; j++) {
                if (matrix[i][0] == 0 || matrix[0][j] == 0) {
                    matrix[i][j] = 0;
                }
            }
        }
        if (flagCol0) {
            for (int i = 0; i < m; i++) {
                matrix[i][0] = 0;
            }
        }
        if (flagRow0) {
            for (int j = 0; j < n; j++) {
                matrix[0][j] = 0;
            }
        }
    }
}

54_螺旋矩阵

class Solution {
    public List<Integer> spiralOrder(int[][] matrix) {
        //边界收缩
        //i行,j列
        //上边界,i,下边界matrix.length-1-i
        //左边界,j.右边界matrix[].length-1-i
        int upEdge = 0, downEdge = matrix.length - 1;
        int leftEdge = 0, rightEdge = matrix[0].length - 1;
        List<Integer> list = new ArrayList<>();
        while(upEdge <= downEdge && leftEdge <= rightEdge){
            //左往右
            for (int j = leftEdge; j <= rightEdge; j++) {
                list.add(matrix[upEdge][j]);
            }
            upEdge++;
            if (upEdge > downEdge) {
                break;
            }
            //上往下
            for (int i = upEdge; i <= downEdge; i++) {
                list.add(matrix[i][rightEdge]);
            }
            rightEdge--;
            if (leftEdge > rightEdge) {
                break;
            }
            //右往左
            for (int j = rightEdge; j >= leftEdge; j--) {
                list.add(matrix[downEdge][j]);
            }
            downEdge--;
            if (upEdge > downEdge) {
                break;
            }
            //下往上
            for (int i = downEdge; i >= upEdge; i--) {
                list.add(matrix[i][leftEdge]);
            }
            leftEdge++;
            if (leftEdge > rightEdge) {
                break;
            }
        }
        return list;
        
    }
}

59_螺旋矩阵||

跳转至螺旋矩阵代码

48_旋转图像

class Solution {
    public void rotate(int[][] matrix) {
        //需要原地旋转,所以空间复杂度最好为o(1)
        //用翻转代替旋转
        //先翻转再纵轴对称调换元素值
        int mLen = matrix.length;
        int nLen = matrix[0].length;
        for (int i = 0; i < mLen; i++) {
            for (int j = i; j < nLen; j++) {
                int temp = matrix[i][j];
                matrix[i][j] = matrix[j][i];
                matrix[j][i] = temp;
            }
        }
        for (int i = 0; i < mLen; i++) {
            for (int j = 0; j < nLen/2; j++) {
                int temp = matrix[i][j];
                matrix[i][j] = matrix[i][mLen - 1 - j];
                matrix[i][mLen - 1 - j] = temp;
            }
        }
    }
}

240_搜索二维矩阵 II

class Solution {
    public boolean searchMatrix(int[][] matrix, int target) {
        //先用二分查找查行再查列复杂度是O(logmlogn)
        //每次都能排除这行或这列剩余元素的半数元素
        //Z字查找法
        //将矩阵逆时针翻转45°,右上角开始遍历,如果比目标值大就j--,反之i++
        //每次都能排除这行或这列剩余元素
        //最多遍历m+n次
        int mLen = matrix.length;
        int nLen = matrix[0].length;
        int currentVal = matrix[0][nLen - 1];
        int currentRow = 0;
        int currentCol = nLen - 1;
        while (true) {
            if (currentCol < 0 || currentRow > mLen - 1) {
                return false;
            }
            currentVal = matrix[currentRow][currentCol];
            if (target > currentVal) {
                currentRow++;
            }
            else if (target < currentVal) {
                currentCol--;
            }
            else{
                return true;
            }
        }
    }
}

贪心

55_跳跃游戏

class Solution {
    public boolean canJump(int[] nums) {
        //贪心
        //记录遍历时当前最大的idx+跳跃能力
        int maxDistance = 0;
        int length = nums.length - 1;
        for (int i = 0; i < nums.length; i++) {
            if (maxDistance < i) {
                return false;
            }
            maxDistance = Math.max(maxDistance, nums[i] + i);
            if (maxDistance >= length) {
                return true;
            }
        }
        return false;
    }
}

45_跳跃游戏 II

class Solution {
    public int jump(int[] nums) {
        //条件,一定能到达索引n-1处
        //distance = step + idx
        int footOnLand = 0;
        //A点所能到达的所有点中,选出一个maxdistance(B,C,D)
        int maxPosition = 0;
        int nextMaxPosition = 0;
        int step = 0;
        if (nums.length == 1) {
            return 0;
        }
        for (int i = 0; i < nums.length;) {
            maxPosition = nums[footOnLand] + footOnLand;//当前A点最远距离
            while (i <= maxPosition) {//找出maxdistance(B,C,D)
            //之前和A都没比过的就不用比了
                if (i < nums.length && (nums[i] + i) >= nextMaxPosition) {
                    nextMaxPosition = nums[i] + i;//迭代下一个点的最大距离
                    footOnLand = i;//迭代落脚点
                }
                i++;//遍历数组
            }
            step++;//步数+1
            if (maxPosition >= nums.length -1) {//最远到达或超过最远点跳出返回
                break;
            }
        }
        return step;
    }
}

139_单词拆分

class Solution {
    public boolean wordBreak(String s, List<String> wordDict) {
        //动态规划&贪心算法
        //定义boolean,dp数组存worddict中的串是否满足拼接s中的0:i
        //dp[j] = dp[i] && s.substring(i,j)
        //目的就是让dp[]的最后一位为true,就说明wordDict满足拼接成s
        Set<String> hashSet = new HashSet<>();
        for(String word : wordDict){
            hashSet.add(word);
        }
        boolean[] dp = new boolean[s.length() + 1];
        dp[0] = true;
        for(int i = 0 ; i < s.length() + 1; i++){
            for(int j = i + 1; j < s.length() + 1; j++){
                if(dp[i] && hashSet.contains(s.substring(i,j))){
                    dp[j] = true;
                }
            }
        }
        return dp[s.length()];

    }
}

回溯

46_全排列(真心捋不明白)

class Solution {
    public List<List<Integer>> permute(int[] nums) {
        // 终止条件:当 cur 的长度等于 nums 的长度时,说明找到一个完整排列,加到 res。
        // 循环尝试:每次从 nums 中挑一个没用过的数字加到 cur,递归探索下一步。
        // 回溯:递归返回后,移除最后一个数字,尝试其他选择。
        // 避免重复:cur.contains(nums[i]) 确保每个数字只用一次。
        List<List<Integer>> res = new ArrayList<>();
        List<Integer> cur = new ArrayList<>();
        backtracking(res, cur, nums);
        return res;
    }
    private static void backtracking(List<List<Integer>> res, 
    List<Integer> cur, int[] nums) {
        // 终止条件
        if (cur.size() == nums.length) {
            res.add(new ArrayList<>(cur));
        }
        // 处理逻辑
        for (int i = 0; i < nums.length; i++) {
            if (!cur.contains(nums[i])) {
                cur.add(nums[i]);
                backtracking(res, cur, nums);
                cur.remove(cur.size() - 1);
            }
        }
    }

}

二分查找

74_搜索二维矩阵

class Solution {
    public boolean searchMatrix(int[][] matrix, int target) {
        //二分查找,相当于将二维数组展平为一维数组
        int mLen = matrix.length, nLen = matrix[0].length;
        int lFlatten = 0, rFlatten = matrix.length * matrix[0].length - 1;
        while (lFlatten <= rFlatten) {
            int midFlatten = (lFlatten + rFlatten) / 2;
            int midFlattenRow = midFlatten / matrix[0].length;
            int midFlattenVol = midFlatten % matrix[0].length;
            if (matrix[midFlattenRow][midFlattenVol] > target) {
                rFlatten = midFlatten - 1;
            }
            else if(matrix[midFlattenRow][midFlattenVol] < target){
                lFlatten = midFlatten + 1;
            }
            else{
                return true;
            }
        }
        return false;
    }
}
//二分查找问题的边界一定要精确
//否则跳不出循环
//当索引比较值大于目标值,结果就不可能为索引比较值
//所以将右边界确定为索引比较值小一个索引的值

33. 搜索旋转排序数组

class Solution {
    public int search(int[] nums, int target) {
        //条件中数组已经排序了,首先想到二分查找
        //相当于数组整体向右移动了nums.length - k个位置
        //新数组的索引为(idx + nums.length - k)/nums.length
        //先要找到k是什么
        //二分一次
        //肯定是l,mid或者mid+1,r有一个是乱序的
        //如果target在乱序中,继续二分乱序
        //如果target在排序中,则在排序中查
        int l = 0, r = nums.length - 1;
        while (l <= r) {
            int mid = (l + r) / 2;
            if (nums[mid] == target) {
                return mid;
            }
            if (nums[l] <= nums[mid]) {
                if (target >= nums[l] && target < nums[mid]) {
                    r = mid - 1;
                }
                else{
                    l = mid + 1;
                }
            }
            else if(nums[mid] <= nums[r]){
                if (target > nums[mid] && target <= nums[r]) {
                    l = mid + 1;
                }
                else{
                    r = mid - 1; 
                }
            }
        }
        return -1;
    }
}

155_最小栈

class MinStack {
    Deque<Integer> xStack;
    Deque<Integer> minStack;
    public MinStack() {
        xStack = new LinkedList<>();
        minStack = new LinkedList<>();
        minStack.push(Integer.MAX_VALUE);
    }
    
    public void push(int val) {
        xStack.push(val);
        minStack.push(Math.min(minStack.peek(), val));
    }
    
    public void pop() {
        xStack.pop();
        minStack.pop();
    }
    
    public int top() {
        return xStack.peek();
    }
    
    public int getMin() {
        return minStack.peek();
    }
}

/**
 * Your MinStack object will be instantiated and called as such:
 * MinStack obj = new MinStack();
 * obj.push(val);
 * obj.pop();
 * int param_3 = obj.top();
 * int param_4 = obj.getMin();
 */**加粗样式**

394_字符串解码

class Solution {
    public String decodeString(String s) {
        //栈
        //两个栈加一个stirngbuilder
        //待倍增的串放在当前串中
        //倍增倍数放在一个栈中
        //括号之前的串放入栈中,待取
        Deque<Integer> stack_num = new LinkedList<>();//存储需要重复字符串的数字
        Deque<String> stack_res = new LinkedList<>();//存储待重复的字符串
        StringBuilder res = new StringBuilder();
        int num = 0;
        for(Character x : s.toCharArray()){
            if(x == '['){
                stack_num.push(num);//把括号前的数字压栈
                stack_res.push(res.toString());//把括号前的字母,将来等待被重复的字符串压栈
                //需要重复数字归零(注意!因为num可能会是n位数,为了方便后续统一x10处理,必须归零)
                num = 0;
                res = new StringBuilder();//res重置
            }
            else if(x == ']'){
                int cur_num = stack_num.pop();//此次要重复的次数数字
                String last_res = stack_res.pop();//紧贴着这一组括号前面的串
                StringBuilder tmp = new StringBuilder();//本次要重复的完整字串
                for(int i = 0; i < cur_num; i++){
                    tmp.append(res);
                } 
                res = new StringBuilder(last_res + tmp); 

            }
            else if (x >= '0' && x <= '9') {
                num = num * 10 + (x - '0');//把字符数字转换为数字
            }
            else{
                res.append(x);
            }

        }
        return res.toString();
    }
}

动态规划

198_打家劫舍

class Solution {
    public int rob(int[] nums) {
        //动态规划
        //也就是倒推,F(n) = F(n-1) + F(n-2)连锁公式
        if (nums == null || nums.length == 0) {
            return 0;
        }
        int length = nums.length;
        if (length == 1) {
            return nums[0];
        }
        int first = nums[0], second = Math.max(nums[0], nums[1]);
        for (int i = 2; i < length; i++) {
            int temp = second;
            second = Math.max(first + nums[i], second);
            first = temp;
        }
        return second;
    }
}

53_最大子数组和

class Solution {
    public int maxSubArray(int[] nums) {
        //动态规划
        // f[i] = max(f[i-1] + nums[i] , nums[i])
        // f[1] = max(f[0] + nums[1] , nums[1])
        int max = nums[0];
        int maxCurrentTail = 0;
        for (int num : nums) {
            maxCurrentTail = Math.max(maxCurrentTail + num, num);
            max = Math.max(max, maxCurrentTail);
        } 
        return max;
    }
}

279_完全平方数

class Solution {
    public int numSquares(int n) {
        //最多的情况1+1+1....+1 = n
        //f[i] = min(f[i-j*j] + 1
        //这个1也就代表着j*j的那个完全平方数 
        //f[0] = 0
        //f[1] = min(f[1-j*j]) + 1
        int[] f = new int[n+1];
        for (int i = 1; i <= n; i++) {
            // 不从0开始遍历是因为没有意义,而且初始化数组元素都为0,比较就没意义了
            int min = Integer.MAX_VALUE;
            for (int j = 1; j * j <= i; j++) {
                min = Math.min(min, f[i - j * j]);
            }
            f[i] = min + 1;
        }
        return f[n];
    }
}

1997_访问完所有房间的第一天

这种方法空间复杂度很高,我自己想的,但是很易理解
class Solution {
    public int firstDayBeenInAllRooms(int[] nextVisit) {
        // idx = nextVisit[i] ,当count为奇数
        // idx = i + 1 ,当count为偶数
        // 0 <= nextVisit[i] <= i
        // 当第一次访问到最后一个房间时,就是访问完所有房间之时
        Map<Integer,Integer> hashMap = new HashMap<>();//记录房间号,访问次数
        int date = 0;
        int idx = 0;
        while (true) {
            if (idx == nextVisit.length - 1) {
                break;
            }
            hashMap.put(idx, hashMap.getOrDefault(idx, 0) + 1);
            if (hashMap.get(idx) % 2 != 0) {
                idx = nextVisit[idx];
            }
            else{
                idx = idx + 1;
            }
            date++;
        }
        return date;
        
    }
}
class Solution {
    public int firstDayBeenInAllRooms(int[] nextVisit) {
        // 设dp[i]为从第0个房间到第i+1个房间的天数
        //那么dp[i] - dp[i-1] = 从第奇数次到i房间到第奇数次到i+1房间的天数
        //设 f[i]为从第奇数次到i房间到第奇数次到i+1房间的天数
        //那么dp[i] - dp[i-1] = f[i]
        //f[i] = f[to] + .....+ f[i-1] + 2
        //那么又有 dp[i] = f[0] + ...+f[i-1]+f[i]
        //dp[i] = 2 * dp[i - 1] - dp[to] + 2
        // to为nextVisit[i]
        int mod = 1000000007;
        int len = nextVisit.length;
        int[] dp = new int[len];

        dp[0] = 2; //初始化原地待一天 + 访问下一个房间一天
        for (int i = 1; i < len; i++) {
            int to = nextVisit[i];
            dp[i] = 2 + dp[i - 1];
            if (to != 0) {
                dp[i] = (dp[i] - dp[to - 1] + mod) % mod; //避免负数
            }

            dp[i] = (dp[i] + dp[i - 1]) % mod;
        }
        return dp[len - 2]; //题目保证n >= 2
    }
}

322_零钱兑换

class Solution {
    public int coinChange(int[] coins, int amount) {
        //设f[i]为凑成i所需的最少硬币数
        //f[i] = min(f[i-j]) + 1 
        // 边界条件 f[0] = 0
        //f[1] = min(f[1-j]) + 1
        int[] count = new int[amount+1];
        count[0] = 0;
        for (int i = 1; i <= amount; i++) {
            count[i] = Integer.MAX_VALUE;
            for (int coin : coins) {
                if(i-coin >= 0 && count[i-coin]!= -1){
                    count[i] = Math.min(count[i],count[i-coin] + 1);
                }
            }
            if (count[i] == Integer.MAX_VALUE) {
                count[i] = -1;
            }
        }
        return count[amount];
    }
}

300. 最长递增子序列

class Solution {
    public int lengthOfLIS(int[] nums) {
        //动态规划
        //dp[i] = max(dp[i] , dp[j] + 1) for i in [0,i)\
        //注意dp[i]中记录的是以idx为i为此处最长严格递增序列的尾巴的最长严格递增序列长度
        //所以最后dp[nums.length - 1]并不一定是此数组的最长严格递增序列长度
        //除了这个方法,还有时间复杂度为nlogn的方法
        int[] dp = new int[nums.length];
        for (int i = 0; i < dp.length; i++) {
            dp[i] = 1;
        }
        int res = 0;
        for (int i = 0; i < nums.length; i++) {
            for (int j = 0; j < i; j++) {
                //前面的dp[j]肯定是这个位置的最长严格递增序列长度
                if (nums[i] > nums[j]) {
                    dp[i] = Math.max(dp[i], dp[j] + 1);
                }
                //那么dp[i]的最长严格递增序列长度就是满足dp[i] > dp[j]条件中的
                //dp[j] + 1的最大的那个
            }
            res = Math.max(res, dp[i]);
        }
        return res;
    }
}

岛屿\图论问题

200_岛屿数量

class Solution {
    void dfs(char[][] grid, int r, int c) {
        //深度优先搜索
        int nr = grid.length;
        int nc = grid[0].length;
        //遍历过一块地,就把它融化掉,防止重复遍历
        //当需要区别海地\土地\被遍历过的土地时,可将被遍历过的土地置为2
        if (r < 0 || c < 0 || r >= nr || c >= nc || grid[r][c] == '0') {
            return;
        }

        grid[r][c] = '0';
        dfs(grid, r - 1, c);
        dfs(grid, r + 1, c);
        dfs(grid, r, c - 1);
        dfs(grid, r, c + 1);
    }

    public int numIslands(char[][] grid) {
        if (grid == null || grid.length == 0) {
            return 0;
        }

        int nr = grid.length;
        int nc = grid[0].length;
        int num_islands = 0;
        for (int r = 0; r < nr; ++r) {
            for (int c = 0; c < nc; ++c) {
                if (grid[r][c] == '1') {
                    ++num_islands;
                    dfs(grid, r, c);
                }
            }
        }

        return num_islands;
    }
}

994_腐烂的橘子

class Solution {

    public int orangesRotting(int[][] grid) {
        //广度优先搜索
        int M = grid.length;
        int N = grid[0].length;
        Queue<int[]> queue = new LinkedList<>();

        int count = 0;//count 表示新鲜橘子的数量
        for(int r= 0; r < M; r++){
            for (int c = 0; c < N; c++) {
                if (grid[r][c] == 1) {
                    count++;
                }else if (grid[r][c] == 2) {
                    queue.offer(new int[]{r,c});
                }
            }
        }
        int round = 0;//round表示腐烂的轮数,或者分钟数
        //这轮腐烂的橘子能感染的感染完了就出栈
        //下一轮的橘子接着感染
        while(count > 0 && !queue.isEmpty()){
            round++;
            int n = queue.size();
            for (int i = 0; i < n; i++) {
                int[] orange = queue.poll();
                int r = orange[0];
                int c = orange[1];
                if (r - 1 >= 0 && grid[r-1][c] == 1) {
                    grid[r-1][c] = 2;
                    count--;
                    queue.add(new int[]{r-1, c});
                }
                if (r+1 < M && grid[r+1][c] == 1) {
                    grid[r+1][c] = 2;
                    count--;
                    queue.add(new int[]{r+1, c});
                }
                if (c-1 >= 0 && grid[r][c-1] == 1) {
                    grid[r][c-1] = 2;
                    count--;
                    queue.add(new int[]{r, c-1});
                }
                if (c+1 < N && grid[r][c+1] == 1) {
                    grid[r][c+1] = 2;
                    count--;
                    queue.add(new int[]{r, c+1});
                }

            }
        }
        if (count > 0) {
            return -1;
        }else{
            return round;
        }
    }
}

前缀和

560_和为 K 的子数组

class Solution {
    public int subarraySum(int[] nums, int k) {
        //如果说nums中都是非负整数,可以用双指针
        //一个用于扩张窗口
        //一个用于收缩窗口
        //但是现在nums中可以是负数,且k也可以是负数
        //可以用前缀和+哈希
        //前缀和设计的基础套路就是动态规划
        int count = 0, pre = 0;
        HashMap < Integer, Integer > mp = new HashMap < > ();
        mp.put(0, 1);
        for (int i = 0; i < nums.length; i++) {
            pre += nums[i];
            if (mp.containsKey(pre - k)) {
                count += mp.get(pre - k);
            }
            mp.put(pre, mp.getOrDefault(pre, 0) + 1);
        }
        return count;
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值