Leetcode刷题记录总结

216 阅读33分钟

本文只是记录一下刷过的题目,记录当下刷题的思考等,方便后续回顾。

高频重点

LRU 算法

思路:LRU算法,最近最少使用,访问后,该元素,会放到最前面。

class LRUCache {
    class DlinkNode{
        int key;
        int val;
        DlinkNode prev;
        DlinkNode next;
        public DlinkNode(){};
        public DlinkNode(int key,int val){
            this.key = key;
            this.val = val;
        }
    }
    DlinkNode tail;
    DlinkNode head;
    int size = 0;
    int capacity=0;
    Map<Integer,DlinkNode> cache = new HashMap();



    public LRUCache(int capacity) {
        tail = new DlinkNode();
        head=  new DlinkNode();
        this.capacity=capacity;
        this.size=0;
        tail.prev = head;
        head.next = tail;
    }
    
    public int get(int key) {
      DlinkNode node = cache.get(key);
        if(node==null){
            return -1;
        }else{
            deleteNode(node);
            addNodeToHead(node);
            return node.val;
        }
    }
    
    public void put(int key, int value) {
        DlinkNode node = cache.get(key);
        if(node!=null){
            node.val= value;
            deleteNode(node);
            addNodeToHead(node);
        }else{
            if(size==capacity){
                DlinkNode removeNode = tail.prev;
                deleteNode(removeNode);
                cache.remove(removeNode.key);
                size--;
            }
            DlinkNode newNode = new DlinkNode(key,value);
            addNodeToHead(newNode);
            size++;
            cache.put(key,newNode);
        }


    }
    void deleteNode(DlinkNode node){
         node.prev.next = node.next;
        node.next.prev = node.prev;
    }
    void addNodeToHead(DlinkNode node){
        node.prev = head;
        node.next = head.next;
        head.next.prev =node;
        head.next = node;
    }
  

}

/**
 * Your LRUCache object will be instantiated and called as such:
 * LRUCache obj = new LRUCache(capacity);
 * int param_1 = obj.get(key);
 * obj.put(key,value);
 */

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

class Solution {
    public int findKthLargest(int[] nums, int k) {
         return partion(nums,0,nums.length-1,nums.length-k);
    }
    int partion(int[]nums,int l,int r,int k){
        if(l==r) return nums[l];
        int pivot = nums[l];int i=l-1;int j=r+1;
        while(i<j){
            do i++; while(nums[i]<pivot);
            do j--;while(nums[j]>pivot);
            if(i<j){
                int temp=nums[i];
                nums[i]=nums[j];
                nums[j]=temp;
            }

        }
        
        
        if(k<=j){
            return partion(nums,l,j,k);
        }else{
            return partion(nums,j+1,r,k);
        }
    }
}

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

class Solution {
    public int lengthOfLongestSubstring(String s) {
        if(s.length()==1){
            return 1;
        }
        Map<Character,Integer> map = new HashMap();
        int max=0;
        int left=0;
        for(int i =0;i<s.length();i++){
            if(map.containsKey(s.charAt(i))){
                 left= Math.max(map.get(s.charAt(i))+1,left);
                 System.out.println(left);
            }
            map.put(s.charAt(i),i);
            max=Math.max(max,i-left+1);
        }
        return max;
    }
}
class Solution {
    public int lengthOfLongestSubstring(String s) {
        //滑动窗口
        char[] ss = s.toCharArray();
        Set<Character> set = new HashSet<>();//去重
        int res = 0;//结果
        for(int left = 0, right = 0; right < s.length(); right++) {//每一轮右端点都扩一个。
            char ch = ss[right];//right指向的元素,也是当前要考虑的元素
            while(set.contains(ch)) {//set中有ch,则缩短左边界,同时从set集合出元素
                set.remove(ss[left]);
                left++;
            }
            set.add(ss[right]);//别忘。将当前元素加入。
            res = Math.max(res, right - left + 1);//计算当前不重复子串的长度。
        }
        return res;
    }
}

leetocde 15三数之和

给你一个整数数组 nums ,判断是否存在三元组 [nums[i], nums[j], nums[k]] 满足 i != ji != k 且 j != k ,同时还满足 nums[i] + nums[j] + nums[k] == 0 。请你返回所有和为 0 且不重复的三元组。

注意: 答案中不可以包含重复的三元组

思路:联想到二数之和,我们可以先固定一个数,然后剩下的列表使用二数之和的写法去解决。但是这个题目有一个特殊的地方是存在重复值,比如类似[-2,0,0,2,2]这样的数据,我们固定-2。然后求解二数之和,如果我们找到一个满足的,那么我们需要跳过这种重复的值。

class Solution {
    public List<List<Integer>> threeSum(int[] nums) {
        Arrays.sort(nums);
        List<List<Integer>> res= new ArrayList();
        for(int i =0;i< nums.length;i++){
            if(nums[i]>0) continue; 
            if(i>0&&nums[i]==nums[i-1]) continue;  //为什么要有这个,为了避免重复的数值,比如-2,-2我们只需要寻找一次就习惯,不应该重复寻找。
            int taerget = -nums[i];
            int  left = i+1;
            int  right = nums.length-1;
            while(left<right){
                if(nums[left]+nums[right] == taerget){
                    res.add(Arrays.asList(nums[left],nums[i],nums[right]));
                    int templeft= nums[left];
                    int tempRight = nums[right];
                    System.out.println(left+"_a");
             
                    while(left<right&&templeft==nums[++left]);
                    while(right>=left && tempRight==nums[--right]);
                }else if(nums[left]+nums[right]<taerget){
                    left++;
                }else{
                    right--;
                }
            }
        }
        return res;
    }
}

树的题目

leetcode 236 二叉树的最近公共祖先

给定一个二叉树, 找到该树中两个指定节点的最近公共祖先。

百度百科中最近公共祖先的定义为:“对于有根树 T 的两个节点 p、q,最近公共祖先表示为一个节点 x,满足 x 是 p、q 的祖先且 x 的深度尽可能大(一个节点也可以是它自己的祖先)。”

示例 1:

转存失败,建议直接上传图片文件

输入: root = [3,5,1,6,2,0,8,null,null,7,4], p = 5, q = 1
输出: 3
解释: 节点 5 和节点 1 的最近公共祖先是节点 3

示例 2:

转存失败,建议直接上传图片文件

  • 树的问题优先考虑递归。
输入: root = [3,5,1,6,2,0,8,null,null,7,4], p = 5, q = 4
输出: 5
解释: 节点 5 和节点 4 的最近公共祖先是节点 5 。因为根据定义最近公共祖先节点可以为节点本身。


 public TreeNode lowestCommonAncestor(TreeNode root, TreeNode p, TreeNode q) {
        if(root ==null|| root==p||root==q) return root;
        TreeNode left = lowestCommonAncestor(root.left,p,q);
        TreeNode right = lowestCommonAncestor(root.right,p,q);
        if(left==null){
            return right;
        }
        if(right==null){
            return left;
        }
        return root;
    }

230二叉搜索树中第 K 小的元素

给定一个二叉搜索树的根节点 root ,和一个整数 k ,请你设计一个算法查找其中第 k ****小的元素(从 1 开始计数)。

二叉搜索树的中序遍历是有序的,也就是意味着我们只需要中序遍历一下,就可以找到k小的元素,我们用一个变量count代表当前中序遍历的返回值。

/**
 * Definition for a binary tree node.
 * public class TreeNode {
 *     int val;
 *     TreeNode left;
 *     TreeNode right;
 *     TreeNode(int x) { val = x; }
 * }
 */

class Solution {
      private int count;
    private int ans;
    public int kthSmallest(TreeNode root, int k) {
        count = 0;
        inorder(root,k);
        return ans;
    }

    private void inorder(TreeNode root, int k){
        if(root == null) return;
        inorder(root.left,k);
        count++;
        if(count == k) ans = root.val;
        else inorder(root.right,k);
        return;
    }
}

226. 翻转二叉树

给你一棵二叉树的根节点 root ,翻转这棵二叉树,并返回其根节点

示例 1:

转存失败,建议直接上传图片文件

  • 思考:是树的题目,树的题目,无非就是三种遍历方式,我们考虑使用哪一种方式去做这道题,这个题目没有特殊的要求,前序,后序都可以。比如我们后序遍历的时候,拿到一个节点的左,右,我们交换一下就可以。后序遍历会自动帮我们完成所有重复的过程。
输入: root = [4,2,7,1,3,6,9]
输出: [4,7,2,9,6,3,1]

/**
 * 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 {
//inorder
    public TreeNode invertTree(TreeNode root) {
        if(root==null) return null;
        TreeNode temp = root.right;
        root.right = root.left;
        root.left =temp;
        invertTree(root.left);
        invertTree(root.right);
        return root;
    }
    
    //后序遍历
       public TreeNode invertTree(TreeNode root) {
        if(root==null) return root;
      
        invertTree(root.left);
        invertTree(root.right);
          TreeNode temp =root.left;
        root.left= root.right;
        root.right=temp;
        return root;
        
    }
    
}

110. 平衡二叉树

给定一个二叉树,判断它是否是 

平衡二叉树


/**
 * 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 boolean isBalanced(TreeNode root) {
        if(root==null) return  true;
        int left = getTreeDepth(root.left);
        int right = getTreeDepth(root.right);

        boolean res=  Math.abs(left-right)<=1?true :false;
        return res&& isBalanced(root.left)&& isBalanced(root.right);
    }
    int getTreeDepth(TreeNode root){
        if(root==null) return 0;
        int leftDepth = getTreeDepth(root.left);
        int rightTreeDepth = getTreeDepth(root.right);
        return Math.max(leftDepth,rightTreeDepth)+1;
    }
}

105. 从前序与中序遍历序列构造二叉树

给定两个整数数组 preorder 和 inorder ,其中 preorder 是二叉树的先序遍历, inorder 是同一棵树的中序遍历,请构造二叉树并返回其根节点。

  • 思路:没有重复元素,用一个map存储元素的值对应的位置,前序遍历和中序遍历的特点,来重新buid tree,前序遍历找到根节点,然后中序遍历中找到对应的位置,然后我们就知道左子树,和右字树,前序遍历我们根据根节点,和左子树的个数,就可以递归走下去了。


class Solution {
    private Map<Integer, Integer> indexMap;

    public TreeNode myBuildTree(int[] preorder, int[] inorder, int preLeft, int preRight, int inLeft, int inRight) {
        if (preLeft >preRight ) {
            return null;
        }
        int inRoot = indexMap.get(preorder[preLeft]);
        TreeNode root = new TreeNode(preorder[preLeft]);
        int leftTreeSize = inRoot - inLeft;
        root.left= myBuildTree(preorder,inorder,preLeft+1,preLeft+leftTreeSize,inLeft,inRoot-1);
        root.right= myBuildTree(preorder,inorder,preLeft+1+leftTreeSize,preRight,inRoot+1,inRight);
            return root;
        }

        public TreeNode buildTree(int[] preorder, int[] inorder) {
            int n = preorder.length;
            // 构造哈希映射,帮助我们快速定位根节点
            indexMap = new HashMap<Integer, Integer>();
            for (int i = 0; i < n; i++) {
                indexMap.put(inorder[i], i);
            }
            return myBuildTree(preorder, inorder, 0, n - 1, 0, n - 1);
        }
}

543. 二叉树的直径

给你一棵二叉树的根节点,返回该树的 直径

二叉树的 直径 是指树中任意两个节点之间最长路径的 长度 。这条路径可能经过也可能不经过根节点 root

两节点之间路径的 长度 由它们之间边数表示。



/**
 * Definition for a binary tree node.
 * public class TreeNode {
 *     int val;
 *     TreeNode left;
 *     TreeNode right;
 *     TreeNode(int x) { val = x; }
 * }
 */
class Solution {
     public int maxd=0;
    public int diameterOfBinaryTree(TreeNode root) {
        depth(root);
        return maxd;
    }
    public int depth(TreeNode node){
        if(node==null){
            return 0;
        }
        int Left = depth(node.left);
        int Right = depth(node.right);
        maxd=Math.max(Left+Right,maxd);//将每个节点最大直径(左子树深度+右子树深度)当前最大值比较并取大者
        return Math.max(Left,Right)+1;//返回节点深度
    }
}

124. 二叉树中的最大路径和

/**
 * 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 {
    int max=Integer.MIN_VALUE;
    public int maxPathSum(TreeNode root) {
        dfs(root);
        return max;

    }
    int dfs(TreeNode root){
        if(root==null) return 0;
        int left = dfs(root.left);
        int right = dfs(root.right);
        max = Math.max(max,left+root.val);
        max = Math.max(max,right+root.val);
        max = Math.max(max,left+right+root.val);
        max = Math.max(max,root.val);
        // return left+right+root.val;
            return Math.max(Math.max(left,right)+root.val,root.val);

    }


}

662. 二叉树最大宽度

//



/**
 * 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 {
    int max = 0;
    Map<Integer,Integer> map =new HashMap();
    public int widthOfBinaryTree(TreeNode root) {
        if(root==null) return 0;
        dfs(root,1,1);
        return max;
    }
    void dfs(TreeNode root,int level,int index){
        if(root ==null) return ;
        if(!map.containsKey(level)){
            map.put(level,index);
        }
        max = Math.max(max,index- map.get(level)+1);
        dfs(root.left,level+1,2*index);
        dfs(root.right,level+1,2*index+1);
    }
}

94. 二叉树的中序遍历

/**
 * 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> inorderTraversal(TreeNode root) {
        List<Integer> res =new ArrayList();
            Stack<TreeNode> stack = new Stack();
            while(!stack.isEmpty()||root!=null){
                if(root!=null){
                    stack.push(root);
                    root=root.left;
                }else{
                    TreeNode node = stack.pop();
                    res.add(node.val);
                    root = node.right;
                }
            }
            return res;
    }
}

145. 二叉树的后序遍历

/**
 * Definition for a binary tree node.
 * public class TreeNode {
 *     int val;
 *     TreeNode left;
 *     TreeNode right;
 *     TreeNode(int x) { val = x; }
 * }
 */
class Solution {
public List<Integer> postorderTraversal(TreeNode root) {
       List<Integer> res = new ArrayList<>();
        if (root == null) return res;

        Deque<TreeNode> stack = new ArrayDeque<>();
        TreeNode prev = null;
        TreeNode cur = root;
        while(cur!=null||!stack.isEmpty()){
            while(cur!=null){
                stack.push(cur);
                cur=cur.left;
            }
            cur= stack.peek();
            if(cur.right==null||cur.right==prev){
                res.add(cur.val);
                stack.pop();
                prev =cur;
                cur = null;
            }else{
                cur = cur.right;
            }
        }
    

        return res;
    }
}


双指针

11. 盛最多水的容器

给定一个长度为 n 的整数数组 height 。有 n 条垂线,第 i 条线的两个端点是 (i, 0) 和 (i, height[i]) 。

找出其中的两条线,使得它们与 x 轴共同构成的容器可以容纳最多的水。

返回容器可以储存的最大水量。

说明: 你不能倾斜容器。

class Solution {
    public int maxArea(int[] height) {
        int maxArea = 0;
        int left=0;
        int right = height.length-1;
        while(left<right){
            maxArea = Math.max(maxArea,Math.min(height[left],height[right])*(right-left));
            if(height[left]<height[right]){
                left++;
            }else{
                right--;
            }
        }
        return maxArea;
    }
}

76. 最小覆盖子串

class Solution {
    public String minWindow(String s, String t) {
        Map<Character,Integer> need = new HashMap();
        Map<Character,Integer> window = new HashMap();
        for(char s1:t.toCharArray()){
            need.put(s1,need.getOrDefault(s1,0)+1);
        }
        int valid = 0;
        int left = 0;
        int right = 0;
        int len =  Integer.MAX_VALUE;
        int leftIndex = 0;
        while(right<s.length()){
            char s1= s.charAt(right++);
            window.put(s1,window.getOrDefault(s1,0)+1);
            if(need.containsKey(s1)){
                if(window.get(s1).equals(need.get(s1))){
                    valid++;
                }
            }
            while(valid==need.size()){
                if(right-left<=len){
                    len = right-left;
                    leftIndex = left;
                }
                char d= s.charAt(left++);
                if(need.containsKey(d)){
                    if(window.get(d).equals(need.get(d))){
                        valid--;
                    }
                    window.put(d,window.get(d)-1);
                }
            }

        }
        return len ==Integer.MAX_VALUE?"":s.substring(leftIndex,leftIndex+len);



    }
}

209. 长度最小的子数组

数学

50. Pow(x, n)

实现 pow(xn) ,即计算 x 的整数 n 次幂函数(即,xn )。


class Solution {

    // 负数我们如何求解,转为正数来。正数如何求解,我们递归,每一次每一次缩小一半的,4=f(2)*f(2) 我们小于判读一下 如果是5 就是f(2)*f(2)*x;
    public double myPow(double x, int n) {
        if(n==0) return 1.0;
        if(n<0){
            return 1/fn(x,-n);
        }else{
            return fn(x,n);
        }
    }
    public double fn(double x,int n){
        if(n==0) return 1.0;
                    double half = fn(x,n/2);
        if(n%2!=0){
            return half*half*x;
        }else{
            return half*half;
        }
    }
}

链表题目

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

给你一个链表,删除链表的倒数第 n **个结点,并且返回链表的头结点。

    public ListNode removeNthFromEnd(ListNode head, int n) {
        ListNode fast =head;
        ListNode dummy = new ListNode(0);
        dummy.next=  head;
        ListNode slow = dummy;
        for(int i =0;i<n;i++){
            fast = fast.next;
        }
        while(fast!=null){
            fast=fast.next;
            slow=  slow.next;
        }
        slow.next=  slow.next.next;
        return dummy.next;

    }

23. 合并 K 个升序链表

给你一个链表数组,每个链表都已经按升序排列。

请你将所有链表合并到一个升序链表中,返回合并后的链表。


/**
 * 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 mergeKLists(ListNode[] lists) {
        if(lists.length == 0 ) return null;
        return mergeListNodes(lists,0,lists.length-1);
    }
    public ListNode mergeListNodes(ListNode[]lists,int left,int right){
       if(left==right){
        return lists[left];
       }
       
        int mid = left+(right-left)/2;
        return mergeTwo(mergeListNodes(lists,left,mid),mergeListNodes(lists,mid+1,right));

    }
    public ListNode mergeTwo(ListNode l1,ListNode l2){
        if(l1==null){
            return l2;
        }else if(l2==null){
            return l1;
        }else if(l1.val<l2.val){
            l1.next=  mergeTwo(l1.next,l2);
            return l1;
        }else{
            l2.next=  mergeTwo(l2.next,l1);
            return l2;
        }
    }
}

//时间复杂度:

-   总的来说,上述代码的时间复杂度是 ,其中 `k` 是链表的数量,`n` 是所有链表的平均节点数。O(nklogk)

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) {
     ListNode dummy = new ListNode(0);
     dummy.next = head;
    ListNode prev =dummy;
    while(prev.next!=null&&prev.next.next!=null){
    ListNode first = prev.next;
    ListNode second =prev.next.next;
    prev.next = second;
    first.next = second.next;
    second.next = first;
    prev= first;

    }
    return dummy.next;


    }
}

113. 路径总和 II

image.png

/**
 * 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 {
    List<List<Integer>> res =new ArrayList();
    public List<List<Integer>> pathSum(TreeNode root, int targetSum) {
        dfs(root,new ArrayList(),targetSum);
        return res;
        
    }
    void dfs(TreeNode root,List<Integer> temp,int targetSum ){
        if(root==null) return;
        temp.add(root.val);
        if(root.left==null&&root.right==null&&targetSum==root.val){
            res.add(new ArrayList(temp));
            temp.remove(temp.size()-1);
            return;
        }
        dfs(root.left,temp,targetSum-root.val);
        dfs(root.right,temp,targetSum-root.val);
        temp.remove(temp.size()-1);
    }


}



. 路径总和 III

image.png

/**
 * 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 int pathSum(TreeNode root, int targetSum) {
        Map<Long,Integer> map =new HashMap();
        map.put(0L,1);
        return dfs(root,targetSum,0,map);
        

    }

    int  dfs(TreeNode root,int targetSum,long current,Map<Long,Integer> map){
        if(root ==null) return 0;
        current+=root.val;
        int res=0;
        res+= map.getOrDefault(current-targetSum,0);
        map.put(current,map.getOrDefault(current,0)+1);
        res+=dfs(root.left,targetSum,current,map);
        res+=dfs(root.right,targetSum,current,map);
        map.put(current,map.get(current)-1);
        return res;
    }
}

25. K 个一组翻转链表

给你链表的头节点 head ,每 k **个节点一组进行翻转,请你返回修改后的链表。

k 是一个正整数,它的值小于或等于链表的长度。如果节点总数不是 k **的整数倍,那么请将最后剩余的节点保持原有顺序。

你不能只是单纯的改变节点内部的值,而是需要实际进行节点交换

public ListNode reverseKGroup(ListNode head, int k) {
        int len = 0;
        ListNode cur =head;
        ListNode dummy = new ListNode(0);
        dummy.next = head;

        while(cur!=null){
            cur = cur.next;
            len++;
        }
        ListNode prev = dummy;
        cur = dummy.next;
        ListNode next = null;
        for(int i =0;i<len/k;i++){
            for(int j =0;j<k-1;j++){
             next = cur.next;
            cur.next = next.next;
            next.next = prev.next;
            prev.next = next;
            }
            prev = cur;
            cur = prev.next;
        }
        return dummy.next;

    }
class Solution {
  public ListNode reverseKGroup(ListNode head, int k) {
      ListNode currentNode = head;
      int len = 0;
      // 计算链表长度
      while (currentNode != null) {
          len++;
          currentNode = currentNode.next;
      }
      // 剩余节点不足k个 直接返回头节点
      if (len < k) {
          return head;
      }

      ListNode pre = head;
      ListNode cur = head.next;
      for (int i = 0;i < k - 1;i++) {
          ListNode next = cur.next;
          cur.next = pre;
          pre = cur;
          cur = next;
      }
      head.next = reverseKGroup(cur, k);
      return pre;
  }
}

206. 反转链表


/**
 * 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 reverseList(ListNode head) {
        ListNode newHead = null;
        ListNode cur = head;
        while(cur!=null){
            ListNode next = cur.next;
            cur.next=newHead;
            newHead=cur;
            cur=next;
        }
        return newHead;
    }
}

92. 反转链表 II



/**
 * 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 reverseBetween(ListNode head, int left, int right) {
        if(head==null) return null;
        ListNode dummy = new ListNode(0);
        dummy.next = head;
        ListNode pre=dummy;
        for(int i=1;i<left;i++){
            pre = pre.next;
        }
        int count = right -left;
        ListNode cur = pre.next;


        for(int i =0;i<count;i++){
            ListNode next = cur.next;
            cur.next = next.next;
            next.next = pre.next;
            pre.next= next;
          
        }
        return dummy.next;
    }
}

143. 重排链表(top)

给定一个单链表 L **的头节点 head ,单链表 L 表示为:

L0 → L1 → … → Ln - 1 → Ln

请将其重新排列后变为:

L0 → Ln → L1 → Ln - 1 → L2 → Ln - 2 → …

不能只是单纯的改变节点内部的值,而是需要实际的进行节点交换

class Solution {
    public void reorderList(ListNode head) {
        if (head == null) {
            return;
        }
        ListNode mid = middleNode(head);
        ListNode l1 = head;
        ListNode l2 = mid.next;
        mid.next = null;
        l2 = reverseList(l2);
        mergeList(l1, l2);
    }

    public ListNode middleNode(ListNode head) {
      ListNode fast =head;
        ListNode slow = head;
        while(fast.next!=null&&fast.next.next!=null){
            fast = fast.next.next;
            slow = slow.next;
        }
        return slow;
    }

    public ListNode reverseList(ListNode head) {
     ListNode prev = null;
        ListNode curr = head;
        while (curr != null) {
            ListNode nextTemp = curr.next;
            curr.next = prev;
            prev = curr;
            curr = nextTemp;
        }
        return prev;
    }

    public void mergeList(ListNode l1, ListNode l2) {
      ListNode l1Node;
ListNode l2Node;
        while(l1!=null&&l2!=null){
             l1Node= l1.next;
              l2Node = l2.next;
            l1.next = l2;
            l1 = l1Node;
            l2.next = l1;
            l2 = l2Node;

        }
    }
}

61. 旋转链表

/**
 * Definition for singly-linked list.
 * public class ListNode {
 *     int val;
 *     ListNode next;
 *     ListNode(int x) { val = x; }
 * }
 */
class Solution {
    public ListNode rotateRight(ListNode head, int k) {

                if (head == null || head.next == null) return head;

        ListNode cur =head;
        int len =1;
        while(cur.next!=null){
            cur=  cur.next;
            len++;
        }
        cur.next = head;
        ListNode cur1= head;
        for(int i=0;i<len-k%len-1;i++){
            cur1 = cur1.next;
        }
        ListNode newNode = cur1.next;
        cur1.next = null;
        return newNode;
       

    }
}

109. 有序链表转换二叉搜索树

给定一个单链表的头节点 head ,其中的元素 按升序排序 ,将其转换为 平衡 二叉搜索树。

示例 1:

输入: head = [-10,-3,0,5,9]
输出: [0,-3,9,-10,null,5]
解释: 一个可能的答案是[0,-3,9,-10,null,5],它表示所示的高度平衡的二叉搜索树。

示例 2:

输入: head = []
输出: []
/**
 * 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; }
 * }
 */
/**
 * 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 TreeNode sortedListToBST(ListNode head) {
        if(head==null) return null;
            ListNode cur = head;
            int len= 0 ;
            while(cur!=null){
                cur= cur.next;
                len++;
            }
            return build(head,0,len-1);
    }
    TreeNode build(ListNode head,int l,int r){
        if(l>r) return null;
        ListNode cur = head;
        int mid = l+(r-l)/2;
        int size = mid-l;
        while(size>0){
            cur = cur.next;
            size--;
        }
        TreeNode root = new TreeNode(cur.val);
        root.left = build(head,l,mid-1);
        root.right = build(cur.next,mid+1,r);
        return root;

    }    
}

## [113. 路径总和 II](https://leetcode.cn/problems/path-sum-ii/)

数学

41. 缺失的第一个正数

给你一个未排序的整数数组 nums ,请你找出其中没有出现的最小的正整数。

请你实现时间复杂度为 O(n) 并且只使用常数级别额外空间的解决方案。

示例 1:

输入:nums = [1,2,0]
输出:3
解释:范围 [1,2] 中的数字都在数组
  • 思路:空间复杂度为 O(1)。那么就只能原地置换元素。题目是求最小的正整数。数组,我们可以交换,存在的正整数交换到它正确的位置。这样我们去遍历,第一个不符合 i+1 的元素,就是缺少的值。
    比如我们有 3,4,-1,1 交换后,应该是 1,-1,3,4 1,3,4 都在其对应的位置,这样自然缺的就是 2
class Solution {
    public int firstMissingPositive(int[] nums) {
        for(int i =0;i<nums.length;i++){
            while( nums[i]>0&&nums[i]<=nums.length&& nums[i]!=i+1&& nums[nums[i]-1]!=nums[i]){
                int temp = nums[nums[i]-1];
                nums[nums[i]-1]=nums[i];
                nums[i]=temp;
            }
        }
        for(int i=0;i<nums.length;i++){
            if(nums[i]!=i+1){
                return i+1;
            }
        }
        return nums.length+1;
    }
}

42. 接雨水

给定 n 个非负整数表示每个宽度为 1 的柱子的高度图,计算按此排列的柱子,下雨之后能接多少雨水。

示例 1:

输入:height = [0,1,0,2,1,0,1,3,2,1,2,1]
输出:6
解释:上面是由数组 [0,1,0,2,1,0,1,3,2,1,2,1] 表示的高度图,在这种情况下,可以接 6 个单位的雨水(蓝色部分表示雨水)。
class Solution {
    public int trap(int[] height) {
        int n = height.length;
        int lmax[] = new int[height.length];
        int rmax[]=new int[height.length];
        lmax[0]=height[0];
        rmax[n-1]=height[n-1];
        for(int i=1;i<n;i++){
            lmax[i] = Math.max(lmax[i-1],height[i]);
        }
        for(int i =n-2;i>=0;i--){
            rmax[i] = Math.max(rmax[i+1],height[i]);
        }
        int res = 0;
        for(int i=1;i<n-1;i++){
            res+=Math.min(lmax[i],rmax[i])-height[i];
        }
        return res;
    }
}

堆的题目

347 前K个高频元素

*给你一个整数数组 nums 和一个整数 k ,请你返回其中出现频率前 k 高的元素。你可以按 任意顺序 返回答案。

class Solution {
    public int[] topKFrequent(int[] nums, int k) {
        //返回频率前K高,第一直觉就是堆,我们维护一个小根堆,前k高的元素放到对头,每次去取就可以了
        // 
        PriorityQueue<int[]> queue = new PriorityQueue<int[]>( (int[]a,int[]b)->{
            return a[1]-b[1];
        });
        Map<Integer,Integer> map =new HashMap();
        for(int num:nums){
            map.put(num,map.getOrDefault(num,0)+1);
        }
        int[]res = new int [k];
        map.forEach((k1,v1)->{
            int key = k1;
            int count= v1;
            if(queue.size()< k){
                queue.offer(new int[]{key,count});
            }else{
                if(queue.peek()[1]<v1){
                    queue.poll();
                    queue.offer(new int[]{key,count});
                }

            }

        });
        for(int i=0;i<res.length;i++){
            res[i] = queue.poll()[0];
        }
        return res;


    }
}

二分题目

33. 搜索旋转排序数组 整数数组 nums 按升序排列,数组中的值 互不相同

在传递给函数之前,nums 在预先未知的某个下标 k0 <= k < nums.length)上进行了 旋转,使数组变为 [nums[k], nums[k+1], ..., nums[n-1], nums[0], nums[1], ..., nums[k-1]](下标 从 0 开始 计数)。例如, [0,1,2,4,5,6,7] 在下标 3 处经旋转后可能变为 [4,5,6,7,0,1,2]

给你 旋转后 的数组 nums 和一个整数 target ,如果 nums 中存在这个目标值 target ,则返回它的下标,否则返回 -1

你必须设计一个时间复杂度为 O(log n) 的算法解决此问题。

示例 1:

输入:nums = [4,5,6,7,0,1,2], target = 0
输出:4

示例 2:

输入:nums = [4,5,6,7,0,1,2], target = 3
输出:-1

示例 3:

输入:nums = [1], target = 0
输出:-1
class Solution {
    public int search(int[] nums, int target) {
        int  i=0;
        if(nums.length==0) return -1;
        int j=nums.length-1;
        while(i<=j){
            int mid = i+(j-i)/2;
            if(nums[mid]==target){
                return mid;
            }
            if(nums[i]<=nums[mid]){
                if(target>=nums[i]&&target<=nums[mid]){
                    j=mid-1;
                }else{
                    i=mid+1;
                }
            }else{
                if(target>nums[mid]&&target<=nums[j]){
                    i=mid+1;
                }else{
                    j=mid-1;
                }

            }
        }
        return -1;
    }
}

34. 在排序数组中查找元素的第一个和最后一个位置

class Solution {
    public int[] searchRange(int[] nums, int target) {
        int[] res = new int[2];
        if(nums.length<1){
            return res;
        }
        int l=0;
        int r=nums.length-1;
        while (l<r){
            int mid =(l+r)/2;
            if(nums[mid]<target) l = mid+1;
            else  r=mid;
        }
        if(nums[r]==target){
            res[0]=r;
        } else{
            res[0]=-1;
        }

        l=0;
        r=nums.length-1;

        while (l<r){
            int mid =(l+r+1)/2;
            if(nums[mid]>target) r= mid-1;
            else  l=mid;
        }
        if(nums[l]==target){
            res[1] = l;
        }else{
            res[1] = -1;
        }
        return res;


    }
}

Stack

32. 最长有效括号

给你一个只包含 '(' 和 ')' 的字符串,找出最长有效(格式正确且连续)括号

子串的长度。


class Solution {
    public int longestValidParentheses(String s) {
        int max = 0;
        Deque<Integer> stack  =new LinkedList();
        stack.push(-1);
        for(int i =0;i<s.length();i++){
            if(s.charAt(i)=='('){
                stack.push(i);
            }else{
                stack.pop();
                if(stack.isEmpty()){
                    stack.push(i);
                }else{
                    max=  Math.max(max,i-stack.peek());
                }
            }
        }
        return max;
    }
}

71. 简化路径

给你一个字符串 path ,表示指向某一文件或目录的 Unix 风格 绝对路径 ****(以 '/' 开头),请你将其转化为 更加简洁的规范路径

在 Unix 风格的文件系统中规则如下:

  • 一个点 '.' 表示当前目录本身。
  • 此外,两个点 '..' 表示将目录切换到上一级(指向父目录)。
  • 任意多个连续的斜杠(即,'//''///')都被视为单个斜杠 '/'
  • 任何其他格式的点(例如,'...''....')均被视为有效的文件/目录名称。

返回的 简化路径 必须遵循下述格式:

  • 始终以斜杠 '/' 开头。
  • 两个目录名之间必须只有一个斜杠 '/'
  • 最后一个目录名(如果存在)不能 ****以 '/' 结尾。
  • 此外,路径仅包含从根目录到目标文件或目录的路径上的目录(即,不含 '.''..')。

返回简化后得到的 规范路径


class Solution {
    public String simplifyPath(String path) {
        Stack<String> stack = new Stack();
        String[]paths = path.split("/+");
        for(String temp:paths){
            if(temp.equals("..")){
                if(!stack.isEmpty()){
                    stack.pop();
                }
            }
                else{
                    if(!temp.equals(".")&&!temp.equals("")){
                        stack.push(temp);
                    }
                }
            

        }
        String res= "";
        while(!stack.isEmpty()){
            res = "/"+stack.pop()+res;
        }
        if(res.length()==0){
            return "/";
        }
        return res;
    
    }
}

动态规划

53. 最大子数组和

class Solution {
    public int maxSubArray(int[] nums) {
        int[]dp=new int[nums.length];
        dp[0]=nums[0];
        int max= dp[0];
        for(int i=1;i<nums.length;i++){
            dp[i]=dp[i-1]>0?dp[i-1]+nums[i]:nums[i];
            max= Math.max(dp[i],max);
        }
        return max;
    }
}


class Solution {
    public int maxSubArray(int[] nums) {
    
        int pre=  nums[0];
        int max = nums[0];
        for(int i=1;i<nums.length;i++){
            int cur = pre>0?pre+nums[i]:nums[i];
            max = Math.max(cur,max);
            pre = cur;

        }
        return max;
    }
}

72. 编辑距离

给你两个单词 word1word2请返回将 word1 转换成 word2 **所使用的最少操作数

你可以对一个单词进行如下三种操作:

  • 插入一个字符
  • 删除一个字符
  • 替换一个字符
class Solution {
    public int minDistance(String word1, String word2) {
        int n1= word1.length();
        int n2=  word2.length();
        int[][]dp=new int[n1+1][n2+1];
        for(int i =1;i<=n1;i++){
            dp[i][0]=dp[i-1][0]+1;
        }
        for(int j=1;j<=n2;j++){
            dp[0][j]=j;
        }
        for(int i =1;i<=n1;i++){
            for(int j =1;j<=n2;j++){
                if(word1.charAt(i-1)!=word2.charAt(j-1)){
                    dp[i][j]= Math.min(Math.min(dp[i-1][j],dp[i][j-1]),dp[i-1][j-1])+1;
                }else{
                    dp[i][j]= dp[i-1][j-1];
                }
            }
        }
        return dp[n1][n2];
    }
}

class Solution {
    public int minDistance(String word1, String word2) {
        int n1= word1.length();
        int n2=  word2.length();
        int[][]dp=new int[n1+1][n2+1];
        for(int i =1;i<=n1;i++){
            dp[i][0]=dp[i-1][0]+1;
        }
        for(int j=1;j<=n2;j++){
            dp[0][j]=j;
        }
        for(int i =1;i<=n1;i++){
            for(int j =1;j<=n2;j++){
                int left = dp[i-1][j]+1;
                int down = dp[i][j-1]+1;
                int left_down = dp[i-1][j-1];
                if(word1.charAt(i-1)!=word2.charAt(j-1)){
                left_down +=1;
                }
                dp[i][j] = Math.min(left, Math.min(down, left_down));
                
            }
        }
        return dp[n1][n2];
    }
}

5. 最长回文子串

给你一个字符串 s,找到 s 中最长的 回文 子串。

示例 1:

输入:s = "babad"
输出:"bab"
解释:"aba" 同样是符合题意的答案。
class Solution {
    public String longestPalindrome(String s) {
        int len = s.length();
        int begin = 0;
        int maxLen=1;
        
        boolean[][]dp=new boolean[s.length()][s.length()];
         for(int i=0;i<s.length();i++){
            dp[i][i]=true;
         }
        for(int i=1;i<len;i++){
            for(int j=0;j<i;j++){
                if(s.charAt(i)!=s.charAt(j)){
                        dp[j][i]=false;
                }else{
                    if(i-j<3){
                        dp[j][i]=true;
                    }else{
                        dp[j][i]=dp[j+1][i-1];
                    }
                }
                if(dp[j][i]&&i-j+1>maxLen){
                    maxLen = i-j+1;
                    begin = j;
                }
            }
        }
        return s.substring(begin,begin+maxLen);


    }
}

贪心题目

55. 跳跃游戏

给你一个非负整数数组 nums ,你最初位于数组的 第一个下标 。数组中的每个元素代表你在该位置可以跳跃的最大长度。

判断你是否能够到达最后一个下标,如果可以,返回 true ;否则,返回 false

class Solution {
    public boolean canJump(int[] nums) {
        int maxStep= nums[0];
        if(nums.length==1) return true;
        for(int i=1;i<nums.length;i++){
           if(i<= maxStep){
                if(i+nums[i]>=maxStep){
                maxStep=i+nums[i];
            }
           }
           
             if(maxStep>=nums.length-1){
                return true;
            }
        }
        return false;
    }
}

45. 跳跃游戏 II

给定一个长度为 n 的 0 索引整数数组 nums。初始位置为 nums[0]

每个元素 nums[i] 表示从索引 i 向前跳转的最大长度。换句话说,如果你在 nums[i] 处,你可以跳转到任意 nums[i + j] 处:

class Solution {
    public int jump(int[] nums) {
        int step = 0;
        int maxStep=0;
        int end = 0;
        for(int i=0;i<nums.length-1;i++){
            maxStep = Math.max(maxStep,i+nums[i]);
            if(i==end){
                
                step++;
                end= maxStep;
                
            }
        }
        return step;
    }
}

回溯算法

46. 全排列

给定一个不含重复数字的数组 nums ,返回其 所有可能的全排列 。你可以 按任意顺序 返回答案。

class Solution {

    List<List<Integer>> res = new ArrayList();
    public List<List<Integer>> permute(int[] nums) {
        back(new ArrayList(),0,nums);
        return res;

    }
    void back(List<Integer> arr,int start,int[]nums){
        if(arr.size()==nums.length){
            res.add(new ArrayList(arr));
            return;
        }
        for(int i=start;i<nums.length;i++){
            if(!arr.contains(nums[i])){
                arr.add(nums[i]);
                back(arr,start,nums);
                arr.remove(arr.size()-1);
            }
        }
    }
}

47. 全排列 II

给定一个可包含重复数字的序列 nums按任意顺序 返回所有不重复的全排列。

示例 1:

输入:nums = [1,1,2]
输出:
[[1,1,2],
 [1,2,1],
 [2,1,1]]

示例 2:

输入:nums = [1,2,3]
输出:[[1,2,3],[1,3,2],[2,1,3],[2,3,1],[3,1,2],[3,2,1]]
class Solution {

      public List<List<Integer>> permuteUnique(int[] nums) {
        List<List<Integer>> res = new ArrayList<>();
        if (nums == null || nums.length == 0) return  res;
        Arrays.sort(nums);
        helper(res, new ArrayList<>(), nums, new boolean[nums.length]);
        return res;
    }

    public void helper(List<List<Integer>> res, List<Integer> list, int[] nums, boolean[] used) {
        if (list.size() == nums.length) {
            res.add(new ArrayList<>(list));
        }
        for (int i = 0; i < nums.length; i++) {
            if (used[i] || i > 0 && nums[i] == nums[i - 1] && !used[i - 1]) continue;
            used[i] = true;
            list.add(nums[i]);
            helper(res, list, nums, used);
            used[i] = false;
            list.remove(list.size() - 1);
        }
}
}

39 组合总数

给你一个 无重复元素 的整数数组 candidates 和一个目标整数 target ,找出 candidates 中可以使数字和为目标数 target 的 所有 **不同组合 ,并以列表形式返回。你可以按 任意顺序 返回这些组合。

candidates 中的 同一个 数字可以 无限制重复被选取 。如果至少一个数字的被选数量不同,则两种组合是不同的。 

对于给定的输入,保证和为 target 的不同组合数少于 150 个。

 

示例 1:

输入: candidates = [2,3,6,7], target = 7
输出: [[2,2,3],[7]]
解释:
2 和 3 可以形成一组候选,2 + 2 + 3 = 7 。注意 2 可以使用多次。
7 也是一个候选, 7 = 7 。
仅有这两种组合。

class Solution {
    List<List<Integer>> res =new ArrayList();
    public List<List<Integer>> combinationSum(int[] candidates, int target) {
        back(candidates,0,target,new ArrayList());
        return res;
    }

    void back(int[]nums,int start, int target,List<Integer> temp){
        if(target==0){
            res.add(new ArrayList(temp));
            return;
        }
        if(target<0){
            return;
        }
        for(int i=start;i<nums.length;i++){
            temp.add(nums[i]);
            back(nums,i,target-nums[i],temp);  //为什么这里面还是i 因为可以重新选,所以继续选,递归终止条件,==0 就返回,如果小于0代表超过了,也应该返回。
            temp.remove(temp.size()-1);
        }
    }
}


40组合总和II

每个数字只能用1次。 给定一个候选人编号的集合 candidates 和一个目标数 target ,找出 candidates 中所有可以使数字和为 target 的组合。

candidates 中的每个数字在每个组合中只能使用 一次 。

    class Solution {
    List<List<Integer>> res = new ArrayList();
    public List<List<Integer>> combinationSum2(int[] candidates, int target) {
        Arrays.sort(candidates);
        dfs(candidates,target,0,new ArrayList());
        return res;
    }
    public void dfs(int[] nums,int target,int start,List<Integer> temp){
        if(target<0){
            return;
        }
        if(target==0){
            res.add(new ArrayList(temp));
            return;
        }
        for(int i =start;i<nums.length;i++){
            if (i>start && nums[i]==nums[i-1]){
                continue;
            } 
            temp.add(nums[i]);
            dfs(nums,target-nums[i],i+1,temp);
            temp.remove(temp.size()-1);
        }
    }
}

下一个排序(无聊的题目)


public class Solution {
    public void nextPermutation(int[] nums) {
        // 考察数学,比较无聊
        // 从倒数第二个元素开始向左遍历,寻找第一个满足 nums[i] < nums[i + 1] 的位置 i
        // 从数组的末尾开始向左遍历,找到第一个比 nums[i] 大的元素 nums[j]
        //交互 nums[i]和nums[j];
        //对i+1位置之后的进行反转
        int i =nums.length-2;
        while(i>=0&&nums[i]>=nums[i+1]){
            i--;
        }
        if(i>=0){
            int j =nums.length-1;
            while(j>=0&&nums[j]<=nums[i]){
                j--;
            }
            swap(nums,i,j);
         

        }
                // 对 i + 1 位置之后的元素进行反转
        reverse(nums, i + 1);


    }


    // 反转数组中从 start 位置到末尾的元素
    private void reverse(int[] nums, int start) {
        int i = start, j = nums.length - 1;
        // 使用双指针法,交换元素,将降序排列变为升序排列
        while (i < j) {
            swap(nums, i, j);
            i++;
            j--;
        }
    }


    // 交换 nums 数组中 i 和 j 位置的元素
    private void swap(int[] nums, int i, int j) {
        int temp = nums[i];
        nums[i] = nums[j];
        nums[j] = temp;
    }
}

集II 可能有重复



public static List<List<Integer>> subsetsWithDup(int[] nums) {
        List<List<Integer>> res = new ArrayList<>();
        if (nums == null || nums.length == 0) return res;
        Arrays.sort(nums);
        helper(res, new ArrayList<>(), nums, 0);
        return res;
    }
    public static void helper(List<List<Integer>> res, List<Integer> list, int[] nums, int index) {
        res.add(new ArrayList<>(list));
        for (int i = index; i < nums.length; i++) {
            if (i != index && nums[i] == nums[i - 1]) continue;
            list.add(nums[i]);
            helper(res, list, nums, i + 1);
            list.remove(list.size() - 1);
        }
    }

132




class Solution {

    List<List<String>> res= new ArrayList();

    public List<List<String>> partition(String s) {

     back(s,0,new ArrayList());

     return res;  

    }

    void back(String s,int start,List<String>temp){

        if(start==s.length()){

            res.add(new ArrayList(temp));

            return;

        }

        for(int i=start;i<s.length();i++){

            String s1= s.substring(start,i+1);

            if(!check(s1)){

                continue;

            }

            temp.add(s1);

            back(s,i+1,temp);

            temp.remove(temp.size()-1);

        }

    }

    private boolean check(String s) {

    if (s == null || s.length() <= 1) {

        return true;

    }

    int left = 0;

    int right = s.length() - 1;

    while (left < right) {

        if (s.charAt(left) != s.charAt(right)) {

            return false;

        }

        left++;

        right--;

    }

    return true;

}

  


}

数组

56. 合并区间

以数组 intervals 表示若干个区间的集合,其中单个区间为 intervals[i] = [starti, endi] 。请你合并所有重叠的区间,并返回 一个不重叠的区间数组,该数组需恰好覆盖输入中的所有区间

class Solution {
    public int[][] merge(int[][] arr) {
        Arrays.sort(arr,new Comparator<int[]>(){

            public int compare(int[]a,int[]b){
                return a[0]-b[0];
            }
        });

        int i =0;
        int n = arr.length;
        List<int[]> res = new ArrayList();
        while(i<n){

            int left = arr[i][0];
            int right =arr[i][1];
            while(i<n-1&& right>=arr[i+1][0]){
                right = Math.max(right,arr[i+1][1]);
                i++;
            }
            res.add(new int[]{left,right});
            i++;
        }
        return res.toArray(new int[res.size()][2]);
    }
}

单调栈

int[] nextGreaterElement(int[] nums) {
    int n = nums.length;
    // 存放答案的数组
    int[] res = new int[n];
    Stack<Integer> s = new Stack<>(); 
    // 倒着往栈里放
    for (int i = n - 1; i >= 0; i--) {
        // 判定个子高矮
        while (!s.isEmpty() && s.peek() <= nums[i]) {
            // 矮个起开,反正也被挡着了
            s.pop();
        }
        // nums[i] 身后的更大元素
        res[i] = s.isEmpty() ? -1 : s.peek();
        s.push(nums[i]);
    }
    return res;
}

739. 每日温度

class Solution {
    public int[] dailyTemperatures(int[] nums) {
        int n = nums.length;
        Stack<Integer> stack = new Stack();
        int res[] = new int[n];
        for(int i =n-1;i>=0;i--){
            while(!stack.isEmpty()&&nums[stack.peek()]<=nums[i]){
                stack.pop();
            }
            res[i]=stack.isEmpty()?0:stack.peek()-i;
            stack.push(i);
        }
        return res;
    }
}

84. 柱状图中最大的矩形

class Solution {
    public int largestRectangleArea(int[] heights) {
        // 这里为了代码简便,在柱体数组的头和尾加了两个高度为 0 的柱体。
        int[] tmp = new int[heights.length + 2];
        System.arraycopy(heights, 0, tmp, 1, heights.length); 
        
        Deque<Integer> stack = new ArrayDeque<>();
        int area = 0;
        for (int i = 0; i < tmp.length; i++) {
            // 对栈中柱体来说,栈中的下一个柱体就是其「左边第一个小于自身的柱体」;
            // 若当前柱体 i 的高度小于栈顶柱体的高度,说明 i 是栈顶柱体的「右边第一个小于栈顶柱体的柱体」。
            // 因此以栈顶柱体为高的矩形的左右宽度边界就确定了,可以计算面积\U0001f336️ ~
            while (!stack.isEmpty() && tmp[i] < tmp[stack.peek()]) {
                int h = tmp[stack.pop()];
                area = Math.max(area, (i - stack.peek() - 1) * h);   
            }
            stack.push(i);
        }

        return area;
    }
}

BFS

127. 单词接龙

class Solution {
    public int ladderLength(String beginWord, String endWord, List<String> wordList) {
        int step= 0;
        Queue<String> queue = new LinkedList();
        if(!wordList.contains(endWord)){
            return 0;
        }
        queue.add(beginWord);
        Set<String> set = new HashSet();
        for(String s:wordList){
            set.add(s);
        }
        while(!queue.isEmpty()){
            step++;
            int levelSize = queue.size();
            for(int i=0;i<levelSize;i++){
                String word = queue.poll();
                if(endWord.equals(word)){
                    return step;
                }
                StringBuilder sb = new StringBuilder(word);
                for(int j=0;j<word.length();j++){
                    for(char c='a';c<='z';c++){
                            if(word.charAt(j)==c){
                                continue;
                            }
                            sb.setCharAt(j,c);
                            if(set.contains(sb.toString())){
                                queue.add(sb.toString());
                                set.remove(sb.toString());
                            }
                        sb.setCharAt(j,word.charAt(j));

                    }
                }
            }
        }
        return 0;
        

    }
}

排序

归并排序

class Solution {
    public int[] sortArray(int[] nums) {
        mergeSort(nums,0,nums.length-1);
        return nums;
    }
    void mergeSort(int[]nums,int l,int r){
        if(l>=r) return;
            int mid = l+(r-l)/2;
            mergeSort(nums,l,mid);
            mergeSort(nums,mid+1,r);
            merge(nums,l,mid,r);
        
        
    }
    void merge(int[]nums,int l,int mid,int r){
        int[]temp = new int[r-l+1];
        int i=l;
        int j=mid+1;
        int k=0;
        while(i<=mid&&j<=r){
            if(nums[i]>=nums[j]){
                temp[k++]=nums[j++];
            }else{
                temp[k++]=nums[i++];
            }
        }
        while(i<=mid){
            temp[k++]=nums[i++];
        }
        while(j<=r){
            temp[k++]=nums[j++];
        }
        for(int a=0;a<temp.length;a++){
            nums[a+l]=temp[a];
        }
    }
}

快速排序

class Solution {
    public int[] sortArray(int[] nums) {
         quick(nums,0,nums.length-1);
         return nums;
    }
    void quick(int[]nums,int l,int r){
        if(l<r){
                int pivot = partion(nums,l,r);
                quick(nums,l,pivot-1);
                quick(nums,pivot+1,r);
        }
    }
    int partion(int[]nums,int left,int right){
        int random= new Random().nextInt(right-left+1)+left;
        int pivot = nums[random];
        swap(nums,left,random);
        int j=left;
        for(int i =left+1;i<=right;i++){
            if(nums[i]<pivot){
                j++;
                swap(nums,i,j);
            }
        }
        swap(nums,j,left);
        return j;
    }


  private void swap(int[] nums, int index1, int index2) {
        int temp = nums[index1];
        nums[index1] = nums[index2];
        nums[index2] = temp;
    }}

模拟,实现题目

6. Z 字形变换 将一个给定字符串 s 根据给定的行数 numRows ,以从上往下、从左到右进行 Z 字形排列。

比如输入字符串为 "PAYPALISHIRING" 行数为 3 时,排列如下:

P   A   H   N
A P L S I I G
Y   I   R

之后,你的输出需要从左往右逐行读取,产生出一个新的字符串,比如:"PAHNAPLSIIGYIR"

请你实现这个将字符串进行指定行数变换的函数:

  • 思路:规律总结:遍历顺序是 0 到 n-1,然后 n-1 到 0
class Solution {
    public String convert(String s, int numRows) {
        if(numRows < 2) return s;
        List<StringBuilder> rows = new ArrayList<StringBuilder>();
        for(int i = 0; i < numRows; i++) rows.add(new StringBuilder());
        int i = 0, flag = -1;
        for(char c : s.toCharArray()) {
            rows.get(i).append(c);
            if(i == 0 || i == numRows -1) flag = - flag;
            i += flag;
        }
        StringBuilder res = new StringBuilder();
        for(StringBuilder row : rows) res.append(row);
        return res.toString();
    }
}

7. 整数反转

给你一个 32 位的有符号整数 x ,返回将 x 中的数字部分反转后的结果。

如果反转后整数超过 32 位的有符号整数的范围 [−231, 231 − 1] ,就返回 0。

假设环境不允许存储 64 位整数(有符号或无符号)。

class Solution {
    public int reverse(int x) {
        long result = 0;    
        while(x!=0){
            result =  result*10+(x%10);
            x= x/10;
        }
        return result > Integer.MAX_VALUE || result < Integer.MIN_VALUE ? 0 : (int) result;
    }
}

8. 字符串转换整数 (atoi)

请你来实现一个 myAtoi(string s) 函数,使其能将字符串转换成一个 32 位有符号整数。

函数 myAtoi(string s) 的算法如下:

  1. 空格: 读入字符串并丢弃无用的前导空格(" "
  2. 符号: 检查下一个字符(假设还未到字符末尾)为 '-' 还是 '+'。如果两者都不存在,则假定结果为正。
  3. 转换: 通过跳过前置零来读取该整数,直到遇到非数字字符或到达字符串的结尾。如果没有读取数字,则结果为0。
  4. 舍入: 如果整数数超过 32 位有符号整数范围 [−231, 231 − 1] ,需要截断这个整数,使其保持在这个范围内。具体来说,小于 −231 的整数应该被舍入为 −231 ,大于 231 − 1 的整数应该被舍入为 231 − 1

返回整数作为最终结果。

  • 要点:去除空格。刚开始+ -判断。
    还有就是溢出判断,我们当前的结果+我们要添加的元素>max,根据公式反推,去校验当前元素要不要添加
public class Solution {
    public int myAtoi(String str) {
        str = str.trim();
        int result = 0;
        boolean isPos = true;
        for(int i = 0; i < str.length(); i++){
            char c = str.charAt(i);
            if(i==0 && (c=='-'||c=='+')){
                isPos = c=='+'?true:false;
            } else if (c>='0' && c<='9'){
                // 检查溢出情况 原因 比如 reusl*10+ digit>max 公式反推即可以得到这个。
                if(result>(Integer.MAX_VALUE - (c - '0'))/10){
                    return isPos? Integer.MAX_VALUE : Integer.MIN_VALUE;
                }
                result *= 10;

                result += c - '0';
            } else {
                return isPos?result:-result;
            }
        }
        return isPos?result:-result;
    }
}

36. 有效的数独

43. 字符串相乘

class Solution {
    public String multiply(String num1, String num2) {
        /**
        num1的第i位(高位从0开始)和num2的第j位相乘的结果在乘积中的位置是[i+j, i+j+1]
        例: 123 * 45,  123的第1位 2 和45的第0位 4 乘积 08 存放在结果的第[1, 2]位中
          index:    0 1 2 3 4  
              
                        1 2 3
                    *     4 5
                    ---------
                          1 5
                        1 0
                      0 5
                    ---------
                      0 6 1 5
                        1 2
                      0 8
                    0 4
                    ---------
                    0 5 5 3 5
        这样我们就可以单独都对每一位进行相乘计算把结果存入相应的index中        
        **/
        
        int n1 = num1.length()-1;
        int n2 = num2.length()-1;
        if(n1 < 0 || n2 < 0) return "";
        int[] mul = new int[n1+n2+2];
        
        for(int i = n1; i >= 0; i--) {
            for(int j = n2; j >= 0; j--) {
                int bitmul = (num1.charAt(i)-'0') * (num2.charAt(j)-'0');      
                bitmul += mul[i+j+1]; // 先加低位判断是否有新的进位
                
                mul[i+j] += bitmul / 10;
                mul[i+j+1] = bitmul % 10;
            }
        }
        
        StringBuilder sb = new StringBuilder();
        int i = 0;
        // 去掉前导0
        while(i < mul.length-1 && mul[i] == 0) 
            i++;
        for(; i < mul.length; ++i)
            sb.append(mul[i]);
        return sb.toString();
    }
}

数学题目

31. 下一个排列

整数数组的一个 排列 就是将其所有成员以序列或线性顺序排列。

  • 例如,arr = [1,2,3] ,以下这些都可以视作 arr 的排列:[1,2,3][1,3,2][3,1,2][2,3,1]

整数数组的 下一个排列 是指其整数的下一个字典序更大的排列。更正式地,如果数组的所有排列根据其字典顺序从小到大排列在一个容器中,那么数组的 下一个排列 就是在这个有序容器中排在它后面的那个排列。如果不存在下一个更大的排列,那么这个数组必须重排为字典序最小的排列(即,其元素按升序排列)。

  • 例如,arr = [1,2,3] 的下一个排列是 [1,3,2]
  • 类似地,arr = [2,3,1] 的下一个排列是 [3,1,2]
  • arr = [3,2,1] 的下一个排列是 [1,2,3] ,因为 [3,2,1] 不存在一个字典序更大的排列。
  • 先找出最大的索引 k 满足 nums[k] < nums[k+1],如果不存在,就翻转整个数组;

再找出另一个最大索引 l 满足 nums[l] > nums[k];

交换 nums[l] 和 nums[k];

最后翻转 nums[k+1:]。

public class Solution {
    public void nextPermutation(int[] nums) {
        // 从倒数第二个元素开始向左遍历,寻找第一个满足 nums[i] < nums[i + 1] 的位置 i
        int i = nums.length - 2;
        while (i >= 0 && nums[i + 1] <= nums[i]) {
            i--;
        }
        // 如果找到了这样的位置 i
        if (i >= 0) {
            // 从数组的末尾开始向左遍历,找到第一个比 nums[i] 大的元素 nums[j]
            int j = nums.length - 1;
            while (j >= 0 && nums[j] <= nums[i]) {
                j--;
            }
            // 交换 nums[i] 和 nums[j]
            swap(nums, i, j);
        }
        // 对 i + 1 位置之后的元素进行反转
        reverse(nums, i + 1);


    }


    // 反转数组中从 start 位置到末尾的元素
    private void reverse(int[] nums, int start) {
        int i = start, j = nums.length - 1;
        // 使用双指针法,交换元素,将降序排列变为升序排列
        while (i < j) {
            swap(nums, i, j);
            i++;
            j--;
        }
    }


    // 交换 nums 数组中 i 和 j 位置的元素
    private void swap(int[] nums, int i, int j) {
        int temp = nums[i];
        nums[i] = nums[j];
        nums[j] = temp;
    }
}

小众题目

75. 颜色分类

给定一个包含红色、白色和蓝色、共 n **个元素的数组 nums原地 ****对它们进行排序,使得相同颜色的元素相邻,并按照红色、白色、蓝色顺序排列。

我们使用整数 012 分别表示红色、白色和蓝色。

必须在不使用库内置的 sort 函数的情况下解决这个问题。

class Solution {
   
    //没有太多意思
    public void sortColors(int[] nums) {
        int ptr= 0 ;
        int n = nums.length;
        for(int i =0;i<n;i++){
            if(nums[i]==0){
                int temp = nums[i];
                nums[i] = nums[ptr];
                nums[ptr]= temp;
                ptr++;
            }
        }
        for(int i =ptr;i<n;i++){
            if(nums[i]==1){
                  int temp = nums[i];
                nums[i] = nums[ptr];
                nums[ptr]= temp;
                ptr++;
            }
        }   
    }
}