高频算法

高频算法

1-10

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

思路:使用堆,建立一个K个元素的小顶堆,当元素比堆顶元素大时,入堆,否则不入堆。最后弹出堆顶元素即为第K个最大的元素。

import java.util.PriorityQueue;
public class Solution {

    public int findKthLargest(int[] nums, int k) {
        int len = nums.length;
        // 使用一个含有 k 个元素的最小堆
        PriorityQueue<Integer> minHeap = new PriorityQueue<>(k, (a, b) -> a - b);
        for (int i = 0; i < k; i++) {
            minHeap.add(nums[i]);
        }
        for (int i = k; i < len; i++) {
            // 看一眼,不拿出,因为有可能没有必要替换
            Integer topEle = minHeap.peek();
            // 只要当前遍历的元素比堆顶元素大,堆顶弹出,遍历的元素进去
            if (nums[i] > topEle) {
                minHeap.poll();
                minHeap.add(nums[i]);
            }
        }
        return minHeap.peek();
    }
}

206. 反转链表(https://www.cnblogs.com/mwl523/p/10749144.html)

思路1: 新建链表,头节点插入
具体操作:新建一个链表dummy,当前指针pCur指向head,每次都头插入新链表
关键:1个头结点,2个指针(包含一个临时保存节点的pNex),4行代码

/**
 * Definition for singly-linked list.
 * public class ListNode {
 *     int val;
 *     ListNode next;
 *     ListNode(int x) { val = x; }
 * }
 */
class Solution {
    public ListNode reverseList(ListNode head) {
       ListNode dummy = new ListNode(-1);
       ListNode pCur = head;
       ListNode pNext = null;
       while(pCur != null) {
           pNext = pCur.next;
           pCur.next = dummy.next;
           dummy.next = pCur;
           pCur = pNext;
       }
       return dummy.next;
    }
}

思路二:使用递归

ListNode reverse(ListNode head) {
    if (head.next == null) return head;
    ListNode last = reverse(head.next);
    head.next.next = head;
    head.next = null;
    return last;
}

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

思路:滑动窗口,一个left和一个right,用一个hashmap保存出现的字符及其下标,每次增加right,判断是否存在重复,从而跟新left,每次计算最大子串长度。

class Solution {
    public int lengthOfLongestSubstring(String s) {
        if(s==null || s.length() == 0) {
            return 0;
        }
        int len = s.length();
        int res = 0;
        int left = 0, right = 0;
        HashMap<Character,Integer> map = new HashMap<>();
        for( ; right<len; right++) {
            if(map.containsKey(s.charAt(right))){
                left = Math.max(left, map.get(s.charAt(right)) + 1);
            }
            map.put(s.charAt(right), right);
            res = Math.max(res, right - left + 1);
        }
        return res;
    }
}

25. K 个一组翻转链表

思路:把链表结点按照k个一组分组(可使用一个指针head依次指向每组的头结点)对于每个分组,先判断它的长度是否大于等于k,若是,翻转这部分链表,否则不需要翻转。

/**
 * Definition for singly-linked list.
 * public class ListNode {
 *     int val;
 *     ListNode next;
 *     ListNode(int x) { val = x; }
 * }
 */
class Solution {
    public static ListNode reverseKGroup(ListNode head, int k) {
        if(head == null || head.next == null) {
            return head;
        }
        ListNode dummy = new ListNode(0);
        dummy.next = head;
        ListNode pre = dummy, end = dummy;
        while (end.next != null) {
            for(int i=0; i<k&&end != null; i++) {
                end = end.next;
            }
            if(end == null) {
                break;
            }
            ListNode next = end.next;
            end.next = null;
            ListNode start = pre.next;
            pre.next = reverse(start);
            start.next = next;
            pre = start;
            end = start;


        }
        return dummy.next;
    }

    public static ListNode reverse(ListNode head) {
        if (head == null || head.next == null) {
            return head;
        }
        ListNode pre = null;
        ListNode cur = head;
        ListNode nextNode = null;
        while(cur != null) {
            nextNode = cur.next;
            cur.next =  pre;
            pre = cur;
            cur = nextNode;
        }
        return pre;
    }
}

15. 三数之和

思路:排序+双指针
首先对数组进行由小到大的排序,然后依次遍历数组(重复元素跳过),选定一个之后,以其右边第一个元素和最右边元素为left和right,双指针法求二数之和问题。其中关键是如何去除重复,对于重复元素跳过。

class Solution {
    List<List<Integer>> res = new ArrayList<>();
    public List<List<Integer>> threeSum(int[] nums) {
        int n = nums.length-1;
        if(n<2){
            return res;
        }
        Arrays.sort(nums);
        for(int i=0;i<=n;i++){
            if(n-i<2){
                break;
            }
            if(i>0 && nums[i] == nums[i-1]){
                continue;
            }
            int l= i+1;
            int r = n;
            int target = -nums[i];
            while(l<r){
                if(nums[l]+nums[r] == target){
                    List<Integer> list = new ArrayList<>();
                    list.add(nums[i]);
                    list.add(nums[l]);
                    list.add(nums[r]);
                    res.add(list);
                    l++;
                    r--;
                   
                } else if(nums[l]+nums[r] < target) {
                    l++;
                    
                } else{
                    r--;
                    
                }
            }
        }
        return res;
    }   
}

146. LRU 缓存机制

最近最少使用算法,有两个接口get和put
思路:使用hashMap和Deque(LinkedList)实现LRU,还要设置一个LRU的最大容量,LRU的构造函数传入最大容量的值,并初始化map和linkedlist。对于get(int key):先从hashMap中获取key的值,判断是否存在,存在则在list中删除该值并在最前面插入该值,否则输出-1。
对于put(int key, int value):首先判断hashMap中是否含有该key,如果含有,则删除list中的该key并在list最前面插入,同时重新往hashMap中插入该key和value并返回。如果不含有,则判断是否超过最大容量,如果超过,则删除list中最后的结点,并在hashMap中删除该结点及其对应的value,然后往hashMap中插入key和value,list中插入key。

class LRUCache {
    private Map<Integer, Integer> cache;
    private Deque<Integer> list;
    private int capacity;


    public LRUCache(int capacity) {
        this.capacity = capacity;
        cache = new HashMap<>();
        list = new LinkedList<>();
    }
    
    public int get(int key) {
        Integer item = cache.get(key);
        if (item == null) {
            return -1;
        }
        changeList(key);
        return item;
    }
    
    public void put(int key, int value) {
        if (cache.containsKey(key)) {
            changeList(key);
            cache.put(key, value);
            return;
        }
        if (list.size() == capacity) {
            Integer last = list.removeLast();
            cache.remove(last);
        }
        cache.put(key, value);
        list.offerFirst(key);
    }


    private void changeList(int key) {
        list.remove(key);
        list.offerFirst(key);
    }
}

/**
 * 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);
 */

1. 两数之和

思路:使用哈希表存储数字及其下标,每次查找hash表中是否有(target-nums[i])

class Solution {
    public int[] twoSum(int[] nums, int target) {
        Map<Integer, Integer> hashtable = new HashMap<Integer, Integer>();
        for (int i = 0; i < nums.length; ++i) {
            if (hashtable.containsKey(target - nums[i])) {
                return new int[]{hashtable.get(target - nums[i]), i};
            }
            hashtable.put(nums[i], i);
        }
        return new int[0];
    }
}

121. 买卖股票的最佳时机

思路:双指针或单调栈
双指针:使用两个指针,一个指针记录访问过的最小值,一个指针一直往后走,然后计算他们的差值,保存最大的即可。
单调栈:(始终保持栈顶元素是所有访问过的元素中最小的)如果当前元素小于栈顶元素,就让栈顶元素出栈,让当前元素入栈。如果访问的元素大于栈顶元素,就要计算他和栈顶元素的差值,我们记录最大的即可。

//双指针
public static int maxProfit(int[] prices) {
    if (prices == null || prices.length == 0)
        return 0;
    int maxPro = 0;//记录最大利润
    int min = prices[0];//记录数组中访问过的最小值
    for (int i = 1; i < prices.length; i++) {
        min = Math.min(min, prices[i]);
        maxPro = Math.max(prices[i] - min, maxPro);
    }
    return maxPro;
}

//单调栈
public int maxProfit(int[] prices) {
    if (prices == null || prices.length == 0)
        return 0;
    Stack<Integer> stack = new Stack<>();
    stack.push(prices[0]);
    int max = 0;
    for (int i = 1; i < prices.length; i++) {
        //如果栈顶元素大于prices[i],那么栈顶元素出栈,
        //把prices[i]压栈,要始终保证栈顶元素是最小的
        if (stack.peek() > prices[i]) {
            stack.pop();
            stack.push(prices[i]);
        } else {
            //否则如果栈顶元素不大于prices[i],就要计算
            //prices[i]和栈顶元素的差值
            max = Math.max(max, prices[i] - stack.peek());
        }
    }
    return max;
}

142. 环形链表 II

思路:使用快慢指针
快指针的路程是满指针路程的两倍,当第一次相遇后,新建一个指针从起点出发,慢指针和新指针同时走,相遇的地方就是环形链表的入口,公式推导,画图一试就知道了,2*(a + b)= a + b + a + c

/**
 * 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) {
        if(head == null) {
            return null;
        }
        ListNode slow = head, fast = head;
        while(fast != null) {
            slow = slow.next;
            if(fast.next != null) {
                fast = fast.next.next;
            } else {
                return null;
            }
            if(fast == slow) {
                ListNode pre = head;
                while(pre != slow) {
                    pre = pre.next;
                    slow = slow.next;
                }
                return pre;
            }
        }
        return null;
    }
}

94. 二叉树的中序遍历

思路:使用栈,双while
首先,特判,然后杀入到左子树最左结点(使用while循环),去判断最左结点是否为空,如果左结点为空,则当前结点入列表,然后将当前结点赋为其right结点,重复while循环。

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

10-20

102. 二叉树的层序遍历

思路:使用队列,每层入队,取队的size,依次遍历,直到队列为空

/**
 * Definition for a binary tree node.
 * public class TreeNode {
 *     int val;
 *     TreeNode left;
 *     TreeNode right;
 *     TreeNode(int x) { val = x; }
 * }
 */
class Solution {
    public List<List<Integer>> levelOrder(TreeNode root) {
        List<List<Integer>> res = new ArrayList<>();
        if(root == null){
            return res;
        }
        Queue<TreeNode> queue = new LinkedList<>();
        queue.offer(root);
        while(!queue.isEmpty()){
            int size = queue.size();     
            List<Integer> subr = new ArrayList<>();
            for(int i=0;i<size;i++){
                TreeNode cur = queue.poll();
                subr.add(cur.val);
                if(cur.left != null){
                    queue.offer(cur.left);
                }
                if(cur.right != null){
                    queue.offer(cur.right);
                }
            }
            res.add(subr);
            
        }
        return res;
    }
}

103. 二叉树的锯齿形层序遍历

思路:也是使用队列,遍历每层,每次判断是奇数层还是偶数层,奇数层直接存入列表,偶数层逆转后存入列表

/**
 * Definition for a binary tree node.
 * public class TreeNode {
 *     int val;
 *     TreeNode left;
 *     TreeNode right;
 *     TreeNode(int x) { val = x; }
 * }
 */
class Solution {
    public List<List<Integer>> zigzagLevelOrder(TreeNode root) {
        List<List<Integer>> res = new ArrayList<>();
        
        if(root == null) {
            return res;
        }
        Queue<TreeNode> queue = new LinkedList<>();
        queue.add(root);
        int level = 1;
        while(!queue.isEmpty()) {
            int size = queue.size();
            List<Integer> levelList = new ArrayList<>();
            for(int i=0; i<size; i++) {
                TreeNode cur = queue.poll();
                levelList.add(cur.val);
                if(cur.left != null) {
                    queue.add(cur.left);
                }
                if(cur.right != null) {
                    queue.add(cur.right);
                }
            }
            if(level % 2 == 0) {
                Collections.reverse(levelList);
            }
            res.add(levelList);
            level++;
        }
        return res;
    }
}

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

思路:递归
lowestCommonAncestor(TreeNode root, TreeNode p, TreeNode q)
若root为p,q的最近公共祖先,则有三种情况:
1、p和q在root的子树中,且分列root异侧(分别在左右子树)
2、p=root且q在root的子树中
3、q=root且p在root的子树中

考虑通过递归对二叉树进行后序遍历,当遇到结点p或q时返回。从底至顶回溯,当结点p,q在结点root的异侧时,结点root即为最近公共祖先,则向上返回root。
在这里插入图片描述

/**
 * Definition for a binary tree node.
 * public class TreeNode {
 *     int val;
 *     TreeNode left;
 *     TreeNode right;
 *     TreeNode(int x) { val = x; }
 * }
 */
class Solution {
    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 && right == null) 
            return null;
        if(left == null) {
            return right;
        }
        if(right == null) {
            return left;
        }
        return root;
    }
}

21. 合并两个有序链表

思路: 用迭代的方法实现,当l1和l2都不是空链表时,判断l1和l2哪一个链表的头结点的值更小,将较小值的结点添加到结里,当一个结点被添加到结果里面之后,将对应链表中的结点向后移一位。

/**
 * 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 mergeTwoLists(ListNode l1, ListNode l2) {
       ListNode dummyHead = new ListNode(-1);
       ListNode pre = dummyHead;
       while(l1 != null && l2 != null) {
           if(l1.val <  l2.val) {
               pre.next = l1;
               l1 = l1.next;
           } else {
               pre.next = l2;
               l2 = l2.next;
           }
           pre = pre.next;
       }
       pre.next = l1 == null ? l2 : l1;
       return dummyHead.next;
    }
}

415. 字符串相加

思路:模拟加法的过程
定义两个指针i和j分别指向num1和num2的末尾,即最低位,同时定义一个变量add维护当前是否有进位,然后从末尾开始
逐位相加即可。

class Solution {
    public String addStrings(String num1, String num2) {
        int i = num1.length()-1, j = num2.length()-1;
        int add = 0;
        StringBuffer ans = new StringBuffer();
        while(i >=0 || j >= 0 || add>0) {
            int x = i<0 ? 0 : num1.charAt(i) - '0';
            int y = j<0 ? 0 : num2.charAt(j) - '0';
            ans.append((x + y + add) % 10);
            add = (x + y + add) /10;
            i --;
            j --;
        }
        return ans.reverse().toString();
    }
}

53. 最大子序和

思路1:使用前缀和数值

class Solution {
    public int maxSubArray(int[] nums) {
        int len = nums.length;
        int[] sumNums = new int[len];
        int sum = 0;
        for(int i=0; i<len; i++) {
            sum += nums[i];
            sumNums[i] = sum;
        }
        int minSum = 0;
        int maxSum = nums[0];
        for(int i=0; i<len; i++) {
            maxSum = Math.max(maxSum, sumNums[i] - minSum);
            minSum = Math.min(minSum, sumNums[i]);
            
        }
        return maxSum ;
    }
}

思路2:动态规划
设f(i)代表以第i个数结尾的连续子树组的最大和。则f(i) = max{f(i-1) + nums[i], nums[i]}

class Solution {
    public int maxSubArray(int[] nums) {
        int pre = 0, maxAns = nums[0];
        for (int x : nums) {
            pre = Math.max(pre + x, x);
            maxAns = Math.max(maxAns, pre);
        }
        return maxAns;
    }
}

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) {
        List<Integer> res = new ArrayList<>();
        if(root == null) {
            return res;
        }
        Queue<TreeNode> queue = new LinkedList<>();
        queue.offer(root);
        while(!queue.isEmpty()) {
            int size = queue.size();
            for(int i=0; i<size; i++) {
                TreeNode cur = queue.poll();
                if(i == 0) {
                    res.add(cur.val);
                }
                if(cur.right != null) {
                    queue.offer(cur.right);
                }
                if(cur.left != null) {
                    queue.offer(cur.left);
                }
            }
        }
        return res;
    }
}

160. 相交链表

思路:双指针法
两个指针,pA和pB分别初始化为链表A和B的头结点,依次遍历,当pA到达A的尾部时,重新指向B的头结点,当pB到达B的尾部时,重新指向A的头结点。pA和pB相遇的结点即为相交结点。

/**
 * Definition for singly-linked list.
 * public class ListNode {
 *     int val;
 *     ListNode next;
 *     ListNode(int x) {
 *         val = x;
 *         next = null;
 *     }
 * }
 */
public class Solution {
    public ListNode getIntersectionNode(ListNode headA, ListNode headB) {
        if (headA == null || headB == null) return null;
       ListNode pA = headA, pB = headB;
       while(pA != pB) {
           pA = pA == null ? headB : pA.next;
           pB = pB == null ? headA : pB.next;
       }
       return pA;
    }
}

20. 有效的括号

思路:使用栈

class Solution {
    public boolean isValid(String s) {
        Stack<Character> stack = new Stack<>();
        for(Character c : s.toCharArray()) {
            if(stack.isEmpty() && (c == ')' || c == ']' || c== '}')){
                stack.push(c);
                break;
            }
            if(c == '(' || c == '[' || c== '{') {
                stack.push(c);
            } else {
                Character tmp = stack.peek();
                if(c == ')' && tmp == '(') {
                    stack.pop();
                    continue;
                }
                if(c == ']' && tmp == '[') {
                    stack.pop();
                    continue;
                }
                if(c == '}' && tmp == '{'){
                    stack.pop();
                    continue;
                }
                stack.push(c);
            }
        }
        return stack.isEmpty();
    }
}

200. 岛屿数量

https://leetcode-cn.com/problems/number-of-islands/solution/dao-yu-lei-wen-ti-de-tong-yong-jie-fa-dfs-bian-li-/
思路:依次遍历矩阵各个点,如果为1,则结果加1,并且使用dfs将该点和上下左右的点都递归赋值为0,遍历完即得到联通分量的数目.

class Solution {
    int res = 0;
    int m;
    int n;
    int[][] dic = {{0,1},{1,0},{0,-1},{-1,0}};
    public int numIslands(char[][] grid) {
        m = grid.length;
        n=grid[0].length;
        for(int i=0; i<m; i++) {
            for(int j=0; j<n; j++) {
                if(grid[i][j] != '0') {
                    res++;
                    dfs(grid, i, j);
                }
            }
        }
        return res;
    }
    private void dfs(char[][] grid, int i, int j){
       if(i<0 || i>= m || j < 0 || j>= n || grid[i][j] == '0') return;
       grid[i][j] = '0';
       for(int[] d : dic) {
           dfs(grid, i + d[0], j + d[1]);
       }
    }
}

20-30

104. 二叉树的最大深度

思路:对于二叉树,首先想到的应该是递归,递归左右子树计算高度

/**
 * 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 maxDepth(TreeNode root) {
        if(root == null) return 0;
        return Math.max(maxDepth(root.left), maxDepth(root.right)) + 1;
    }
}

54. 螺旋矩阵

思路:按层模拟,一圈一圈的往内遍历,限度left,right,top和buttom,然后依次遍历
1、从左向右遍历上侧元素,依次为(top,left)到(top,right)。
2、从上到下遍历右侧元素,依次为(top+1, left)到(buttom,right)。
3、如果left < right 且 top < buttom, 则从右到左遍历下侧元素,依次为(buttom, right-1)到(buttom, left + 1),以及以下到上遍历左侧元素,依次为(buttom,left)到(top+1, left)。
4、遍历一层后,left,top分别加1,right 和bottom分别减1
1到4在一个while循环中完成。

class Solution {
    public List<Integer> spiralOrder(int[][] matrix) {
        List<Integer> order = new ArrayList<Integer>();
        if (matrix == null || matrix.length == 0 || matrix[0].length == 0) {
            return order;
        }
        int rows = matrix.length, columns = matrix[0].length;
        int left = 0, right = columns - 1, top = 0, bottom = rows - 1;
        while (left <= right && top <= bottom) {
            for (int column = left; column <= right; column++) {
                order.add(matrix[top][column]);
            }
            for (int row = top + 1; row <= bottom; row++) {
                order.add(matrix[row][right]);
            }
            if (left < right && top < bottom) {
                for (int column = right - 1; column > left; column--) {
                    order.add(matrix[bottom][column]);
                }
                for (int row = bottom; row > top; row--) {
                    order.add(matrix[row][left]);
                }
            }
            left++;
            right--;
            top++;
            bottom--;
        }
        return order;
    }
}

92. 反转链表 II

思路:使用双指针和头插法
1、首先新建一个链表dummy,两个指针pre和cur,移动指针使pre指向要反转部分的前一个结点,cur指向反转部分开头(这好办,将dummy.next指向head,pre指向dummy,cur指向dummy.next,两指针走left步即可)
2、然后使用头插法(将cur后面的元素删除,然后插入pre的后面),将接下来right-left个结点反转(定义一个pNext结点,记录当前结点cur的下一个位置,然后将cur指向他的下下一个结点,pNext指向pre的next结点,然后pre的next结点改为pNext)

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

88. 合并两个有序数组

思路:双指针,从大到小比较,大的放在nums1的最右端

class Solution {
    public void merge(int[] nums1, int m, int[] nums2, int n) {
        int i = m-1, j = n-1;
        while(i >=0 && j >= 0) {
            if(nums1[i] > nums2[j]) {
                nums1[i+j+1] = nums1[i];
                i--;
            } else {
                nums1[i+j+1] = nums2[j];
                j--;
            }
        }
        while(j >= 0) {
             nums1[j] = nums2[j];
             j--;
        }
    }
}

69. x 的平方根

思路:使用二分查找,比较mid和x/mid的大小(注意,这里mid为int类型),基本二分查找的正常实现

class Solution {
    public int mySqrt(int x) {
        if(x <= 1) return x;
        int l = 1, h = x;
        while(l <= h) {
            int mid = l + (h-l)/2;
            int sqrt = x / mid;
            if(mid == sqrt) {
                return mid;
            } else if (mid >= sqrt) {
                h = mid -1;
            } else {
                l = mid + 1;
            }
        } 
        return h;
    }
}

判断循环条件是l<h还是l<=h,举例子判断下是否有死循环即可
最后输出h而不是l,也用特例判断

141. 环形链表

思路:使用快慢指针

/**
 * Definition for singly-linked list.
 * class ListNode {
 *     int val;
 *     ListNode next;
 *     ListNode(int x) {
 *         val = x;
 *         next = null;
 *     }
 * }
 */
public class Solution {
    public boolean hasCycle(ListNode head) {
        if(head == null || head.next==null){
            return false;
        }
        ListNode fast = head.next;
        ListNode slow = head;
        while(fast != slow){
            if(fast.next == null || fast.next.next == null)   return false;
            fast = fast.next.next;
            slow = slow.next;
        }
        return true;
    }
}

543. 二叉树的直径

思路:看见二叉树首先想到递归
分析可知二叉树的直径为最大的左子树深度+右子树深度。
用该函数无法递归时,考虑到改成求深度的函数进行递归,返回深度,用一个全局变量存储最大直径
res = Math.max(res, leftDepth + rightDepth)

/**
 * Definition for a binary tree node.
 * public class TreeNode {
 *     int val;
 *     TreeNode left;
 *     TreeNode right;
 *     TreeNode(int x) { val = x; }
 * }
 */
class Solution {
    int res = 0;
    public int diameterOfBinaryTree(TreeNode root) {
        if(root == null) return 0;
        depth(root);
        return res;
    }
    private int depth(TreeNode root) {
        if(root == null) {
            return 0;
        }
        int leftD = depth(root.left);
        int rightD = depth(root.right);
        res = Math.max(res, leftD + rightD);
        return Math.max(leftD, rightD) + 1;
    }
}

110. 平衡二叉树

思路:和求543二叉树的直径这里异曲同工,都是考虑递归
平衡二叉树与左右子树的深度有关并且当前函数是否为平衡二叉树,因此递归求深度的函数,使用一个全局变量客,在递归过程中判断是否为平衡二叉树。

/**
 * 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 {
    boolean res = true;
    public boolean isBalanced(TreeNode root) {
        depth(root);
        return res;
    }
    private int depth(TreeNode root) {
        if(root == null) return 0;
        int leftD = depth(root.left);
        int rightD = depth(root.right);
        boolean tmp = (Math.abs(leftD - rightD) <= 1);
        res &= tmp;
        return Math.max(leftD, rightD) + 1;
    }
}

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

思路:
前序:[根,左,右]
中序:[左,根,右]
根据前序中的根从中序中找出根的下标,然后可以得到左子树和右子树的个数,通过根构建root,然后递归得到root.left 和root.right

二叉树,首先想到递归。
原函数无法/不方便递归,则新建一个类似得递归函数,可以仅参数不同。
数组通过下标就可获取全部信息。

/**
 * 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 {
    Map<Integer, Integer> map = new HashMap<>();
    public TreeNode buildTree(int[] preorder, int[] inorder) {
        for(int i=0; i<inorder.length; i++) {
            map.put(inorder[i], i);
        }
        int len = preorder.length - 1;
        return buildTree(preorder, 0, len, inorder, 0, len);
    }

    private TreeNode buildTree(int[] preorder, int preLeft, int preRight, int[] inorder, int inLeft, int inRight) {
        if(preLeft > preRight || inLeft > inRight) {
            return null;
        }
        TreeNode root = new TreeNode(preorder[preLeft]);
        int index = map.get(preorder[preLeft]);
        int inLeftLen = index - inLeft;
        int inRightLen = inRight - index;
        root.left = buildTree(preorder, preLeft+1, preLeft + inLeftLen, inorder, inLeft, index -1);
        root.right = buildTree(preorder, preLeft + inLeftLen + 1, preRight, inorder, index + 1, inRight);
        return root;
    }
}

113. 路径总和 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 {
    ArrayList<List<Integer>> results = new ArrayList<>();
    public List<List<Integer>> pathSum(TreeNode root, int targetSum) {
        List<Integer> res = new ArrayList<>();
        pathSum(root, targetSum, res);
        return results;
    }
    private void pathSum(TreeNode root, int target, List<Integer> res) {
        if(root == null) {
            return;
        }
        res.add(root.val);
        if(target == root.val && root.left == null && root.right == null) {
            results.add(new ArrayList<>(res));
        } else {
            pathSum(root.left, target - root.val, res);
            pathSum(root.right, target - root.val, res);
        }
        res.remove(res.size()-1);
    }
}

30-40

42. 接雨水

方法一:用两个数组记录左边的最大值和右边的最大值,两者中最小的减去当前高度即为第i个位置能接的雨水量。

class Solution {
    public int trap(int[] height) {
        int len = height.length;
        int[] maxLeft = new int[len];
        int[] maxRight = new int[len];
        int maxL = 0, maxR = 0;
        for(int i=0; i<len; i++) {
            maxL = Math.max(maxL, height[i]);
            maxR = Math.max(maxR, height[len - i - 1]);
            maxLeft[i] = maxL;
            maxRight[len - i -1] = maxR;
        }
        int res = 0;
        for(int i=0; i<len; i++) {
            res += (Math.min(maxLeft[i], maxRight[i]) - height[i]);
        }
        return res;
    }
}

方法二:
使用单调栈(单调递减栈)-栈中元素单调递减,当待插入元素大于栈顶元素时候,用while操作出栈,我们的计算面积操作也是在这里进行的
单调栈中存放的是下标,但是我们计算和比较都是用的下标对应的高度
当待插入元素大于栈顶元素时,取出栈顶元素,求凹进去的面积

class Solution {
    public int trap(int[] height) {
        Stack<Integer> stack = new Stack<>();
        int cur = 0, len = height.length;
        int sum = 0;
        while(cur < len) {
            while(!stack.isEmpty() && height[stack.peek()] < height[cur]) {
                int tmp = stack.pop();
                if(stack.isEmpty()) {
                    break;
                }
                int dis = cur - stack.peek() - 1;
                int h = Math.min(height[stack.peek()], height[cur]) - height[tmp];
                sum += dis * h;
            }
            stack.push(cur++);
        }
        return sum;
    }
}

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) {
        maxGain(root);
        return max;
    }
    public int maxGain(TreeNode root) {
        if(root == null) return 0;

        int leftG = Math.max(maxGain(root.left), 0);
        int rightG = Math.max(maxGain(root.right), 0);
        max = Math.max(leftG + rightG + root.val, max);
        return root.val + Math.max(leftG, rightG);
    }
}

23. 合并K个升序链表

思路:使用合并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 mergeKLists(ListNode[] lists) {
        ListNode dummy = null;
        int size = lists.length;
        for(int i=0; i<size; i++) {
            dummy = margeTwoLists(dummy, lists[i]);
        }
        return dummy;
    }

    private ListNode margeTwoLists(ListNode list1, ListNode list2) {
        if(list1 == null) return list2;
        if(list2 == null) return list1;
        ListNode head = new ListNode(-1);
        ListNode pre = head, pList1 = list1, pList2 = list2;
        while(pList1 != null && pList2 != null) {
            if(pList1.val <= pList2.val) {
                pre.next = pList1;
                pList1 = pList1.next;
            } else {
                pre.next = pList2;
                pList2 = pList2.next;
            }
            pre = pre.next;
        }
        if(pList1 != null) {
            pre.next = pList1;
        }
        if(pList2 != null) {
            pre.next = pList2;
        }
        return head.next;
    } 
}

33. 搜索旋转排序数组

思路:二分法,将其划分为两份
中间元素把区间划分为两部分,这两部分一定有一部分是有序的
每次对有序那一部分进行操作

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

46. 全排列

思路:dfs + 回溯

class Solution {
    public List<List<Integer>> permute(int[] nums) {
        int len = nums.length;
        List<List<Integer>> res = new ArrayList<>();
        if(len == 0) return res;
        Deque<Integer> path = new LinkedList<>();
        boolean[] used = new boolean[len];
        dfs(nums, 0, len,  path, used, res);
        return res;
    }
    private void dfs(int[] nums, int level, int len,  Deque<Integer> path, boolean[] used,List<List<Integer>> res) {
        if(level == len) {
            res.add(new ArrayList<>(path));
            return;
        }
        for(int i=0; i<len; i++) {
            if(used[i]) {
                continue;
            }
            path.addFirst(nums[i]);
            used[i] = true;
            dfs(nums, level+1, len, path, used, res);
            path.removeFirst();
            used[i] = false;
        }
    }
}

354. 俄罗斯套娃信封问题

思路:信封能够嵌套的条件是信封的长和宽必须都比这个信封大,长和宽两个量不好控制,那我们转化为固定其中一个量,求另一个量,
如何固定?利用排序,将其中一个维度(长)升序,长相等则根据第二个维度降序,这样就只考虑第二个维度(宽)了
即求第二个维度的最长递增子序列
需要注意的是,第一个维度相等的情况下该怎么处理。

class Solution {
    public int maxEnvelopes(int[][] envelopes) {
        if(envelopes == null || envelopes.length == 0) {
            return 0;
        }
        Arrays.sort(envelopes, new Comparator<int[]>(){
            public int compare(int[] nums1, int[] nums2) {
                if(nums1[0] == nums2[0]){
                    return nums2[1] - nums1[1];
                } else {
                    return nums1[0] - nums2[0];
                }
            }
        });
        return getLongestSeq(envelopes);
    }
    private int getLongestSeq(int[][] envelopes) {
        int len = envelopes.length;
        int[] dp = new int[len];
        dp[0] = 1;
        for(int i=1; i<len; i++) {
            dp[i] = 1;
            for(int j=0; j<i; j++) {
                if(envelopes[i][1] > envelopes[j][1]) {
                    dp[i] = Math.max(dp[i], dp[j]+1);
                }
            }
        }
        int res = 0;
        for(int i=0; i<len; i++) {
            res = Math.max(dp[i], res);
        }
        return res;
    }
}

144. 二叉树的前序遍历

思路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 {
    List<Integer> res = new ArrayList<>();
    public List<Integer> preorderTraversal(TreeNode root) {
        preorderTraversal1(root);
        return res;
    }
    public void preorderTraversal1(TreeNode root) {
        if(root == null) {
            return;
        }
        res.add(root.val);
        preorderTraversal1(root.left);
        preorderTraversal1(root.right);
    }
}

思路2:非递归方式
使用栈

/**
 * 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> preorderTraversal(TreeNode root) { 
        List<Integer> res = new ArrayList<>();
        if(root == null) return res;
        Deque<TreeNode> stack = new LinkedList<>();
        stack.addFirst(root);
        while(!stack.isEmpty())  {
            TreeNode cur = stack.removeFirst();
            res.add(cur.val);
            if(cur.right != null) {
                stack.addFirst(cur.right);
            }
            if(cur.left != null) {
                stack.addFirst(cur.left);
            }
        }
        return res;
    }
}

5. 最长回文子串

思路:动态规划,
确定状态 dp[i][j] = 字符串s从i到j是是否为回文串
转移方程:dp[i][j] = (s[i] == s[j]) && dp[i+1][j-1]
边界和初始:dp[i][i] = true 记录最大长度及起始下标
顺序:j从1到len-1, i从0 到 j-1

注意:长度要把握准,i-j还是i-j+1,写的时候仔细思考

class Solution {
    public String longestPalindrome(String s) {
        int len = s.length();
        if(len < 2) {
            return s;
        }
        int maxLen = 1;
        int begin = 0;
        boolean[][] dp = new boolean[len][len];
        char[] cs = s.toCharArray();
        for(int i=0; i<len; i++) {
            dp[i][i] = true;
        }
        for(int j=1 ; j<len; j++) {
            for(int i=0; i<j; i++) {
                if(cs[i] != cs[j]) {
                    dp[i][j] = false;
                } else {
                    if(j - i < 3) {
                        dp[i][j] = true;
                    } else {
                        dp[i][j] = dp[i+1][j-1];
                    }
                }
                if(dp[i][j] && j - i + 1 > maxLen) {
                    maxLen = j - i + 1;
                    begin = i;
                }
            }
        }
        return s.substring(begin, begin + maxLen);
    }
}

思路2: 中心扩散法。
回文串,则前后相等
利用回文的性质,对每个点进行探测
然后,回文串分为奇数长度和偶数长度
你以一个中心点进行探测的时候,最后得到的是奇数长度,无法探测出baab这种串
那么在探测前就要判断是否连续两个元素相等,相等则加,再探测

class Solution {
    public String longestPalindrome(String s) {


        int len = s.length();
        if(len < 2) {
            return s;
        }
        char[] cs = s.toCharArray();
        int maxLen = 1, start = 0;
        for(int i=0; i<len; ) {
            int left = i, right = i;
            while(right < len - 1 && cs[right] == cs[right+1]) {
                right++;
            }
            i = right + 1;
            while(right < len-1 && left > 0 && cs[right+1] == cs[left-1]) {
                left --;
                right++;
            }
            if(right - left + 1 > maxLen) {
                maxLen = right - left + 1;
                start = left;
            }
        }
        return s.substring(start, start + maxLen);
    }
 }

剑指 Offer 22. 链表中倒数第k个节点

思路:两个指针,一个指针先走k步,然后两个指针同时走,先走的那个到达末尾,另一个则到达倒数第k个节点

/**
 * Definition for singly-linked list.
 * public class ListNode {
 *     int val;
 *     ListNode next;
 *     ListNode(int x) { val = x; }
 * }
 */
class Solution {
    public ListNode getKthFromEnd(ListNode head, int k) {
        ListNode fast = head, slow = head;
        while(k > 0) {
            fast = fast.next;
            k --;
        }
        while(fast != null) {
            fast = fast.next;
            slow = slow.next;
        }
        return slow;
    }
}

98. 验证二叉搜索树

思路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 {
    List<Integer> res = new ArrayList<>();
    public boolean isValidBST(TreeNode root) {
        inOrder(root);
        boolean r = true;
        for(int i=0; i<res.size()-1; i++) {
            if(res.get(i) >= res.get(i+1)) {
                r &= false;
            }
        }
        return r;
    }
    private void inOrder(TreeNode root) {
        if(root == null) {
            return;
        }
        inOrder(root.left);
        res.add(root.val);
        inOrder(root.right);
    }
}

思路2: 递归
比较左边做大的和右边最小的
注意边界值和数据类型,这里使用的是long类型

class Solution {
    public boolean isValidBST(TreeNode root) {
        return isValidBST(root, Long.MIN_VALUE, Long.MAX_VALUE);
    }
    public boolean isValidBST(TreeNode root, long min, long max) {
        if(root == null) {
            return true;
        }
        if(root.val <= min || root.val >= max) {
            return false;
        }
        return isValidBST(root.left, min, root.val) && isValidBST(root.right, root.val, max);
    }
}

40-50

56. 合并区间

https://mp.weixin.qq.com/s/ioUlNa4ZToCrun3qb4y4Ow 这篇文章,详解了区间类题目 ***

思路:先对各区间进行排序,然后遍历各区间,如果前一个的第二维度比后一个的第一维度大时,则合并区间
可以先将数组设为原数组大小,然后再用Arrays.copyOf取其一部分

class Solution {
    public int[][] merge(int[][] intervals) {
        if(intervals.length == 1) {
            return intervals;
        } 
        Arrays.sort(intervals, (v1, v2) -> (v1[0] - v2[0]));
        int[][] res = new int[intervals.length][2];
        int index = -1;
        for(int[] interval : intervals) {
            if(index == -1 || interval[0] > res[index][1]) {
                res[++index] = interval;
            } else {
                res[index][1] = Math.max(res[index][1], interval[1]);
            }
        }
        return Arrays.copyOf(res, index+1);
    }
}

148. 排序链表

思路1:递归的归并排序
先将链表切分为两半(快慢指针),然后归并这两部分

public ListNode sortList(ListNode head) {
        // 1、递归结束条件
        if (head == null || head.next == null) {
            return head;
        }


        // 2、找到链表中间节点并断开链表 & 递归下探
        ListNode midNode = middleNode(head);
        ListNode rightHead = midNode.next;
        midNode.next = null;


        ListNode left = sortList(head);
        ListNode right = sortList(rightHead);


        // 3、当前层业务操作(合并有序链表)
        return mergeTwoLists(left, right);
    }
    
    //  找到链表中间节点(876. 链表的中间结点)
    private ListNode middleNode(ListNode head) {
        if (head == null || head.next == null) {
            return head;
        }
        ListNode slow = head;
        ListNode fast = head.next.next;


        while (fast != null && fast.next != null) {
            slow = slow.next;
            fast = fast.next.next;
        }


        return slow;
    }


    // 合并两个有序链表(21. 合并两个有序链表)
    private ListNode mergeTwoLists(ListNode l1, ListNode l2) {
        ListNode sentry = new ListNode(-1);
        ListNode curr = sentry;


        while(l1 != null && l2 != null) {
            if(l1.val < l2.val) {
                curr.next = l1;
                l1 = l1.next;
            } else {
                curr.next = l2;
                l2 = l2.next;
            }


            curr = curr.next;
        }


        curr.next = l1 != null ? l1 : l2;
        return sentry.next;
    }
 }

151. 翻转字符串里的单词

思路:使用栈

class Solution {
    public String reverseWords(String s) {
        if(s.length() < 2) {
            return s;
        }
        s = s.trim();
        int left = 0, right = s.length() - 1;
        
        Deque<String> stack = new ArrayDeque<>();
        StringBuilder sb = new StringBuilder();


        while(left <= right) {
            if(sb.length() != 0 && s.charAt(left) == ' ') {
                stack.offerFirst(sb.toString());
                sb.setLength(0);
            } else if (s.charAt(left) != ' ') {
                sb.append(s.charAt(left));
            }
            left ++;
        }
        stack.offerFirst(sb.toString());
        return String.join(" ", stack);


    }
}

155. 最小栈

思路:使用两个栈进行操作
一个栈保存数据,另外一个栈保存当前最小值
两个栈每次都同时操作,保持同进同出

class MinStack {

    Stack<Integer> data;
    Stack<Integer> helper;


    /** initialize your data structure here. */
    public MinStack() {
        data = new Stack<>();
        helper = new Stack<>();


    }
    
    public void push(int x) {
        data.push(x);
        if(helper.isEmpty()|| helper.peek() >= x) {
            helper.push(x);
        } else {   
            helper.push(helper.peek());
        }
    }
    
    public void pop() {
        data.pop();
        helper.pop();
    }
    
    public int top() {
        if(!data.isEmpty()) {
            return data.peek();
        } else {
            System.out.println("stack is empty");
        }
        return -1;
    }
    
    public int getMin() {
        if(!helper.isEmpty()) {
            return helper.peek();
        } else {
            System.out.println("stack is empty");
        }
        return -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 res = Integer.MIN_VALUE;
    public int maxPathSum(TreeNode root) {
        depth(root);
        return res;
    }
    public int depth(TreeNode root) {
        if (root == null) {
            return 0;
        }
        int left = Math.max(depth(root.left), 0);
        int right = Math.max(depth(root.right) , 0);
        res = Math.max(left + right + root.val, res);
        return Math.max(left, right) + root.val;
    }
}

300. 最长递增子序列

思路:使用动态规划
它之前的元素与它进行比较,如果它大,则dp加一
转移方程如下:dp[j] = Math.max(dp[j], dp[i] + 1);

class Solution {
    public int lengthOfLIS(int[] nums) {
        int len = nums.length;
        if(nums == null || len ==0) {
            return 0;
        }
        if(len == 1) {
            return 1;
        }
        int[] dp = new int[len];
        dp[0] = 1;
        for(int j=1; j < len; j++) {
            dp[j] = 1;
            for(int i=0; i<j; i++) 
            {
                if(nums[i] < nums[j]) {
                    dp[j] = Math.max(dp[j], dp[i] + 1);
                }
            }
        }
        int res = 0;
        for(int i=0; i< len; i++) {
            res = Math.max(res, dp[i]);
        }
        return res;
    }
}

4. 寻找两个正序数组的中位数

思路:用两个指针遍历数组,找出中间的两个值
然后根据数组长度,判断总共有奇数个数还是偶数个数
奇数个数:则为正中间点,偶数:则为中间两个值相加除2

class Solution {
    public double findMedianSortedArrays(int[] nums1, int[] nums2) {
        int len1 = nums1.length;
        int len2 = nums2.length;

        int loc = (len1 + len2) / 2;
        int index = (len1 + len2) % 2;
        int num = 0;
        int per = 0;

        int a = 0;
        int b = 0;

        while(a < len1 || b <len2){
                int temp1 = a < len1 ? nums1[a] : Integer.MAX_VALUE;
                int temp2 = b < len2 ? nums2[b] : Integer.MAX_VALUE;
                if(temp1 < temp2) {
                    a++;
                    if(a + b == loc) {
                        per = temp1;
                    }
                    if(a + b == loc+1) {
                        num = temp1;
                    }
                }else {
                    b++;
                    if(a + b == loc) {
                        per = temp2;
                     }
                     if(a + b == loc+1) {
                        num = temp2;
                     }
                }
            }
        if(index == 1) {
            return num;
        } else {
            return (per + num) / 2.0;
        }
    }
}

1143. 最长公共子序列

思路:使用动态规划

class Solution {
    public int longestCommonSubsequence(String text1, String text2) {
        int len1 = text1.length();
        int len2 = text2.length();


        int[][] dp = new int[len1+1][len2+1];
        for(int i=1; i<=len1; i++) {
            for(int j=1; j<=len2; j++) {
                if(text1.charAt(i-1) == text2.charAt(j-1)) {
                    dp[i][j] = dp[i-1][j-1] + 1;
                } else {
                    dp[i][j] = Math.max(dp[i-1][j], dp[i][j-1]);
                }
            }
        }
        return dp[len1][len2];
    }
}

169. 多数元素

思路:摩尔投票法
我们维护一个候选众数 candidate 和它出现的次数 count。初始时 candidate 可以为任意值,count 为 0;
我们遍历数组 nums 中的所有元素,对于每个元素 x,在判断 x 之前,如果 count 的值为 0,我们先将 x 的值赋予 candidate,随后我们判断 x:
如果 x 与 candidate 相等,那么计数器 count 的值增加 1;
如果 x 与 candidate 不等,那么计数器 count 的值减少 1。
在遍历完成后,candidate 即为整个数组的众数。

class Solution {
    public int majorityElement(int[] nums) {
        int count = 0;
        Integer candidate = null;

        for (int num : nums) {
            if (count == 0) {
                candidate = num;
            }
            count += (num == candidate) ? 1 : -1;
        }
        return candidate;
    }
}

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

思路:使用二分查找
首先用正常的思路,找出一个与目标值相等的mid,然后mid依次往前遍历和依次往后遍历,得到第一个位置和最后一个位置

class Solution {
    public int[] searchRange(int[] nums, int target) {
        if(nums == null || nums.length == 0) {
            return new int[]{-1,-1};
        }
        int len = nums.length;
        int left = 0, right = len-1;
        int start = 0, end = 0;
        int mid = 0;
        while(left <= right) 
        {
            mid = left + (right - left) / 2;
            if (nums[mid] == target) {
                break;
            } else if(nums[mid] > target) {
                right = mid - 1;
            } else {
                left = mid + 1;
            }
        }
        if(nums[mid] == target) {
            start = mid;
            end = mid;
            while(start > 0 && nums[start-1] == target) {
                start--;
            }
            while(end < len-1 && nums[end+1] == target) {
                end ++;
            }
            return new int[]{start,end};
        }
        return new int[]{-1,-1};
    }
}

60-70

958. 二叉树的完全性检验

思路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 {
    public boolean isCompleteTree(TreeNode root) {
        Queue<TreeNode> queue = new LinkedList<>();
        queue.offer(root);
        boolean isEmptyNode = false;
        while(!queue.isEmpty()) {
            TreeNode cur = queue.poll();
            if(isEmptyNode && cur != null) {
                return false;
            }
            if(cur == null) {
                isEmptyNode = true;
                continue;
            }
            queue.offer(cur.left);
            queue.offer(cur.right);
        }
        return true;
    }
}

思路2: 对二叉树进行标号, 设root为n,则它的左子结点为2n,它的右子结点为2n + 1;
如果是完全二叉树,这标号是严格无间隔升序的,如1,2,3,4,5。。。
所以,只用比较最后一个结点的标号和二叉树结点个数是否相等即可,如果相等,则为完全二叉树。
即:树的size 是否 等于 最后一个结点的indexCode

class Solution {
    // 树的大小
    int treeSize = 0;
    //最好一个结点的下标,即最大下标
    int maxCode = 0;
    public boolean isCompleteTree(TreeNode root) {
        if(root == null) {
            return true;
        }
        completeTree(root, 1);
        return treeSize == maxCode;
    }

    private void completeTree(TreeNode root, int index) {
        if(root == null) {
            return;
        }
        treeSize ++;
        maxCode = Math.max(maxCode, index);
        completeTree(root.left, 2*index);
        completeTree(root.right, 2*index + 1);
    }
}

93. 复原 IP 地址

思路:使用DFS,设置一个K存储是第几部分,用StringBuilder存储临时结果,使用回溯的方式找到所有可能的结果;
注意递归的出口 以及 特殊值0 的处理

class Solution { 
    public List<String> restoreIpAddresses(String s) {
        List<String> results = new ArrayList<>();   
        if( s== null || s.length() < 4 || s.length()>12) { return results; }   
        StringBuilder tmpAddress = new StringBuilder();
        doRestore(0,results, tmpAddress, s);
        return results;
    }
    private void doRestore(int k, List<String> results ,StringBuilder tmpAddress, String s) {
        if(k == 4 || s.length() == 0) {
            if(k==4 && s.length() == 0) {
                results.add(tmpAddress.toString());
            }
            return;
        }
        for (int i=0; i < s.length() && i <= 2; i++) {
            if(i != 0 && s.charAt(0) == '0') {
                break;
            }
            String part = s.substring(0, i+1);
            if(Integer.valueOf(part) <= 255) {
                if(tmpAddress.length() != 0) {
                    part = "." + part;
                }
                tmpAddress.append(part);
                doRestore(k+1, results, tmpAddress, s.substring(i+1));
                tmpAddress.delete(tmpAddress.length() - part.length(), tmpAddress.length());
            } 
        }
    }
}

64. 最小路径和

思路:二维动态规划

class Solution {
    public int minPathSum(int[][] grid) {
        if(grid == null || grid.length == 0 || grid[0].length == 0) {
            return 0;
        }
        int n = grid.length, m = grid[0].length;
        int[][] dp = new int[n][m];
        dp[0][0] = grid[0][0];
        for(int i=1; i<n; i++) {
            dp[i][0] = dp[i-1][0] + grid[i][0];
        }
        for(int j=1; j<m; j++) {
            dp[0][j] = dp[0][j-1] + grid[0][j];
        }
        for(int i=1; i<n; i++) {
            for(int j=1; j<m; j++) {
               dp[i][j] = Math.min(dp[i-1][j] , dp[i][j-1]) + grid[i][j];
            }
        }
        return dp[n-1][m-1];
    }
}

240. 搜索二维矩阵 II

思路1:缩小区间法

class Solution {
    public boolean searchMatrix(int[][] matrix, int target) {
        int n = matrix.length;
        int m = matrix[0].length;
        int i=0, j = m-1;
        while(i < n && j>=0) {
            if(matrix[i][j] == target) {
                return true;
            } else if(matrix[i][j] < target) {
                i++;
            } else {
                j--;
            }
        }
        return false;
    }
}

78. 子集

思路:回溯法
深入理解回溯:https://leetcode-cn.com/problems/subsets/solution/xiang-xi-jie-shao-di-gui-hui-su-de-tao-lu-by-reedf/

class Solution {
    public List<List<Integer>> subsets(int[] nums) {
        List<List<Integer>> results = new ArrayList<>();
        // res.add(new ArrayList<>());
        int len = nums.length;
        for(int i=0; i<=len; i++){
            dfs(i, 0,  nums, new ArrayList<>(), results);
        }
        return results;
    }

    private void dfs(int size, int start,  int[] nums, List<Integer> res, List<List<Integer>> results) {
        if(res.size() == size) {
            results.add(new ArrayList<>(res));
            return;
        }
        for(int i=start; i<nums.length; i++) {
            res.add(nums[i]);
            dfs(size, i+1, nums, res, results);
            res.remove(res.size()-1);
        }
    }
}
  1. 组合总和
    思路:回溯,
    注意:重复组合怎么产生的,由于每次都是从0开始,所以可以引入一个start,每次从start开始
  2. 组合
    思路:回溯
    回溯三部曲:1:参数 2:出口 3: 调用 4:回退
  3. 子集 II
    思路:回溯
    注意:重复元素的处理,如果与前一个元素相等,而且前一个元素未执行过,则contineu;
    用一个visited数组记录状态,并且要是相同元素相邻,需要先对数组排序。
  4. 组合总和 II
    思路:回溯

143. 重排链表

思路1:使用栈实现,先将链表存入栈中,然后依次弹出栈,插入链表中,当链表节点与栈中节点相等,则停止插入

/**
 * 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 void reorderList(ListNode head) {
        if(head == null || head.next == null) {
            return;
        }
        Deque<ListNode> stack = new ArrayDeque<>();
        ListNode dummy = head;
        while(dummy != null) {
            stack.push(dummy);
            dummy = dummy.next;
        }
        ListNode stackCur = new ListNode(-1);
        ListNode cur = head;
        while(cur.next != stackCur) {
            stackCur = stack.pop();
            stackCur.next = cur.next;
            cur.next = stackCur;
            cur = cur.next.next;
        }
        cur.next = null;
    }
}

思路2: 1、找中点断开 2、后面一部分逆序 3、从新组合两部分 ***

// 找中点+反转后半部分+合并前后两部分
public void reorderList(ListNode head) {
    if(head==null || head.next==null || head.next.next==null)return;
    
    // 1. 找中点,让slow指向中点,或左中点位置
    ListNode slow = head, fast = head.next;
    while (fast!=null && fast.next != null) {
        slow = slow.next;
        fast = fast.next.next;
    }

    // 2. 断开中点,反转后半部分
    ListNode head2 = null, next = slow.next;
    slow.next = null;
    slow = next;
    while(slow != null) {
        next = slow.next;
        slow.next = head2;
        head2 = slow;
        slow = next;
    }

    // 3. 合并链表head和head2
    ListNode curr = head;
    ListNode curr2 = head2;
    while(curr != null && curr2!=null) {
        next = curr.next;
        curr.next = curr2;
        curr2 = curr2.next;
        curr.next.next = next;
        curr = next;
    }
}

//将各部分都抽出函数
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 slow = head;
        ListNode fast = head;
        while (fast.next != null && fast.next.next != null) {
            slow = slow.next;
            fast = fast.next.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 l1_tmp;
        ListNode l2_tmp;
        while (l1 != null && l2 != null) {
            l1_tmp = l1.next;
            l2_tmp = l2.next;

            l1.next = l2;
            l1 = l1_tmp;

            l2.next = l1;
            l2 = l2_tmp;
        }
    }
}

239. 滑动窗口最大值

方法一:优先队列
初始时,我们将数组nums 的前 k 个元素放入优先队列中。每当我们向右移动窗口时,我们就可以把一个新的元素放入优先队列中,此时堆顶的元素就是堆中所有元素的最大值。然而这个最大值可能并不在滑动窗口中,在这种情况下,这个值在数组 nums 中的位置出现在滑动窗口左边界的左侧。因此,当我们后续继续向右移动窗口时,这个值就永远不可能出现在滑动窗口中了,我们可以将其永久地从优先队列中移除。

我们不断地移除堆顶的元素,直到其确实出现在滑动窗口中。此时,堆顶元素就是滑动窗口中的最大值。为了方便判断堆顶元素与滑动窗口的位置关系,我们可以在优先队列中存储二元组 (num,index),表示元素 num 在数组中的下标为 index。

class Solution {
    public int[] maxSlidingWindow(int[] nums, int k) {
        int n = nums.length;
        PriorityQueue<int[]> pq = new PriorityQueue<int[]>(new Comparator<int[]>() {
            public int compare(int[] pair1, int[] pair2) {
                return pair1[0] != pair2[0] ? pair2[0] - pair1[0] : pair2[1] - pair1[1];
            }
        });
        for (int i = 0; i < k; ++i) {
            pq.offer(new int[]{nums[i], i});
        }
        int[] ans = new int[n - k + 1];
        ans[0] = pq.peek()[0];
        for (int i = k; i < n; ++i) {
            pq.offer(new int[]{nums[i], i});
            while (pq.peek()[1] <= i - k) {
                pq.poll();
            }
            ans[i - k + 1] = pq.peek()[0];
        }
        return ans;
    }
}

思路2:
双向队列解决滑动窗口最大值

遍历数组,将 数 存放在双向队列中,并用 L,R 来标记窗口的左边界和右边界。队列中保存的并不是真的 数,而是该数值对应的数组下标位置,并且数组中的数要从大到小排序。如果当前遍历的数比队尾的值大,则需要弹出队尾值,直到队列重新满足从大到小的要求。刚开始遍历时,L 和 R 都为 0,有一个形成窗口的过程,此过程没有最大值,L 不动,R 向右移。当窗口大小形成时,L 和 R 一起向右移,每次移动时,判断队首的值的数组下标是否在 [L,R] 中,如果不在则需要弹出队首的值,当前窗口的最大值即为队首的数

输入: nums = [1,3,-1,-3,5,3,6,7], 和 k = 3
输出: [3,3,5,5,6,7]

解释过程中队列中都是具体的值,方便理解,具体见代码。
初始状态:L=R=0,队列:{}
i=0,nums[0]=1。队列为空,直接加入。队列:{1}
i=1,nums[1]=3。队尾值为13>1,弹出队尾值,加入3。队列:{3}
i=2,nums[2]=-1。队尾值为3-1<3,直接加入。队列:{3,-1}。此时窗口已经形成,L=0,R=2,result=[3]
i=3,nums[3]=-3。队尾值为-1-3<-1,直接加入。队列:{3,-1,-3}。队首3对应的下标为1,L=1,R=3,有效。result=[3,3]
i=4,nums[4]=5。队尾值为-35>-3,依次弹出后加入。队列:{5}。此时L=2,R=4,有效。result=[3,3,5]
i=5,nums[5]=3。队尾值为53<5,直接加入。队列:{5,3}。此时L=3,R=5,有效。result=[3,3,5,5]
i=6,nums[6]=6。队尾值为36>3,依次弹出后加入。队列:{6}。此时L=4,R=6,有效。result=[3,3,5,5,6]
i=7,nums[7]=7。队尾值为67>6,弹出队尾值后加入。队列:{7}。此时L=5,R=7,有效。result=[3,3,5,5,6,7]

class Solution {
    public int[] maxSlidingWindow(int[] nums, int k) {
        if(nums == null || nums.length < 2) return nums;
        // 双向队列 保存当前窗口最大值的数组位置 保证队列中数组位置的数值按从大到小排序
        LinkedList<Integer> queue = new LinkedList();
        // 结果数组
        int[] result = new int[nums.length-k+1];
        // 遍历nums数组
        for(int i = 0;i < nums.length;i++){
            // 保证从大到小 如果前面数小则需要依次弹出,直至满足要求
            while(!queue.isEmpty() && nums[queue.peekLast()] <= nums[i]){
                queue.pollLast();
            }
            // 添加当前值对应的数组下标
            queue.addLast(i);
            // 判断当前队列中队首的值是否有效
            if(queue.peek() <= i-k){
                queue.poll();   
            } 
            // 当窗口长度为k时 保存当前窗口中最大值
            if(i+1 >= k){
                result[i+1-k] = nums[queue.peek()];
            }
        }
        return result;
    }
}

200. 岛屿数量

import java.util.LinkedList;
import java.util.Queue;


public class Solution {


    private final static int[][] DIRECTIONS = {{-1, 0}, {0, -1}, {1, 0}, {0, 1}};
    private int rows;
    private int cols;
    private char[][] grid;
    private boolean[][] visited;


    public int numIslands(char[][] grid) {
        rows = grid.length;
        if (rows == 0) {
            return 0;
        }
        cols = grid[0].length;
        this.grid = grid;
        visited = new boolean[rows][cols];


        int count = 0;
        for (int i = 0; i < rows; i++) {
            for (int j = 0; j < cols; j++) {
                if (!visited[i][j] && grid[i][j] == '1') {
                    bfs(i, j);
                    count++;
                }
            }
        }
        return count;
    }
    private void bfs(int i, int j) {
        Queue<Integer> queue = new LinkedList<>();
        queue.offer(i * cols + j);
        // 注意:这里要标记上已经访问过
        visited[i][j] = true;
        while (!queue.isEmpty()) {
            int cur = queue.poll();
            int curX = cur / cols;
            int curY = cur % cols;
            for (int k = 0; k < 4; k++) {
                int newX = curX + DIRECTIONS[k][0];
                int newY = curY + DIRECTIONS[k][1];
                if (inArea(newX, newY) && grid[newX][newY] == '1' && !visited[newX][newY]) {
                    queue.offer(newX * cols + newY);
                    // 特别注意:在放入队列以后,要马上标记成已经访问过,语义也是十分清楚的:反正只要进入了队列,迟早都会遍历到它
                    // 而不是在出队列的时候再标记,如果是出队列的时候再标记,会造成很多重复的结点进入队列,造成重复的操作,这句话如果你没有写对地方,代码会严重超时的
                    visited[newX][newY] = true;
                }
            }
        }
    }


    private boolean inArea(int x, int y) {
        return x >= 0 && x < rows && y >= 0 && y < cols;
    }
}


912. 排序数组

思路:快速排序
选择一个pivot,将数组分为两部分,左边的都比它小,右边的都比它大

class Solution {
    public int[] sortArray(int[] nums) {
        randomSortArray(nums, 0, nums.length-1);
        return nums;
    }


    private  static void randomSortArray(int[] nums, int start, int end) {
        if(nums == null || start >= end) {
            return;
        }
        int i = start, j = end;
        int pivotkey = nums[start];
        while (i < j) {
            while(i < j && nums[j] >= pivotkey) j--;
            if(i < j) nums[i++] = nums[j];
            while(i<j && nums[i] <= pivotkey) i++;
            if(i<j) nums[j--] = nums[i];
        }
        nums[i] = pivotkey;
        randomSortArray(nums, start,i-1);
        randomSortArray(nums, i+1, end);
    }
}

718. 最长重复子数组

思路1:动态规划
用dp[i][j]表示数组A以第i个数结尾和数组B的以第j个数结尾的最长重复子数组

class Solution {
    public int findLength(int[] A, int[] B) {
        int n = A.length, m = B.length;
        int[][] dp = new int[n+1][m+1];
        int res = 0;
        for(int i=1; i<=n; i++) {
            for(int j=1; j<= m; j++) {
                if(A[i-1] == B[j-1]) {
                    dp[i][j] = dp[i-1][j-1] + 1;
                    res = Math.max(dp[i][j], res);
                }
            }
        }
        return res;
    }
}

思路2:滑动窗口
枚举 A 和 B 所有的对齐方式。对齐的方式有两类:第一类为 A 不变,B 的首元素与 A 中的某个元素对齐;第二类为 B 不变,A 的首元素与 B 中的某个元素对齐。对于每一种对齐方式,我们计算它们相对位置相同的重复子数组即可。

class Solution {
    public int findLength(int[] A, int[] B) {
        int n = A.length, m = B.length;
        int ret = 0;
        for (int i = 0; i < n; i++) {
            int len = Math.min(m, n - i);
            int maxlen = maxLength(A, B, i, 0, len);
            ret = Math.max(ret, maxlen);
        }
        for (int i = 0; i < m; i++) {
            int len = Math.min(n, m - i);
            int maxlen = maxLength(A, B, 0, i, len);
            ret = Math.max(ret, maxlen);
        }
        return ret;
    }


    public int maxLength(int[] A, int[] B, int addA, int addB, int len) {
        int ret = 0, k = 0;
        for (int i = 0; i < len; i++) {
            if (A[addA + i] == B[addB + i]) {
                k++;
            } else {
                k = 0;
            }
            ret = Math.max(ret, k);
        }
        return ret;
    }
}

70-80

232. 用栈实现队列

class MyQueue {
    Stack<Integer> stack1;
    Stack<Integer> stack2;


    /** Initialize your data structure here. */
    public MyQueue() {
        stack1 = new Stack<>();
        stack2 = new Stack<>();


    }
    
    /** Push element x to the back of queue. */
    public void push(int x) {
        stack1.push(x);


    }
    
    /** Removes the element from in front of queue and returns that element. */
    public int pop() {
        if (stack2.isEmpty()) {
            while(!stack1.isEmpty()) {
                stack2.push(stack1.pop());
            }
        }
        return stack2.pop();
    }
    
    /** Get the front element. */
    public int peek() {
        if (stack2.isEmpty()) {
            while(!stack1.isEmpty()) {
                stack2.push(stack1.pop());
            }
        }
        return stack2.peek();
    }
    /** Returns whether the queue is empty. */
    public boolean empty() {
        return stack2.isEmpty() && stack1.isEmpty();
    }
}

面试题:给一个字符类型的数组chas和一个整数size,请把大小为size的左半区整体右移到右半区,右半区整体移动到左边。

import java.util.*;

public class Main {
    //NC翻转字符串(2)
    public static String reverse(String str,int start,int end){
        char[] ch = str.toCharArray();
        while (start < end){    
            char tmp = ch[start];
            ch[start] = ch[end];
            ch[end] = tmp;
            end--;start++;
        }
        return String.valueOf(ch);
    }

    public static String reverse2(String str,int k){
       if(str == null ||str.length() == 0 || k<=0 ){
           return null;
       }
      str = reverse(str,0,k-1);
      str = reverse(str,k,str.length()-1);
      str = reverse(str,0,str.length()-1);
      return str;
    }

    public static void main(String[] args) {
        Scanner scanner = new Scanner(System.in);
        while (scanner.hasNext()){
        int k = scanner.nextInt();
        String str = scanner.next();
        String ret = reverse2(str,k);
        System.out.println(ret);
    }
}

470. 用 Rand7() 实现 Rand10()

思路:rand7() 到 rand10(),只用将那只要我们把小的数映射到一个大的数,且等概率。
(rand7() - 1) * 7 + rand7()
首先:rand7() - 1得到数到集合为(0,1,2,3,4,5,6)
然后; * 7得到:(0,7,14,21,28,35,42)
最后:+ rand7():(1,2,3,4,5,6,7)

/**
 * The rand7() API is already defined in the parent class SolBase.
 * public int rand7();
 * @return a random integer in the range 1 to 7
 */
class Solution extends SolBase {
    public int rand10() {
        int num = (rand7() -1) * 7 + rand7();
        while(num > 40) {
            num = (rand7() -1) * 7 + rand7();
        }
        return num % 10 + 1;
    }
}

rand5得到rand7

int Rand5()
{
    int m = rand() % 5 + 1;
    return m;
}

int Rand7()
{
    int x = 22;
    while (x > 21)
        x = Rand5() + (Rand5() - 1) * 5;
    return x % 7 + 1;
}

剑指 Offer 15. 二进制中1的个数

思路1:如果一个有符号数,右移操作时,用符号位填补最左边的数字。
本题问1的个数,考虑只有1&1 = 1,所以可以将每一位与1做与操作,得到结果为1则表示该位为1。
这里需考虑是将整数右移还是将1左移:整数右移需要考虑为负数的情况,如何判断循环结束,可能造成死循环,而将1左移可以避免这个问题。

public class Solution {
    // you need to treat n as an unsigned value
    public int hammingWeight(int n) {
        int flag = 1;
        int count = 0;
        for(int i=0; i<32; i++) {
            if((flag & n) != 0) {
                count ++;
            }
            flag = flag << 1;
        }
        return count;
    }
}

思路2:

  1. 核心思路:n & (n - 1) 会把n中的最后一个1变成0
  2. 循环中每次去除n的最后一个1,res记录循环次数,就是1的个数
public class Solution {
    // you need to treat n as an unsigned value
    public int hammingWeight(int n) {
        int res = 0;
        while(n != 0){
            n &= n - 1;
            res++;
        }
        return res;
    }
}

468. 验证IP地址

思路1:对字符串进行切分

class Solution {
    public String validIPAddress(String IP) {
        String[] IP4Arr = IP.split("\\.",-1);
        if(IP4Arr.length == 4){
            return isIP4Arr(IP4Arr);
        }
        String[] IP6Arr = IP.split(":",-1);
        if(IP6Arr.length == 8){
            return isIP6Arr(IP6Arr);
        }
        return "Neither";
    }


    public String isIP4Arr(String[] IP4Arr){
        for(String ip : IP4Arr){
            if(ip.length() > 3 || ip.length() <= 0){
                return "Neither";
            }
            for(int i = 0 ;i < ip.length();++i){
                if(!Character.isDigit(ip.charAt(i))){
                    return "Neither";
                }
            }
            int num = Integer.parseInt(ip);
            if(num > 255 || String.valueOf(num).length() != ip.length()){
                return "Neither";
            }
        }
        return "IPv4";
    }


     public String isIP6Arr(String[] IP6Arr){
        for(String ip : IP6Arr){
            if(ip.length() > 4 || ip.length() <= 0){
                return "Neither";
            }
            for(int i = 0 ;i < ip.length();++i){
                char c = ip.charAt(i);
                if(!Character.isDigit(c) && !( 'a' <= c && c <= 'f') && !('A' <= c && c <= 'F')){
                    return "Neither";
                }
            }
        }
        return "IPv6";
    }
}

72. 编辑距离

思路:动态规划 word1 和 word2
int[][] dp = new int[n+1][m+1]

if word1.charAt(i) == word2.charAt(j) : dp[i][j] = dp[i-1][j-1];
else : dp[i][j] = min(dp[i-1][j], dp[i][j-1], dp[i-1][j-1]) + 1;

class Solution {
    public int minDistance(String word1, String word2) {
        int n = word1.length();
        int m = word2.length();

        if(n == 0) {
            return m;
        }
        if(m == 0) {
            return n;
        }

        int[][] dp = new int[n+1][m+1];


        for(int i=1; i<=n; i++) {
            dp[i][0] = i;
        }
        for(int j=1; j<=m; j++) {
            dp[0][j] = j;
        }

        for(int i=1; i<=n; i++) {
            for(int j=1; j<=m; j++) {
                if(word1.charAt(i-1) == word2.charAt(j-1)) {
                    dp[i][j] = dp[i-1][j-1];
                } else {
                    dp[i][j] = Math.min(dp[i-1][j], dp[i][j-1]);
                    dp[i][j] = Math.min(dp[i][j], dp[i-1][j-1]) + 1;
                }
            }
        }
        return dp[n][m];
    }
}

36进制运算

**
 * 36进制由0-9,a-z,共36个字符表示,最小为'0'
 * '0''9'对应十进制的09'a''z'对应十进制的1035
 * 例如:'1b' 换算成10进制等于 1 * 36^1 + 11 * 36^0 = 36 + 11 = 47
 * 要求按照加法规则计算出任意两个36进制正整数的和
 * 如:按照加法规则,计算'1b' + '2x' = '48'
 *
 * 要求:不允许把36进制数字整体转为10进制数字,计算出10进制数字的相加结果再转回为36进制
 *
 * @param args
 */

public static void main(String [] args) {

	String result = addFunWithStr("1b", "2x");

	System.out.println("result = " + result);


}

/**
 * 获取值
 * @param ch
 * @return
 */
public static int getIntFromChar(char ch) {

	int ret = -1;

	if (ch >='0' && ch <= '9') {
		ret = ch - '0';
	} else if (ch >= 'a' && ch <= 'z') {

		ret = (ch - 'a') + 10;
	}

	return ret;

}

public static String addFunWithStr(String param1, String param2) {

	StringBuffer stringBuffer = new StringBuffer();
	String symbol = "0123456789abcdefghijklmnopqrstuvwxyz";
	int param1Len = param1.length();
	int param2Len = param2.length();

	int i = param1Len - 1;
	int j = param2Len - 1;

	if (i < 0 || j < 0) {
		return null;
	}
	

	int temp = 0;
	while (i >= 0 && j >= 0) {

		char ch_1 = param1.charAt(i);
		char ch_2 = param2.charAt(j);

		int v1 = getIntFromChar(ch_1);
		int v2 = getIntFromChar(ch_2);

		int ret = v1 + v2;

		if (ret >= 36) {
			int index = ret - 36 + temp;
			char sv = symbol.charAt(index);
			stringBuffer.append(sv);
			temp = 1; //进位
		} else {
			int index = ret + temp;
			char sv = symbol.charAt(index);
			stringBuffer.append(sv);
			temp = 0;
		}

		i--;
		j--;

	}

	while (i >= 0) {

		char ch_1 = param1.charAt(i);
		stringBuffer.append(ch_1);

		i--;
	}

	while (j >= 0) {
		char ch_2 = param2.charAt(i);
		stringBuffer.append(ch_2);
		j--;
	}

	StringBuffer result = stringBuffer.reverse();

	return result.toString();

}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值