《剑指offer》Java题解

文章目录

数组中重复的数字

解法一:排序、遍历
解法二:哈希计数
解法三:

class Solution {
    public int findRepeatNumber(int[] nums) {
        // 利用数字特点,逐个插桩归位,如果位置上已经是对应值,说明重复
        for (int i = 0; i < nums.length; i++) {
            while (nums[i] != i) {
                int temp = nums[i];
                if (temp == nums[temp])
                    return temp;
                nums[i] = nums[temp];
                nums[temp] = temp;
            }
        }
        return -1;
    }
}

二维数组中的查找

public class Solution {
    public boolean Find(int target, int [][] array) {
        int row = array.length;
        int col = array[0].length;
        // 解法一
        // 逐行二分查找
        /*
        for (int i = 0; i < row; i++) {
            int left = 0, right = col - 1;
            while (left <= right) {
                int mid = (left + right) >>> 1;
                if (array[i][mid] == target)
                    return true;
                else if (array[i][mid] > target)
                    right = mid - 1;
                else
                    left = mid + 1;
            }
        }
        return false;
        */
        // 解法二
        // 站在右上角看数组,其实是个二叉排序树,所以可以按照二叉排序树的查找方式进行二分查找
        // 从右上角开始比较
        int m = 0;
        int n = col - 1;
        while (m < row && n >= 0) {
            if (matrix[m][n] == target)
                return true;
            // 若小于,那么该行前边的所有元素肯定都小于,直接下一行
            else if (matrix[m][n] < target)
                m++;
            // 若大于,该列下边所有元素肯定都大于,直接往前走一列
            else
                n--;
        }
        return false;
    }
}

原地替换字符串空格

class Solution {
public:
	void replaceSpace(char *str,int length) {
        if (str == NULL)
            return;
        int numOfBlank = 0;
        int i = 0;
        // 统计空格个数
        while (str[i] != '\0') {
            if (str[i] == ' ')
                numOfBlank++;
            i++;
        }
        // 双指针
        // 找到新字符串的末尾位置,从后往前替换
        int newLen = length + numOfBlank * 2;
        while (length >= 0 && newLen > length) {
            if (str[length] == ' ') {
                str[newLen--] = '0';
                str[newLen--] = '2';
                str[newLen--] = '%';
            } else {
                str[newLen--] = str[length];
            }
            length--;
        }

	}
};
// 从前往后,没啥意思
class Solution {
    public String replaceSpace(String s) {
        StringBuilder sb = new StringBuilder();
        for (char c : s.toCharArray()) {
            if (c == ' ')
                sb.append("%20");
            else
                sb.append(c);
        }
        return sb.toString();
    }
}
// 从后往前,学习
public class Solution {
    public String replaceBlank(String input) {
        if (input == null) {
            return null;
        }
        int blankNum = 0;
        int length = input.length();
        int newLength = 0;
        for (int i = 0; i < length; i++) {
            if (input.charAt(i) == ' ') {
                blankNum++;
            }
        }
        // 替换后的字符串长度
        newLength = length + 2 * blankNum;
        char[] newChars = new char[newLength];
        int index = newLength - 1;
        for (int i = length - 1; i >= 0; i--) {
            char c = input.charAt(i);
            if (c == ' ') {
                newChars[index--] = '0';
                newChars[index--] = '2';
                newChars[index--] = '%';
            } else {
                newChars[index--] = c;
            }
        }
        return new String(newChars);
    }
}

从尾到头打印链表

/**
*    public class ListNode {
*        int val;
*        ListNode next = null;
*
*        ListNode(int val) {
*            this.val = val;
*        }
*    }
*
*/
import java.util.ArrayList;
import java.util.Stack;
public class Solution {
    // 解法一:栈
    public ArrayList<Integer> printListFromTailToHead(ListNode listNode) {
        Stack<ListNode> stack = new Stack<>();
        ArrayList<Integer> list = new ArrayList<>();
        while (listNode != null) {
            stack.push(listNode);
            listNode = listNode.next;
        }
        while (!stack.empty()) {
            list.add(stack.pop().val);
        }
        return list;
    }
    // 解法二:递归,其实还是栈...
    public static ArrayList<Integer> printListFromTailToHead(ListNode listNode) {
        ArrayList<Integer> list = new ArrayList<>();

        if (listNode != null) {
            if (listNode.next != null) {
                list = printListFromTailToHead(listNode.next);
            }
            list.add(listNode.val);
        }
        return list;
    }
}

重建二叉树

/**
 * Definition for binary tree
 * public class TreeNode {
 *     int val;
 *     TreeNode left;
 *     TreeNode right;
 *     TreeNode(int x) { val = x; }
 * }
 */
public class Solution {
    private int[] pre;
    private int[] in;
    public TreeNode reConstructBinaryTree(int [] pre, int [] in) {
        if (pre == null || pre.length == 0)
            return null;
        this.pre = pre;
        this.in = in;
        return create(0, pre.length - 1, 0, in.length - 1);
    }
    
    private TreeNode create(int preL, int preR, int inL, int inR) {
        if (preL > preR || inL > inR)
            return null;
        
        TreeNode root = new TreeNode(pre[preL]);
        // 寻找中序遍历中根节点的位置
        int k;
        for (k = inL; k < inR; k++) {
            if (in[k] == pre[preL])
                break;
        }
        int len = k - inL;
        root.left = create(preL + 1, preL + len, inL, k - 1);
        root.right = create(preL + len + 1, preR, k + 1, inR);
        return root;
    }
}

二叉树的下一个结点

/*
public class TreeLinkNode {
    int val;
    TreeLinkNode left = null;
    TreeLinkNode right = null;
    TreeLinkNode next = null;

    TreeLinkNode(int val) {
        this.val = val;
    }
}
*/
public class Solution {
    public TreeLinkNode GetNext(TreeLinkNode pNode)
    {
        if (pNode == null)
            return null;
        // 如果结点有右孩子,那么下一个结点是右孩子的最左结点
        if (pNode.right != null) {
            TreeLinkNode node = pNode.right;
            while (node.left != null)
                node = node.left;
            return node;
        // 否则,如果结点的父结点不为空
        }
        /*
        else if (pNode.next != null) {
            // 那么往上追溯,直到找到一个结点,是其父结点的左孩子
            // 那么下一个结点就是这个结点的父节点。
            // 如果没有这样的结点则返回null
            // 这里涵盖了该结点自身就是左孩子的情况,那么循环会直接返回,
            // 即它的父结点就是中序遍历的下一个结点
            TreeLinkNode nextNode = pNode.next;
            TreeLinkNode curNode = pNode;
            while (nextNode != null && curNode != nextNode.left) {
                curNode = nextNode;
                nextNode = nextNode.next;
            }
            return nextNode;
        }
        return null;
        */
        // 另一种写法
        while (pNode.next != null) {
            if (pNode.next.left == pNode)
                return pNode.next;
            pNode = pNode.next;
        }
        return null;
    }
}

用两个栈实现队列

import java.util.Stack;

public class Solution {
    Stack<Integer> stack1 = new Stack<Integer>();
    Stack<Integer> stack2 = new Stack<Integer>();
    
    public void push(int node) {
        stack1.push(node);
    }
    
    public int pop() {
        if (stack2.empty()) {
            if (stack1.isEmpty())
                return -1;
            while (!stack1.empty()) {
                stack2.push(stack1.pop());
            }
        }
        return stack2.pop();
    }
}

斐波那契数列

public class Solution {
    public int Fibonacci(int n) {
        if (n <= 0)
            return 0;
        if (n == 1)
            return 1;
        int a = 0;
        int b = 1;
        int MOD = 1_000_000_007
        for (int i = 2; i <= n; i++) {
            // 这里针对溢出的判断也可以采用减法,即如果 c 大于MOD,就减去MOD
            int c = (a + b) % MOD;
            a = b;
            b = c;
        }
        return b;
    }
}

青蛙跳台阶也是斐波那契:
青蛙一次能跳1个或2个台阶,问跳上n个台阶有几种跳法?

public class Solution {
    public int JumpFloor(int target) {
        if (target  <= 2)
            return target;
        int a = 1;
        int b = 2;
        for (int i = 3; i <= target; i++) {
            int c = a + b;
            a = b;
            b = c;
        }
        return b;
    }
}

矩形覆盖也是斐波那契:
用2*1的小矩形横着或者竖着去覆盖更大的矩形。请问用n个2*1的小矩形无重叠地覆盖一个2*n的大矩形,总共有多少种方法?

public class Solution {
    public int RectCover(int target) {
        if (target <= 2)
            return target;
        int a = 1;
        int b = 2;
        for (int i = 3; i <= target; i++) {
            int c = a + b;
            a = b;
            b = c;
        }
        return b;
    }
}

变态青蛙跳台阶

public class Solution {
    public int JumpFloorII(int target) {
        if (target <= 1)
            return 1;
        int count = 0;
        // 由于这次可以一下跳任意多个台阶,所以最后一步可能从任意一个位置到达
        // 所以总次数等于之前所有台阶的次数之和,而不是简单的前两个位置次数之和
        for (int i = 1; i <= target; i++) {
            count += JumpFloorII(i - 1);
        }
        return count;
    }
}

另外,可以发现,这次上n个台阶次数为2的n-1次方

public class Solution {
    public int JumpFloorII(int target) {
        return (int)Math.pow(2.0, target - 1);
    }
}

旋转数组的最小数字

import java.util.ArrayList;
public class Solution {
    public int minNumberInRotateArray(int [] array) {
        int left = 0;
        int right = array.length - 1;
        while (left < right) {
            int mid = (left + right) >>> 1;
            if (array[mid] > array[right])
                left = mid + 1;
            else if (array[mid] < array[right])
                right = mid;
            else
                right--;
        }
        return array[right];
    }
}

矩阵中的路径

public class Solution {
    private boolean[][] visited;
    private int rows;
    private int cols;
    private char[] str;
    private char[] matrix;
    
    public boolean hasPath(char[] matrix, int rows, int cols, char[] str) {
        if (matrix == null || rows < 1 || cols < 1 || str == null)
            return false;
        
        this.visited = new boolean[rows][cols];
        this.rows = rows;
        this.cols = cols;
        this.str = str;
        this.matrix = matrix;
        int index = 0;
        for (int i = 0; i < rows; i++) {
            for (int j = 0; j < cols; j++) {
                if (hasPath0(i, j, index))
                    return true;
            }
        }
        return false;
    }

    private boolean hasPath0(int x, int y, int index) {
        // 如果当前下标已经达到给定字符串的长度,则说明已经匹配完成,返回 true
        if (index == str.length)
            return true;
        
        boolean hasPath = false;
        // 判断边界条件、是否访问过、当前对应元素是否相等
        boolean flag = x >= 0 && x < rows
            && y >= 0 && y < cols
            && !visited[x][y]
            && matrix[x * cols + y] == str[index];
        // 如果满足条件,则尝试这一步
        if (flag) {
            // 匹配长度加1
            index++;
            // 标记当前位置已经访问
            visited[x][y] = true;
            // 递归周围四个位置
            hasPath = hasPath0(x + 1, y, index)
                || hasPath0(x - 1, y, index)
                || hasPath0(x, y + 1, index)
                || hasPath0(x, y - 1, index);
            // 若当前周围四个都不满足,则说明这一点是死胡同,需要回溯
            // 并且要将当前位置的访问标志恢复为 false
            if (!hasPath) {
                visited[x][y] = false;
            }
        }
        return hasPath;
    }

}

机器人的运动范围

类似上题,回溯

public class Solution {
    private int rows;
    private int cols;
    private int threshold;
    private boolean[][] visited;
    public int movingCount(int threshold, int rows, int cols) {
        if (threshold < 0 || rows <= 0 || cols <= 0)
            return 0;
        
        this.rows = rows;
        this.cols = cols;
        this.threshold = threshold;
        this.visited = new boolean[rows][cols];
        
        return helper(0, 0);
    }
    
    private int helper(int x, int y) {
        int count = 0;
        
        boolean flag = x >= 0 && x < rows
            && y >= 0 && y < cols
            && !visited[x][y]
            && getDigitSum(x) + getDigitSum(y) <= threshold;
        if (flag) {
            visited[x][y] = true;
            count = 1 + helper(x - 1, y)
                + helper(x + 1, y)
                + helper(x, y - 1)
                + helper(x, y + 1);
        }
        return count;
    }
    
    private int getDigitSum(int x) {
        int sum = 0;
        while (x > 0) {
            sum += x % 10;
            x /= 10;
        }
        return sum;
    }
}

剪绳子

public class Solution {
    private int[] dp;
    public int cutRope(int target) {
        if (target < 2)
            return 0;
        if (target == 2)
            return 1;
        if (target == 3)
            return 2;
        /*
        // 动态规划
        dp = new int[target + 1];
        dp[1] = 1;
        dp[2] = 2;
        dp[3] = 3;
        int max;
        for (int i = 4; i <= target; i++) {
            max = 0;
            for (int j = 1; j <= i / 2; j++) {
                int count = dp[j] * dp[i - j];
                max = Math.max(max, count);
            }
            dp[i] = max;
        }
        return dp[target];
        */
        // 贪心
        int timesOf3 = target / 3;
        if (target % 3 == 1)
            timesOf3--;
        int timesOf2 = (target - timesOf3 * 3) / 2;
        return (int)Math.pow(3, timesOf3) * (int)Math.pow(2, timesOf2);
    }
}

二进制中1的个数

public class Solution {
    public int NumberOf1(int n) {
        /*
        // Java解法
        int count = 0;
        while (n != 0) {
            count += (n & 1);
            n >>>= 1;
        }
        return count;
        */
        /*
        // 针对C/C++类语言(不区分循环右移和逻辑右移)的解法,使测试位左移即可。
        int count = 0;
        int flag = 1;
        while (flag != 0) {
            if ((n & flag) != 0)
                count++;
            flag <<= 1;
        }
        return count;
        */
        // 一个数减1,会使得这个数的最右边1变成0,而该位右边原本自然全是0,现在变成全是1;
        // 然后将该数 n 与 n-1 做与运算,将会清空最右边1及其右边各位,而左边保持不变;
        // 下次再进行这样的运算,又会清除最右边的1,即原数中的右数第二个1,以此类推;
        // 原数中有几个1,循环执行几次,所以一般循环次数要比上述方法少,效率高。
        // 例如:n = 1100. n-1 = 1011
        // n & (n - 1) = 1000,count加1,n = 1000,n - 1 = 0111
        // n & (n - 1) = 0. count加1,结束循环,count = 2

        int count = 0;
        while (n != 0) {
            count++;
            n &= (n - 1);
        }
        return count;
    }
}

延申:

  1. 判断一个数是不是2的整数次幂。

因为2的整数次幂只有最高位是1,所以只需要判断这个数 n 与 n-1 的与运算结果是不是0就可以了。

  1. 输入两个整数 m 和 n ,计算需要改变 m 中的多少位才能让它变成 n。比如10(1010)和13(1101),需要改变3位。

只需要计算两个数有多少位是不一样的即可,这里可以借助异或操作——相同为0,不同为1;然后统计异或结果中 1 的个数即可。

数值的整数次方

public class Solution {
    public double Power(double base, int exponent) {
        // 标志结果是否需要取倒数
        boolean flag = false;
        double ans = 1.0;
        if (Math.abs(base) < 1e-6 && exponent < 0)
            throw new ArithmeticException("by zero");
        // 0的0次幂,未定义。
        if (Math.abs(base) < 1e-6 && exponent == 0)
            return Double.NaN;
        // 这里有一个问题:若exponent = Integer.MIN_VALUE,取反还是自身
        if (exponent < 0) {
            exponent = -exponent;
            flag =  true;
        }
        // 这里隐含溢出的问题
        while (exponent != 0) {
            if ((exponent & 1) == 1)
                ans *= base;
            base *= base;
            exponent >>>= 1;
            
        }
        return flag ? 1.0 / ans : ans;
  }
}

打印从1到最大的n位数

陷阱,n可能很大,超过基本类型的表示范围

public class Solution {
    private int[] array;
    public void printMaxToNDigits(int n) {
        if (n <= 0)
            return;
        array = new int[n];
        helper(0);
    }
    private void helper(int n) {
        for (int i = 0; i < 10; i++) {
            // 如果还没填充完,就递归填充
            if (n != array.length) {
                array[n] = i;
                helper(n + 1);                
            } else {
                boolean isBeginning0 = true;
                for (int j = 0; j < array.length; j++) {
                    // 过滤前缀0
                    if (isBeginning0 && array[j] != 0)
                        isBeginning0 = false;
                    if (!isBeginning0)
                        System.out.print(array[j]);
                }
                System.out.println();
                return;
            }
        }
    }
}

O(1)时间内,删除链表中给定结点

给定头结点和待删除结点。

/*
 public class ListNode {
    int val;
    ListNode next = null;

    ListNode(int val) {
        this.val = val;
    }
}
*/
public class Solution {
    public void deleteNode(ListNode head, ListNode deleteNode) {
        if (deleteNode == null || head == null)
            return;
        // 如果待删除结点有后续结点,为了保证O(1)复杂度,可以将后继结点数据复制到待删除结点
        // 然后删除后继结点即可。
        if (deleteNode.next != null) {
            ListNode nextNode = deleteNode.next;
            deleteNode.val = nextNode.val;
            deleteNode.next = nextNode.next;
            nextNode = null;
        // 只有一个结点
        } else if (deleteNode == head) {
            head = null;
        // 是尾结点,只能从前往后遍历找到前一个结点了。
        } else {
            ListNode temp = head;
            while (temp.next != deleteNode)
                temp = temp.next;
            temp.next = null;
            deleteNode = null;
        }
        
    }
}

删除链表中重复的结点

/*
 public class ListNode {
    int val;
    ListNode next = null;

    ListNode(int val) {
        this.val = val;
    }
}
*/
public class Solution {
    public ListNode deleteDuplication(ListNode pHead)
    {
        if (pHead == null)
            return null;
        // 防止头结点被删除
        ListNode dummy = new ListNode(-1);
        dummy.next = pHead;
        // pre指向遍历结点的前结点
        ListNode pre = dummy;
        ListNode p = pHead;
        while (p != null && p.next != null) {
            // 如果重复,则往后遍历到不同的val再赋值
            if (p.val == p.next.val) {
                int val = p.val;
                while (p != null && p.val == val)
                    p = p.next;
                pre.next = p;
            // 不重复,直接往后走
            } else {
                pre = p;
                p = p.next;
            }
        }
        return dummy.next;
    }
}

正则表达式匹配

public class Solution {
    private char[] str;
    private char[] pattern;
    
    public boolean match(char[] str, char[] pattern) {
        if (str == null || pattern == null)
            return false;
        // 如果都只有1位,那么有两种情况匹配成功
        // 1. 对应位相等
        // 2. pattern[0] == '.',即可与任意字符匹配
        if (str.length == 1 && pattern.length == 1)
            return str[0] == pattern[0] || pattern[0] == '.';
        this.str = str;
        this.pattern = pattern;
        return matchHelper(0, 0);
    }
    private boolean matchHelper(int sIndex, int pIndex) {
        // 匹配完成
        if (sIndex == str.length && pIndex == pattern.length)
            return true;
        // str未到末尾,pattern就匹配完了
        if (sIndex != str.length && pIndex == pattern.length)
            return false;
        // 对于'*'的处理
        if (pIndex + 1 < pattern.length && pattern[pIndex + 1] == '*') {
            // 如果当前位匹配,分两种情况,一种是严格匹配,一种是pattern位是'.'
            if ((sIndex < str.length && str[sIndex] == pattern[pIndex])
               || (sIndex < str.length && pattern[pIndex] == '.')) {
                // 这里又有三种情况
                // 1. '*'匹配0个字符,直接跳过pattern对应的这两位
                // 2. '*'匹配1个字符,继续进行下一个状态的匹配
                // 3. '*'匹配多个字符,pattern位保持不变,str后移
                return matchHelper(sIndex, pIndex + 2)
                    || matchHelper(sIndex + 1, pIndex + 2)
                    || matchHelper(sIndex + 1, pIndex);
            } else {
                // 如果当前位置不匹配,此时不能直接返回false,还有一种情况:'*'匹配0个字符
                // 应跳过pattern对应两位继续匹配
                // 比如str[sIndex] = 'a'
                // pattern[pIndex] = 'b'
                // pattern[pIndex + 1] = '*'
                // 此时可以理解为匹配0个'b'
                return matchHelper(sIndex, pIndex + 2);
            }
        }
        // 如果当前位置匹配,则往后推进,两种情况:
        // 1. 当前pattern位为'.',且str并没有匹配完
        // 2. 严格匹配
        if ((pattern[pIndex] == '.' && sIndex != str.length) || 
            sIndex != str.length && pattern[pIndex] == str[sIndex])
            return matchHelper(sIndex + 1, pIndex + 1);
        return false;
    }
}

表示数值的字符串

public class Solution {
    public boolean isNumeric(char[] str) {
        if (str == null) {
            return false;
        }
        int index = 0;
        int eCount = 0;
        int point = 0;
        // 如果第一个字符是符号就跳过
        if (str[0] == '-' || str[0] == '+')
            index++;

        for (int i = index; i < str.length; i++) {
            if (str[i] == '-' || str[i] == '+') {
                if (str[i - 1] != 'e' && str[i - 1] != 'E')
                    return false;
                continue;
            }

            if (str[i] == 'e' || str[i] == 'E') {
                eCount++;
                if (eCount > 1)
                    return false;
                if (i == 0 || str[i - 1] < 48 || str[i - 1] > 57 || i == str.length - 1)
                    return false;
                // e后边不能有小数点,这里以point的计数来判断这种情况
                point++;
                continue;
            }

            if (str[i] == '.') {
                point++;
                if (point > 1)
                    return false;
                continue;
            }
            // 出现非数字且不是e/E则返回false(小数点和符号用continue跳过了)
            if ((str[i] < 48 || str[i] > 57) && (str[i] != 'e') && (str[i] != 'E'))
                return false;
        }
        return true;
        
    }
    
}

调整数组顺序使奇数位于偶数前边并保持相对顺序

public class Solution {
    public void reOrderArray(int [] array) {
        if (array == null || array.length == 0)
            return;
        // 插入排序的思想
        for (int i = 1; i < array.length; i++) {
            // 如果是奇数,则往前插
            if ((array[i] & 0x1) == 1) {
                int j = i - 1;
                // 保存当前值
                int temp = array[i];
                // 遍历,直到数组开头,或者到达一个奇数为止
                while (j >= 0 && (array[j] & 0x1) == 0) {
                    // 往后挪一位
                    array[j + 1] = array[j];
                    j--;
                }
                // 将原array[i]插入
                array[j + 1] = temp;
            }
        }
    }
}

如果不考虑相对顺序可以参考快排 partation 的思想:

public class Solution {
    public void reOrderArray(int [] array) {
        if (array == null || array.length == 0)
            return;
        int odd = 0;
        int even = array.length - 1;
        while (odd < even) {
            while (odd < even && (array[odd] & 0x1) == 1)
                odd++;
            while (evev > odd && (array[even] & 0x1) == 0)
                even--;
            if (odd < even) {
                int temp = array[odd];
                array[odd] = array[eve];
                array[even] = temp;
            }
        }
    }
}

链表中倒数第k个结点

双指针

/*
public class ListNode {
    int val;
    ListNode next = null;

    ListNode(int val) {
        this.val = val;
    }
}*/
public class Solution {
    public ListNode FindKthToTail(ListNode head,int k) {
        if (head == null || k == 0)
            return null;
        ListNode p1 = head;
        ListNode p2 = head;
        for (int i = 0; i < k - 1; i++) {
            // 如果当前链表结点个数够k个
            if (p1.next != null)
                p1 = p1.next;
            // 结点个数不足k个
            else
                return null;
        }
        
        while (p1.next != null) {
            p1 = p1.next;
            p2 = p2.next;
        }
        return p2;
    }
}

链表中环的入口结点

双指针经典题目
判断环:两个指针会不会相遇
找入口:相遇后,其中一个放回起始点,然后相同速度移动,会相遇在入口结点。
环上结点的个数:相遇后,一个保持不动,另一个继续遍历并计数,再次相遇即可得到环内结点数。

/*
 public class ListNode {
    int val;
    ListNode next = null;

    ListNode(int val) {
        this.val = val;
    }
}
*/
public class Solution {

    public ListNode EntryNodeOfLoop(ListNode pHead) {
        if (pHead == null)
            return null;
        ListNode slow = pHead.next;
        if (slow == null)
            return null;
        ListNode first = slow.next;
        boolean hasLoop = false;
        while (first != null && slow != null) {
            if (first == slow) {
                hasLoop = true;
                break;
            }
            slow = slow.next;
            first = first.next;
            if (first != null)
                first = first.next;
        }
        if (!hasLoop) {
            return null;
        } else {
            slow = pHead;
            while (first != slow) {
                slow = slow.next;
                first = first.next;
            }
            return slow;
        }
        
    }
}

反转链表

/*
public class ListNode {
    int val;
    ListNode next = null;

    ListNode(int val) {
        this.val = val;
    }
}*/
public class Solution {
    public ListNode ReverseList(ListNode head) {
        ListNode reHead = null;
        ListNode p = head;
        ListNode pre = null;
        while (p != null) {
            ListNode next = p.next;
            // 下个结点为空,也就是当前这个结点是最后一个结点了
            if (next == null)
                reHead = p;
            // 掉转指针
            p.next = pre;
            // 往后遍历
            pre = p;
            p = next;
        }
        return reHead;
    }
}

合并两个排序的链表

/*
public class ListNode {
    int val;
    ListNode next = null;

    ListNode(int val) {
        this.val = val;
    }
}*/
public class Solution {
    public ListNode Merge(ListNode list1,ListNode list2) {
        if (list1 == null)
            return list2;
        if (list2 == null)
            return list1;
        ListNode head = null;
        
        if (list1.val < list2.val) {
            head = list1;
            list1 = list1.next;
        } else {
            head = list2;
            list2 = list2.next;
        }
        ListNode p = head;
        while (list1 != null && list2 != null) {
            if (list1.val < list2.val) {
                p.next = list1;
                p = list1;
                list1 = list1.next;
            } else {
                p.next = list2;
                p = list2;
                list2 = list2.next;
            }
        }
        if (list1 != null)
            p.next = list1;
        if (list2 != null)
            p.next = list2;
        return head;
    }
}

二叉树的镜像

/**
public class TreeNode {
    int val = 0;
    TreeNode left = null;
    TreeNode right = null;

    public TreeNode(int val) {
        this.val = val;

    }

}
*/
import java.util.Stack;
public class Solution {
    public void Mirror(TreeNode root) {
        // 递归
        /*
        if (root == null)
            return;
        if (root.left == null && root.right == null)
            return;
        TreeNode temp = root.left;
        root.left = root.right;
        root.right = temp;
        if (root.left != null)
            Mirror(root.left);
        if (root.right != null)
            Mirror(root.right);
        */
        if (root == null) {
            return ;
        }
        Stack<TreeNode> stack = new Stack<>();

        while (root != null || !stack.isEmpty()) {
            while (root != null) {
                // 交换左右子节点
                if (root.left != null || root.right != null) {
                    TreeNode temp = root.left;
                    root.left = root.right;
                    root.right = temp;
                }

                stack.push(root);
                root = root.left;
            }

            root = stack.pop();
            root = root.right;
        }
    }
}

对称的二叉树

/*
public class TreeNode {
    int val = 0;
    TreeNode left = null;
    TreeNode right = null;

    public TreeNode(int val) {
        this.val = val;

    }

}
*/
public class Solution {
    boolean isSymmetrical(TreeNode pRoot) {
        return helper(pRoot, pRoot);
    }
    // 定义对称的前序遍历,先遍历右孩子,后遍历左孩子,再与传统前序遍历比较,如果都对应相等,则是对称二叉树
    private boolean helper(TreeNode root1, TreeNode root2) {
        // 要将对应的空结点也考虑在内
        if (root1 == null && root2 == null)
            return true;
        if (root1 == null || root2 == null)
            return false;
        if (root1.val != root2.val)
            return false;
        return helper(root1.left, root2.right) && helper(root1.right, root2.left);
    }
}

顺时针打印矩阵

import java.util.ArrayList;
public class Solution {
    public ArrayList<Integer> printMatrix(int [][] matrix) {
       if (matrix == null || matrix.length == 0 || matrix[0].length == 0)
           return null;
        int row = matrix.length;
        int col = matrix[0].length;
        
        ArrayList<Integer> result = new ArrayList<>(row * col);
        int up = 0;
        int down = row - 1;
        int left = 0;
        int right = col - 1;
        
        while (true) {
            // 从左往右
            for (int i = left; i <= right; i++)
                result.add(matrix[up][i]);
            up++;
            if (up > down)
                break;
            // 从上往下
            for (int i = up; i <= down; i++)
                result.add(matrix[i][right]);
            right--;
            if (left > right)
                break;
            // 从右往左
            for (int i = right; i >= left; i--)
                result.add(matrix[down][i]);
            down--;
            if (up > down)
                break;
            // 从下往上
            for (int i = down; i >= up; i--)
                result.add(matrix[i][left]);
            left++;
            if (left > right)
                break;
        }
        return result;
        
    }
}

包含min函数的栈

解法一:辅助栈

import java.util.Stack;

public class Solution {

    private Stack<Integer> dataStack = new Stack<>();
    private Stack<Integer> minStack = new Stack<>();
    public void push(int node) {
        if (minStack.empty()) {
            minStack.push(node);
        } else {
            int min = minStack.peek();
            if (node > min)
                minStack.push(min);
            else
                minStack.push(node);
        }
        dataStack.push(node);
    }
    
    public void pop() {
        if (!dataStack.empty()) {
            dataStack.pop();
            minStack.pop();
        }
    }
    
    public int top() {
        if (!dataStack.empty())
            return dataStack.peek();
        return -1;
    }
    
    public int min() {
        if (!minStack.empty())
            return minStack.peek();
        return -1;
    }
}

解法二:只用一个栈

import java.util.Stack;

public class Solution {

    private Stack<Integer> stack = new Stack<>();
    private int min = Integer.MAX_VALUE;
    
    public void push(int node) {
        // 如果这个值比栈中元素都小,则将原始min压入栈,并更新min,
        if (min >= node) {
            stack.push(min);
            min = node;
        }
        // 否则就直接压入,min依然保存栈中最小值
        stack.push(node);
    }
    
    public void pop() {
        // 如果栈顶元素等于min,说明这个值入栈的时候更新了min,它下边还压着原来的min
        // 所以再把这个元素弹出的时候,也要还原原来的min
        if (stack.pop() == min)
            min = stack.pop();
    }
    
    public int top() {
        return stack.peek();
    }
    
    public int min() {
        return min;
    }
}

栈的压入、弹出序列

乱七八糟写法:

import java.util.Stack;

public class Solution {
    private Stack<Integer> stack = new Stack<>();
    public boolean IsPopOrder(int [] pushA,int [] popA) {
        if (pushA == null || popA == null || pushA.length != popA.length)
            return false;
        int len = pushA.length;
        stack.push(pushA[0]);
        int index1 = 1;
        int index2 = 0;
        while (index1 < len && index2 < len) {
            while (stack.peek() != popA[index2] && index1 < len) {
                stack.push(pushA[index1]);
                index1++;
            }
            if (stack.peek() == popA[index2]) {
                stack.pop();
                index2++;
            } else
                return false;
        }
        if (index2 < len) {
            while (!stack.empty() && stack.peek() == popA[index2]) {
                stack.pop();
                index2++;
            }
        }
        return index2 == len;
    }
}

解法二:有点单调栈的意思,匹配则往后移,不然就一直压栈

import java.util.Stack;

public class Solution {
    public boolean IsPopOrder(int [] pushA,int [] popA) {
        if (pushA == null || popA == null || pushA.length != popA.length)
            return false;
        
        Stack<Integer> stack = new Stack<>();
        int index = 0;
        for (int i = 0; i < pushA.length; i++) {
            stack.push(pushA[i]);
            while (!stack.empty() && stack.peek() == popA[index]) {
                stack.pop();
                index++;
            }
        }
        return stack.empty();
    }
}

从上往下打印二叉树

层序遍历

import java.util.ArrayList;
import java.util.LinkedList;
import java.util.Queue;
/**
public class TreeNode {
    int val = 0;
    TreeNode left = null;
    TreeNode right = null;

    public TreeNode(int val) {
        this.val = val;

    }

}
*/
public class Solution {
    public ArrayList<Integer> PrintFromTopToBottom(TreeNode root) {
        ArrayList<Integer> result = new ArrayList<>();
        if (root == null)
            return result;
        Queue<TreeNode> queue = new LinkedList<>();
        queue.offer(root);
        while (!queue.isEmpty()) {
            TreeNode node = queue.poll();
            result.add(node.val);
            if (node.left != null)
                queue.offer(node.left);
            if (node.right != null)
                queue.offer(node.right);
        }
        return result;
    }
}

层序遍历的扩展——对于需要记录层数或者每层结点个数等等的问题。

public class Solution {
    public ArrayList<Integer> PrintFromTopToBottom(TreeNode root) {
        ArrayList<Integer> result = new ArrayList<>();
        if (root == null)
            return result;
        Queue<TreeNode> queue = new LinkedList<>();
        // 统计下一层的结点个数,待count遍历完后赋值。
        int nextLevel = 0;
        // 保存当前层结点个数,在遍历中递减,减到0说明遍历完一层
        int count = 1;
        queue.offer(root);
        while (!queue.isEmpty()) {
            TreeNode node = queue.poll();
            result.add(node.val);
            count--;
            if (node.left != null) {
                queue.offer(node.left);
                nextLevel++;
            }
            if (node.right != null) {
                queue.offer(node.right);
                nextLevel++;
            }
            if (count == 0) {
                count = nextLevel;
                nextLevel = 0;
            }
        }
        return result;
    }
}

按之字形顺序打印二叉树

import java.util.*;

/*
public class TreeNode {
    int val = 0;
    TreeNode left = null;
    TreeNode right = null;

    public TreeNode(int val) {
        this.val = val;

    }

}
*/
public class Solution {
    public ArrayList<ArrayList<Integer> > Print(TreeNode pRoot) {
        ArrayList<ArrayList<Integer>> result = new ArrayList<>();
        if (pRoot == null)
            return result;
        Queue<TreeNode> queue = new LinkedList<>();
        queue.offer(pRoot);
        int depth = 1;
        while (!queue.isEmpty()) {
            LinkedList<Integer> temp = new LinkedList<>();
            int size = queue.size();
            for (int i = 0; i < size; i++) {
                TreeNode node = queue.poll();
                // 奇数层,从左往右
                if ((depth & 0x1) != 0)
                    temp.offerLast(node.val);
                else
                    temp.offerFirst(node.val);
                if (node.left != null)
                    queue.offer(node.left);
                if (node.right != null)
                    queue.offer(node.right);
            }
            result.add(new ArrayList(temp));
            depth++;
                
        }
        return result;
    }

}

二叉搜素树的后序遍历序列

public class Solution {
    private int[] sequence;
    public boolean VerifySquenceOfBST(int [] sequence) {
        if (sequence == null || sequence.length == 0)
            return false;
        this.sequence = sequence;
        return helper(0, sequence.length - 1);
    }
    
    private boolean helper(int left, int right) {
        if (left > right)
            return false;
        int root = sequence[right];
        int i = 0;
        // 寻找左子树范围,循环终止于第一个大于根结点的结点,即右子树第一个结点
        for (; i < right; i++) {
            if (sequence[i] > root)
                break;
        }
        int j = i;
        for (; j < right; j++) {
            // 如果右子树有小于根节点的,则不符合二叉搜索树要求,返回false
            if (sequence[j] < root)
                return false;
        }
        
        // 如果存在左子树,则判断左子树
        boolean l = true;
        if (i > 0)
            l = helper(0, i - 1);
        // 如果存在右子树,则判断右子树
        boolean r = true;
        if (i < right)
            r = helper(i, right - 1);
        return l && r;
    }
}

二叉树中和为某一值的路径

import java.util.ArrayList;
import java.util.Stack;
/**
public class TreeNode {
    int val = 0;
    TreeNode left = null;
    TreeNode right = null;

    public TreeNode(int val) {
        this.val = val;

    }

}
*/
public class Solution {
    private int target;
    private Stack<Integer> stack = new Stack<>();
    private ArrayList<ArrayList<Integer>> result;
    public ArrayList<ArrayList<Integer>> FindPath(TreeNode root,int target) {
        result = new ArrayList<>();
        if (root == null)
            return result;
        this.target = target;
        helper(root, 0);
        return result;
    }
    private void helper(TreeNode root, int sum) {
        sum += root.val;
        stack.push(root.val);
        // 剪枝,如果当前结果已经大于预期值则直接返回
        // 注意,如果结点值存在复制,则不能有这个判断
        if (sum > target)
            return;
        boolean isLeaf = root.left == null && root.right == null;
        // 这里的返回同样基于结点值非负的前提,否则不能返回,因为后续可能有负值
        if (sum == target) {
            if (isLeaf)
                result.add(new ArrayList(stack));
            else
                return;
        }
        if (root.left != null)
            helper(root.left, sum);
        if (root.right != null)
            helper(root.right, sum);
        // 判断完当前当前结点的子结点后,需要回溯,要将当前结点从路径中删掉
        stack.pop();
    }
}

复杂链表的复制

解法一:暴力,先以next字段构造好链表,然后再处理random字段。对于原始链表中的每一个random,都在新链表中从头遍历寻找。平方复杂度

/*
public class RandomListNode {
    int label;
    RandomListNode next = null;
    RandomListNode random = null;

    RandomListNode(int label) {
        this.label = label;
    }
}
*/
public class Solution {
    public RandomListNode Clone(RandomListNode pHead) {
        if (pHead == null)
            return null;
        RandomListNode head = new RandomListNode(pHead.label);
        RandomListNode p = head;
        RandomListNode q = pHead.next;
        // 以next字段复制链表
        while (q != null) {
            RandomListNode temp = new RandomListNode(q.label);
            p.next = temp;
            p = temp;
            q = q.next;
        }
        // 处理random字段,齐头并进
        q = pHead;
        RandomListNode r = head;
        while (q != null) {
            if (q.random != null) {
                // 对于当前结点的random,在链表中遍历label等于该值的结点
                int val = q.random.label;
                p = head;
                while (p != null && p.label != val)
                    p = p.next;
                r.random = p;
            } else {
                r.random = null;
            }
            // 同步后移
            r = r.next;
            q = q.next;
        }
        return head;
    }
}

解法二:哈希表,以空间换时间。时间、空间复杂度都为O(n)

import java.util.*;
/*
public class RandomListNode {
    int label;
    RandomListNode next = null;
    RandomListNode random = null;

    RandomListNode(int label) {
        this.label = label;
    }
}
*/
public class Solution {
    public RandomListNode Clone(RandomListNode pHead) {
        if (pHead == null)
            return null;
        RandomListNode head = new RandomListNode(pHead.label);
        Map<RandomListNode, RandomListNode> map = new HashMap<>();
        RandomListNode p = head;
        map.put(pHead, head);
        RandomListNode q = pHead.next;
        while (q != null) {
            RandomListNode temp = new RandomListNode(q.label);
            p.next = temp;
            p = temp;
            map.put(q, p);
            q = q.next;
        }
        p = head;
        q = pHead;
        while (q != null) {
            p.random = map.get(q.random);
            p = p.next;
            q = q.next;
        }
        return head;
    }
}

解法三:新旧链表合一,借用原来的指针关系设置新链表的random字段,未使用额外空间,时间复杂度为O(n)

/*
public class RandomListNode {
    int label;
    RandomListNode next = null;
    RandomListNode random = null;

    RandomListNode(int label) {
        this.label = label;
    }
}
*/
public class Solution {
    public RandomListNode Clone(RandomListNode pHead) {
        if (pHead == null)
            return null;
        RandomListNode head = new RandomListNode(pHead.label);
        // 第一步
        // 逐个复制,并把当前结点插入到原链表对应结点的后边
        RandomListNode q = pHead.next;
        pHead.next = head;
        head.next = q;
        while (q != null) {
            RandomListNode temp = new RandomListNode(q.label);
            temp.next = q.next;
            q.next = temp;
            q = temp.next;
        }
        // 第二步,处理random
        // 由于每个新结点都在每个旧结点的后边,所以新结点的random,就在对应旧结点的random的next处
        q = pHead;
        while (q != null) {
            // 这里防止空指针异常
            if (q.random != null)
                q.next.random = q.random.next;
            // q只遍历旧结点,跳过下一个对应的新结点
            // 此外,由于每个结点都有对应的新结点在其后边,所以这里不会出现空指针异常
            q = q.next.next;
        }
        // 第三步,新旧链表拆分
        q = pHead;
        while (q != null) {
            // 该结点对应的新结点
            RandomListNode cloned = q.next;
            // 原始链表中的下一个结点
            RandomListNode src = cloned.next;
            // 将原始链表的next字段指回原来的结点
            q.next = src;
            // 将新结点的next字段指向下一个新结点
            if (src != null)
                cloned.next = src.next;
            // 原始结点向后遍历
            q = src;
        }
        return head;
    }
}

二叉搜索树与双向链表

/**
public class TreeNode {
    int val = 0;
    TreeNode left = null;
    TreeNode right = null;

    public TreeNode(int val) {
        this.val = val;

    }

}
*/
public class Solution {
    public TreeNode Convert(TreeNode pRootOfTree) {
        if (pRootOfTree == null)
            return null;
        TreeNode last = helper(pRootOfTree, null);
        while (last.left != null) {
            last = last.left;
        }
        return last;
    }
    // 传入当前结点和这个结点前排好序的最后一个结点
    // 基于中序遍历
    private TreeNode helper(TreeNode cur, TreeNode last) {
        if (cur == null)
            return null;
        // 递归处理左子树
        if (cur.left != null)
            last = helper(cur.left, last);
        // 处理当前结点,将当前结点与左子树最后一个结点相连接
        cur.left = last;
        if (last != null)
            last.right = cur;
        // 更新最后一个结点为cur,因为cur此时是处理完的最后一个结点
        last = cur;
        // 递归处理右子树
        if (cur.right != null)
            last = helper(cur.right, last);
        return last;
    }
}

序列化二叉树

还是前序遍历的思想

/*
public class TreeNode {
    int val = 0;
    TreeNode left = null;
    TreeNode right = null;

    public TreeNode(int val) {
        this.val = val;

    }

}
*/
public class Solution {
    String Serialize(TreeNode root) {
        if (root == null) {
            return "";
        }
        StringBuilder sb = new StringBuilder();
        helpSerialize(root, sb);
        return sb.toString();
    }
    private void helpSerialize(TreeNode root, StringBuilder sb) {
        if (root == null) {
            sb.append("#!");
            return;
        }
        sb.append(root.val).append("!");
        if (root.left != null) {
            helpSerialize(root.left, sb);
        } else {
            sb.append("#!");
        }
        if (root.right != null) {
            helpSerialize(root.right, sb);
        } else {
            sb.append("#!");
        }
    }
    
    private int index = 0;
    private String[] split;
    TreeNode Deserialize(String str) {
        if (str == null || str.length() == 0)
            return null;
        this.split = str.split("!");
        return helpDeserialize();
    }
    private TreeNode helpDeserialize() {
        if (split[index].equals("#")) {
            index++;
            return null;
        }
        TreeNode node = new TreeNode(Integer.valueOf(split[index]));
        index++;
        node.left = helpDeserialize();
        node.right = helpDeserialize();
        return node;
    }
}
public class Serializer62 {
    public static String serialize(TreeNode root) {
        StringBuffer sb = new StringBuffer();
        if (root == null) {
            sb.append("#,");
            return sb.toString();
        }

        sb.append(root.val + ",");
        sb.append(serialize(root.left));
        sb.append(serialize(root.right));
        return sb.toString();
    }

    private static int index = -1;

    public static TreeNode deserialize(String str) {
        index++;
        int len = str.length();
        String[] strArray = str.split(",");
        TreeNode node = null;

        if (index >= len) {
            return null;
        }

        if (!strArray[index].equals("#")) {
            node = new TreeNode(Integer.valueOf(strArray[index]));
            node.left = deserialize(str);
            node.right = deserialize(str);
        }
        return node;
    }
}

字符串的排列

import java.util.ArrayList;
import java.util.TreeSet;
public class Solution {
    private char[] chars;
    private TreeSet<String> set;
    private int len;
    public ArrayList<String> Permutation(String str) {
        if (str == null || str.length() == 0)
            return new ArrayList<String>();
        set = new TreeSet<>();
        this.chars = str.toCharArray();
        this.len = chars.length;
        helper(0);
        return new ArrayList<String>(set);
    }
    
    private void helper(int index) {
        if (index == len) {
            set.add(new String(chars));
            return;
        }
        for (int i = index; i < len; i++) {

            char c = chars[index];
            chars[index] = chars[i];
            chars[i] = c;

            helper(index + 1);

            c = chars[index];
            chars[index] = chars[i];
            chars[i] = c;

        }
    }
}

数组中出现次数超过一半的数字

public class Solution {
    public int MoreThanHalfNum_Solution(int [] array) {
        if (array == null || array.length == 0)
            return 0;
        int count = 1;
        int data = array[0];
        for (int i = 1; i < array.length; i++) {
            if (count == 0) {
                data = array[i];
                count = 1;
            } else if (array[i] == data) {
                count++;
            } else {
                count--;
            }
        }
        
        if (count > 0) {
            count = 0;
            for (int i : array) {
                if (i == data)
                    count++;
            }
            if (count > array.length / 2)
                return data;
        }
        
        return 0;
    }
}

快排思想

public class Solution {
    private int[] array;
    public int MoreThanHalfNum_Solution(int [] array) {
        if (array == null || array.length == 0)
            return 0;
        this.array = array;
        int middle = array.length >>> 1;
        
        int left = 0;
        int right = array.length - 1;
        int index = partation(left, right);
        while (index != middle) {
            if (index > middle)
                right = index - 1;
            else
                left = index + 1;
            index = partation(left, right);
        }

        int count = 0;
        for (int i : array) {
            if (i == array[index])
                count++;
        }
        if (count > array.length / 2)
            return array[index];
        else
            return 0;

    }
    private int partation(int left, int right) {
        int temp = array[left];
        int begin = left;
        while (left < right) {
            while (left < right && array[left] <= temp)
                left++;
            //array[right] = array[left];
            
            while (left < right && array[right] > temp)
                right--;
            //array[left] = array[right];
            if (left < right) {
                int i = array[left];
                array[left] = array[right];
                array[right] = i;
                left++;
                right--;
            }
        }
        array[begin] = array[left];
        array[left] = temp;
        return left;
    }
    // 挖坑法
    private int partation2(int left, int right) {
        int temp = array[right];
        while (left < right) {
            while (left < right && array[left] <= temp)
                left++;
            array[right] = array[left];
            
            while (left < right && array[right] > temp)
                right--;
            array[left] = array[right];
        }
        array[left] = temp;
        return left;
    }
}

最小的K个数

解法一:快排的partation思想
解法二:大根堆

import java.util.PriorityQueue;
import java.util.Comparator;
import java.util.ArrayList;
public class Solution {
    public ArrayList<Integer> GetLeastNumbers_Solution(int [] input, int k) {
        if (input == null || input.length == 0 || k <= 0 || k > input.length)
            return new ArrayList<Integer>();
        // 大根堆
        PriorityQueue<Integer> pq = new PriorityQueue<>(new Comparator<Integer>() {
            @Override
            public int compare(Integer i1, Integer i2) {
                return i2 - i1;
            }
        });
        // 如果堆中元素不足k个,则直接插入,否则比较堆顶元素,即最大元素,如果大于当前元素,则替换
        for (int i = 0; i < input.length; i++) {
            if (pq.size() < k)
                pq.offer(input[i]);
            else if (pq.peek() > input[i]) {
                pq.poll();
                pq.offer(input[i]);
            }
        }
        return new ArrayList<Integer>(pq);
    }
}

数据流中的中位数

import java.util.PriorityQueue;
public class Solution {
    // 大根堆存储左边元素
    private PriorityQueue<Integer> left = new PriorityQueue<>((i1, i2) -> i2 - i1);
    // 小根堆存储右边元素
    private PriorityQueue<Integer> right = new PriorityQueue<>();
    // 统计数据流中的元素
    private int N = 0;
    public void Insert(Integer num) {
        // 数据流中有偶数个数,那么可以把这个数放入右边
        // 不过需要先和左边最大值比较,放较大的数
        if ((N & 0x1) == 0) {
            if (!left.isEmpty() && num >= left.peek())
                right.offer(num);
            else {
                left.offer(num);
                right.offer(left.poll());
            }
        // 如果当前有奇数个数,那么就是右边比左边多一个
        // 为了保持平衡,需要把这个数放左边,同样也需要和右边最小值比较
        } else {
            if (!right.isEmpty() && num < right.peek())
                left.offer(num);
            else {
                right.offer(num);
                left.offer(right.poll());
            }
        }
        N++;
    }

    public Double GetMedian() {
        if ((N & 0x1) == 0)
            return Double.valueOf((left.peek() + right.peek()) / 2.0);
        else
            return (double)right.peek();
    }


}

连续子数组的最大和

public class Solution {
    public int FindGreatestSumOfSubArray(int[] array) {
        if (array == null || array.length == 0)
            return -1;
        int[] dp = new int[array.length];
        dp[0] = array[0];
        for (int i = 1; i < array.length; i++) {
            dp[i] = Math.max(array[i], dp[i - 1] + array[i]);
        }
        int max = dp[0];
        for (int i = 1; i < dp.length; i++) {
            if (dp[i] > max)
                max = dp[i];
        }
        return max;
    }
}

整数中1出现的次数

public class Solution {
    public int NumberOf1Between1AndN_Solution(int n) {
        int count = 0;

        //思路是分别计算个位、十位、百位........上出现 1 的个数。
        // 分左右两边计算
        // 如果 a 的个位大于1,那么考虑左边会有 (a / 10 + 1) 个,该个位是1的,因为当前是以i为单位,每个数后边的低位还有整i个,所以再乘以i,
        // 如果 a 个位是1,按理说,左边也该有(a / 10 + 1)个, 但是此时右边是半截,只有b个数,并不满足 i 个,所以要单独考虑,加8是为了保证大于等于2的时候才会进1
        //以  n =216为例:
        //个位上: 01 ,11,21,31,.....211。个位上共出现(216/10)+ 1个 1 。因为除法取整,210~216间个位上的1取不到,所以加8进位。n=211怎么办,这里把最后取到的个位数为1的单独考虑,先往下看。
        //十位上:10~19,110~119,210~216.   十位上可看成 求(216/10)=21 个位上的1的个数然后乘10。这里再次把最后取到的十位数为1的单独拿出来,即210~216要单独考虑 ,个数为(216%10)+1 .这里加8就避免了判断的过程。
        //后面以此类推。
        //时间复杂度 O(logN)

        for (int i = 1; i <= n; i *= 10) {
            // a 取n的高位
            int a = n / i;
            // b 取低位
            int b = n % i;
            count += (a + 8) / 10 * i;
            if ((a % 10 == 1))
                count += b + 1;
        }
        return count;
    }
}

把数组排成最小的数

import java.util.*;
import java.util.stream.*;

public class Solution {
    public String PrintMinNumber(int [] numbers) {
        if (numbers == null || numbers.length == 0)
            return "";
        /*
        String[] str = new String[numbers.length];
        // 转为字符串
        for (int i = 0; i < numbers.length; i++) {
            str[i] = String.valueOf(numbers[i]);
        }
        // 比较两个数的大小,就看这两个数排在一起组成的字符串的大小,并决定谁前谁后
        Arrays.sort(str, (s1, s2) -> (s1 + s2).compareTo(s2 + s1));
        StringBuilder sb = new StringBuilder();
        for (String s : str)
            sb.append(s);
        return sb.toString();
        */
        return IntStream.of(numbers)
            .mapToObj(i -> String.valueOf(i))
            .sorted((s1, s2) -> (s1 + s2).compareTo(s2 + s1))
            .collect(Collectors.joining());
    }
}

把数字翻译成字符串

public class Solution {
    public int numDecodings(String s) {
        if (s == null || s.length() == 0)
            return 0;
        if (s.charAt(0) == '0')
            return 0;

        int len = s.length();
        int[] dp = new int[len + 1];

        // 类似于上楼梯,每个位置的次数都由前两个位置的次数决定
        dp[0] = 1;
        for (int i = 0; i < len; i++) {
            // 如果当前位为0,那么不能从i走,只能从i - 1处到i + 1处,所以这里不加i的记录
            // 如果当前位不是0,那么是可能从i走,也可能从i - 1走,这里先把从i走的加上
            dp[i + 1] = s.charAt(i) == '0' ? 0 : dp[i];
            // 如果满足两位的情况,那么可能从当前i走到i + 1,也可能从i - 1走到i + 1
            // 所以这里要再加上i - 1处的记录
            if (i > 0 && (s.charAt(i - 1) == '1'
            ||(s.charAt(i - 1) == '2' && s.charAt(i) <= '6'))) {
                dp[i + 1] += dp[i - 1];
            }
        }
        return dp[len];
        
    }
}

礼物的最大值

import java.util.*;

public class Bonus {
    public int getMost(int[][] board) {
        // write code here
        if (board == null || board.length == 0 || board[0].length == 0)
            return 0;
        int row = board.length;
        int col = board[0].length;
        int[][] dp = new int[row][col];
        for (int i = 0; i < row; i++) {
            for (int j = 0; j < col; j++) {
                int left = 0, up = 0;
                if (i > 0)
                    up = dp[i - 1][j];
                if (j > 0)
                    left = dp[i][j - 1];
                dp[i][j] = Math.max(left, up) + board[i][j];
            }
        }
        return dp[row - 1][col - 1];
    }
}

优化:

import java.util.*;

public class Bonus {
    public int getMost(int[][] board) {
        // write code here
        if (board == null || board.length == 0 || board[0].length == 0)
            return 0;
        int row = board.length;
        int col = board[0].length;
        int[] dp = new int[col];
        // 例如:第0行的时候,一行扫描完后,dp[j]记录的是当前行到达这个位置礼物的最大值,因为只有left会根据上一个位置更新,up一直是0
        // 第1行的时候,首先up=dp[0],也就是上一行扫描到此得到的最大礼物值,然后dp[0]会更新,即再加上当前位置的值
        // 然后随着j往后走,每一个位置的up都等于dp[j],因为还没更新dp[j],它还是保存的上一行这个位置的最大价值
        // 而left变了,left=dp[j - 1],dp[j - 1]在上一次已经修改,就是j - 1处的礼物最大值,也就是所需的当前位置的left。
        for (int i = 0; i < row; i++) {
            for (int j = 0; j < col; j++) {
                int left = 0, up = 0;
                // 此时dp[j]表示的是上一行扫描到这个列时候,礼物的最大值
                if (i > 0)
                    up = dp[j];
                // 此时dp[j - 1]表示的是目前按列扫描,当前位置左边位置礼物的最大值
                if (j > 0)
                    left = dp[j - 1];
                // 按列扫描的时候,这里得到的dp[j]表示走到[i][j]最大价值
                dp[j] = Math.max(left, up) + board[i][j];
            }
        }
        return dp[col - 1];
    }
}

最长不含重复字符的子字符串

动态规划:f(i)表示到位置i满足要求的最长的子字符串
1. 如果当前位置字符没出现过,那么长度加1f(i) = f(i - 1) + 1
2. 如果当前这个字符之前出现过了
  2.1 判断两次出现的距离 d(所以这里需要记录每个字母上次出现的位置),如果d 小于等于f(i - 1),说明上个字符是参与在f(i - 1)中的,那么当前位置又出现就打断了之前的字符串,
  只能把上次出现的位置以前的丢弃,但是中间夹着的是不重复的,所以f(i) = d
  2.2 如果两次出现的距离 d 大于f(i - 1),说明这个字符虽然之前出现过,但是并没有参与在f(i - 1)的计算中,并不在它所指的子串里,所以这里这个元素对于上个位置的最长字串也是第一次,直接f(i) = f(i - 1) + 1即可。
public class Solution {
    public int longestSubstringWithoutDuplication(String s) {
        if (s == null || s.length() == 0)
            return 0;
        int cur = 0;
        int max = 0;
        // 记录字符上次出现的位置
        int[] position = new int[26];
        Arrays.fill(position, -1);
        for (int i = 0; i < s.length(); i++) {
            int index = s.charAt(i) - 'a';
            int preIndex = position[index];
            // 当前元素没出现过,或者距离很远,不在当前记录的子串中
            if (preIndex < 0 || i - preIndex > cur)
                cur++;
            else {
                // 循环里只需要在这里判断下就行了
                if (cur > max)
                    max = cur;
                // 将当前长度更新为d
                cur = i - preIndex;
            }
            // 记录或更新当前元素出现的位置
            position[index];

        }
        // 不需要在循环里每次判断,循环里只需要保证在将cur改为d的时候,和最大值比较就行了
        if (cur > max)
            max = cur;
        return max;
    }
}

丑数(只含2、3、5因子的数字)

public class Solution {
    public int GetUglyNumber_Solution(int index) {
        // 1-6 分别对应前6个丑数
        if (index <= 6)
            return index;
        // 记录经过计算可能会成为下一丑数的丑数,该丑数已经记录在当前数组
        int i2 = 0, i3 = 0, i5 = 0;
        // 数组只保存丑数
        int[] dp = new int[index];
        dp[0] = 1;
        for (int i = 1; i < index; i++) {
            // 乘以2可能会成为下个丑数的值。3、5类似
            int next2 = dp[i2] * 2;
            int next3 = dp[i3] * 3;
            int next5 = dp[i5] * 5;
            // 取三者最小值
            dp[i] = Math.min(next2, Math.min(next3, next5));
            // 如果最终取出来是i2对应的那个数,那么i2可以很安全地加1,因为这个数乘2的值已经被记录在dp[i]了。
            // 3、5类似
            if (dp[i] == next2)
                i2++;
            if (dp[i] == next3)
                i3++;
            if (dp[i] == next5)
                i5++;
            
            
        }
        return dp[index - 1];
    }
}

第一个只出现一次的字符

public class Solution {
    public int FirstNotRepeatingChar(String str) {
        if (str == null || str.length() == 0)
            return -1;
        int[] chars = new int[256];
        for (int i = 0; i < str.length(); i++) {
            chars[str.charAt(i)]++;
        }
        for (int i = 0; i < str.length(); i++) {
            if (chars[str.charAt(i)] == 1)
                return i;
        }
        return -1;
    }
}

字符流中第一个不重复的字符

import java.util.*;
public class Solution {
    private int[] chars = new int[256];
    private Queue<Character> queue = new LinkedList<>();
    //Insert one char from stringstream
    public void Insert(char ch)
    {
        chars[ch]++;
        queue.offer(ch);
        while (!queue.isEmpty() && chars[queue.peek()] > 1)
            queue.poll();
    }
  //return the first appearence once char in current stringstream
    public char FirstAppearingOnce()
    {
        return queue.isEmpty() ? '#' : queue.peek();
    }
}

数组的逆序对数

public class Solution {
    private long count = 0;
    private int[] temp;
    public int InversePairs(int [] array) {
        if (array == null || array.length == 0)
            return 0;
        temp = new int[array.length];
        mergeSort(array, 0, array.length - 1);
        return (int)(count % 1_000_000_007);
    }
    private void mergeSort(int[] array, int left, int right) {
        if (left + 1 > right) {
            return;
        }
        int mid = (left + right) >>> 1;
        mergeSort(array, left, mid);
        mergeSort(array, mid + 1, right);
        merge(array, left, mid, right);
    }
    private void merge(int[] array, int left, int mid, int right) {
        // i, j指向待归并的两个数组的起始位置,k指向辅助数组的位置
        int i = left, k = left, j = mid + 1;
        while (i <= mid && j <= right) {
            // 不存在逆序
            if (array[i] <= array[j]) {
                temp[k] = array[i++];
            } else {
                temp[k] = array[j++];
                // 因为子数组是有序的,所以如果array[i] > array[j],那么左半边数组剩下的值都比array[j]大
                this.count += mid - i + 1;
            }
            k++;
        }
        // 如果存在某个数组还有剩余,那么按序放入temp即可
        while (i <= mid)
            temp[k++] = array[i++];
        while (j <= right)
            temp[k++] = array[j++];
        // 将排好序的数组复制回原数组
        for (k = left; k <= right; k++)
            array[k] = temp[k];
    }
}

两个链表的第一个公共结点

/*
public class ListNode {
    int val;
    ListNode next = null;

    ListNode(int val) {
        this.val = val;
    }
}*/
public class Solution {
    public ListNode FindFirstCommonNode(ListNode pHead1, ListNode pHead2) {
         if (pHead1 == null || pHead2 == null)
             return null;
        ListNode p1 = pHead1;
        ListNode p2 = pHead2;
        // 计算两个链表长度
        int len1 = 0, len2 = 0;
        while (p1 != null) {
            len1++;
            p1 = p1.next;
        }
        while (p2 != null) {
            len2++;
            p2 = p2.next;
        }
        // 让较长的链表先往后走,保证两者同步
        int d = Math.abs(len1 - len2);
        p1 = pHead1;
        p2 = pHead2;
        if (len1 <= len2) {
            while (d != 0) {
                d--;
                p2 = p2.next;
            }
        } else {
            while (d != 0) {
                d--;
                p1 = p1.next;
            }
        }
        
        while (p1 != null && p2 != null) {
            // 找到公共结点直接返回
            if (p1 == p2)
                return p1;
            p1 = p1.next;
            p2 = p2.next;
        }
        return null;
    }
}

数字在排序数组中出现的次数

解法一:

public class Solution {
    public int GetNumberOfK(int [] array , int k) {
        if (array == null || array.length == 0)
           return 0;
        int first = binarySearchFirst(array, k);
        int last = binarySearchLast(array, k);
        if (first != -1 && last != -1)
            return last - first + 1;
        return 0;
    }
    // 寻找左边界
    private int binarySearchFirst(int[] array, int k) {
        int left = 0;
        int right = array.length;
        
        while (left < right) {
            int mid = (left + right) >>> 1;
            if (array[mid] > k)
                right = mid;
            else if (array[mid] < k)
                left = mid + 1;
            else
                right = mid;
        }
        if (left == array.length)
            return -1;
        return array[left] == k ? left : -1;
    }
    // 寻找右边界
    private int binarySearchLast(int[] array, int k) {
        int left = 0;
        int right = array.length;
        
        while (left < right) {
            int mid = (left + right) >>> 1;
            if (array[mid] > k)
                right = mid;
            else if (array[mid] < k)
                left = mid + 1;
            else
                left = mid + 1;
        }
        return left - 1;
    }
}

解法二:只定义一个寻找左边界的二分查找,右边界可以以查找K + 1代替(比K大的数,它的左边界肯定是在K的右边界的下一个位置),不过这里注意不能在二分查找里排除没查找到的情况,要如实返回最终所有的位置,因为搜索K + 1是为了要最终搜索的位置而不管搜没搜到。

public int GetNumberOfK(int[] nums, int K) {
    int first = binarySearch(nums, K);
    int last = binarySearch(nums, K + 1);
    // 因为改造的二分法并不保证最终返回的结果是不是K,所以要在这里判断
    // 这里的判断对应上边解法一寻找左边界的判断。
    return (first == nums.length || nums[first] != K) ? 0 : last - first;
}

private int binarySearch(int[] nums, int K) {
    int left = 0, right = nums.length;
    while (left < right) {
        int mid = (left + right) >>> 1;
        if (nums[mid] >= K)
            right = mid;
        else
            left = mid + 1;
    }
    return left;
}

二叉搜索树的第K个结点

/*
public class TreeNode {
    int val = 0;
    TreeNode left = null;
    TreeNode right = null;

    public TreeNode(int val) {
        this.val = val;

    }

}
*/
public class Solution {
    private int count = 0;
    private TreeNode result;
    TreeNode KthNode(TreeNode pRoot, int k) {
        if (pRoot == null || k == 0)
            return null;
        helper(pRoot, k);
        return result;
    }
    private void helper(TreeNode root, int k) {
        if (root == null || count >= k)
            return;
        helper(root.left, k);
        count++;
        if (count == k) {
            result = root;
            return;
        }
        helper(root.right, k);
    }


}

二叉树的深度

/**
public class TreeNode {
    int val = 0;
    TreeNode left = null;
    TreeNode right = null;

    public TreeNode(int val) {
        this.val = val;

    }

}
*/
public class Solution {
    public int TreeDepth(TreeNode root) {
        if (root == null)
            return 0;
        int left = TreeDepth(root.left);
        int right = TreeDepth(root.right);
        return Math.max(left, right) + 1;
    }
}

数组中出现一次的数字

//num1,num2分别为长度为1的数组。传出参数
//将num1[0],num2[0]设置为返回结果
public class Solution {
    // 思路来源于数组中只有一个数字出现一次,其他出现两次,那么将数组元素逐个异或,最终就得到了所求的那个数字
    // 因为出现两次的异或结果为0
    // 本题基于这个思想,先对数组元素逐个异或
    // 异或结果等于两个所求数字的异或结果,必不为0
    // 寻找异或结果中的最右边1,那么这两个数字在该位上肯定是不同的,所以异或结果才能是1
    // 以此位为掩码,可以将数组一分为二,一个该位为0,另一个该位为1,那么所求的两个数必然被分到两个数组
    // 而相同的数肯定位于同一个数组,所以问题回到在数组中寻找一个出现一次的数,逐个异或即可
    public void FindNumsAppearOnce(int [] array,int num1[] , int num2[]) {
        int temp = 0;
        for (int num : array) {
            temp ^= num;
        }
        /*
        int bit = 1;
        while ((temp & bit) == 0)
            bit <<= 1;
        */
        int bit = temp & (-temp);
        for (int num : array) {
            if ((num & bit) != 0)
                num1[0] ^= num;
            else
                num2[0] ^= num;
        }
    }
}

数组中唯一只出现一次的数字,其他数字出现3次

沿用位运算,计算 int 型每一位的和,如果该位能被3整除,说明所求的数这一位是0,否则是1。
因为对于其他出现3次的数,每一位的和都是能被3整除的。

public class Solution {
    public int findNumAppearOnce(int[] array) {
        if (array == null || array.length == 0)
            return -1;
        int[] bitSum = new int[32];

        for (int i = 0; i < array.length; i++) {
            int mask = 1;
            for (int j = 31; j >= 0; j--) {
                int bit = array[i] & mask;
                if (bit != 0)
                    bitSum[j] += 1;
                mask <<= 1;
            }
        }
        int result = 0;
        for (int i = 0; i < 32; i++) {
            result <<= 1;
            result += bitSum[i] % 3;
        }
        return result;
    }
}

和为sum的两个数字

import java.util.ArrayList;
public class Solution {
    public ArrayList<Integer> FindNumbersWithSum(int [] array,int sum) {
        ArrayList<Integer> result = new ArrayList<>();
        if (array == null || array.length == 0)
            return result;
        int left = 0;
        int right = array.length - 1;
        while (left < right) {
            int s = array[left] + array[right];
            if (s > sum)
                right--;
            else if (s < sum)
                left++;
            else {
                result.add(array[left]);
                result.add(array[right]);
                break;
            }
        }
        return result;
    }
}

和为S的连续正数序列

import java.util.ArrayList;
public class Solution {
    public ArrayList<ArrayList<Integer> > FindContinuousSequence(int sum) {
       ArrayList<ArrayList<Integer>> result = new ArrayList<>();
        if (sum <= 0)
            return result;
        int small = 1;
        int big = 2;
        int curSum = small + big;
        int mid = (1 + sum) >>> 1;
        while (small < mid) {
            if (curSum < sum) {
                big++;
                curSum += big;
            } else if (curSum > sum) {
                curSum -= small;
                small++;
            } else {
                ArrayList<Integer> temp = new ArrayList<>();
                for (int i = small; i <= big; i++)
                    temp.add(i);
                result.add(temp);
                big++;
                curSum += big;
            }
        }
        return result;
    }
}

反转单词顺序列

public class Solution {
    // 整体反转,逐个单词反转
    public String ReverseSentence(String str) {
        if (str == null || str.length() <= 1)
            return str;
        char[] ch = str.toCharArray();
        int len = ch.length;
        reverse(ch, 0, len - 1);
        // 记录一个单词的左右边界
        int i = 0, j = 0;
        while (j <= len) {
            // 如果遇到空格,或者达到字符串末尾,则表示一个单词结束,开始反转
            if (j == len || ch[j] == ' ') {
                reverse(ch, i, j - 1);
                i = j + 1;
            }
            j++;
        }
        return new String(ch);
    }
    private void reverse(char[] ch, int left, int right) {
        while (left < right) {
            char c = ch[left];
            ch[left] = ch[right];
            ch[right] = c;
            left++;
            right--;
        }
    }
}

左旋转字符串

public class Solution {
    public String LeftRotateString(String str,int n) {
        if (str == null || str.length() <= 1)
            return str;
        char[] ch = str.toCharArray();
        int len = ch.length;
        n %= len;
        n = (n + len) % len;
        
        reverse(ch, 0, n - 1);
        reverse(ch, n, len - 1);
        reverse(ch, 0, len - 1);
        return new String(ch);
    }
    private void reverse(char[] ch, int left, int right) {
        while (left < right) {
            char c = ch[left];
            ch[left] = ch[right];
            ch[right] = c;
            left++;
            right--;
        }
    }
}

滑动窗口的最大值

import java.util.ArrayList;
import java.util.PriorityQueue;
public class Solution {
    public ArrayList<Integer> maxInWindows(int [] num, int size) {
        ArrayList<Integer> result = new ArrayList<>();
        if (num == null || num.length == 0 || size <= 0 || size > num.length)
            return result;
        PriorityQueue<Integer> heap = new PriorityQueue<>((o1, o2) -> o2 - o1);  /* 大顶堆 */
        for (int i = 0; i < size; i++)
            heap.add(num[i]);
        result.add(heap.peek());
        for (int i = 0, j = i + size; j < num.length; i++, j++) {            /* 维护一个大小为 size 的大顶堆 */
            heap.remove(num[i]);
            heap.add(num[j]);
            result.add(heap.peek());
        }
        return result;
    }
}

n个骰子的点数

解法一:动态规划,二维数组,dp[i][j]表示前i个骰子掷出点数为j的次数

public List<Map.Entry<Integer, Double>> dicesSum(int n) {
    final int face = 6;
    final int pointNum = face * n;
    long[][] dp = new long[n + 1][pointNum + 1];
    // 第一个骰子掷出各个点数的次数均为1
    for (int i = 1; i <= face; i++)
        dp[1][i] = 1;
    // 骰子数
    for (int i = 2; i <= n; i++) {
        // j表示投掷点数,使用 i 个骰子最小点数为 i
        for (int j = i; j <= pointNum; j++) {
            // 状态转移方程,k 表示本轮投掷点数,注意 k 要同时小于 face 和 j。
            for (int k = 1; k <= face && k <= j; k++)
                dp[i][j] += dp[i - 1][j - k];
        }
     
    }


    final double totalNum = Math.pow(6, n);
    List<Map.Entry<Integer, Double>> ret = new ArrayList<>();
    for (int i = n; i <= pointNum; i++)
        ret.add(new AbstractMap.SimpleEntry<>(i, dp[n][i] / totalNum));

    return ret;
}

解法一改进:动态规划+旋转数组。注意到解法一虽然dp数组有 n 行,但是每次都只用了前一行的数据,
随意可以改进dp数组,设置旋转标记,来回使用这两行。将空间复杂度从 O(n^2) 降为 O(n)。

public List<Map.Entry<Integer, Double>> dicesSum(int n) {
    final int face = 6;
    final int pointNum = face * n;
    long[][] dp = new long[2][pointNum + 1];

    for (int i = 1; i <= face; i++)
        dp[0][i] = 1;
    // 旋转标记
    int flag = 1;
    // 注意这里循环也改变了 flag
    for (int i = 2; i <= n; i++, flag = 1 - flag) {
        // 旋转数组清零
        for (int j = 0; j <= pointNum; j++)
            dp[flag][j] = 0;                          

        for (int j = i; j <= pointNum; j++)
            for (int k = 1; k <= face && k <= j; k++)
                dp[flag][j] += dp[1 - flag][j - k];
    }

    final double totalNum = Math.pow(6, n);
    List<Map.Entry<Integer, Double>> ret = new ArrayList<>();
    for (int i = n; i <= pointNum; i++)
        ret.add(new AbstractMap.SimpleEntry<>(i, dp[1 - flag][i] / totalNum));

    return ret;
}

构建乘积数组

B[0] 1 A[1] … A[n]
B[1] A[0] 1 … A[n]
.
.
B[n] A[0] A[1] … 1
构成矩阵,主对角线为1,下边第一个循环计算左下角元素,第二个循环计算右上角

import java.util.ArrayList;
public class Solution {
    public int[] multiply(int[] A) {
        if (A == null || A.length == 0)
            return A;
        int len = A.length;
        int[] B = new int[len];
        // B[0]没有左下角元素,所以赋值1
        B[0] = 1;
        // 借用上一个B的计算结果来累乘这个B
        for (int i = 1; i < len; i++) {
            B[i] = B[i - 1] * A[i - 1];
        }
        // 从右下角往左上角计算
        int temp = 1;
        // B[len - 1]没有右边元素,所以temp初始化为 1
        for (int i = len - 1; i >= 0; i--) {
            B[i] = B[i] * temp;
            // temp 统计右边元素
            temp *= A[i];
        }
        return B;
    }
}

扑克牌顺子

import java.util.Arrays;
public class Solution {
    public boolean isContinuous(int [] numbers) {
        if (numbers.length < 5)
            return false;

        Arrays.sort(numbers);

        // 统计癞子数量
        int cnt = 0;
        for (int num : numbers)
            if (num == 0)
                cnt++;

        // 使用癞子去补全不连续的顺子
        for (int i = cnt; i < numbers.length - 1; i++) {
            // 有对子直接返回 false
            if (numbers[i + 1] == numbers[i])
                return false;
            // 用癞子补空缺
            cnt -= numbers[i + 1] - numbers[i] - 1;
        }

        return cnt >= 0;
    }
}

约瑟夫环

f(n, m) = (f(n - 1, m) + m) % n;

public class Solution {
    public int LastRemaining_Solution(int n, int m) {
        if(n < 1 || m < 1) return -1;
        int s = 0;
        for(int i = 2; i <= n; i++){
            s = (s + m) % i;
        }
        return s;
    }
}

不用加减乘除做加法

public class Solution {
    public int Add(int num1,int num2) {
        // 直到没有进位
        while(num2 != 0){
            // 异或计算诸位相加的结果,没有进位
            int sum = num1 ^ num2;
            // 与运算计算进位,因为只有1&1=1,即产生进位,然后左移1位
            int carray = (num1 & num2) << 1;
            num1 = sum;
            num2 = carray;
        }
        return num1;
    }
}

把二叉树打印成多行

import java.util.*;

/*
public class TreeNode {
    int val = 0;
    TreeNode left = null;
    TreeNode right = null;

    public TreeNode(int val) {
        this.val = val;

    }

}
*/
public class Solution {
    ArrayList<ArrayList<Integer> > Print(TreeNode pRoot) {
        ArrayList<ArrayList<Integer>> result = new ArrayList<>();
        if (pRoot == null)
            return result;
        Queue<TreeNode> queue = new LinkedList<>();
        queue.offer(pRoot);
        while (!queue.isEmpty()) {
            int count = queue.size();
            ArrayList<Integer> temp = new ArrayList<>();
            while (count-- > 0) {
                TreeNode node = queue.poll();
                temp.add(node.val);
                if (node.left != null)
                    queue.offer(node.left);
                if (node.right != null)
                    queue.offer(node.right);
            }
            result.add(temp);
        }
        return result;
    }
    
}

股票的最大利润

public int maxProfit(int[] prices) {
    if (prices == null || prices.length == 0)
        return 0;
    int soFarMin = prices[0];
    int maxProfit = 0;
    for (int i = 1; i < prices.length; i++) {
        soFarMin = Math.min(soFarMin, prices[i]);
        maxProfit = Math.max(maxProfit, prices[i] - soFarMin);
    }
    return maxProfit;
}

加法

计算1+2+…+n,不能使用乘除法、for、while、if、else、switch、case等关键字及条件判断语句(A?B:C)。
利用逻辑与 && 的短路原则,将递归结束条件 n > 0 放在前边,递归计算放在后边。因为是用于逻辑与,所以要将后边的递归计算变为逻辑值。

public class Solution {
    public int Sum_Solution(int n) {
        int sum = n;
        boolean flag = (n > 0) && ((sum += Sum_Solution(n - 1)) > 0);
        return sum;
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值