剑指67题解

剑指offer

需要md私信

1 二维数组中的查找

二维数组中的查找

在一个二维数组中(每个一维数组的长度相同),每一行都按照从左到右递增的顺序排序,每一列都按照从上到下递增的顺序排序。请完成一个函数,输入这样的一个二维数组和一个整数,判断数组中是否含有该整数。

思路:从右上角开始往左下角塞选

public class select2DArry {
    public boolean Find(int target, int[][] array) {
        int m = array.length;
        if (m == 0) throw new IllegalArgumentException();
        int n = array[0].length;

        for (int i = 0, j = n - 1; i < m && j >= 0; ) {
            if (array[i][j] == target) {
                return true;
            } else if (array[i][j] < target) i++;
            else j--;
        }
        return false;
    }
}

2 替换空格

请实现一个函数,将一个字符串中的每个空格替换成“%20”。例如,当字符串为We Are Happy.则经过替换之后的字符串为We%20Are%20Happy。

替换空格

public class Solution {
    public String replaceSpace(StringBuffer str) {
        if(str == null || str.toString().equals(""))return "";
        int count = 0;
		for(char c : str.toString().toCharArray())
            if(c == ' ')
                count++;
        char []res = new char[str.length() + 2 * count];
        int i = res.length - 1, j = str.length() - 1;
        while(i >= 0){
            char c = str.charAt(j --);
            if(c != ' '){
                res[i --] = c;
            }else{
                res[i--] = '0';
                res[i--] = '2';
                res[i--] = '%';
            }
        }
        return new String(res);
    }
}

3 从尾到头打印链表

输入一个链表,按链表从尾到头的顺序返回一个ArrayList。

从尾到头打印链表

public class PrintList {
    ArrayList<Integer> res = new ArrayList<>();

    public ArrayList<Integer> printListFromTailToHead(ListNode listNode) {
        if (listNode != null) {
            printListFromTailToHead(listNode.next);
            res.add(listNode.val);
        }
        return res;
    }
}

4 重建二叉树

输入某二叉树的前序遍历和中序遍历的结果,请重建出该二叉树。假设输入的前序遍历和中序遍历的结果中都不含重复的数字。例如输入前序遍历序列{1,2,4,7,3,5,6,8}和中序遍历序列{4,7,2,1,5,3,8,6},则重建二叉树并返回。

重建二叉树

public class BuildTree {
    public TreeNode reConstructBinaryTree(int[] pre, int[] in) {
        int preL = pre.length, inL = in.length;
        if (pre == null || in == null || preL != inL)
            throw new IllegalArgumentException();

        return build(pre, 0, preL - 1, in, 0, inL - 1);
    }

    //{1,2,4,7,3,5,6,8} 前序遍历  1  是根 (第一个元素)
    //{4,7,2,1,5,3,8,6}    {4,7,2           (左孩子)1(右孩子)           5,3,8,6} 进行递归
    private TreeNode build(int[] pre, int ps, int pe, int[] in, int is, int ie) {
        if (ps > pe || is > ie) return null;

        TreeNode root = new TreeNode(pre[ps]);
        for (int i = is; i <= ie; i++) {
            if (pre[ps] == in[i]) {
                root.left = build(pre, ps + 1, i - is + ps, in, is, i - 1);
                root.right = build(pre, i - is + ps + 1, pe, in, i + 1, ie );
                break;
            }
        }
        return root;
    }
}

5 两个栈实现队列

用两个栈来实现一个队列,完成队列的Push和Pop操作。 队列中的元素为int类型。

用两个栈实现队列

public class Stack2Queue {
    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.isEmpty()) return stack2.pop();
            if(stack1.isEmpty()) throw new NullPointerException();
            while(!stack1.isEmpty()) {
                stack2.push(stack1.pop());
            }
            return stack2.pop();
        }
    }
}

6 旋转数组的最小数字

把一个数组最开始的若干个元素搬到数组的末尾,我们称之为数组的旋转。
输入一个非递减排序的数组的一个旋转,输出旋转数组的最小元素。
例如数组{3,4,5,1,2}为{1,2,3,4,5}的一个旋转,该数组的最小值为1。
NOTE:给出的所有元素都大于0,若数组大小为0,请返回0。

旋转数组的最小数字

思路:二分搜索,如果大于说明这个元素一定不是最小,如果小于说明有可能,如果等于则为重复数字,向左(递减)消除重复数字

public class minNumber {
    public int minNumberInRotateArray(int [] array) {
        if(array.length == 0) return 0;

        int l = 0, r = array.length - 1;
        while(l < r) {
            int mid  = l - (l - r) / 2;
            if(array[mid] > array[r]) l = mid + 1;
            else if(array[mid] == array[r]) r --;
            else r = mid;
        }
        return array[l];
    }
}

7 斐波那契数列

大家都知道斐波那契数列,现在要求输入一个整数n,请你输出斐波那契数列的第n项(从0开始,第0项为0)。n<=39

斐波那契数列

  • 解法一:使用记忆化搜索
/**
 * f(n) = f(n - 1) + f(n - 2)
 */
public class Fibonacci {
    int memo[];
    public int Fibonacci(int n) {
        memo = new int[n + 1];
        return helper(n);
    }
    private int helper(int n) {
        if(n < 2 )return n;
        if(memo[n] != 0) return memo[n];
        memo[n] = helper(n - 1) + helper(n - 2);
        return memo[n];
    }
}
  • 解法二:动态规划
public int Fibonacci2(int n) {
    if(n < 2 )return n;
    return n;
    int fn1 = 0;
    int fn2 = 1;
    int f;
    for(int i = 2 ; i <= n ; i++){
        f = fn2;
        fn2 = fn2 + fn1;
        fn1 = f;
    }
    return fn2;
}

8 跳台阶

一只青蛙一次可以跳上1级台阶,也可以跳上2级。求该青蛙跳上一个n级的台阶总共有多少种跳法(先后次序不同算不同的结果)。

跳台阶

同上斐波那契数列一致

9 变态跳台阶

一只青蛙一次可以跳上1级台阶,也可以跳上2级……它也可以跳上n级。求该青蛙跳上一个n级的台阶总共有多少种跳法。

变态跳台阶

/**
f(1) = 1
f(2) = f(2-1) + f(2-2)         //f(2-2) 表示2阶一次跳2阶的次数。
f(3) = f(3-1) + f(3-2) + f(3-3)
...
f(n) = f(n-1) + f(n-2) + f(n-3) + ... + f(n-(n-1)) + f(n-n)
f(n) = f(n-1)+f(n-2)+...+f(n-(n-1)) + f(n-n) => f(0) + f(1) + f(2) + f(3) + ... + f(n-1)

f(n-1) = f(0) + f(1)+f(2)+f(3) + ... + f((n-1)-1) = f(0) + f(1) + f(2) + f(3) + ... + f(n-2)
f(n) = f(0) + f(1) + f(2) + f(3) + ... + f(n-2) + f(n-1) = f(n-1) + f(n-1) = 2*f(n-1)
**/

public class Solution {
    public int JumpFloorII(int target) {
        if (target <= 0) {
            return -1;
        } else if (target == 1) {
            return 1;
        } else {
            return 2 * JumpFloorII(target - 1);
        }
    }
}

10 矩阵覆盖

我们可以用2 * 1的小矩形横着或者竖着去覆盖更大的矩形。请问用n个2 *1的小矩形无重叠地覆盖一个2 * n的大矩形,总共有多少种方法?

矩形覆盖

同理斐波那数列

public class Solution {
    public int RectCover(int target) {
        if(target == 0)return 0;
        if(target == 1)return 1;
        if(target == 2)return 2;
        int memo[] = new int[target + 1];
        memo[1] = 1;
        memo[2] = 2;
        for(int i = 3; i <= target; i ++ ){
            memo[i] = memo[i - 1] + memo[i - 2];
        }
        return memo[target];
    }
}

11 二进制中1的个数

输入一个整数,输出该数二进制表示中1的个数。其中负数用补码表示。

二进制中1的个数

//一个数 & (这个数 - 1) 会消除掉这个数二进制的最右的一个1
public class Solution {
    public int NumberOf1(int n) {
        int count = 0;
        while(n != 0) {
            n &= n - 1;
            count ++;
        }
        return count;
    }
}

12 数值的整数次方

给定一个double类型的浮点数base和int类型的整数exponent。求base的exponent次方。保证base和exponent不同时为0

数值的整数次方

public class Solution {
    public double Power(double base, int exponent) {
        if(exponent == 0)return 1;
        if(exponent == 1)return base;
        //先不考虑exponent的正负  
        int n = exponent < 0 ? -exponent : exponent;
        //任何大于1的指数都可以化成两个指数相加  奇数的话最后在多乘一遍
        double res = Power(base,n >> 1);
        
        res = res * res;
        
        if((n & 1) == 1)
            res *= base;
        //如果指数小于0,则化成分数
        if(exponent < 0)
            res = 1 / res;
        return res;
  }
}

13 调整数组顺序使奇数位于偶数前面

输入一个整数数组,实现一个函数来调整该数组中数字的顺序,使得所有的奇数位于数组的前半部分,所有的偶数位于数组的后半部分,并保证奇数和奇数,偶数和偶数之间的相对位置不变。

调整数组顺序使奇数位于偶数前面

思路:使用冒泡排序,将偶数下沉到后面

public class Solution {
    //使用冒泡算法  前偶后奇就交换
    public void reOrderArray(int[] array) {
        for (int i = 0; i < array.length; i++) {
            for (int j = 1; j < array.length - i; j++) {
                //前一个奇数,后一个偶数就互换位置
                if (array[j - 1] % 2 == 0 &&
                        array[j] % 2 != 0) {
                    int temp = array[j];
                    array[j] = array[j - 1];
                    array[j - 1] = temp;
                }
            }
        }
    }
}

14 链表中倒数第k个结点

输入一个链表,输出该链表中倒数第k个结点。

链表中倒数第k个结点

思路:先让快指针走k步,之后和起点指针一起走,快指针到达尾部,慢指针的位置就是倒数第k个节点

public class Solution {
    public ListNode FindKthToTail(ListNode head,int k) {
        if(head == null) return null;
        ListNode fast = head;
        ListNode pre = new ListNode(0);
        pre.next = head;
        
        while(k -- > 0) {
            if(fast == null) return null;
            fast = fast.next;
        }
        
        while(fast != null) {
            fast = fast.next;
            pre = pre.next;
        }
        // 如果要删除倒数第k个节点
        // pre.next = pre.next.next;
        return pre.next;
    }
}

15 反转链表

输入一个链表,反转链表后,输出新链表的表头。

反转链表

public class Solution {
    public ListNode ReverseList(ListNode head) {
        if(head == null)return null;
        ListNode pre = null;
        ListNode next;
        
        while(head != null) {
            next = head.next;
            head.next = pre;
            pre = head;
            head = next;
        }
        return pre;
    }
}

16 合并两个排序的链表

输入两个单调递增的链表,输出两个链表合成后的链表,当然我们需要合成后的链表满足单调不减规则。

合并两个排序的链表

public class Solution {
    public ListNode Merge(ListNode list1,ListNode list2) {
        if(list1 == null) return list2;
        if(list2 == null)  return list1;
        ListNode head;
        if(list1.val > list2.val) {
            head = list2;
            head.next = Merge(list1,list2.next);
        } else {
            head = list1;
            head.next = Merge(list1.next,list2);
        }
        return head;
    }
}

17 树的子结构

输入两棵二叉树A,B,判断B是不是A的子结构。(ps:我们约定空树不是任意一个树的子结构)

树的子结构

public class Solution {
    public boolean HasSubtree(TreeNode root1,TreeNode root2) {
        if(root1 == null || root2 == null)return false;
        return isSubTree(root1,root2) || HasSubtree(root1.left,root2) ||
            HasSubtree(root1.right,root2);
    }
    
    private boolean isSubTree(TreeNode node1, TreeNode node2) {
        if(node2 == null)return true;
        if(node1 == null)return false;
        return node1.val == node2.val && isSubTree(node1.left,node2.left) && 
            isSubTree(node1.right,node2.right);
    }
}

18 二叉树的镜像

操作给定的二叉树,将其变换为源二叉树的镜像。

二叉树的镜像

public class Solution {
    public void Mirror(TreeNode root) {
        if(root != null){
            TreeNode temp = root.left;
            root.left = root.right;
            root.right = temp;
            Mirror(root.left);
            Mirror(root.right);
        }
    }
}

19 顺时针打印矩阵

顺时针打印矩阵

public class Solution {
    public ArrayList<Integer> printMatrix(int[][] matrix) {
        if (matrix.length == 0)
            return new ArrayList<>();
        ArrayList<Integer> res = new ArrayList<>();

        int index = 0;
        int row = matrix.length;
        int col = matrix[0].length;

        while (index * 2 < row && index * 2 < col) {
            printMatrix(res, matrix, index, row, col);
            ++index;
        }
        return res;
    }

    private void printMatrix(List<Integer> res, int[][] matrix, int index, int row, int col) {
        row = row - index - 1;
        col = col - index - 1;
        for (int i = index; i <= col; i++) {
            res.add(matrix[index][i]);
        }
        if (index < row) {
            for (int i = index + 1; i <= row; i++) {
                res.add(matrix[i][col]);
            }
        }
        if (index < row && index < col) {
            for (int i = col - 1; i >= index; i--) {
                res.add(matrix[row][i]);
            }
        }

        if (index < col && row - index > 1) {
            for (int i = row - 1; i >= index + 1; i--) {
                res.add(matrix[i][index]);
            }
        }
    }
}

20 包含min函数的栈

定义栈的数据结构,请在该类型中实现一个能够得到栈中所含最小元素的min函数(时间复杂度应为O(1))。

注意:保证测试中不会当栈为空的时候,对栈调用pop()或者min()或者top()方法。

包含min函数的栈

public class Solution {

    int min = Integer.MAX_VALUE;
    Stack<Integer> stack = new Stack<>();

    //保存上一个最小值
    public void push(int node) {
        if (node < min) {
            stack.push(min);
            min = node;
        }
        stack.push(node);
    }

    //如果弹出的是最小值,则设置上一个最小值
    public void pop() {
        if (min == stack.pop())
            min = stack.pop();
    }

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

    public int min() {
        return min;
    }
}

21 栈的压入、弹出序列

输入两个整数序列,第一个序列表示栈的压入顺序,请判断第二个序列是否可能为该栈的弹出顺序。假设压入栈的所有数字均不相等。例如序列1,2,3,4,5是某栈的压入顺序,序列4,5,3,2,1是该压栈序列对应的一个弹出序列,但4,3,5,1,2就不可能是该压栈序列的弹出序列。(注意:这两个序列的长度是相等的)

栈的压入、弹出序列

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

22 从上往下打印二叉树

从上往下打印出二叉树的每个节点,同层节点从左至右打印。

从上往下打印二叉树

public class Solution {
    public ArrayList<Integer> PrintFromTopToBottom(TreeNode root) {
        ArrayList<Integer> res = new ArrayList<>();
        if(root == null)return res;
        Queue<TreeNode> q = new LinkedList<>();
        q.add(root);
        
        while(!q.isEmpty()) {
            TreeNode node = q.remove();
            res.add(node.val);
            if(node.left != null) q.add(node.left);
            if(node.right != null) q.add(node.right);
        }
        return res;
    }
}

23 二叉搜索树的后序遍历序列

输入一个整数数组,判断该数组是不是某二叉搜索树的后序遍历的结果。如果是则输出Yes,否则输出No。假设输入的数组的任意两个数字都互不相同

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

思路:BST的后序序列的合法序列是,对于一个序列S,最后一个元素是x (也就是根),如果去掉最后一个元素的序列为T,那么T满足:T可以分成两段,前一段(左子树)小于x,后一段(右子树)大于x,且这两段(子树)都是合法的后序序列

public class Solution {
    public boolean VerifySquenceOfBST(int [] sequence) {
        if(sequence.length == 0)return false;
        return helper(sequence, 0, sequence.length - 1);
    }
    
    private boolean helper(int[] sequence, int l, int r) {
        int rootIndex = r;
        if(l < r) {
            //找到分割点
            while(l < r && sequence[rootIndex] <= sequence[r]) r--;
            for(int i = l; i < r; i ++) {
                if(sequence[i] >= sequence[rootIndex])return false;
            }
            return helper(sequence, l, r) && helper(sequence, r, rootIndex - 1);
        }
        return true;
    }
}

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

输入一颗二叉树的根节点和一个整数,打印出二叉树中结点值的和为输入整数的所有路径。路径定义为从树的根结点开始往下一直到叶结点所经过的结点形成一条路径。(注意: 在返回值的list中,数组长度大的数组靠前)

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

public class Solution {
    ArrayList<ArrayList<Integer>> res;
    public ArrayList<ArrayList<Integer>> FindPath(TreeNode root,int target) {
        res = new ArrayList<>();
        if(root == null) return res;
        helper(root, new ArrayList<>(), 0, target);
        return res;
    }
    private void helper(TreeNode node, ArrayList<Integer> list, int sum,int target) {
        list.add(node.val);
        sum += node.val;
        //满足两个条件,一个是必须到根节点,一个是target满足
        if(sum == target && node.left == null && node.right == null) {
            res.add(new ArrayList<>(list));
        }
        if(node.left != null) helper(node.left, list, sum, target);
        if(node.right != null) helper(node.right, list, sum, target);
        //在满足之后也需要回溯
        list.remove(list.size() - 1);
    }
}

25 复杂链表的复制

输入一个复杂链表(每个节点中有节点值,以及两个指针,一个指向下一个节点,另一个特殊指针指向任意一个节点),返回结果为复制后复杂链表的head。(注意,输出结果中请不要返回参数中的节点引用,否则判题程序会直接返回空)

复杂链表的复制

public class Solution {
    public RandomListNode Clone(RandomListNode head)
    {
        if(head == null)return null;
        RandomListNode node = head;
        //复制普通节点  直接扩展一倍
        while(node != null){
            RandomListNode dummyNode = new RandomListNode(node.label);
            dummyNode.next = node.next; 
            node.next = dummyNode;
            node = dummyNode.next;
        }
        //复制随机节点
        RandomListNode node2 = head;
        while(node2 != null){
            if(node2.random != null)
                node2.next.random = node2.random.next;
            node2 = node2.next.next;
        }
        //分裂链表
        RandomListNode cHead = head.next;
        RandomListNode node3 = cHead;
        RandomListNode p = head;
        p.next = node3.next;
        p = p.next;
        
        while(p != null){
            node3.next = p.next;
            node3 = node3.next;
            
            p.next = node3.next;
            p = p.next;
        }
        
        return cHead;
    }
}

26 二叉搜索树与双向链表

输入一棵二叉搜索树,将该二叉搜索树转换成一个排序的双向链表。要求不能创建任何新的结点,只能调整树中结点指针的指向。

二叉搜索树与双向链表

public class Solution {
    TreeNode head = null;
    TreeNode end = null;
    public TreeNode Convert(TreeNode pRootOfTree) {
        ConvertSub(pRootOfTree);
        return head;
    }
    //直接终须遍历
    public void ConvertSub(TreeNode pRootOfTree) {
        if(pRootOfTree == null)
            return ;
        Convert(pRootOfTree.left);
        //第一个节点记录头尾指针
        if(end == null){
            head = pRootOfTree;
            end = pRootOfTree;
        }else{
            //不断的向后移动尾节点,直到最后一个节点
            end.right = pRootOfTree;
            pRootOfTree.left = end;
            end = pRootOfTree;
        }
        Convert(pRootOfTree.right);
    }
}

27 字符串的排列

输入一个字符串,按字典序打印出该字符串中字符的所有排列。例如输入字符串abc,则打印出由字符a,b,c所能排列出来的所有字符串abc,acb,bac,bca,cab和cba。

字符串的排列

public class Permutation {
    ArrayList<String> res;
    boolean exist[];

    public ArrayList<String> Permutation(String str) {
        res = new ArrayList<>();
        if (str == null || "".equals(str)) return res;
        exist = new boolean[str.length() + 1];
        char[] chars = str.toCharArray();
        Arrays.sort(chars);
        helper(new String(chars), "");
        return res;
    }

    private void helper(String str, String t) {
        if (str.length() == t.length()) {
            res.add(t);
            return;
        }
        for (int i = 0; i < str.length(); i++) {
            if (!exist[i]) {
                //消除重复字母
                if (i > 0 && str.charAt(i) == str.charAt(i - 1) && !exist[i - 1])
                    continue;
                exist[i] = true;
                t += str.charAt(i);
                helper(str, t);
                t = t.substring(0, t.length() - 1);
                exist[i] = false;
            }
        }
    }
}

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

数组中有一个数字出现的次数超过数组长度的一半,请找出这个数字。例如输入一个长度为9的数组{1,2,3,2,2,2,5,4,2}。由于数字2在数组中出现了5次,超过数组长度的一半,因此输出2。如果不存在则输出0。

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

public class Solution {
    public int MoreThanHalfNum_Solution(int [] array) {
        HashMap<Integer,Integer> map = new HashMap<>();
        int max = array.length/2 + 1;
        for(int i = 0; i < array.length; i++)
        {
            int k = array[i];
            int count = map.getOrDefault(k,0);
            if(count + 1 == max)
                return k;
            else
                map.put(k,count + 1);
        }
        return 0;
    }
}

29 最小的K个数

输入n个整数,找出其中最小的K个数。例如输入4,5,1,6,2,7,3,8这8个数字,则最小的4个数字是1,2,3,4,。

最小的K个数

  • 解法一:使用最大堆(nlog(k))
public class Solution {
   public ArrayList<Integer> GetLeastNumbers_Solution(int[] input, int k) {
       ArrayList<Integer> result = new ArrayList<Integer>();
       int length = input.length;
       if(k > length || k == 0){
           return result;
       }
        PriorityQueue<Integer> maxHeap = new PriorityQueue<Integer>(k, new Comparator<Integer>() {
 
            @Override
            public int compare(Integer o1, Integer o2) {
                return o2.compareTo(o1);
            }
        });
        for (int i = 0; i < length; i++) {
            if (maxHeap.size() != k) {
                maxHeap.offer(input[i]);
            } else if (maxHeap.peek() > input[i]) {
                Integer temp = maxHeap.poll();
                temp = null;
                maxHeap.offer(input[i]);
            }
        }
        for (Integer integer : maxHeap) {
            result.add(integer);
        }
        return result;
    }
}
  • 解法二:partition思想 O(n)
    public ArrayList<Integer> GetLeastNumbers_Solution1(int[] input, int k) {
        ArrayList<Integer> leastNumbers = new ArrayList<Integer>();
        while (input == null || k <= 0 || k > input.length)
            return leastNumbers;
        int start = 0;
        int end = input.length - 1;
        int index = partition(input, start, end);
        while (index != k - 1) {
            if (index < k - 1) {
                start = index + 1;
                index = partition(input, start, end);
            } else {
                end = index - 1;
                index = partition(input, start, end);
            }
        }
        for (int i = 0; i < k; i++) {
            leastNumbers.add(input[i]);
        }
        return leastNumbers;
    }

    private int partition(int[] arr, int start, int end) {
        int pivotKey = arr[start];
        while (start < end) {
            while (start < end && arr[end] >= pivotKey)
                end--;
            swap(arr, start, end);
            while (start < end && arr[start] <= pivotKey)
                start++;
            swap(arr, start, end);
        }
        return start;
    }

    private void swap(int[] arr, int i, int j) {
        int temp = arr[i];
        arr[i] = arr[j];
        arr[j] = temp;
    }

30 连续子数组的最大和

连续子数组的最大和

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

31 整数中1出现的次数(从1到n整数中1出现的次数)(难)

求出113的整数中1出现的次数,并算出1001300的整数中1出现的次数?为此他特别数了一下1~13中包含1的数字有1、10、11、12、13因此共出现6次,但是对于后面问题他就没辙了。ACMer希望你们帮帮他,并把问题更加普遍化,可以很快的求出任意非负整数区间中1出现的次数(从1 到 n 中1出现的次数)。

整数中1出现的次数(从1到n整数中1出现的次数)

public class Solution {
    int countDigitOne(int n) {
        int ones = 0;
        for (long long m = 1; m <= n; m *= 10) {
            int a = n/m, b = n%m;
            ones += (a + 8) / 10 * m + (a % 10 == 1) * (b + 1);
        }
        return ones;
    }
}

32 把数组排成最小的数

输入一个正整数数组,把数组里所有数字拼接起来排成一个数,打印能拼接出的所有数字中最小的一个。例如输入数组{3,32,321},则打印出这三个数字能排成的最小数字为321323。

把数组排成最小的数

public class Solution {
    public String PrintMinNumber(int [] numbers) {
        ArrayList<Integer> list = new ArrayList<>();
        for (int i = 0; i < numbers.length; i++) {
            list.add(numbers[i]);
        }
        Collections.sort(list, new Comparator<Integer>() {
            @Override
            public int compare(Integer o1, Integer o2) {
              String s1 = o1 + "" + o2;
              String s2 = o2 + "" + o1;
              return s1.compareTo(s2);
            }
        });
        String res = "";
        for (int i = 0; i < list.size(); i++) {
            res += list.get(i);
        }
				return res;
    }
}

33 丑数

把只包含质因子2、3和5的数称作丑数(Ugly Number)。例如6、8都是丑数,但14不是,因为它包含质因子7。 习惯上我们把1当做是第一个丑数。求按从小到大的顺序的第N个丑数。

丑数

public class Solution {
    public int GetUglyNumber_Solution(int index) {
        if(index <= 0)return 0;
        List<Integer> list = new ArrayList<>();
        list.add(1);
        int i2 = 0, i3 = 0, i5 = 0;
        
        while(list.size() < index) {
            int t2 = list.get(i2) * 2;
            int t3 = list.get(i3) * 3;
            int t5 = list.get(i5) * 5;
            int min = Math.min(t2, Math.min(t3, t5));
            if(t2 == min)i2 ++;
            if(t3 == min)i3 ++;
            if(t5 == min)i5 ++;
            list.add(min);
        }
        return list.get(index - 1);
    }
}

34 第一个只出现一次的字符位置

在一个字符串(0<=字符串长度<=10000,全部由字母组成)中找到第一个只出现一次的字符,并返回它的位置, 如果没有则返回 -1(需要区分大小写).

第一个只出现一次的字符位置

public class Solution {
    	public int FirstNotRepeatingChar(String str) {
		HashMap<Character,Integer> map = new HashMap<>();
		for (int i = 0; i < str.length(); i++) {
			char c = str.charAt(i);
			int count = map.getOrDefault(c, 0);
			map.put(c, count + 1);
		}
		
		for (int i = 0; i < str.length(); i++) {
			char c = str.charAt(i);
			if(map.get(c) == 1)
				return i;
		}
		return -1;
    }
}

35 数组中的逆序对(难)

在数组中的两个数字,如果前面一个数字大于后面的数字,则这两个数字组成一个逆序对。输入一个数组,求出这个数组中的逆序对的总数P。并将P对1000000007取模的结果输出。 即输出P%1000000007

数组中的逆序对

public class Solution {
    public int InversePairs(int [] array) {
        if(array==null||array.length==0)
        {
            return 0;
        }
        int[] copy = new int[array.length];
        for(int i=0;i<array.length;i++)
        {
            copy[i] = array[i];
        }
        int count = InversePairsCore(array,copy,0,array.length-1);//数值过大求余
        return count;
         
    }
    private int InversePairsCore(int[] array,int[] copy,int low,int high)
    {
        if(low==high)
        {
            return 0;
        }
        int mid = (low+high)>>1;
        int leftCount = InversePairsCore(array,copy,low,mid)%1000000007;
        int rightCount = InversePairsCore(array,copy,mid+1,high)%1000000007;
        int count = 0;
        int i=mid;
        int j=high;
        int locCopy = high;
        while(i>=low&&j>mid)
        {
            if(array[i]>array[j])
            {
                count += j-mid;
                copy[locCopy--] = array[i--];
                if(count>=1000000007)//数值过大求余
                {
                    count%=1000000007;
                }
            }
            else
            {
                copy[locCopy--] = array[j--];
            }
        }
        for(;i>=low;i--)
        {
            copy[locCopy--]=array[i];
        }
        for(;j>mid;j--)
        {
            copy[locCopy--]=array[j];
        }
        for(int s=low;s<=high;s++)
        {
            array[s] = copy[s];
        }
        return (leftCount+rightCount+count)%1000000007;
    }
}

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

输入两个链表,找出它们的第一个公共结点。(注意因为传入数据是链表,所以错误测试数据的提示是用其他方式显示的,保证传入数据是正确的)

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

思路:

假定 List1长度: a+n List2长度:b+n, 且 a<b那么 p1 会先到链表尾部, 这时p2 走到 a+n位置,将p1换成List2头部接着p2再走b+n-(n+a) =b-a 步到链表尾部,这时p1也走到List2b-a位置,还差a步就到可能的第一个公共节点。将p2换成 List1头部,p2走a步也到可能的第一个公共节点。如果恰好p1==p2,那么p1就是第一个公共节点。 或者p1和p2一起走n步到达列表尾部,二者没有公共节点,退出循环。 同理a>=b.时间复杂度O(n+a+b)

public class Solution {
    public ListNode FindFirstCommonNode(ListNode pHead1, ListNode pHead2) {
        ListNode p = pHead1;
        ListNode q = pHead2;
        
        while(q != p) {
            if(q != null) {
                q = q.next;
            } else {
                q = pHead1;
            }
            
            if(p != null) {
                p = p.next;
            } else {
                p = pHead2;
            }
        }
        return q;
    }
}

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

统计一个数字在排序数组中出现的次数。

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

思路:使用二分搜索,之后像左右搜索数量

public class Solution {
    public int GetNumberOfK(int [] arr , int k) {
        int index = binarySearch(arr,0,arr.length - 1,k);
        if(index == -1)return 0;
        int times = 1;
        int l = index - 1;
        int r = index + 1;
        while(r < arr.length && arr[r ++] == k)times ++;
        while(l >= 0 && arr[l --] == k)times ++;
        return times;
    }
    public int binarySearch(int arr[],int l,int r,int target){
        while(l <= r){
            int mid = (l + r)/2;
            if(arr[mid] == target){
                return mid;
            }else if(arr[mid] < target)l = mid + 1;
            else r = mid - 1;
        }
        return -1;
    }
}

38 二叉树的深度

输入一棵二叉树,求该树的深度。从根结点到叶结点依次经过的结点(含根、叶结点)形成树的一条路径,最长路径的长度为树的深度。

二叉树的深度

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

39 平衡二叉树

输入一棵二叉树,判断该二叉树是否是平衡二叉树。

平衡二叉树

public class Solution {
    public boolean IsBalanced_Solution(TreeNode root) {
        if(root == null)return true;
        return IsBalanced_Solution(root.left) && IsBalanced_Solution(root.right)
            && Math.abs(TreeDepth(root.right) - TreeDepth(root.left)) < 2;
    }
    
    public int TreeDepth(TreeNode root) {
        if(root == null) return 0;
        return Math.max(TreeDepth(root.left), TreeDepth(root.right)) + 1;
    }
}

40 数组中只出现一次的数字

一个整型数组里除了两个数字之外,其他的数字都出现了两次。请写程序找出这两个只出现一次的数字。

数组中只出现一次的数字

//num1,num2分别为长度为1的数组。传出参数
//将num1[0],num2[0]设置为返回结果
public class Solution {
    public void FindNumsAppearOnce(int [] array,int num1[] , int num2[]) {
        int t = array[0];
        for(int i = 1; i < array.length; i ++) {
            t ^= array[i];
        }
        
        int index = 1;
        while((t & index) == 0) {
            index = index << 1;
        }
        
        for(int i = 0; i < array.length; i ++) {
            if((array[i] & index) == 0) {
                num1[0] ^= array[i];
            }
            else {
                num2[0] ^= array[i];
            }
        }
    }
}

41 和为S的连续正数序列

输出所有和为S的连续正数序列。序列内按照从小至大的顺序,序列间按照开始数字从小到大的顺序

和为S的连续正数序列

思路:使用滑动窗口

public class Solution {
    public ArrayList<ArrayList<Integer>> FindContinuousSequence(int sum) {
        ArrayList<ArrayList<Integer>> allList = new ArrayList<>();
        int left = 1, right = 2;
        while (left < right) {
            int toSum = (left + right) * (right - left + 1) / 2;
            if (toSum == sum) {
                ArrayList<Integer> list = new ArrayList<>();
                for (int i = left; i <= right; i++) {
                    list.add(i);
                }
                allList.add(list);
                left++;
            } else if (toSum < sum) right++;
            else left++;
        }
        return allList;
    }
}

42 和为S的两个数字

输入一个递增排序的数组和一个数字S,在数组中查找两个数,使得他们的和正好是S,如果有多对数字的和等于S,输出两个数的乘积最小的。

和为S的两个数字

思路:使用滑动窗口

public class Solution {
    public ArrayList<Integer> FindNumbersWithSum(int [] array,int sum) {
        ArrayList<Integer> res = new ArrayList<>();
        if(array == null || array.length == 0) return res;
        int l = 0, r = array.length - 1;
        int mul = Integer.MAX_VALUE;
        while(l < r) {
            if(array[l] + array[r] == sum) {
                if(mul > array[l] * array[r]) {
                    res.add(array[l]);
                    res.add(array[r]);
                    mul = array[l] * array[r];
                }
                l ++;
            } else if(array[l] + array[r] > sum) r --;
            else l ++;
        }
        return res;
    }
}

43 左旋转字符串

汇编语言中有一种移位指令叫做循环左移(ROL),现在有个简单的任务,就是用字符串模拟这个指令的运算结果。对于一个给定的字符序列S,请你把其循环左移K位后的序列输出。例如,字符序列S=”abcXYZdef”,要求输出循环左移3位后的结果,即“XYZdefabc”。是不是很简单?OK,搞定它!

左旋转字符串

原理:

假设有字符串s 1 2 3 4 5 6 7左移3位

第一步:反转0 - n3 2 1 | 4 5 6 7

第二步:反转 n - s.length3 2 1 | 7 6 5 4

第三步:反转整个字符串:4 5 6 7 1 2 3

public class Solution {
    public String LeftRotateString(String str,int n) {
        char[] chars = str.toCharArray();        
        if(chars.length < n) return "";
        reverse(chars, 0, n-1);
        reverse(chars, n, chars.length-1);
        reverse(chars, 0, chars.length-1);
        StringBuilder sb = new StringBuilder(chars.length);
        for(char c:chars){
            sb.append(c);
        }
        return sb.toString();
    }
     
    public void reverse(char[] chars,int low,int high){
        char temp;
        while(low<high){
            temp = chars[low];
            chars[low] = chars[high];
            chars[high] = temp;
            low++;
            high--;
        }
    }
}

44 翻转单词顺序列

反转单词输出顺序

翻转单词顺序列

public class Solution {
    public String ReverseSentence(String str) {
        if(str == null)return null;
        if(str.trim().equals("")){
            return str;
        }
        String strs[] = str.split(" ");
        String res = "";
        for(int i = strs.length - 1; i >= 0; i--){
            res += strs[i];
            if(i != 0)
                res += " ";
        }
        return res;
    }
}

45 扑克牌顺子

大小王可以变为任意数,判断是否为顺子

扑克牌顺子

public class Solution {
    public boolean isContinuous(int [] numbers) {
        if(numbers == null || numbers.length != 5)return false;
        int arr[] = new int[14];
        int min = 14;
        int max = 0;
        for(int i = 0; i < numbers.length; i ++) {
            if(numbers[i] == 0)continue;
            arr[numbers[i]]++;
            if(arr[numbers[i]] > 1)return false;
            if(numbers[i] > max) max = numbers[i];
            if(numbers[i] < min) min = numbers[i];
            if(max - min >= 5) return false;
        }
        return true;
    }
}

46 孩子们的游戏(圆圈中最后剩下的数

约瑟夫环

孩子们的游戏(圆圈中最后剩下的数)

约瑟夫环——公式法递推公式

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

47 求1+2+3+…+n

求1+2+3+…+n,要求不能使用乘除法、for、while、if、else、switch、case等关键字及条件判断语句(A?B:C)。

求1+2+3+…+n

思路:利用短路性

public class Solution {
    public int Sum_Solution(int n) {
        int sum = n;
        boolean flag = (n > 0) && ((sum +=Sum_Solution(n - 1)) > 0);
        return sum;
    }
}

48 不用加减乘除做加法

写一个函数,求两个整数之和,要求在函数体内不得使用+、-、*、/四则运算符号。

不用加减乘除做加法

public class Solution {
    public int Add(int num1,int num2) {
        if(num2 == 0)return num1;
        int h = (num1 & num2) << 1;
        return Add(num1 ^ num2, h);        
    }
}

49 把字符串转换成整数

将一个字符串转换成一个整数,要求不能使用字符串转换整数的库函数。 数值为0或者字符串不是一个合法的数值则返回0

把字符串转换成整数

public class Solution {
    public int StrToInt(String str) {
        if(str == null ||str.length()==0 || str.trim().equals("")) return 0;
        char[] chars = str.trim().toCharArray();
        int res=0,flag=1,start=0,tmp=0;
        if(chars[0] == '-') {
            flag = -1;
            start = 1;   
        }
        if(chars[0] == '+') start=1;
        for(int i =start;i < chars.length;i++){
            if(chars[i]>'9'||chars[i]<'0')
                return 0;
            //当前个位数字
            int dight = (int)(chars[i]-'0');
            tmp = res*10+dight;
            //判断正数溢出
            if(flag ==1 && res*10>Integer.MAX_VALUE - dight)
               return 0;
            //判断负数溢出
            if(flag == -1 && res*10*flag < Integer.MIN_VALUE + dight)
                return 0;
            res = tmp;
        }
         return flag* res;
    }
}

50 数组中重复的数字

在一个长度为n的数组里的所有数字都在0到n-1的范围内。 数组中某些数字是重复的,但不知道有几个数字是重复的。也不知道每个数字重复几次。请找出数组中任意一个重复的数字。 例如,如果输入长度为7的数组{2,3,1,0,2,5,3},那么对应的输出是第一个重复的数字2。

数组中重复的数字

public class Solution {
    public boolean duplicate(int numbers[],int length,int [] duplication) {
        duplication[0] = -1;
        for(int i = 0; i < length; i ++){
            while(numbers[i] != i)
            if(numbers[numbers[i]] == numbers[i]){
                duplication[0] = numbers[i];
                return true;
            }else{
                int temp = numbers[i];
                numbers[i]= numbers[temp];
                numbers[temp] = temp;
            }
        }
        return false;
    }
}

51 构建乘积数组

给定一个数组A[0,1,…,n-1],请构建一个数组B[0,1,…,n-1],其中B中的元素B[i]=A[0]A[1]…*A[i-1]A[i+1]…*A[n-1]。不能使用除法。(注意:规定B[0] = A[1] * A[2] * … * A[n-1],B[n-1] = A[0] * A[1] * … * A[n-2];)

Input:  [1,2,3,4]
Output: [24,12,8,6]

构建乘积数组

class Solution {
    public int[] productExceptSelf(int[] nums) {
        int []res = new int[nums.length];
        res[0] = 1;
        //计算上三角
        for(int i = 1; i < nums.length; i ++){
            res[i] = res[i - 1] * nums[i - 1];
        }
        //计算下三角
        int right = 1;
        for(int i = nums.length - 1; i >= 0 ; i --){
            res[i] = res[i] * right;
            right = nums[i] * right;
        }
        return res;
    }
}

52 正则表达式匹配(难)

请实现一个函数用来匹配包括’.‘和’‘的正则表达式。模式中的字符’.‘表示任意一个字符,而’'表示它前面的字符可以出现任意次(包含0次)。 在本题中,匹配是指字符串的所有字符匹配整个模式。例如,字符串"aaa"与模式"a.a"和"abaca"匹配,但是与"aa.a"和"ab*a"均不匹配

正则表达式匹配

53 表示数值的字符串

请实现一个函数用来判断字符串是否表示数值(包括整数和小数)。例如,字符串"+100",“5e2”,"-123",“3.1416"和”-1E-16"都表示数值。 但是"12e",“1a3.14”,“1.2.3”,"±5"和"12e+4.3"都不是。

表示数值的字符串

public class Solution {
    private int index = 0;
  
    public boolean isNumeric(char[] str) {
        if (str.length < 1)
            return false;
         
        boolean flag = scanInteger(str);
         
        if (index < str.length && str[index] == '.') {
            index++;
            flag = scanUnsignedInteger(str) || flag;
        }
         
        if (index < str.length && (str[index] == 'E' || str[index] == 'e')) {
            index++;
            flag = flag && scanInteger(str);
        }
         
        return flag && index == str.length;
         
    }
     
    private boolean scanInteger(char[] str) {
        if (index < str.length && (str[index] == '+' || str[index] == '-') )
            index++;
        return scanUnsignedInteger(str);
         
    }
     
    private boolean scanUnsignedInteger(char[] str) {
        int start = index;
        while (index < str.length && str[index] >= '0' && str[index] <= '9')
            index++;
        return start < index; //是否存在整数
    }
}

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

请实现一个函数用来找出字符流中第一个只出现一次的字符。例如,当从字符流中只读出前两个字符"go"时,第一个只出现一次的字符是"g"。当从该字符流中读出前六个字符“google"时,第一个只出现一次的字符是"l"。

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

public class Solution {
    //Insert one char from stringstream
    int count[] = new int[128];
    Queue<Character> q = new LinkedList<>();
    public void Insert(char ch)
    {
        count[ch] ++;
        if(count[ch] == 1)
            q.add(ch);
    }
    //return the first appearence once char in current stringstream
    public char FirstAppearingOnce()
    {
        while(!q.isEmpty() && count[q.peek()] >= 2)q.poll();
        if(!q.isEmpty())return q.peek();
        return '#';
    }
}

55 链表中环的入口结点

给一个链表,若其中包含环,请找出该链表的环的入口结点,否则,输出null。

链表中环的入口结点

public class Solution {

    public ListNode EntryNodeOfLoop(ListNode pHead)
    {
        if(pHead == null || pHead.next == null)return null;
        ListNode p = pHead;
        ListNode q = pHead;
        while(q != null && q.next !=null){
            p = p.next;
            q = q.next.next;
            if(p == q){
                q = pHead;
                while(p != q){
                    p = p.next;
                    q = q.next;
                }
                return q;
            }
        }
        return null;
    }
}

56 删除链表中重复的结点

在一个排序的链表中,存在重复的结点,请删除该链表中重复的结点,重复的结点不保留,返回链表头指针。 例如,链表1->2->3->3->4->4->5 处理后为 1->2->5

删除链表中重复的结点

public class Solution {
    public ListNode deleteDuplication(ListNode pHead)
    {
        if(pHead == null)return null;
        ListNode dummy = new ListNode(0);
        dummy.next = pHead;
        ListNode p = dummy;
        ListNode q = pHead;
        while(q != null && q.next != null){
            ListNode next = q.next;
            if(q.val == next.val){
                while(next != null && next.val == q.val){
                    next = next.next;
                }
                p.next = next;
                q = next;
            }else{
                q = q.next;
                p = p.next;
            }
        }
        return dummy.next;
    }
}

57 二叉树的下一个结点

给定一个二叉树和其中的一个结点,请找出中序遍历顺序的下一个结点并且返回。注意,树中的结点不仅包含左右子结点,同时包含指向父结点的指针。

二叉树的下一个结点

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;
        }
      	//如果不存在右节点
      	//如果节点为父节点的左节点,下一个节点为父节点
      	//如果不为左节点,则找到第一个满足上面条件的节点
        while(pNode.next != null){
            if(pNode.next.left == pNode)
                return pNode.next;
            pNode = pNode.next;
        }
        return null;
    }
}

58 对称的二叉树

请实现一个函数,用来判断一颗二叉树是不是对称的。注意,如果一个二叉树同此二叉树的镜像是同样的,定义其为对称的。

对称的二叉树

public class Solution {
    boolean isSymmetrical(TreeNode root)
    {
        if(root == null)return true;
        return isSymmetrical(root.left, root.right);
    }
    
    private boolean isSymmetrical(TreeNode node1, TreeNode node2) {
        if(node1 == null)return node2 == null;
        if(node2 == null)return node1 == null;
        return isSymmetrical(node1.left, node2.right) && isSymmetrical(node1.right,node2.left)
            && node1.val == node2.val;
    }
}

59 按之字形顺序打印二叉树

请实现一个函数按照之字形打印二叉树,即第一行按照从左到右的顺序打印,第二层按照从右至左的顺序打印,第三行按照从左到右的顺序打印,其他行以此类推。

按之字形顺序打印二叉树

public class Solution {
    public ArrayList<ArrayList<Integer>> Print(TreeNode root) {
        ArrayList<ArrayList<Integer>> res = new ArrayList<>();
        if(root == null)return res;
        Stack<TreeNode> s1 = new Stack<>();
        Stack<TreeNode> s2 = new Stack<>();
        s1.push(root);
        while(!s1.isEmpty() || !s2.isEmpty()) {
            ArrayList<Integer> list = new ArrayList<>();
            if(!s1.isEmpty()) {
                while(!s1.isEmpty()) {
                    TreeNode node = s1.pop();
                    list.add(node.val);
                    if(node.left != null)s2.push(node.left);
                    if(node.right != null)s2.push(node.right);
                }
            } else {
                while(!s2.isEmpty()) {
                    TreeNode node = s2.pop();
                    list.add(node.val);
                    if(node.right != null)s1.push(node.right);
                    if(node.left != null)s1.push(node.left);
                }
            }
            res.add(new ArrayList<>(list));
        }
        return res;
    }
}

60 把二叉树打印成多行

从上到下按层打印二叉树,同一层结点从左至右输出。每一层输出一行。

把二叉树打印成多行

public class Solution {
    ArrayList<ArrayList<Integer>> Print(TreeNode root) {
        ArrayList<ArrayList<Integer>> res = new ArrayList<>();
        if(root == null)return res;
        Queue<TreeNode> q = new LinkedList<>();
        q.add(root);
        
        while(!q.isEmpty()) {
            int index = q.size();
            ArrayList<Integer> list = new ArrayList<>();
            while(index -- > 0) {
                TreeNode node = q.remove();
                list.add(node.val);
                if(node.left != null)q.add(node.left);
                if(node.right != null)q.add(node.right);
            }
            res.add(list);
        }
        return res;
    }
}

61 序列化二叉树

请实现两个函数,分别用来序列化和反序列化二叉树二叉树的序列化是指:把一棵二叉树按照某种遍历方式的结果以某种格式保存为字符串,从而使得内存中建立起来的二叉树可以持久保存。序列化可以基于先序、中序、后序、层序的二叉树遍历方式来进行修改,序列化的结果是一个字符串,序列化时通过 某种符号表示空节点(#),以 ! 表示一个结点值的结束(value!)。二叉树的反序列化是指:根据某种遍历顺序得到的序列化字符串结果str,重构二叉树。

序列化二叉树

public class Solution {
    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();
    }
    int index = -1;
    TreeNode Deserialize(String str) {
        String []res = str.split(",");
        TreeNode root = null;
        if(!res[++ index].equals("#")) {
            root = new TreeNode(Integer.parseInt(res[index]));
            root.left = Deserialize(str);
            root.right = Deserialize(str);
        }
        return root;
    }
}

62 二叉搜索树的第k个结点

给定一棵二叉搜索树,请找出其中的第k小的结点。例如, (5,3,7,2,4,6,8) 中,按结点数值大小顺序第三小结点的值为4。

二叉搜索树的第k个结点

public class Solution {
    TreeNode KthNode(TreeNode root, int k)
    {
        if(root == null || k == 0)return null;
        Stack<TreeNode> stack = new Stack<>();
        TreeNode node = root;
        int count = 0;
        while(!stack.isEmpty() || node != null) {
            while(node != null) {
                stack.push(node);
                node = node.left;
            }
            node = stack.pop();
            if(++ count == k)return node;
            node = node.right;
        }
        return null;
    }
}

63 数据流中的中位数

如何得到一个数据流中的中位数?如果从数据流中读出奇数个数值,那么中位数就是所有数值排序之后位于中间的数值。如果从数据流中读出偶数个数值,那么中位数就是所有数值排序之后中间两个数的平均值。我们使用Insert()方法读取数据流,使用GetMedian()方法获取当前读取数据的中位数。

数据流中的中位数

public class Solution {
    PriorityQueue<Integer> min = new PriorityQueue<>();
    PriorityQueue<Integer> max = new PriorityQueue<>(new Comparator<Integer>(){
        @Override
        public int compare(Integer o1, Integer o2){
            return o2 - o1;
        }
    });
    public void Insert(Integer num) {
        if(max.isEmpty() || num <= max.peek())
            max.add(num);
        else min.add(num);
        
        if(max.size() == min.size() + 2)
            min.add(max.poll());
        if(max.size() + 1 == min.size())
            max.add(min.poll());
    }

    public Double GetMedian() {
        return max.size() == min.size() ? (max.peek() + min.peek()) / 2.0 : max.peek() * 1.0;
    }
}

64 滑动窗口的最大值

给定一个数组和滑动窗口的大小,找出所有滑动窗口里数值的最大值。例如,如果输入数组{2,3,4,2,6,2,5,1}及滑动窗口的大小3,那么一共存在6个滑动窗口,他们的最大值分别为{4,4,6,6,6,5}; 针对数组{2,3,4,2,6,2,5,1}的滑动窗口有以下6个: {[2,3,4],2,6,2,5,1}, {2,[3,4,2],6,2,5,1}, {2,3,[4,2,6],2,5,1}, {2,3,4,[2,6,2],5,1}, {2,3,4,2,[6,2,5],1}, {2,3,4,2,6,[2,5,1]}。

滑动窗口的最大值

public class Solution {
    public ArrayList<Integer> maxInWindows(int [] num, int size)
    {
        ArrayList<Integer> res = new ArrayList<>();
        if(num == null || num.length == 0 || size == 0)return res;
        LinkedList<Integer> dq = new LinkedList<>();
        
        for(int i = 0; i < num.length; i ++) {
          	//确保队列存的是最大值和第二大值
            while(!dq.isEmpty() && num[dq.peekLast()] <= num[i])
                dq.pollLast();
            
            dq.addLast(i);
            //删除过期最大值
            if(dq.peekFirst() + size == i)
                dq.pollFirst();
            //当窗口达到大小才添加最大值
            if(i + 1 >= size) {
                res.add(num[dq.peekFirst()]);
            }
        }
        return res;
    }
}

65 矩阵中的路径

请设计一个函数,用来判断在一个矩阵中是否存在一条包含某字符串所有字符的路径。

矩阵中的路径

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

    private boolean isSatisfy(char[] matrix, char[] str, int i, int j, int k) {
        int index = i * cols + j;
        if(i < 0 || i >= rows || j < 0 || j >= cols 
           || visited[index] || matrix[index] != str[k])
            return false;
        
        if(k == str.length - 1) return true;//出口
        visited[index] = true;//递
        if(isSatisfy(matrix, str, i + 1, j, k + 1) ||
           isSatisfy(matrix, str, i - 1, j, k + 1) ||
           isSatisfy(matrix, str, i, j + 1, k + 1) ||
           isSatisfy(matrix, str, i, j - 1, k + 1))
            return true;
        visited[index] = false;//归
        return false;
    }
}

66 机器人的运动范围

地上有一个m行和n列的方格。一个机器人从坐标0,0的格子开始移动,每一次只能向左,右,上,下四个方向移动一格,但是不能进入行坐标和列坐标的数位之和大于k的格子。 例如,当k为18时,机器人能够进入方格(35,37),因为3+5+3+7 = 18。但是,它不能进入方格(35,38),因为3+5+3+8 = 19。请问该机器人能够达到多少个格子?

机器人的运动范围

public class Solution {
    boolean visited[];
    int rows,cols;
    public int movingCount(int k, int rows, int cols)
    {
        if(rows == 0)return 0;
        if(k < 0)return 0;
        this.rows = rows;this.cols = cols;
        visited = new boolean[rows * cols];
        return fill(0, 0, k);
    }
    private int fill(int i, int j, int k) {
        int index = i * cols + j;
        if(i < 0 || i >= rows || j < 0 || j >= cols 
           || visited[index] || !trueSum(i, j, k))
            return 0;
        
        visited[index] = true;
        int count = fill(i + 1, j, k) +
           fill(i - 1, j, k) +
           fill(i, j + 1, k) +
           fill(i, j - 1, k) + 1;
        return count;
    }
    
    private boolean trueSum(int i, int j, int k){
        String s = "" + i + j;
        int sum = 0;
        for(char a : s.toCharArray()) {
            sum += a - '0';
        }
        return sum <= k;
    }
}

67 剪绳子

给你一根长度为n的绳子,请把绳子剪成整数长的m段(m、n都是整数,n>1并且m>1),每段绳子的长度记为k[0],k[1],…,k[m]。请问k[0]xk[1]x…xk[m]可能的最大乘积是多少?例如,当绳子的长度是8时,我们把它剪成长度分别为2、3、3的三段,此时得到的最大乘积是18。

剪绳子

思路:每个绳子都可以分成两个最优的两段

public class Solution {
    public int cutRope(int target) {
        //如果必须切一刀的最大值
        if(target == 1)return 1;
        if(target == 2)return 1;
        if(target == 3)return 2;
        int memo[] = new int[target + 1];
        //因为在寻找最大子问题时候,不是必须切,所以2,3的最大值不一样
        memo[1] = 1;
        memo[2] = 2;
        memo[3] = 3;
        for(int i = 4; i <= target; i ++) {
            //i 分成两段,所以不需要循环到i
            for(int j = 1; j <= i / 2; j ++) {
                memo[i] = Math.max(memo[i], memo[j] * memo[i - j]);
            }
        }
        return memo[target];
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值