剑指offer 后篇

1.丑数

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

下一个丑数一定是前面某个丑数乘以2或者3或者5能得到的最小值,对应乘2、3、5的丑数分别记为p、q、r,看2p、3q、5r哪个数比较小,就是下一个丑数。比如说如果最小的是2p,下一个丑数就是这个数,更新p为p后一个丑数。注意,可能存在有不止一个数同时最小,这样的话对应都要更新。

public int GetUglyNumber_Solution(int index) {
    
    if(index == 0) return 0;
    
    int[] uglyNumbers = new int[index];
    uglyNumbers[0] = 1;
    
    int i2 = 0, i3 = 0, i5 = 0;
    
    for(int i = 1; i < index; i++){
        int m2 = uglyNumbers[i2] * 2;
        int m3 = uglyNumbers[i3] * 3;
        int m5 = uglyNumbers[i5] * 5;
        
        int min = Math.min(Math.min(m2,m3),m5);
        uglyNumbers[i] = min;
        
        if(min == m2) i2++;
        if(min == m3) i3++;
        if(min == m5) i5++;
     
    }
    
    return uglyNumbers[index-1];   
}


2.第一个只出现一次的字符

在一个字符串(1<=字符串长度<=10000,全部由字母组成)中找到第一个只出现一次的字符,并返回它的位置

public int FirstNotRepeatingChar(String str) {
    if(str == null || str == "") return -1;
    
    HashMap map = new HashMap();
    
    for(int i = 0; i < str.length(); i++){
        char ch = str.charAt(i);
        if(map.containsKey(ch)){
            if(map.get(ch)) map.put(ch,false);
        }else{
            map.put(ch,true);
        }
    }
    
    for(int i = 0; i < str.length(); i++){
        char ch = str.charAt(i);
        if(map.get(ch)) return i;
    }
    
    return -1;      
}


3.数组中的逆序对

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


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

输入两个链表,找出它们的第一个公共结点。

public ListNode FindFirstCommonNode(ListNode pHead1, ListNode pHead2) {
    
    ListNode p1 = pHead1;
    ListNode p2 = pHead2;
    
    int count1 = 0, count2 = 0;
    while(p1 != null){
        count1++;
        p1 = p1.next;
    }
    
    while(p2 != null){
        count2++;
        p2 = p2.next;
    }
    
   
    p1 = pHead1;
    p2 = pHead2;
    
    for(int i = 0; i < Math.abs(count1 - count2); i++){
        if(count1 > count2) p1 = p1.next;
        else p2 = p2.next;
    }
    
    while(p1 != null && p2 != null){
        if(p1 == p2) return p1;
        p1 = p1.next;
        p2 = p2.next;
    }
    
    return null;   
}


5.数字在排序字母中出现的次数

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

看有序数组就想到二分查找,找到最小和最大的index。最要注意的是当high与low相差1时mid取哪个数的问题,把特殊情况拿出来。

public class Solution {
    public int GetNumberOfK(int [] array , int k) {

        int lower = getLower(array, k, 0, array.length - 1);
        int upper = getUpper(array, k, 0, array.length - 1);

        if(lower != -1 && upper != -1) return upper - lower + 1;
        return 0;
    }

    public int getLower(int[] array, int k, int low, int high){

        if(low > high) return -1;

        int mid = (low + high) / 2;
        if(k == array[mid]){
            if(low == mid) return mid;
            else return getLower(array, k, low, mid);
        }
        else if(k > array[mid]) return getLower(array, k, mid + 1, high);
        else return getLower(array, k, low, mid - 1);

    }

    public int getUpper(int [] array, int k, int low, int high){

        int mid = (low + high) / 2;
        while(low <= high){
            mid = (low + high) / 2;
            if(array[mid] == k){
                if(array[high] == k) return high;
                if(high - 1 == mid) return mid;
                else low = mid;
            }else if(array[mid] > k) high = mid - 1;
            else low = mid + 1;
        }

        return -1;     
    }
}


6.二叉树的深度

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

public int TreeDepth(TreeNode root) {
    if(root == null) return 0;
    
    int left = TreeDepth(root.left);
    int right = TreeDepth(root.right);
    
    return 1 + (left > right ? left : right);      
}


7.平衡二叉树

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

 public class Solution {
    public boolean IsBalanced_Solution(TreeNode root) {
       return getDepth(root) != -1;
    }


    public int getDepth(TreeNode root){
        if(root == null) return 0;

        int left = getDepth(root.left);
        int right = getDepth(root.right);

        if(left == -1 || right == -1) return -1;

        if(left - right > 1 || right - left > 1) return -1;

        return 1 + (left > right ? left : right);
    }
}


8. 数组中只出现一次的数字

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

一个思路是HashSet,遍历数组,第一次出现先放入set,再次出现移除该元素,最后剩2个元素。

另一个思路是用异或,基于两个规则:两个相同的数异或为0,0异某数等于该数。于是将所有数异或的结果为两个只出现一次的数异或的结果,并且出现1的位置表示此位不同。根据第一次出现1的那一位是取0或者1把数组分为2份,每一份都包含一个出现一次的数和若干个出现2次的数,这样就可以分别算出这两个出现一次的数是多少了。

public void FindNumsAppearOnce(int [] array,int num1[] , int num2[]) {
    HashSet set = new HashSet();
    
    for(int elem: array){
        if(set.contains(elem)){
            set.remove(elem);
        }else{
            set.add(elem);
        }
    }
    
    Iterator iter = set.iterator();
    num1[0] = (int)iter.next();
    num2[0] = (int)iter.next();
}


9.和为S的连续正数序列

小明很喜欢数学,有一天他在做数学作业时,要求计算出9~16的和,他马上就写出了正确答案是100。但是他并不满足于此,他在想究竟有多少种连续的正数序列的和为100(至少包括两个数)。没多久,他就得到另一组连续正数和为100的序列:18,19,20,21,22。现在把问题交给你,你能不能也很快的找出所有和为S的连续正数序列? Good Luck!

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

设x~y有k个数,k = y - x + 1,
所以sum = (x + y) * k / 2 = (x + y)(y - x + 1) / 2。
进一步化简得y(y+1) = 2sum + xx - x,如果确定一个x,则右边为常数。检查右边开根号后向下取整和向上取整的乘积是否等于右边。
满足x < y 且 x、y均为正整数。

public ArrayList > FindContinuousSequence(int sum) {
    
    ArrayList> list = new ArrayList>();

    sum = 2 * sum;
    
    int x = 1;
    int y;
    
    while(true){
        int c = sum + x * x - x;
        double cSqrt = Math.sqrt(c);
        y = (int)Math.floor(cSqrt);
        if(x >= y) return list;
        if(y * (y + 1) == c){
            ArrayList temp = new ArrayList();
            for(int i = x; i <= y; i++){
                temp.add(i);
            }
            list.add(temp);
        } 
        x++;
    }       
}


10.和为S的两个数字

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

跟leetcode上的第一题2sum差不多,用hashSet解决,因为哈希表查找时间复杂度是O(1)。不一样的是可能存在多个解,每次出一对新结果都要跟上一次结果比较;也可能不存在解,用一个标记判断解的存在。

public ArrayList FindNumbersWithSum(int [] array,int sum) {
    
    
    ArrayList list = new ArrayList();
    
    HashSet set = new HashSet();
    
    boolean found = false;
    int first = 0, second = 0;
    
    for(int elem: array){
        if(set.contains(sum - elem)){
            if((!found) || (found && first * second > elem * (sum - elem))){ 
                first = sum - elem;
                second = elem;    
            }
            found = true;
        }
        set.add(elem);
    }
    
    if(found){
        list.add(first);
        list.add(second);
    }
    
    return list;       
}


11.左旋转字符串

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

public String LeftRotateString(String str,int n) {
    
    if(n == 0) return str;
    if(str == null || str.length() < n) return "";
    
    String first = str.substring(0,n);
    String second = str.substring(n);
    
    return second + first;
        
}

12.翻转单词顺序列

牛客最近来了一个新员工Fish,每天早晨总是会拿着一本英文杂志,写些句子在本子上。同事Cat对Fish写的内容颇感兴趣,有一天他向Fish借来翻看,但却读不懂它的意思。例如,“student. a am I”。后来才意识到,这家伙原来把句子单词的顺序翻转了,正确的句子应该是“I am a student.”。Cat对一一的翻转这些单词顺序可不在行,你能帮助他么?

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


13.扑克牌顺子

LL今天心情特别好,因为他去买了一副扑克牌,发现里面居然有2个大王,2个小王(一副牌原本是54张_)...他随机从中抽出了5张牌,想测测自己的手气,看看能不能抽到顺子,如果抽到的话,他决定去买体育彩票,嘿嘿!!“红心A,黑桃3,小王,大王,方片5”,“Oh My God!”不是顺子.....LL不高兴了,他想了想,决定大\小 王可以看成任何数字,并且A看作1,J为11,Q为12,K为13。上面的5张牌就可以变成“1,2,3,4,5”(大小王分别看作2和4),“So Lucky!”。LL决定去买体育彩票啦。 现在,要求你使用这幅牌模拟上面的过程,然后告诉我们LL的运气如何。如果牌能组成顺子就输出true,否则就输出false。为了方便起见,你可以认为大小王是0。

不考虑大小王,如果一个牌是顺子那么按大小排序后相邻间隔都为1;
如果一个牌是顺子那么加上大小王也能构成顺子,如果一个牌排序后总间隔不大于大小王数量,那么就可以把大小王插入其中形成顺子。

import java.util.*;

public class Solution {
    public boolean isContinuous(int [] numbers) {
        if(numbers == null || numbers.length != 5){
            return false;
        }
        int num0 = 0;
        Arrays.sort(numbers);
        int i = 0;
        while(i < numbers.length){
            if(numbers[i] == 0){
                num0++;
                i++;
            }else{
                break;
            }
        }
        for(; i < numbers.length - 1; i++){
            int gap = numbers[i+1] - numbers[i];
            if(gap < 1){
                return false;
            }else{
                num0 -= (gap - 1);
                if(num0 < 0){
                    return false;
                }
            }
        }
        return true;
    }
}


14.孩子们的游戏

每年六一儿童节,牛客都会准备一些小礼物去看望孤儿院的小朋友,今年亦是如此。HF作为牛客的资深元老,自然也准备了一些小游戏。其中,有个游戏是这样的:首先,让小朋友们围成一个大圈。然后,他随机指定一个数m,让编号为0的小朋友开始报数。每次喊到m-1的那个小朋友要出列唱首歌,然后可以在礼品箱中任意的挑选礼物,并且不再回到圈中,从他的下一个小朋友开始,继续0...m-1报数....这样下去....直到剩下最后一个小朋友,可以不用表演,并且拿到牛客名贵的“名侦探柯南”典藏版(名额有限哦!!_)。请你试着想下,哪个小朋友会得到这份礼品呢?(注:小朋友的编号是从0到n-1)

用链表模拟这个环,注意第二个for循环里面i

public class Solution {
    public int LastRemaining_Solution(int n, int m) {
        if(n < 2 || m <= 0){
            return -1;
        }
        ListNode head = new ListNode(0);
        ListNode p = head;
        for(int i = 1; i < n; i++){
            p.next = new ListNode(i);
            p = p.next;
        }
        p.next = head;
        while(head.next != head){
            for(int i = 0; i < m-2; i++){
                head = head.next;
            }
            head.next = head.next.next;
            head = head.next;
        }
        return head.val;
    }
}

或者用数组或可变LIST来模拟这个情况。


15.求1+2+3+...+n

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

public int Sum_Solution(int n) {
    int sum = 0;
    Boolean b = (n>0) && ((sum = Sum_Solution(n-1) + n) > 0;
    return sum;  
}


16.不用加减乘除做加法

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


17.把字符串转换成整数

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

倒着遍历字符串中的字符,按位累加起来,第一个字符可能为符号“+”或"-"要单独处理。第一个字符不应为0,但我没设这个条件时仍然通过了,说明测试并不严密吧。

public class Solution {
    public int StrToInt(String str) {
        
        if (str == null) return 0;
        int len = str.length();
        if(len == 0) return 0;
        
        int sum = 0;
        int i = 0;
        for(; i < len - 1; i++){
            char ch = str.charAt(len - i - 1);
            if(ch < '0' || ch > '9'){
                return 0;
            }
            
            int digit = ch - '0';
            sum += digit * pow(10,i);
        }
        
        char ch = str.charAt(0);
        
        if(ch == '-') return sum * (-1);
        if(ch == '+') return sum;
        if(ch > '0' && ch <= '9'){
            int digit = ch - '0';
            return sum + digit * pow(10,i);
        } 
        
        return 0;   
        
    }
    
    
    public int pow(int base ,int k){
        if(k == 0) return 1;
        return pow(base, k-1) * base;
    }
}


18.数组中重复的数字

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

    public boolean duplicate(int numbers[],int length,int [] duplication) {
        
        if(length == 0) return false;
        
        int[] copy = new int[length];
        
        for(int number : numbers){
            if(number < 0 || number > length - 1) return false;
            if(copy[number] == 0){
                copy[number] = 1;
            }else{
                duplication[0] = number;
                return true; 
            }
        }
        
        return false;
    }
}


19.构建乘积数组

给定一个数组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]。不能使用除法。
解决思路是记录两个数组forward和backward,分别从前往后作乘积和从后往前做乘积。

import java.util.ArrayList;
public class Solution {
    public int[] multiply(int[] A) {
        
        if(A == null || A.length == 0 || A.length == 1) return A;
        int n = A.length;
        int[] B = new int[n];
        int[] forward = new int[n];
        int[] backward = new int[n];
        
        forward[0] = 1;
        backward[0] = 1;
        
        for(int i = 1; i < n; i++){
            forward[i] = forward[i - 1] * A[i - 1];
            backward[i] = backward[i - 1] * A[n - i];
        }
        
        for(int i = 0; i < n; i++){
            B[i] = forward[i] * backward[n - i - 1];
        }
        
        return B;
        
    }
}

或者B[i] = forward[i] * backward[i]

import java.util.ArrayList;
public class Solution {
    public int[] multiply(int[] A) {
        
        if(A == null || A.length == 0 || A.length == 1) return A;
        int n = A.length;
        int[] B = new int[n];
        int[] forward = new int[n];
        int[] backward = new int[n];
        
        forward[0] = 1;
        backward[n-1] = 1;
        
        for(int i = 1; i < n; i++){
            forward[i] = forward[i - 1] * A[i - 1];            
        }
        
        for(int i = n - 2; i >= 0; i--){
            backward[i] = backward[i + 1] * A[i + 1]; 
        }
        
        for(int i = 0; i < n; i++){
            B[i] = forward[i] * backward[i];
        }
        
        return B;
        
    }
}


20.正则表达式匹配

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



二叉树的下一个结点

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

如果该节点有右子树,则下一个节点是右子树的最左节点;
如果该节点没有右子树,则:
  如果该节点是爸爸的左子树,则下一个节点是爸爸,如果不是就继续向上直到寻找到某个祖先是它的爸爸的左子树为止,则此爸爸是下一个节点。如果一直都没找到这样的说明到了最后一个,返回NULL。

/*
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){
            pNode = pNode.right;
            while(pNode.left!= null){
                pNode = pNode.left;
            }
            return pNode;
        }else{
            while(pNode.next != null){
                TreeLinkNode pTempNode = pNode;
                pNode = pNode.next;
                if(pNode.left == pTempNode){
                    return pNode;
                }
            }
        }
        return null;
    }
}



对称的二叉树

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

/*
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)
    {
        if(pRoot == null){
            return true;
        }
        return judge(pRoot.left, pRoot.right);
    }
    
    boolean judge(TreeNode tLeft, TreeNode tRight){
        if(tLeft == null && tRight == null){
            return true;
        }else if(tLeft != null && tRight != null && tLeft.val == tRight.val){
            return judge(tLeft.left, tRight.right) && judge(tLeft.right, tRight.left);
        }else{
            return false;
        }
    }
}


按之字型顺序打印二叉树

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

栈先进后出的特点正好能实现每一次的反转,从左到右顺序的下一代也是从左到右(先左子树后右子树),从右到左顺序的下一代是从右到左(先右子树后左子树),按这个顺序再传递给下一个stack,读取时即实现反转。所以一次迭代是从左到右读一次再从右到左读一次。

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 {
    public ArrayList > Print(TreeNode pRoot) {
        ArrayList> res = new ArrayList>();
        if(pRoot == null){
            return res;
        }
        Stack stack = new Stack();
        Stack reverseStack = new Stack();
        stack.push(pRoot);
        while(!stack.isEmpty()){
            ArrayList arr = new ArrayList();
            while(!stack.isEmpty()){
                TreeNode node = stack.pop();
                arr.add(node.val);
                if(node.left != null){
                    reverseStack.push(node.left);
                }
                if(node.right != null){
                    reverseStack.push(node.right);
                }
            }
            res.add(arr);
            ArrayList reverseArr = new ArrayList();
            if(reverseStack.isEmpty()){
                break;
            }
            while(!reverseStack.isEmpty()){
                TreeNode node = reverseStack.pop();
                reverseArr.add(node.val);
                if(node.right != null){
                    stack.push(node.right);
                }
                if(node.left != null){
                    stack.push(node.left);    
                }
            }
            res.add(reverseArr);         
        }
        return res;
    }
}


把二叉树打印成多行

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

import java.util.ArrayList;

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

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

    }

}
*/
public class Solution {
    ArrayList > Print(TreeNode pRoot) {
        ArrayList> res = new ArrayList>();
        if(pRoot == null){
            return res;
        }
        ArrayList arr = new ArrayList();
        ArrayList nextArr = new ArrayList();
        arr.add(pRoot);
        while(!arr.isEmpty()){
            ArrayList current = new ArrayList();
            for(TreeNode node : arr){
                current.add(node.val);
                if(node.left != null){
                    nextArr.add(node.left);
                }
                if(node.right != null){
                    nextArr.add(node.right);
                }
            }
            arr.clear();
            for(TreeNode node : nextArr){
                arr.add(node);
            }
            nextArr.clear();
            res.add(current);
        }
        return res;        
    }  
}


序列化二叉树

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

题目没看懂



二叉搜索树的第K个结点

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

二叉搜索树中左节点<父节点<右节点,所以中序遍历即是按从小到大来遍历,再取第K个值即可。

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

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

    }

}
*/
public class Solution {     
    TreeNode KthNode(TreeNode pRoot, int k)
    {
        ArrayList arr = new ArrayList();
        traverse(pRoot, arr);
        if(k < 1){
            return null;
        }
        if(k <= arr.size()){
            return arr.get(k-1);
        }
        return null;
    }
    
    public void traverse(TreeNode pRoot, ArrayList arr){
        if(pRoot == null){
            return;
        }
        traverse(pRoot.left, arr);
        arr.add(pRoot);
        traverse(pRoot.right, arr);
    }
}

但是这样的话就多花了时间和存储空间,因为过程中如果遍历到第K个就可以停止了,所以改一下代码为:

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

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

    }

}
*/
public class Solution {   
    int count = 0;
    TreeNode KthNode(TreeNode pRoot, int k)
    {  
        if(pRoot == null){
            return null;
        }       
        TreeNode node = KthNode(pRoot.left, k);
        if(node != null){
            return node;
        }
        count++;
        if(k == count){
            return pRoot;
        } 
        node = KthNode(pRoot.right, k);
        if(node != null){
            return node;
        }        
        return null;      
    } 
    
}


数据流的中位数

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



滑动窗口的最大值

给定一个数组和滑动窗口的大小,找出所有滑动窗口里数值的最大值。例如,如果输入数组{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]}。

暴力解法用python写实在是很简单。

# -*- coding:utf-8 -*-
class Solution:
    def maxInWindows(self, num, size):
        res = []
        if size <= 0:
            return res
        for i in range(len(num)-size+1):
            res.append(max(num[i:i+size]))
        return res

还有一个双端队列解法没看懂。。。



矩阵中的路径



机器人的运动范围

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

public class Solution {
    int count = 0;
    public int movingCount(int threshold, int rows, int cols)
    {
        boolean[][] visited = new boolean[rows][cols];
        forward(threshold, rows, cols, 0, 0, visited);
        return count;
    }
    
    public void forward(int threshold, int rows, int cols, int row, int col, boolean[][] visited){
        if(row >= 0 && row < rows && col >= 0 && col < cols && 
           !visited[row][col] && sum(row) + sum(col) <= threshold){
            visited[row][col] = true;
            count++;
            forward(threshold, rows, cols, row - 1, col, visited);
            forward(threshold, rows, cols, row + 1, col, visited);
            forward(threshold, rows, cols, row, col - 1, visited);
            forward(threshold, rows, cols, row, col + 1, visited);
        }
        
    }
    
    private int sum(int x){
        int res = 0;
        int temp = x;
        while(temp > 0){
            res += temp % 10;
            temp /= 10;
        }
        return res;
    }
}



剪绳子

给你一根长度为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。

典型的动态规划题,cutRope(target) = max(cutRope(target-j)*j),当target == 2时是一个例外情况,因为它作为target较大时分解为一个2与另外的数相乘最好,但是target等于2时必须分成1**1。

public class Solution {
    public int cutRope(int target) {
        if(target == 2){
            return 1;
        }
        int[] max_mal = new int[target+1];
        max_mal[1] = 1;
        max_mal[2] = 2;
        for(int i = 3; i < target+1; i++){
            int temp_max = 1;
            for(int j = 1; j < i; j++){
                int temp = max_mal[j] * (i-j);
                if(temp > temp_max){
                    temp_max = temp;
                }
            }
            max_mal[i] = temp_max;         
        }     
        return max_mal[target]; 
    }
}

你可能感兴趣的:(剑指offer 后篇)