算法刷题整理

一.字符串处理

1. 空格替换 力扣

class Solution {
// 简单方法:replaceAll()
    public String replaceSpace1(String s) {
        if(s == null || s.length() == 0){
            return s;
        }
        return s.replaceAll(" ", "%20");
    }
// 常规方法:StringBuilder
    public String replaceSpace2(String s) {
        if(s == null || s.length() == 0){
            return s;
        }
        StringBuilder sb = new StringBuilder();
        for(int i = 0; i<s.length(); i++){
            if(s.charAt(i) == ' '){
                sb.append("%20");
            }else{
                sb.append(s.charAt(i));
            }
        }
        return sb.toString();
    }
}

2.左旋转字符串 力扣

class Solution {
    // 简单方法,额外内存
    public String reverseLeftWords(String s, int n) {
        if(s == null || s.length() <= n){
            return s;
        }
        String s1 = s.substring(0, n);
        String s2 = s.substring(n, s.length());
        return s2+s1;
    }

    // 不使用额外内存方法 按n切开,各自翻转字符串,最后整体翻转一次
    public String reverseLeftWords(String s, int n) {
        if (s == null || s.length() == 0) {
            return s;
        }
        char[] ss = s.toCharArray();
        reserve(ss, 0, n-1);
        reserve(ss, n, ss.length-1);
        reserve(ss, 0, ss.length-1);
        return new String(ss);
    }
    public void reserve(char[] s, int left, int right) {
        while (left <= right) {
            char temp = s[left];
            s[left] = s[right];
            s[right] = temp;
            left++;
            right--;
        }
    }
}

3.表示数值的字符串 力扣

通过有限状态机处理
class Solution {
    public boolean isNumber(String s) {
            if(s == null || s.length() == 0){
                return false;
            }
            Map[] stage = {
                    new HashMap(){{put('k', 0); put('d',2); put('s', 1); put('t',3);}},  // 0
                    new HashMap(){{put('d', 2); put('t',3);}}, // 1
                    new HashMap(){{put('d', 2);put('t',9);put('e',5);put('k', 8);}}, // 2
                    new HashMap(){{put('d', 4);}}, // 3
                    new HashMap(){{put('d', 4);put('e',5);put('k', 8);}}, // 4
                    new HashMap(){{put('s', 6);put('d',7);}}, // 5
                    new HashMap(){{put('d', 7);}}, // 6
                    new HashMap(){{put('d', 7); put('k', 8);}}, // 7
                    new HashMap(){{put('k', 8);}}, // 8
                    new HashMap(){{put('k', 8);put('d', 4);put('e', 5);}} // 9
            };
            int p = 0;
            for(int i=0; i<s.length(); i++){
                if(!stage[p].containsKey(getChar(s.charAt(i)))){
                    return false;
                }
                p = (Integer)stage[p].get(getChar(s.charAt(i)));
            }
            if(p == 2||p==4||p==7||p==8||p==9){
                return true;
            }
            return false;
    }

    private Character getChar(char c) {
        if(c>='0'&&c<='9'){
            return 'd';
        }
        if(c == '.'){
            return 't';
        }
        if(c == ' '){
            return 'k';
        }
        if(c == 'e' || c == 'E'){
            return 'e';
        }
        if(c == '+' || c == '-'){
            return 's';
        }
        return null;
    }
}

4. 把字符串转换成整数力扣

Java 中的整数范围
整数
最小值-2147483648
最大值2147483647
public int strToInt(String str) {
        if (str == null || str.length() == 0) {
            return 0;
        }
        int i = 0;
        int count = 0;
        int flag = 1;
        while (i < str.length() && str.charAt(i) == ' ') {
            i++;
        }
        if (i < str.length() && (str.charAt(i) == '+' || str.charAt(i) == '-')) {
            flag = str.charAt(i) == '+' ? 1 : -1;
            i++;
        }
        while (i < str.length()) {
            int tmp = str.charAt(i) - '0';
            if (tmp < 0 || tmp > 9) {
                return count*flag;
            }
            if (count < Integer.MAX_VALUE / 10 || (count == Integer.MAX_VALUE / 10 && tmp <= 7)) {
                count = count * 10 + tmp;
            }else{
                return flag>0?Integer.MAX_VALUE:Integer.MIN_VALUE;
            }
            i++;
        }
        return count*flag;
    }

 反转单词顺序 力扣

// split分割
public String reverseWords(String s) {
        if(s==null ||s.length() == 0){
            return s;
        }
        String[] words = s.split(" ");
        StringBuffer sb = new StringBuffer();
        for(int i = words.length-1; i>=0; i--){
            if(!words[i].equals("")){
                sb.append(words[i]);
                sb.append(" ");
            }
        }
        String result = sb.toString();

        // 注意判断长度
        return result.length()>0?result.substring(0, result.length()-1):"";
    }

// 双指针反转,不去除空格,每个单词反转,再整体反转
public String reverseWords(String s) {
        if (s == null || s.length() == 0) {
            return s;
        }
        char[] chars = s.toCharArray();
        int i = 0;
        int j = 0;
        while (i < chars.length) {
            while (i < chars.length && chars[i] == ' ') {
                i++;
                j++;
            }
            while (j < chars.length && chars[j] != ' ') {
                j++;
            }
            swap(chars, i, j-1);
            i=j;
        }
        swap(chars, 0, chars.length-1);
        return String.valueOf(chars);
    }

第一个只出现一次的字符(LinkedHashMap/普通hash遍历两次)

// 普通哈希
class Solution {
    public char firstUniqChar(String s) {
        HashMap<Character, Boolean> dic = new HashMap<>();
        char[] sc = s.toCharArray();
        for(char c : sc)
            dic.put(c, !dic.containsKey(c));
        for(char c : sc)
            if(dic.get(c)) return c;
        return ' ';
    }
}

// 有序哈希LinedHashMap
class Solution {
    public char firstUniqChar(String s) {
        Map<Character, Boolean> dic = new LinkedHashMap<>();
        char[] sc = s.toCharArray();
        for(char c : sc)
            dic.put(c, !dic.containsKey(c));
        for(Map.Entry<Character, Boolean> d : dic.entrySet()){
           if(d.getValue()) return d.getKey();
        }
        return ' ';
    }
}

剑指 Offer 38. 字符串的排列

    Set<String> result = new HashSet<>();
    public String[] permutation(String s) {
        if (s == null || s.length() == 0) {
            return new String[0];
        }
        permutation(s.toCharArray(), 0);
        return (String[]) new ArrayList(result).toArray(new String[result.size()]);
    }

    // 每个位置的字符和后面的字符替换
    public void permutation(char[] s, int index) {
        if (index == s.length - 1) {
            result.add(String.valueOf(s));
            return;
        }
        // set用于剪枝
        HashSet<Character> set = new HashSet<>();
        for (int i = index; i < s.length; i++) {
            if (!set.contains(chars[i])) {
                set.add(chars[i]);
                swap(s, i, index);
                permutation(s, index + 1);
                swap(s, i, index);
            }
        }
    }

    public void swap(char[] s, int i, int j) {
        char tmp = s[i];
        s[i] = s[j];
        s[j] = tmp;
    }

12. 整数转罗马数字

public String intToRoman(int num) {
        // I 1
        // IV 4
        // IX 9
        // X 10
        // XL 40
        // L 50
        // XC 90
        // C 100
        // CD 400
        // D 500
        // CM 900
        // M 1000

        if(num<=0){
            return "";
        }
        StringBuilder sb = new StringBuilder();
        int[] nums = new int[]{1000,900,500,400,100,90,50,40,10,9,5,4,1};
        String[] strs = new String[]{"M", "CM", "D", "CD", "C", "XC", "L", "XL", "X", "IX", "V", "IV", "I"};
        for(int i =0; i<nums.length;i++){
            while(num-nums[i]>=0){
                num = num-nums[i];
                sb.append(strs[i]);
            }
            if(num == 0){
                break;
            }
        }
        return sb.toString();
    }

13. 罗马数字转整数

public int romanToInt(String s) {
        if(s == null || s.length() == 0){
            return 0;
        }
        Map<Character, Integer> str2num = new HashMap<>(){{
            put('I', 1);
            put('V', 5);
            put('X', 10);
            put('L', 50);
            put('C', 100);
            put('D', 500);
            put('M', 1000);
        }};
        int result = 0;
// 前小于后,为负数,前大于后,为正数
        for(int i = 0;i<s.length();i++){
            if(i+1<s.length() && str2num.get(s.charAt(i))<str2num.get(s.charAt(i+1))){
                result += -str2num.get(s.charAt(i));
            }else{
                result += str2num.get(s.charAt(i));
            }
        }
        return result;
    }

二、链表

1.反转链表:用到中间node "next"

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

2.复制复杂链表 剑指 Offer 35. 复杂链表的复制 - 力扣(LeetCode)

// 通过两个哈希表,内存占用多
public ListNode copyRandomList(ListNode head) {
        if (head == null) {
            return head;
        }
        Map<ListNode, ListNode> old2new = Maps.newHashMap();
        Map<ListNode, ListNode> new2old = Maps.newHashMap();
        ListNode newHead = genNewListNodeByOld(head, old2new, new2old);
        ListNode cur = newHead;
        while (head.next != null) {
            cur.next = genNewListNodeByOld(head.next, old2new, new2old);
            cur = cur.next;
            head = head.next;
        }
        cur = newHead;
        while (cur != null) {
            cur.random = old2new.get(new2old.get(cur).random);
            cur = cur.next;
        }
        return newHead;
    }

    public ListNode genNewListNodeByOld(ListNode oldNode, Map<ListNode, ListNode> old2New,
                                        Map<ListNode, ListNode> new2Old) {
        ListNode newNode = new ListNode(oldNode.val);
        old2New.put(oldNode, newNode);
        new2Old.put(newNode, oldNode);
        return newNode;
    }


// 通过链表重构加链表拆分
public ListNode copyRandomList(ListNode head) {
        if (head == null) {
            return head;
        }
        ListNode newHead = head;
        while(head!=null){
            ListNode newNode = new ListNode(head.val);
            newNode.next = head.next;
            head.next = newNode;
            head = head.next.next;
        }
        head = newHead;
        while(head!=null){
            // 注意: 特别处理null!!!
            head.next.random = head.random == null?null:head.random.next; 
            head = head.next.next;
        }
        head = newHead;
        newHead = newHead.next;
        while(head!=null){
            ListNode newNode = head.next;
            head.next = head.next.next;
            // 注意: 特别处理null!!!
            newNode.next = newNode.next == null?null:newNode.next.next; 
            head = head.next;
        }
        return newHead;
    }

删除链表中的一个节点 剑指 Offer 18. 删除链表的节点 - 力扣(LeetCode)

 public ListNode deleteNode(ListNode head, int val) {
        ListNode begin = head;
        ListNode preview = null;
        while (begin != null) {
            if (begin.val == val) {
                if (preview != null) { // 注意判空
                    preview.next = begin.next;
                } else {
                    head = head.next;
                }
            }
            preview = begin;
            begin = begin.next;
        }
        return head;
    }

获取链表倒数第k个节点

// 通过arrayList获取
public ListNode getKthFromEnd(ListNode head, int k) {
        if(head == null){
            return null;
        }
        List<ListNode> nodes = new ArrayList<>();
        while(head!=null){
            nodes.add(head);
            head = head.next;
        }
        if(nodes.size()<k){
            return null;
        }
        return nodes.get(nodes.size()-k);
    }

// 定义两个指针,第一个走了k个,第二个再走,第一个到终点时第二个到倒数k点
public ListNode getKthFromEnd(ListNode head, int k) {
        if(head == null){
            return head;
        }
        ListNode latter = head;
        while(head!=null&&k>0){
            head = head.next;
            k--;
        }
        if(head == null&&k>0){
            return null;
        }
        while(head!=null){
            latter = latter.next;
            head = head.next;
        }
        return latter;
    }

合并排序链表

 public ListNode mergeTwoLists(ListNode l1, ListNode l2) {
        ListNode begin = new ListNode(0);
        ListNode p = begin;
        while(l1!=null && l2!=null){
            if(l1.val<=l2.val){
                begin.next = l1;
                l1 = l1.next;
            }else{
                begin.next = l2;
                l2 = l2.next;
            }
            begin = begin.next;
        }
        while(l1!=null){
            begin.next = l1;
            l1 = l1.next;
            begin = begin.next;
        }
        while(l2!=null){
            begin.next = l2;
            l2 = l2.next;
            begin = begin.next;
        }
        return p.next;
    }

剑指 Offer 52. 两个链表的第一个公共节点  

// 直观方法,两个stack
ListNode getIntersectionNode(ListNode headA, ListNode headB) {
        Stack<ListNode> a = new Stack<>();
        Stack<ListNode> b = new Stack<>();
        for(ListNode head = headA; head !=null; head = head.next){
            a.push(head);
        }
        for(ListNode head = headB; head!=null; head = head.next){
            b.push(head);
        }
        ListNode common = null;
        while(a.size()>0 && b.size()>0 && a.peek() == b.peek()){
            common = a.pop();
            b.pop();
        }
        return common;
    }

// 数学方法,双指针相遇
A走完到common, a+b-c
B走完到common, b+a-c
相等的的点就是重合的点

ListNode getIntersectionNode(ListNode headA, ListNode headB) {
        ListNode a = headA;
        ListNode b = headB;
        while(a!=b){
           a = a == null?headB:a.next;
           b = b == null?headA:b.next;
        }
        return a;
    }

三、栈

1.从头到尾打印链表力扣

public int[] reversePrint(ListNode head) {
        if(head == null){
            return new int[0];
        }
        Stack<ListNode> stack = new Stack<>();
        while(head!=null){
            stack.push(head);
            head = head.next;
        }
        int[] vals = new int[stack.size()];
        for(int i = 0; i<vals.length; i++){
            vals[i] = stack.pop().val;
        }
        return vals;
    }

两个栈实现队列力扣

class CQueue {
    public static Stack<Integer> stack1;
    public static Stack<Integer> stack2;

    public CQueue() {
        stack1 = new Stack<>();
        stack2 = new Stack<>();
    }

    public void appendTail(int value) {
        stack1.push(value);
    }

    public int deleteHead() {
        if (stack2.size() == 0) {
            while (stack1.size() > 0) {
                stack2.push(stack1.pop());
            }
        }
        return stack2.size() > 0 ? stack2.pop() : -1;
    }
}

 包含min函数的栈 力扣

class MinStack {
    Stack<Integer> mainStack;
    Stack<Integer> minStack;

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

    public void push(int x) {
        mainStack.push(x);
        if (minStack.empty() || x <= minStack.peek()) {
            minStack.push(x);
        }
    }

    public void pop() {
        int x = mainStack.pop();
        if (minStack.size() > 0 && minStack.peek() == x) {
            minStack.pop();
        }
    }

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

    public int min() {
        return minStack.peek();
    }
}

堆 

剑指 Offer 41. 数据流中的中位数

class MedianFinder {
    private PriorityQueue<Integer> maxQ;
    private PriorityQueue<Integer> minQ;

    /**
     * initialize your data structure here.
     */
    public MedianFinder() {
        maxQ = new PriorityQueue<>((o1, o2) -> o2 - o1);
        minQ = new PriorityQueue<>((o1, o2) -> o1 - o2);
    }

    public void addNum(int num) {
        if (maxQ.size() == 0) {
            maxQ.offer(num);
            return;
        }
        if (num >= maxQ.peek()) {
            minQ.offer(num);
        }else{
            maxQ.offer(num);
        }
        while (maxQ.size() - minQ.size() > 1) {
            minQ.offer(maxQ.poll());
        }
        while (minQ.size() - maxQ.size() > 1) {
            maxQ.offer(minQ.poll());
        }
    }

    public double findMedian() {
        if (minQ.size() == maxQ.size() && maxQ.size() != 0) {
            return (minQ.peek() + maxQ.peek()) / 2.0;
        } else {
            return minQ.size() > maxQ.size() ? minQ.peek() : maxQ.peek();
        }
    }
}

 栈的压入弹出

剑指 Offer 31. 栈的压入、弹出序列

public boolean validateStackSequences(int[] pushed, int[] popped) {
        if(pushed == null || popped == null || pushed.length != popped.length){
            return false;
        }
        Stack<Integer> mockStack = new Stack<>();
        int cursorP = 0;
        for(int i = 0; i<pushed.length; i++){
            mockStack.push(pushed[i]);
            while(mockStack.size()>0 && cursorP<popped.length && mockStack.peek() == popped[cursorP]){
                mockStack.pop();
                cursorP++;
            }
        }
        return mockStack.empty();
    }

队列

队列最大值(类滑动窗口)
剑指 Offer 59 - II. 队列的最大值

class MaxQueue {
    LinkedList<Integer> mainQueue;
    LinkedList<Integer> maxQueue;

    public MaxQueue() {
        mainQueue = new LinkedList<Integer>();
        maxQueue = new LinkedList<Integer>();
    }

    public int max_value() {
        if (maxQueue.size() == 0) {
            return -1;
        }
        return maxQueue.peek();
    }

    public void push_back(int value) {

        while (maxQueue.size() > 0 && mainQueue.getLast() < value) {
            maxQueue.removeLast();
        }
        mainQueue.offer(value);
    }

    public int pop_front() {
        int value = mainQueue.isEmpty() ? -1 : mainQueue.poll();
        if (maxQueue.size() != 0 && mainQueue.peek() == value) {
            maxQueue.poll();
        }
        return value;
    }

    public static void main(String args[]) {
        MaxQueue maxQueue = new MaxQueue();
        maxQueue.push_back(1);
        maxQueue.push_back(2);
        maxQueue.max_value();
        maxQueue.pop_front();
        maxQueue.max_value();
    }
}

数组


剑指 Offer 21. 调整数组顺序使奇数位于偶数前面 力扣

public int[] exchange(int[] nums) {
        if (nums == null || nums.length <= 1) {
            return nums;
        }
        int i = 0;
        int j = nums.length - 1;
        while (i < j) {
            while (i < j && nums[i] % 2 == 1) {
                i++;
            }
            while (i < j && nums[j] % 2 == 0) {
                j--;
            }
            int tmp = nums[i];
            nums[i] = nums[j];
            nums[j] = tmp;
        }
        return nums;
    }

和为s的两个数字 力扣

public int[] twoSum(int[] nums, int target) {
        if (nums == null || nums.length == 0) {
            return null;
        }
        int left = 0;
        int right = nums.length - 1;
        while (left < right) {
            if (target == nums[left] + nums[right]) {
                return new int[] {nums[left], nums[right]};
            } else if (target < nums[left] + nums[right]) {
                right--;
            } else {
                left++;
            }
        }
        return null;
    }

滑动窗口最大值 

public int[] maxSlidingWindow(int[] nums, int k) {
        if (nums == null || nums.length == 0) {
            return null;
        }
        List<Integer> maxList = new ArrayList<>();

        // 队列用于记录最后最大值
        LinkedList<Integer> q = new LinkedList<>();
        // 用于记录最大值
        Integer max = nums[0];
        // 窗口未满不用移动
        for (int i = 0; i < nums.length && i < k; i++) {
            while (q.size() > 0 && nums[i] > q.getLast()) {
                q.removeLast();
            }
            q.add(nums[i]);
            max = max > nums[i] ? max : nums[i];
        }
        maxList.add(max);
        // 窗口满了以后,每移动一步,判断窗口开始值前一个(i-k)是否为最大值,是则移除
        for (int i = k; i < nums.length; i++) {
            while (q.size() > 0 && nums[i] > q.getLast()) {
                q.removeLast();
            }
            q.add(nums[i]);
            max = max > nums[i] ? max : nums[i];
            if (nums[i - k] == max) {
                q.removeFirst();
                max = q.getFirst();
            }
            maxList.add(max);
        }
        int a[] = new int[maxList.size()];
        for (int i = 0; i < maxList.size(); i++) {
            a[i] = maxList.get(i);
        }
        return a;
    }

螺旋打印数组力扣

public int[] spiralOrder(int[][] matrix) {
        if (matrix == null || matrix.length == 0) {
            return new int[0];
        }
        int rowEnd = matrix.length - 1;
        int colEnd = matrix[0].length - 1;
        int rowBegin = 0;
        int colBegin = 0;
        int r = 0;
        int c = 0;
        int[] result = new int[matrix.length * matrix[0].length];
        int cursor = 0;
        while (rowBegin < rowEnd && colBegin < colEnd) {
            while (c < colEnd) {
                result[cursor++] = matrix[r][c++];
            }
            while (r < rowEnd) {
                result[cursor++] = matrix[r++][c];
            }
            while (c > colBegin) {
                result[cursor++] = matrix[r][c--];
            }
            while (r > rowBegin) {
                result[cursor++] = matrix[r--][c];
            }
            rowBegin++;
            rowEnd--;
            colBegin++;
            colEnd--;
            r = rowBegin;
            c = colBegin;
        }
        if (rowBegin == rowEnd) {
            for (c = colBegin; c <= colEnd; c++) {
                result[cursor++] = matrix[r][c];
            }
        } else if(colBegin == colEnd){
            for (r = rowBegin; r <= rowEnd; r++) {
                result[cursor++] = matrix[r][c];
            }
        }
        return result;
    }

数组的重复数字

 力扣

// 通过哈希表
public int findRepeatNumber(int[] nums) {
        if(nums == null || nums.length == 0){
            return -1;
        }
        Set<Integer>  numSet = new HashSet<>();
        for(int i = 0;i<nums.length;i++){
            if(numSet.contains(nums[i])){
                return nums[i];
            }
            numSet.add(nums[i]);
        }
        return -1;
    }

// 交换
public int findRepeatNumber(int[] nums) {
        if (nums == null || nums.length == 0) {
            return -1;
        }
        int i = 0;
        while (i < nums.length) {
            if (nums[i] == i) {
                i++;
            } else {
                if (nums[i] == nums[nums[i]]) {
                    return nums[i];
                }
                swap(nums, i, nums[i]);
            }
        }
        return -1;
    }

 * 排序数组查找数字,个数(二分查找)

剑指 Offer 53 - I. 在排序数组中查找数字 I 

public static int search(int[] nums, int target) {
        if(nums == null || nums.length == 0){
            return 0;
        }
        // 查找右边界
        int end = findBand(nums, target);
        // 查找左边界
        int begin = findBand(nums, target-1);
        return end-begin;
    }
    public static int findBand(int nums[], int target){
        int left = 0;
        int right = nums.length-1;
        while(left<=right){
            int mid = (left+right)/2;
            if(nums[mid]<=target){
                left = mid+1;
            }else {
                right = mid-1;
            }
        }
        return left;
    }

0-n-1中缺失的数字(二分查找)

剑指 Offer 53 - II. 0~n-1中缺失的数字 - 力扣(LeetCode)

public int missingNumber(int[] nums) {
        if(nums == null || nums.length == 0){
            return -1;
        }
        int left = 0;
        int right = nums.length-1;
        while(left<=right){
            int mid = (left+right)/2;
            if(nums[mid] == mid){
                left = mid+1;
            }else{
                right = mid-1;
            }
        }
        return left;
    }

二维数组中的查找

public boolean findNumberIn2DArray(int[][] matrix, int target) {
        if(matrix == null || matrix.length == 0){
            return false;
        }
        int i = matrix[0].length-1;
        int j = 0;
        while(i>=0 && j<matrix.length){
            if(matrix[j][i]>target){
                i--;
            }else if(matrix[j][i]<target){
                j++;
            }else {
                return true;
            }
        }
        return false;
    }

旋转数组最小值(二分查找,注意边界)

力扣

public int minArray(int[] numbers) {
        if(numbers == null || numbers.length == 0){
            return -1;
        }
        int left = 0;
        int right = numbers.length-1;
        while(left<right){
            int mid = (left+right)/2;
            // 大于,一定在右边
            if(numbers[mid]>numbers[right]){
                left = mid+1;
            // 小于,可能为最小值或在左边
            }else if(numbers[mid]<numbers[right]){
                right = mid;
            }else{ // 等于,可能在左边也可能在右边
                right--;
            }
        }
        return numbers[right];
    }

剑指 Offer 51. 数组中的逆序对

class Solution {
    private int count = 0;
    public int reversePairs(int[] nums) {
        if (nums == null || nums.length == 0) {
            return 0;
        }
        countPairs(nums, 0, nums.length-1);
        return count;
    }

    private void countPairs(int[] nums, int left, int right) {
        if (left >= right) {
            return;
        }
        int mid = (left + right) / 2;
        countPairs(nums, left, mid);
        countPairs(nums, mid + 1, right);
        merge(nums, left, mid, right);
    }

    private void merge(int[] nums, int left, int mid, int right) {
        int i = left;
        int j = mid + 1;
        int[] tmp = new int[right-left+1];
        int index = 0;
        while (i <= mid && j <= right) {
            if(nums[i]<=nums[j]){
                tmp[index++] = nums[i++];
            }else{
                count+=(mid+1-i);
                tmp[index++] = nums[j++];
            }
        }
        while(i<=mid){
            tmp[index++] = nums[i++];
        }
        while(j<=right){
            tmp[index++] = nums[j++];
        }
        for(int k = 0; k<tmp.length; k++){
            nums[k+left] = tmp[k];
        }
    }
}

剑指 Offer 40. 最小的k个数

class Solution {
    public int[] getLeastNumbers(int[] arr, int k) {
        if (arr == null || arr.length == 0 || arr.length <= k) {
            return arr;
        }
        sortk(arr, 0, arr.length - 1, k);

        return Arrays.copyOfRange(arr, 0, k);
    }

    private void sortk(int[] arr, int left, int right, int k) {
        int mid = division(arr, left, right, k);
        if(mid==k) return;
        if(mid<k){
            sortk(arr, mid+1, right, k);
        }else{
            sortk(arr, left, mid-1, k);
        }
    }

    private int division(int[] arr, int left, int right, int k) {
        int base = arr[left];
        while (left < right) {
            while (left < right && base <= arr[right]) {
                right--;
            }
            arr[left] = arr[right];
            while (left < right && base > arr[left]) {
                left++;
            }
            arr[right] = arr[left];
        }
        arr[left] = base;
        return left;
    }
}

剑指 Offer 39. 数组中出现次数超过一半的数字

public int majorityElement(int[] nums) {
        if(nums == null || nums.length == 0){
            return 0;
        }
        int cur = nums[0];
        int count = 1;
        for(int i = 1;i<nums.length;i++){
            if(nums[i]!=cur){
                count--;
            }else{
                count++;
            }
            if(count==0){
                cur = nums[i];
                count=1;
            }
        }
        return cur;
    }

剑指 Offer 66. 构建乘积数组

public int[] constructArr(int[] a) {
        if(a==null || a.length == 0){
            return new int[0];
        }
        int[] a1 = new int[a.length];
        int[] a2 = new int[a.length];
        a1[0] = 1;
        a2[a.length-1] = 1;
        for(int i = 1;i<a.length; i++){
            a1[i] = a1[i-1]*a[i-1];
        }
        for(int i = a.length-2; i>=0; i--){
            a2[i] = a2[i+1]*a[i+1];
        }
        int[] result = new int[a.length];
        for(int i = 0; i<a.length; i++){
            result[i] = a1[i]*a2[i];
        }
        return result;
    }

剑指 Offer 57 - II. 和为s的连续正数序列

public int[][] findContinuousSequence(int target) {
        if (target <= 2) {
            return new int[0][0];
        }
        List<int[]> result = new ArrayList<>();
        int begin = 1;
        int end = 2;
        while (end <= (target / 2 + 1) && begin < end) {
            int sum = (begin + end) * (end-begin+1) / 2;
            if(sum == target) result.add(genList(begin++, end++));
            if(sum<target) end++;
            if(sum>target) begin++;
        }
        return result.toArray(new int[result.size()][]);
    }

    private int[] genList(int begin, int end) {
        int[] r = new int[end-begin+1];
        for(int i = 0; i<= (end-begin); i++){
            r[i] = begin+i;
        }
        return r;
    }

15. 三数之和

public List<List<Integer>> threeSum(int[] nums) {
        if (nums == null || nums.length == 0) {
            return null;
        }
        Arrays.sort(nums);
        List<List<Integer>> lists = new ArrayList<>();
        for (int i = 0; i < nums.length; i++) {
            if(i>0&&nums[i-1]==nums[i]){
                continue;
            }
            int begin = i + 1;
            int end = nums.length-1;
            while (begin < end) {
                int sum = nums[begin] + nums[end]+nums[i];
                if (sum==0) {
                    List<Integer> tmp = new ArrayList<>();
                    tmp.add(nums[i]);
                    tmp.add(nums[begin]);
                    tmp.add(nums[end]);
                    lists.add(tmp);
                    while(begin<end && nums[begin+1] == nums[begin]){
                        begin++;
                    }
                    begin++;
                    while(begin<end && nums[end-1]==nums[end]){
                        end--;
                    }
                    end--;
                } else if (sum < 0) {
                    while(begin<end && nums[begin+1] == nums[begin]){
                        begin++;
                    }
                    begin++;
                } else if (sum > 0) {
                    while(begin<end && nums[end-1]==nums[end]){
                        end--;
                    }
                    end--;
                }
            }
        }
        return lists;
    }

16. 最接近的三数之和

public int threeSumClosest(int[] nums, int target) {
        if(nums == null || nums.length == 0){
            return target;
        }
        Arrays.sort(nums);
        int best = Integer.MAX_VALUE;
        for(int i = 0; i< nums.length; i++){
            int begin = i+1;
            int end = nums.length-1;
            while(begin<end){
                int tmp = target-nums[i]-nums[begin]-nums[end];
                if(tmp==0){
                    return target;
                }
                if(tmp>0){
                    begin++;
                }else if(tmp<0){
                    end--;
                }
                best = Math.abs(tmp)>Math.abs(best)?best:tmp;
            }
        }
        return target-best;
    }

23. 合并 K 个升序链表

public ListNode mergeKLists(ListNode[] lists) {
        if(lists == null ||lists.length == 0){
            return null;
        }
        PriorityQueue<NodeMap> queue = new PriorityQueue<>();
        for(int i = 0; i<lists.length; i++){
            ListNode tmp = lists[i];
            while(tmp!=null){
                queue.offer(new NodeMap(tmp.val, tmp));
                tmp = tmp.next;
            }
        }
        if(queue.size() == 0){
            return null;
        }
        ListNode head = queue.poll().node;
        ListNode tail = head;
        while(queue.size()>0){
            tail.next = queue.poll().node;
            tail = tail.next;
        }
// 注意设置null
        tail.next = null;
        return head;
    }

    public class NodeMap implements Comparable<NodeMap>{
        int val;
        ListNode node;
        
        public NodeMap(int val, ListNode node){
            this.val = val;
            this.node = node;
        }
// 继承comparable,和comparator,注意记一下
        @Override
        public int compareTo(NodeMap o) {
            return this.val-o.val;
        }
    }

数组路径 

剑指 Offer 12. 矩阵中的路径

    // 遍历每个节点为开始节点,尝试头节点不要写在递归里,会死循环
    public boolean exist(char[][] board, String word) {
        if(board == null || word == null){
            return false;
        } 
        boolean[][] isUsed = new boolean[board.length][board[0].length];
        for(int i=0;i<board.length;i++){
            for(int j = 0; j<board[0].length;j++){
                if(exist(board,i,j,0,word,isUsed)){
                    return true;
                }
            }
        }
        return false;
    }
    public boolean exist(char[][] board, int i, int j, int w, String word, boolean[][] isUsed){
        if(w == word.length()){
            return true;
        }
        if(i<0 || i>=board.length || j<0 || j>=board[0].length || isUsed[i][j] || word.charAt(w) != board[i][j]){
            return false;
        }
        isUsed[i][j] = true;
        w=w+1;
        boolean result = exist(board, i+1, j, w, word, isUsed) 
        ||exist(board, i, j+1, w, word, isUsed)
        ||exist(board, i, j-1, w, word, isUsed)
        ||exist(board, i-1, j, w, word, isUsed);
        isUsed[i][j] = false;
        return result;
    }

剑指 Offer 13. 机器人的运动范围

private static int[][] pathMap;
    public int movingCount(int m, int n, int k) {
        pathMap = new int[m][n];
        int maxPath = 0;
        moving(0,0,k,m,n);
        for(int i = 0;i<m;i++){
            for(int j = 0; j<n;j++){
                if(pathMap[i][j] == 1) maxPath++;
            }
        }
        return maxPath;
    }
    public void moving(int i, int j, int k, int m, int n){
        if(j<0 || j>= m || i<0 || i >= n|| countSum(i)+countSum(j)>k || pathMap[j][i] == 1){
            return;
        }
        pathMap[j][i] = 1;
        moving(i+1,j,k,m,n);
        moving(i-1,j,k,m,n);
        moving(i,j-1,k,m,n);
        moving(i,j+1,k,m,n);
    }

    public int countSum(int i){
        int result = 0;
        while(i>0){
            result += i%10;
            i = i/10;
        }
        return result;
    }

从上到下打印二叉树

public int[] levelOrder(TreeNode root) {
        if(root == null){
            return new int[0];
        }
        List<Integer> valList = new ArrayList<>();
        Queue<TreeNode>  tmpQueue = new LinkedList<>();
        tmpQueue.offer(root);
        
        while(tmpQueue.size()>0){
            TreeNode node = tmpQueue.poll();
            valList.add(node.val);
            if(node.left!=null){
                tmpQueue.offer(node.left);
            }
            if(node.right!=null){
                tmpQueue.offer(node.right);
            }
        }

        int[] result = new int[valList.size()];
        for(int i = 0; i<valList.size();i++){
            result[i] = valList.get(i);
        }
        return result;
    }

从上到下分层打印二叉树

力扣

public List<List<Integer>> levelOrder(TreeNode root) {
        List<List<Integer>> result = new ArrayList<>();
        if(root==null){
            return result;
        }
        Queue<TreeNode> queue1 = new LinkedList<>();
        Queue<TreeNode> queue2 = new LinkedList<>();
        queue1.offer(root);
        List<Integer> tmp = new ArrayList<>();
        while(queue1.size()>0){
            TreeNode curNode = queue1.poll();
            tmp.add(curNode.val);
            if(curNode.left!=null){
                queue2.offer(curNode.left);
            }
            if(curNode.right!=null){
                queue2.offer(curNode.right);
            }
            if(queue1.size() == 0){
                result.add(tmp);
                tmp = new ArrayList<>();
                Queue<TreeNode> tmpp = queue1;
                queue1 = queue2;
                queue2 = tmpp;
            }
        }
        return result;
    }

* 之字形分层打印二叉树 力扣

public List<List<Integer>> levelOrder(TreeNode root) {
        List<List<Integer>> result = new ArrayList<>();
        if(root == null){
            return result;
        }
        // 注意此处申明,必须是LinkedList才有addFirst方法!!
        LinkedList<Integer> tmpResult = new LinkedList<>();
        boolean flag = true;
        Queue<TreeNode> queue1 = new LinkedList<>();
        Queue<TreeNode> queue2 = new LinkedList<>();
        queue1.offer(root);
        while(queue1.size()>0){
            // 之字形,在上一题的基础上,反正打印就行了,不用转换队列写入顺序!!
            root = queue1.poll();
            if(flag){
                tmpResult.add(root.val);
            }else{
                tmpResult.addFirst(root.val);
            }
            if(root.left!=null){
                queue2.offer(root.left);
            }
            if(root.right!=null){
                queue2.offer(root.right);
            }
                
            if(queue1.size() == 0){
                result.add(tmpResult);
                tmpResult = new LinkedList<>();
                queue1 = queue2;
                queue2 = new LinkedList<>();
                flag = !flag;
            }
        }
        return result;
    }

* 判断子树 力扣

public boolean isSubStructure(TreeNode A, TreeNode B) {
        // 由于约定空树不为子树,因此需为不等于null,三种可能:相同树,为右子树,为左子树
        return (A!=null && B!=null &&(isSameTree(A, B)||isSubStructure(A.left, B)||isSubStructure(A.right, B)));
    }

    // 相同树, 
    public boolean isSameTree(TreeNode A, TreeNode B) {
        // B为null,说明递归到头了,返回true
        if(B == null){
            return true;
        }
        // 否则,A为null,说明A cover不住B,返回false
        if(A == null || A.val!=B.val){
            return false;
        }
        // 循环判断左右节点
        return isSameTree(A.left, B.left) && isSameTree(A.right, B.right);
    }

剑指 Offer 27. 二叉树的镜像

public TreeNode mirrorTree(TreeNode root) {
        if(root == null){
            return null;
        }
        TreeNode tmp = root.left;
        root.left = mirrorTree(root.right);
        root.right = mirrorTree(tmp);
        return root;
    }

剑指 Offer 28. 对称的二叉树 - 力扣(LeetCode)

    public boolean isSymmetric(TreeNode root) {
        if(root == null){
            return true;
        }
        return isSymmetric(root.left, root.right);
    }
    
    // 判断节点1的左节点和节点2的右节点,节点1的右节点和节点2的左节点是否相等。相等则两颗树是对称的
    // 此方法为判断两个节点对应的树是否对称
    public boolean isSymmetric(TreeNode left, TreeNode right){
        if(left == null && right == null){
            return true;
        }
        if(left == null || right == null){
            return false;
        }
        return left.val == right.val && isSymmetric(left.left, right.right) 
        && isSymmetric(left.right, right.left);
    }

剑指 Offer 34. 二叉树中和为某一值的路径

    
    private List<List<Integer>> paths;
    public List<List<Integer>> pathSum(TreeNode root, int target) {
        paths = new ArrayList<>();
        if(root == null){
            return paths;
        }
        findPath(root, target, new ArrayList<>());
        return paths;
    }
    public void findPath(TreeNode root, int target, List<Integer> path){
        if(root == null){
            return;
        }
        target = target-root.val;
        path.add(root.val);
        if(target == 0 && root.left == null && root.right == null){
            paths.add(new ArrayList<>(path));
        }else{
            findPath(root.left, target, path);
            findPath(root.right, target, path);
        }
        path.remove(path.size()-1);
    }

剑指 Offer 36. 二叉搜索树与双向链表

    private Node preNode;
    private Node head;
    public Node treeToDoublyList(Node root) {
        if (root == null) {
            return null;
        }
        // 中序遍历搜索二叉树
        iterateNode(root);
        head.left = preNode;
        preNode.right = head;
        return head;
    }

    public Node iterateNode(Node node) {
        if (node.left != null) {
            iterateNode(node.left);
        }
        // 当前节点的左节点指向前一个节点,前一个结点的右节点指向当前节点
        node.left = preNode;
        if(preNode == null){
            head = node;
        }else{
            preNode.right = node;
        }
        preNode = node;
        if (node.right != null) {
            iterateNode(node.right);
        }
        return node;
    }

剑指 Offer 54. 二叉搜索树的第k大节点

// 第k大,就是倒排第k个!!
class Solution {
    int count = 0;
    int result = 0;
    public int kthLargest(TreeNode root, int k) {
        if(root == null){
            return result;
        }
        kthLargest(root.right, k);
        count++;
        if(k == count){
            result = root.val;
            return result;
        }
        kthLargest(root.left, k);
        return result;
    }
}

剑指 Offer 55 - I. 二叉树的深度

class Solution {
    private int max = 0;
    public int maxDepth(TreeNode root) {
        if(root == null){
            return 0;
        }
        dfs(root, 0);
        return max;
    }

    public void dfs(TreeNode node, int n){
        if(node == null){
            max = max<n?n:max;
            return;
        }
        dfs(node.left, n+1);
        dfs(node.right, n+1);
    }
}

剑指 Offer 55 - II. 平衡二叉树

    boolean balance = true;
    public boolean isBalanced(TreeNode root) {
        if(root == null){
            return balance;
        }
        dfs(root);
        return balance;
    }
    public int dfs(TreeNode root){
        if(root==null){
            return 0;
        }
        int leftLength = dfs(root.left)+1;
        int rightLength = dfs(root.right)+1;
        if(Math.abs(leftLength-rightLength)>1){
            balance = false;
        }
        return Math.max(leftLength, rightLength);
    }

剑指 Offer 68 - I. 二叉搜索树的最近公共祖先

public TreeNode lowestCommonAncestor(TreeNode root, TreeNode p, TreeNode q) {
        if(root == null || p == null || q == null){
            return null;
        }
        if((root.val-p.val)*(root.val-q.val)<=0){
            return root;
        }
        if(root.val<p.val){
            return lowestCommonAncestor(root.right, p, q);
        }else{
            return lowestCommonAncestor(root.left,p, q);
        }
    }

剑指 Offer 68 - II. 二叉树的最近公共祖先

// 找到节点后,向上递归
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;
        }
        // left,right都不为null,说明是最近公共节点
        return root;
    }

剑指 Offer 37. 序列化二叉树

public class Codec {

    private static final String NULL_MARK = "#";                            
    private StringBuilder sb;                                               
    private int cursor;                                                     
                                                                        
    // Encodes a tree to a single string.                                   
    public String serialize(TreeNode root) {                                
        sb = new StringBuilder();                                           
        serializeDfs(root);                                                 
        String sbr = sb.toString();                                         
        return sbr.length() == 0 ? null : sbr.substring(0, sbr.length() - 1);
}                                                                       
                                                                        
    private void serializeDfs(TreeNode root) {                              
        if (root == null) {                                                 
            sb.append(NULL_MARK);                                           
            sb.append(",");                                                 
            return;                                                         
        }                                                                   
        sb.append(root.val);                                                
        sb.append(",");                                                     
        serializeDfs(root.left);                                            
        serializeDfs(root.right);                                           
    }                                                                       
                                                                        
    // Decodes your encoded data to tree.                                   
    public TreeNode deserialize(String data) {                              
        cursor = 0;                                                         
        if (data == null || data.length() == 0) {                           
            return null;                                                    
        }                                                                   
        return deserializeDfs(data.split(","));                             
    }                                                                       
                                                                        
    private TreeNode deserializeDfs(String[] data) {                        
        if (cursor >= data.length || data[cursor].equals(NULL_MARK)) {      
            cursor++;                                                       
            return null;                                                    
        }                                                                   
        TreeNode node = new TreeNode(Integer.parseInt(data[cursor]));       
        cursor++;                                                           
        node.left = deserializeDfs(data);                                   
        node.right = deserializeDfs(data);                                  
        return node;                                                        
    }                                                                           
}

剑指 Offer 07. 重建二叉树

class Solution {
    private int index;

    public TreeNode buildTree(int[] preorder, int[] inorder) {
        if (preorder == null || inorder == null || preorder.length != inorder.length) {
            return null;
        }
        index = 0;
        return buildTree(preorder, inorder, 0, preorder.length - 1);
    }

    public TreeNode buildTree(int[] preorder, int[] inorder, int begin, int end) {
        if (begin > end) {
            return null;
        }
        int val = preorder[index++];
        int mid = findMid(val, inorder, begin, end);
        if (mid < 0) {
            return null;
        }
        TreeNode curNode = new TreeNode(val);
        curNode.left = buildTree(preorder, inorder, begin, mid - 1);
        curNode.right = buildTree(preorder, inorder, mid + 1, end);
        return curNode;
    }

    private int findMid(int val, int[] inorder, int begin, int end) {
        for (int i = begin; i <= end; i++) {
            if (val == inorder[i]) {
                return i;
            }
        }
        return -1;
    }
}

剑指 Offer 33. 二叉搜索树的后序遍历序列

    public boolean verifyPostorder(int[] postorder) {
        if (postorder == null || postorder.length == 0) {
            return true;
        }
        return verifyPostOrder(postorder, 0, postorder.length - 1);
    }

    public boolean verifyPostOrder(int[] postorder, int begin, int end) {
        if (begin >= end) {
            return true;
        }
        int root = postorder[end];
        int leftRoot = findLeftRoot(postorder, begin, end - 1, root);
        for (int i = leftRoot + 1; i < end; i++) {
            if (postorder[i] < root) {
                return false;
            }
        }
        return (leftRoot < begin ? true : verifyPostOrder(postorder, begin, leftRoot))
                && (leftRoot + 1 >= end ? true : verifyPostOrder(postorder, leftRoot + 1, end - 1));
    }

    private int findLeftRoot(int[] postorder, int begin, int end, int root) {
        for (int i = begin; i <= end; i++) {
            if (root < postorder[i]) {
                return i - 1;
            }
        }
        return end;
    }

数学·运算符

剑指 Offer 64. 求1+2+…+n

// 利用短路和等于号的传递
public int sumNums(int n) {
        boolean a = n==1 || (n = n+sumNums(n-1)) == 1;
        return n;
    }

剑指 Offer 16. 数值的整数次方

    //  从大到小算,递归, 注意,n为最小int值时,-会失效
    public double myPow(double x, int n) {
        long b = n;
        if (n >= 0) {
            return pow(x, b);
        }
        else {
            return 1 / pow(x, -b);
        }
    }

    public double pow(double x, long n) {
        if (n == 0) {
            return 1;
        }
        if (n == 1) {
            return x;
        }
        double cal = pow(x, n / 2);
        return cal * cal * (n % 2 == 1 ? x : 1);
    }

    // 从小到大算,避免递归
    public double myPow(double x, int n) {
        x = n < 0 ? 1 / x : x;
        long b = n;
        b  = (b>= 0) ? b : -b;
        double res = 1.0;
        while (b > 0) {
            res = res * ((b & 1) == 1 ? x : 1.0);
            x = x*x; //相当于翻倍
            b = b/2;
        }
        return res;
    }

剑指 Offer 17. 打印从1到最大的n位数

// 一种方案,直接算最大值,循环输出
public int[] printNumbers(int n) {
        int max = 1;
        while(n>0){
            max = 10*max;
            n--;
        }
        int[] result = new int[max-1];
        for(int i = 1;i<max;i++){
            result[i-1] = i;
        }
        return result;
    }

// 另一种方案,递归,可用String输出大数
    private List<String> result = new ArrayList<>();
    public List<String> printNumbers(int n) {
        calNumbers(n, "");
        result.remove("");
        return result;
    }

    public void calNumbers(int n, String value) {
        if (n == 0) {
            result.add(value);
            return;
        }
        for (int i = 0; i <= 9; i++) {
            // 过滤以0为开头的情况
            if (value.equals("") && i == 0) {
                calNumbers(n - 1, value);
            }else{
                calNumbers(n - 1, value + i);
            }
        }
    }

剑指 Offer 45. 把数组排成最小的数

public String minNumber(int[] nums) {
        if (nums == null || nums.length == 0) {
            return null;
        }
        String[] nStrs = new String[nums.length];
        for (int i = 0; i < nums.length; i++) {
            nStrs[i] = String.valueOf(nums[i]);
        }
// 注意此处lambda表达式写法
// Arrays.sort(strs, new Comparator<String>() {         
//          public int compare(String o1, String o2) {
//             return (o1+o2).compareTo(o2+o1);
//          }
//       });
        Arrays.sort(nStrs, (s1, s2) -> (s1 + s2).compareTo(s2 + s1));
        StringBuilder sb = new StringBuilder();
        for (int i = 0; i < nStrs.length; i++) {
            sb.append(nStrs[i]);
        }
        return sb.toString();
    }

剑指 Offer 61. 扑克牌中的顺子

public boolean isStraight(int[] nums) {
        if (nums == null || nums.length == 0) {
            return false;
        }
        Set<Integer> sets = new HashSet<>();
        int zeros = 0;
        int min = 14;
        int max = 0;
        for (int i = 0; i < nums.length; i++) {
            if (nums[i] == 0) {
                zeros++;
            } else {
                min = Math.min(min, nums[i]);
                max = Math.max(max, nums[i]);
                // 判断重复数字
                if (sets.contains(nums[i])) {
                    return false;
                }
                sets.add(nums[i]);
            }
        }
        // (如果最大值减最小值的差值,减去非0数字的个数的个数)->缺的个数,小于0的个数,那为真
        if (((max - min + 1) - (nums.length - zeros)) <= zeros) {
            return true;
        }
        return false;
    }

动态规划

剑指 Offer 10- I. 斐波那契数列

public int fib(int n) {
        if(n == 0) return 0;
        int a = 0;
        int b = 1;
        for (int i = 2; i <= n; i++) {
            int sum = (a + b) % 1000000007;
            a = b;
            b = sum;
        }
        return b;
    }

剑指 Offer 10- II. 青蛙跳台阶问题

public int numWays(int n) {
        if(n == 0){
            return 1;
        }
        int a = 1;
        int b = 1;
        for(int i = 2; i<=n; i++){
            int tmp = b;
            b = (a+b)%1000000007;
            a = tmp;
        }
        return b;
    }

剑指 Offer 63. 股票的最大利润

// 若前面所有数组只和大于0,则保留,遍历找到最大值
public int maxProfit(int[] prices) {
        if(prices == null || prices.length == 0){
            return 0;
        }
        int max = 0;
        int sum = 0;
        for(int i = 1; i < prices.length; i++){
            sum += prices[i]-prices[i-1];
            if(sum<0) {
                sum = 0;
            }else{
                max = Math.max(sum, max);
            }
        }
        return max;
    }

// 遍历,找到当前节点前一个最小的cost, 比较当前节点和最小cost的差值,取最大
    public int maxProfit(int[] prices) {
        if (prices == null || prices.length == 0) {
            return 0;
        }
        int a = 0;
        int cost = prices[0];
        for (int i = 1; i < prices.length; i++) {
            cost = Math.min(prices[i-1], cost);
            a = Math.max(a, prices[i] - cost);
        }
        return a;
    }

剑指 Offer 42. 连续子数组的最大和

public int maxSubArray(int[] nums) {
        if (nums == null || nums.length == 0) {
            return 0;
        }
        int max = nums[0];
        int count = nums[0];
        for (int i = 1; i < nums.length; i++) {
            if (count < 0) {
                count = 0;
            }
            count += nums[i];
            max = Math.max(count, max);
        }
        return max;
    }

剑指 Offer 47. 礼物的最大价值

public int maxValue(int[][] grid) {
        if (grid == null || grid.length == 0) {
            return 0;
        }
        int[][] result = new int[grid.length][grid[0].length];
        for (int i = 0; i < grid.length; i++) {
            for (int j = 0; j < grid[0].length; j++) {
                if (i == 0 && j == 0) {
                    result[i][j] = grid[i][j];
                } else if (i == 0) {
                    result[i][j] = result[i][j - 1] + grid[i][j];
                } else if (j == 0) {
                    result[i][j] = result[i - 1][j] + grid[i][j];
                } else {
                    result[i][j] = Math.max(result[i - 1][j], result[i][j - 1]) + grid[i][j];
                }
            }
        }
        return result[grid.length - 1][grid[0].length - 1];
    }

剑指 Offer 46. 把数字翻译成字符串

public int translateNum(int num) {
        String numStr = String.valueOf(num);
        return transCount(numStr, numStr.length() - 1);
    }

    private int transCount(String numStr, int index) {
        if (index <= 0) {
            return 1;
        }
        int twoCharacterNum = Integer.parseInt(numStr.substring(index - 1, index + 1));
        return (twoCharacterNum < 26 && twoCharacterNum > 9) ?
                transCount(numStr, index - 1)
                        + transCount(numStr, index - 2)
                : transCount(numStr, index - 1);
    }

剑指 Offer 48. 最长不含重复字符的子字符串

public int lengthOfLongestSubstring(String s) {
        if (s == null || s.length() == 0) {
            return 0;
        }
        int maxL = 0;
        Queue<Character> q = new LinkedList<>();
        Set<Character> sets = new HashSet<>();
        for (int i = 0; i < s.length(); i++) {
            if (sets.contains(s.charAt(i))) {
                Character tmp = q.poll();
                while (tmp != s.charAt(i)) {
                    sets.remove(tmp);
                    tmp = q.poll();
                }
                sets.remove(tmp);
            }
            sets.add(s.charAt(i));
            q.offer(s.charAt(i));
            maxL = Math.max(sets.size(), maxL);
        }
        return maxL;
    }

    public int lengthOfLongestSubstring(String s) {
        if (s == null || s.length() == 0) {
            return 0;
        }
        Map<Character, Integer> indexMap = new HashMap<>();
        int maxLen = 0;
        int left = 0;
        for (int i = 0; i < s.length(); i++) {
            char cur = s.charAt(i);
            if (indexMap.containsKey(cur)) {
                left = Math.max(left,indexMap.get(cur) + 1);
            }
            indexMap.put(cur, i);
            maxLen = i - left + 1 > maxLen ? i - left + 1 : maxLen;
        }
        return maxLen;
    }

**剑指 Offer 19. 正则表达式匹配

public boolean isMatch(String s, String p) {
        if (s == null || p == null) {
            return true;
        }
        int m = s.length() + 1;
        int n = p.length() + 1;
        boolean[][] dp = new boolean[m][n];
        dp[0][0] = true;
        // s长度为0时,p是否匹配
        for (int j = 1; j < p.length(); j += 2) {
            dp[0][j+1] = dp[0][(j+1)-2] && p.charAt(j) == '*';
        }
        // 动态规划考虑的是到i长度s和j长度的p是否匹配
        for (int i = 1; i < m; i++) {
            for (int j = 1; j < n; j++) {
                if (p.charAt(j - 1) == '*') {
                    // 1.为a*号,且无a 2.为a*号,有a
                    dp[i][j] = dp[i][j - 2] || dp[i - 1][j] && (s.charAt(i - 1) == p.charAt(j - 2) || p.charAt(j - 2) == '.');
                } else {
                    // 3.无*号
                    dp[i][j] = dp[i - 1][j - 1] && (s.charAt(i - 1) == p.charAt(j - 1) || p.charAt(j - 1) == '.');
                }
            }
        }
        return dp[m-1][n-1];
    }

剑指 Offer 49. 丑数

public int nthUglyNumber1(int n) {
        int a2 = 0, a3 = 0, a5 = 0;
        int[] dp = new int[n];
        dp[0] = 1;
        // a2,a3,a5代表当前i是否乘过2,3,5
        for (int i = 1; i <= n-1; i++) {
            dp[i] = Math.min(Math.min(dp[a2] * 2, dp[a3] * 3), dp[a5] * 5);
            if (dp[i] == dp[a2] * 2) {
                a2++;
            }
            if (dp[i] == dp[a3] * 3) {
                a3++;
            }
            if (dp[i] == dp[a5] * 5) {
                a5++;
            }
        }
        return dp[n-1];
    }

**剑指 Offer 60. n个骰子的点数

// j表示上一次的累计和,k表示这一次投的值
public double[] dicesProbability(int n) {
        double[] bp = new double[6];
        Arrays.fill(bp, 1.0/6.0);
        for(int i=2;i<=n;i++){
            double[] tmp = new double[i*6-i+1];
            for(int j = 0;j<bp.length;j++){
                for(int k = 0; k<6; k++){
                    tmp[j+k] += bp[j]*(1.0/6.0);
                }
            }
            bp = tmp;
        }
        return bp;
    }

剑指 Offer 14- I. 剪绳子

public int cuttingRope(int n) {
        if (n <= 1) {
            return n;
        }
        if (n == 2) {
            return 1;
        }
        if (n == 3) {
            return 2;
        }
        int[] value = new int[n + 1];
        // 动态规划,写入每个拆分原子的最大拆分值
        for (int i = 1; i <= n; i++) {
            int max = i;
            for (int j = 1; j < i/2+1; j++) {
                int otherUnit = value[i - j];
                max = otherUnit * j > max ? otherUnit * j : max;
            }
            value[i] = max;
        }
        return value[n];
    }

 运算符

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

// >> 右移 ; >>>无符号右移!
public int hammingWeight(int n) {
        int count = 0;
        while(n!=0){
            if((n&1)==1) count++;
            n = n>>>1;
        }
        return count;
    }

剑指 Offer 65. 不用加减乘除做加法

public int add(int a, int b) {
        while(b!=0){
            int n = a^b;
            b = (a&b)<<1;
            a = n;
        }
        return a;
    }

剑指 Offer 56 - I. 数组中数字出现的次数

// 注意运算符优先级
public int[] singleNumbers(int[] nums) {
        int m = 0;
        for(int i= 0; i<nums.length;i++){
            m ^= nums[i];
        }
        int count = 1;
        while((m&1)!=1){
            count = count<<1;
            m = m>>1;
        }
        int[] result = new int[2];
        for(int i = 0; i<nums.length;i++){
            if((nums[i]&count) != 0){
                result[1] ^= nums[i];
            }else{
                result[0]^=nums[i];
            }
        }
        return result;
    }

剑指 Offer 56 - II. 数组中数字出现的次数 II

// 运算符优先级不确定,就加括号!!!!
public int singleNumber(int[] nums) {
        int[] bucket = new int[32];
        for(int i= 0;i<nums.length; i++){
            int j = 31;
            while(nums[i]!=0){
                bucket[j--] += nums[i]&1;
                nums[i] = nums[i]>>>1;
            }
        }
        int result = 0;
        for(int i=0;i<32;i++){
            result = (result<<1) + bucket[i]%3;
        }
        return result;
    }

 

29. 两数相除

public int divide(int dividend, int divisor) {
// 注意前置判断
        if(divisor == 0){
            return 0;
        }
        if(dividend == Integer.MIN_VALUE && divisor == -1){
            return Integer.MAX_VALUE;
        }
        if(dividend == Integer.MIN_VALUE && divisor == 1){
            return Integer.MIN_VALUE;
        }
// 用负数算
        boolean isRev = false;
        if(dividend>0){
            isRev = !isRev;
            dividend = -dividend;
        }
        if(divisor>0){
            isRev = !isRev;
            divisor = -divisor;
        }
// 用两个数组存储成倍的除数
        int count = 0;
        int x = 1;
        List<Integer> dNum = new ArrayList<>();
        List<Integer> dNex = new ArrayList<>();
        int maxDNum = divisor;
        while(maxDNum>=dividend && maxDNum<0){
            dNum.add(maxDNum);
            dNex.add(x);
            x = x<<1;
            maxDNum = maxDNum<<1;
        }
        for(int i = dNum.size()-1; i>=0; i--){
            if(dNum.get(i)>=dividend){
                count += dNex.get(i);
                dividend -= dNum.get(i);
            }
        }
        return isRev?-count:count;
    }
}

数学

剑指 Offer 14- II. 剪绳子 II

// 特别注意数据类型!!!
public int cuttingRope(int n) {
        if (n <= 3) {
            return n - 1;
        }
        int cut = n / 3;
        int left = n % 3;
        if (left == 1) {
            cut = cut - 1;
        }
        long max = left == 0 ? 1 : left == 1 ? 4 : 2;
        for (int i = 1; i <= cut; i++) {
            max = max*3%1000000007;
        }
        return (int)max;
    }

剑指 Offer 62. 圆圈中最后剩下的数字

// 常规写法
    public int lastRemaining(int n, int m) {
        if(n == 0 || m == 0){
            return n;
        }
        int cur = -1;
        List<Integer> list = new ArrayList<>();
        for(int i = 0;i<n;i++){
            list.add(i);
        }
        while(list.size()>1){
            cur = cur+m;
            cur = cur%(list.size());
            list.remove(cur);
            cur--;
        }
        return list.get(0);
    }

// 数学写法,没看懂
public int lastRemaining(int n, int m) {
        if(n == 0 || m == 0){
            return n;
        }
        int count = 0;
        for(int i = 1; i<=n; i++){
            count = (count+(m%i))%i;
        }
        return count;
    }

剑指 Offer 43. 1~n 整数中 1 出现的次数

public int countDigitOne(int n) {
        int digit = 1;
        int cur = n%10;
        int low = 0;
        int high = n/10;
        int result = 0;
        while(cur!=0 || high!=0){
// cur 为0, 高位*位数
            if(cur == 0){
                result += high*digit;
// cur为1,高位*位数+(1+低位)
            }else if(cur == 1){
                result += (high)*digit+low+1;
            }else if(cur>1){
// cur大于1, (高位+1)*位数
                result += (high+1)*digit;
            }
            low += cur*digit;
            cur = high%10;
            high = high/10;
            digit = digit*10;
        }
        return result;
    }

剑指 Offer 44. 数字序列中某一位的数字

// 很一般的算法,注意都用long
public int findNthDigit(int n) {
        long nn = (long)n+1;
        long digit = 1;
        long mul = 1;
        long count = 1;
// 计算位数
        while(count<nn){
            count+=9*digit*mul;
            digit++;
            mul = mul*10;
        }
        digit--;
        mul = mul/10;
// 找到开始的位置
        count = count-mul*digit*9;
        long left = nn-count;
        long begin = mul-1;
        begin = begin+left/digit;
        if(left%digit == 0){
            String str = String.valueOf(begin);
            return str.charAt(str.length()-1)-'0';
        }else{
            String str = String.valueOf(begin+1);
            return str.charAt((int)(left%digit-1))-'0';
        }
    }

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值