剑指offer编程题66道题 36-66

36.两个链表的第一个公共节点

题目描述

输入两个链表,找出它们的第一个公共结点。
1.具有重合节点的两个链表是一个Y字性,用两个堆栈放这两个链表,从尾部开始遍历,直到遍历到最后一个重合节点。
这种算法时间复杂度为O(m+n),但是空间复杂度也是O(m+n),相当于是空间换时间
/*
public class ListNode {
    int val;
    ListNode next = null;

    ListNode(int val) {
        this.val = val;
    }
}*/
import java.util.*;
public class Solution {
    public ListNode FindFirstCommonNode(ListNode pHead1, ListNode pHead2) {
        Stack<ListNode> s1=new Stack<ListNode>();
        Stack<ListNode> s2=new Stack<ListNode>();
        
        while(pHead1!=null){
            s1.add(pHead1);
            pHead1=pHead1.next;
        }
        
        while(pHead2!=null){
            s2.add(pHead2);
            pHead2=pHead2.next;
        }
        
        ListNode foundNode =null;
        ListNode node =null;
         while(!s1.isEmpty() && !s2.isEmpty()){
            node=s1.pop();
            if(node==s2.pop())
                foundNode =node;
            else 
                return foundNode;
        }
        return foundNode;
    }
}
View Code

2.利用HashSet中元素不能重复的原理,首先遍历一个链表进行存放,然后遍历另外一个链表,找到第一个与Set中元素相同的节点。

public ListNode FindFirstCommonNode(ListNode pHead1, ListNode pHead2) {
            ListNode current1 = pHead1;
            ListNode current2 = pHead2;
     
     
            HashSet <ListNode> hashSet = new HashSet<ListNode>();
            while (current1 != null) {
                hashSet.add(current1);
                current1 = current1.next;
            }
            while (current2 != null) {
                if (hashSet.contains(current2))
                    return current2;
                current2 = current2.next;
            }
     
            return null;
     
        }
View Code

 

3.首先遍历两个链表得到它们的长度,就能知道哪个链表比较长,以及长的链表比短的链表多几个节点。第二次遍历的时候,在较长的链表上线走若干步,接着同时在两个链表上遍历,找到的第一个相同的节点就是它们的第一个公共节点。

链接:https://www.nowcoder.com/questionTerminal/6ab1d9a29e88450685099d45c9e31e46
来源:牛客网

public ListNode FindFirstCommonNodeII(ListNode pHead1, ListNode pHead2) {
        ListNode current1 = pHead1;// 链表1
        ListNode current2 = pHead2;// 链表2
        if (pHead1 == null || pHead2 == null)
            return null;
        int length1 = getLength(current1);
        int length2 = getLength(current2);
        // 两连表的长度差
         
        // 如果链表1的长度大于链表2的长度
        if (length1 >= length2) {
            int len = length1 - length2;
            // 先遍历链表1,遍历的长度就是两链表的长度差
            while (len > 0) {
                current1 = current1.next;
                len--;
            }
 
        }
        // 如果链表2的长度大于链表1的长度
        else if (length1 < length2) {
            int len = length2 - length1;
            // 先遍历链表1,遍历的长度就是两链表的长度差
            while (len > 0) {
                current2 = current2.next;
                len--;
            }
 
        }
        //开始齐头并进,直到找到第一个公共结点
        while(current1!=current2){
            current1=current1.next;
            current2=current2.next;
        }
        return current1;
 
    }
 
    // 求指定链表的长度
    public static int getLength(ListNode pHead) {
        int length = 0;
 
        ListNode current = pHead;
        while (current != null) {
            length++;
            current = current.next;
        }
        return length;
    }
View Code

 

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

题目描述

统计一个数字在排序数组中出现的次数。
注意是在排序后的数组中,遍历数组找到目标数字,当遍历到和目标数字不相等的位置后就可以不再遍历,减小时间复杂度。
public class Solution {
    public int GetNumberOfK(int [] array , int k) {
        int number = 0;
        int flag = 0;
        for(int i: array){
            if(i == k){
                number++;
                flag = 1;
             }else if(i != k && flag == 1){
               return number;
            }
        }
        return number;
    }
}
View Code

 二分法:

public class Solution {
    public int GetNumberOfK(int [] array , int k) {
        int length = array.length;
        if(length == 0){
            return 0;
        }
        int firstK = getFirstK(array, k, 0, length-1);
        int lastK = getLastK(array, k, 0, length-1);
        if(firstK != -1 && lastK != -1){
             return lastK - firstK + 1;
        }
        return 0;
    }
    //递归写法
    private int getFirstK(int [] array , int k, int start, int end){
        if(start > end){
            return -1;
        }
        int mid = (start + end) >> 1;
        if(array[mid] > k){
            return getFirstK(array, k, start, mid-1);
        }else if (array[mid] < k){
            return getFirstK(array, k, mid+1, end);
        }else if(mid-1 >=0 && array[mid-1] == k){
            return getFirstK(array, k, start, mid-1);
        }else{
            return mid;
        }
    }
    //循环写法
    private int getLastK(int [] array , int k, int start, int end){
        int length = array.length;
        int mid = (start + end) >> 1;
        while(start <= end){
            if(array[mid] > k){
                end = mid-1;
            }else if(array[mid] < k){
                start = mid+1;
            }else if(mid+1 < length && array[mid+1] == k){
                start = mid+1;
            }else{
                return mid;
            }
            mid = (start + end) >> 1;
        }
        return -1;
    }
}
View Code

 

public class Solution {
    public int GetNumberOfK(int [] array , int k) {
        int first = firstGreatOrEqual(array, k);
        int last = firstGreat(array, k);
        return last - first;
    }
    
    public  int firstGreatOrEqual(int[] array,int key) {
        int left = 0;
        int right = array.length - 1;

        while (left <= right) {
            int mid = (left + right) / 2;
            if (array[mid] >= key) {
                right = mid - 1;
            }
            else {
                left = mid + 1;
            }
        }
        return left;
    }
    
    public  int firstGreat(int[] array, int key) {
        int left = 0;
        int right = array.length - 1;

        while (left <= right) {
            int mid = (left + right) / 2;
            if (array[mid] > key) {
                right = mid - 1;
            }
            else {
                left = mid + 1;
            }
        }
        return left;
    }
    
}
View Code

 

 

38.二叉树的深度

题目描述

输入一棵二叉树,求该树的深度。从根结点到叶结点依次经过的结点(含根、叶结点)形成树的一条路径,最长路径的长度为树的深度。
1.采用递归方法
/**
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 (left>right)?(left+1):(right+1);
    }
}
View Code

2.采用层次遍历的方式

先知道下一层树的个数,然后count++,相等时候,结束一层的层序遍历。

链接:https://www.nowcoder.com/questionTerminal/435fb86331474282a3499955f0a41e8b
来源:牛客网

import java.util.Queue;
import java.util.LinkedList;
 
public class Solution {
    public int TreeDepth(TreeNode pRoot)
    {
        if(pRoot == null){
            return 0;
        }
        Queue<TreeNode> queue = new LinkedList<TreeNode>();
        queue.add(pRoot);
        int depth = 0, count = 0, nextCount = 1;
        while(queue.size()!=0){
            TreeNode top = queue.poll();
            count++;
            if(top.left != null){
                queue.add(top.left);
            }
            if(top.right != null){
                queue.add(top.right);
            }
            if(count == nextCount){
                nextCount = queue.size();
                count = 0;
                depth++;
            }
        }
        return depth;
    }
}
View Code

 

39.平衡二叉树

题目描述

输入一棵二叉树,判断该二叉树是否是平衡二叉树。
平衡二叉树左右树的高度不超过1。
递归获取树的高度
public class Solution {
    private boolean isBalanced = true;
    public boolean IsBalanced_Solution(TreeNode root) {
        height(root);
        return isBalanced;
    }
    
    private int height(TreeNode root){
        if(root == null)
            return 0;
        int left = height(root.left);
        int right = height(root.right);
        if(Math.abs(left-right)>1)
             isBalanced = false;
        return 1+Math.max(left,right);
    }
}
View Code

 

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

题目描述

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

思路:
//使用堆栈来做辅助功能,将数组先排序,依次入栈,每一次数组入栈时和当前堆栈的栈头比较,如果当前堆栈为空,就入栈,如果和当前栈头的元素相同就出栈,当数组中左右元素都入栈完毕,那么当前栈中剩余的2个元素就是只出现一次的两个元素

时间复杂度为O(n2),空间复杂度为O(n)

//num1,num2分别为长度为1的数组。传出参数
//将num1[0],num2[0]设置为返回结果
import java.util.*;
public class Solution {
    public void FindNumsAppearOnce(int [] array,int num1[] , int num2[]) {
    Arrays.sort(array);
    Stack<Integer> stack = new Stack<Integer>();
    int len = array.length;

    if(array == null){
        num1[0] = 0;
        num2[0] = 0;
    }
    for(int x = 0;x<len;x++){
        if(stack.isEmpty()){
            stack.push(array[x]);
        }else{
            if(stack.peek() == array[x])
                stack.pop();
            else
                stack.push(array[x]);
        }
    }
    num1[0] = stack.pop();
    num2[0] = stack.pop();
}
}
View Code

 用ArrayList,思路和前面的一致

时间复杂度为O(n2),空间复杂度为O(n)

remove不装箱的话会被当作按照下标删除,add会自动装箱

链接:https://www.nowcoder.com/questionTerminal/e02fdb54d7524710a7d664d082bb7811
来源:牛客网

import java.util.ArrayList;
public class Solution {
        public void FindNumsAppearOnce(int [] array,int num1[] , int num2[]) {
                ArrayList<Integer>list=new ArrayList<Integer>();
                for(int i=0;i<array.length;i++)
                    {
                        if(!list.contains(array[i]))
                            list.add(array[i]);
                        else
                            list.remove(new Integer(array[i]));
                    }
                if(list.size()>1)
                    {
                        num1[0]=list.get(0);
                        num2[0]=list.get(1);
                    }
        }
}
View Code

 最好的方法:

时间复杂度为O(n),空间复杂度为O(1)

此题考察的是异或运算的特点:即两个相同的数异或结果为0。
此题用了两次异或运算特点:
(1)第一次使用异或运算,得到了两个只出现一次的数相异或的结果。
(2)因为两个只出现一次的数肯定不同,即他们的异或结果一定不为0,一定有一个位上有1。另外一个此位上没有1,我们可以根据此位上是否有1,将整个数组重新划分成两部分,一部分此位上一定有1,另一部分此位上一定没有1,然后分别对每部分求异或,因为划分后的两部分有这样的特点:其他数都出现两次,只有一个数只出现一次。因此,我们又可以运用异或运算,分别得到两部分只出现一次的数。
//num1,num2分别为长度为1的数组。传出参数
//将num1[0],num2[0]设置为返回结果
import java.util.*;
public class Solution {
    public void FindNumsAppearOnce(int [] array,int num1[] , int num2[]) {
       
        int diff = 0;
        for(int num : array)
            diff ^= num;
        for(int num : array){
            if((num & diff) == 0)
                num1[0] ^= num;
            else
                num2[0] ^= num;
        }
    }
}
View Code

 

41.和为S的连续证书序列

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

链接:https://www.nowcoder.com/questionTerminal/c451a3fd84b64cb19485dad758a55ebe
来源:牛客网

import java.util.ArrayList;
/*
*初始化small=1,big=2;
*small到big序列和小于sum,big++;大于sum,small++;
*当small增加到(1+sum)/2是停止
*/
public class Solution {
    public ArrayList<ArrayList<Integer>> FindContinuousSequence(int sum) {
        ArrayList<ArrayList<Integer>> lists=new ArrayList<ArrayList<Integer>>();
        if(sum<=1){return lists;}
        int small=1;
        int big=2;
        while(small!=(1+sum)/2){          //当small==(1+sum)/2的时候停止
            int curSum=sumOfList(small,big);
            if(curSum==sum){
                ArrayList<Integer> list=new ArrayList<Integer>();
                for(int i=small;i<=big;i++){
                    list.add(i);
                }
                lists.add(list);
                small++;big++;
            }else if(curSum<sum){
                big++;
            }else{
                small++;
            }
        }
        return lists;
    }
     
    public int sumOfList(int head,int leap){        //计算当前序列的和
        int sum=head;
        for(int i=head+1;i<=leap;i++){
            sum+=i;
        }
        return sum;
    }
}
View Code

 

42.和为S 的两个数字

题目描述

输入一个递增排序的数组和一个数字S,在数组中查找两个数,使得他们的和正好是S,如果有多对数字的和等于S,输出两个数的乘积最小的。
思路:左右夹逼方法,遇到的第一组满足和为S的数字即是乘积最小的。
import java.util.ArrayList;
public class Solution {
    public ArrayList<Integer> FindNumbersWithSum(int [] array,int sum) {
        ArrayList<Integer> al = new ArrayList<Integer>();
        if(array == null)
            return al;
        int ahead = 0;
        int behind = array.length-1;
        while(ahead < behind){
            int curSum = array[ahead] + array[behind];
            if(sum == curSum){
                al.add(array[ahead]);
                al.add(array[behind]);
                break;
            }else if(curSum > sum){
                behind--;
            }else{
                ahead++;
            }
        }
        return al;
    }
}
View Code

 

 43.左旋转字符串

题目描述

汇编语言中有一种移位指令叫做循环左移(ROL),现在有个简单的任务,就是用字符串模拟这个指令的运算结果。对于一个给定的字符序列S,请你把其循环左移K位后的序列输出。例如,字符序列S=”abcXYZdef”,要求输出循环左移3位后的结果,即“XYZdefabc”
public class Solution {
  public String LeftRotateString(String str,int n) {
        if(str.length() == 0)
            return "";
        
        char[] a = str.toCharArray();
        char[] result = new char[a.length];
        for(int i=0;i<a.length-n;i++){
            result[i] = a[n+i];
        }
        for(int i=0;i<n;i++){
            result[a.length-n+i] = a[i];
        }
        
        return String.valueOf(result);
                
    }
}
View Code
public class Solution {
 public String LeftRotateString(String str,int n) {
             if(str.length()==0)
                 return "";
            int len = str.length();
            str += str;
            return str.substring(n, len+n);
}
}
View Code
public class Solution {
 public String LeftRotateString(String str,int n) {
             if(str.length()==0)
                 return "";
        //把原字符串截取成俩字符串,然后拼接
        String s1 = str.substring(0, n);
        String s2 = str.substring(n,str.length());
        return s2 + s1;
}
}
View Code

 

 44.翻转单词顺序列

题目描述

牛客最近来了一个新员工Fish,每天早晨总是会拿着一本英文杂志,写些句子在本子上。同事Cat对Fish写的内容颇感兴趣,有一天他向Fish借来翻看,但却读不懂它的意思。例如,“student. a am I”。后来才意识到,这家伙原来把句子单词的顺序翻转了,正确的句子应该是“I am a student.”。Cat对一一的翻转这些单词顺序可不在行,你能帮助他么?
注意://要trim(),可能输入多个空格组成的字符串
public class Solution {
     public String ReverseSentence(String str) {
        if(str==null||str.length()==0||str.trim().length()==0)
            return str;
        
        String[] a = str.split(" ");
        StringBuffer sb = new StringBuffer();
        for(int i = 0;i<a.length;i++){
            if(i!=a.length-1)
                sb.append(String.valueOf(a[a.length-1-i])).append(" ");
            else
                sb.append(String.valueOf(a[a.length-1-i]));
        }
        return sb.toString();
    }
}
View Code

 

45.扑克牌算子

思路1:

1.将数组排序

2.找到第一个不出现0的位置,记作min

3.max - min < 5 则是顺子

import java.util.Arrays;
public class Solution {
    public boolean isContinuous(int [] numbers) {
        if(numbers == null || numbers.length == 0)
            return false;
        
        Arrays.sort(numbers);
        int length = numbers.length;
        int positionFlag = 0;
        boolean endZeroFlag = true;
        for(int i= 0; i<length-1;i++){
            if(numbers[i] == 0){
                positionFlag = i+1;
                endZeroFlag = false;
            }else {
                endZeroFlag = true;
            }
            if(endZeroFlag == true && numbers[i] == numbers[i+1])
                return false;
        }
        
        if(numbers[length-1] - numbers[positionFlag] >= 5)
            return false;
        else
        return true;
    }
}
View Code

 思路2:

1、排序 
2、计算所有相邻数字间隔总数 
3、计算0的个数 
4、如果2、3相等,就是顺子 
5、如果出现对子,则不是顺子
链接:https://www.nowcoder.com/questionTerminal/762836f4d43d43ca9deb273b3de8e1f4
来源:牛客网

import java.util.Arrays;
public class Solution {
    public boolean isContinuous(int[] numbers) {
        int numOfZero = 0;
        int numOfInterval = 0;
        int length = numbers.length;
        if(length == 0){
           return false;
        }
        Arrays.sort(numbers);
        for (int i = 0; i < length - 1; i++) {
            // 计算癞子数量
            if (numbers[i] == 0) {
                numOfZero++;
                continue;
            }
            // 对子,直接返回
            if (numbers[i] == numbers[i + 1]) {
                return false;
            }
            numOfInterval += numbers[i + 1] - numbers[i] - 1;
        }
        if (numOfZero >= numOfInterval) {
            return true;
        }
        return false;
    }
}
View Code

 

 46.圆圈中最后剩下的数字

0,1,2,。。。n这n个数字排成一个圆圈,从数字0开始,每次从这个圆圈中删除第m个数字,求出这个圆圈里剩下的最后一个数字。

1.数组来模拟环

public static int findLastNumber(int n,int m){
        if(n<1||m<1) return -1;
        int[] array = new int[n];
        int i = -1,step = 0, count = n;
        while(count>0){   //跳出循环时将最后一个元素也设置为了-1
            i++;          //指向上一个被删除对象的下一个元素。
            if(i>=n) i=0//模拟环。
            if(array[i] == -1) continue; //跳过被删除的对象。
            step++;                     //记录已走过的。
            if(step==m) {               //找到待删除的对象。
                array[i]=-1;
                step = 0;
                count--;
            }        
        }
        return i;//返回跳出循环时的i,即最后一个被设置为-1的元素
    }
View Code

2.用ArrayList做

 index = (index + m) % data.size();

import java.util.ArrayList;
public class Solution {
    public int LastRemaining_Solution(int n, int m) {
        if (m == 0 || n == 0) {
            return -1;
        }
        ArrayList<Integer> data = new ArrayList<Integer>();
        for (int i = 0; i < n; i++) {
            data.add(i);
        }
        int index = -1;
        while (data.size() > 1) {
            index = (index + m) % data.size();
            data.remove(index);
            index--;
        }
        return data.get(0);
    }
}
View Code

 

 47.求1+2+3+...+n

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

思路1:

通常求求1+2+3+...+n除了用(乘法)公式n(n+1)/2,无外乎循环和递归两种思路,由于已经明确限制for和while的使用,循环已经不能再用了。递归函数也需要用if或者条件判断语句判断是继续递归下去还是终止递归,但是现在题目已经不允许使用这两种语句。

1.需利用逻辑与的短路特性实现递归终止。
2.当n==0时,(n>0)&&((sum+=Sum_Solution(n-1))>0)只执行前面的判断,为false,然后直接返回0
3.当n>0时,执行sum+=Sum_Solution(n-1),实现递归计算Sum_Solution(n)。
public class Solution {
    public int Sum_Solution(int n) {
        int sum = n;
        boolean ans = (n>0) && ((sum += Sum_Solution(n-1))>0);
        return sum;
    }
}
View Code

 思路2:

用异常退出递归

public class Solution {
    public int Sum_Solution(int n) {
        return sum(n);
    }
    int sum(int n){
        try{
            int i = 1%n;
            return n+sum(n-1);
        }
        catch(Exception e){
            return 0;
        }
    }
}
View Code

 思路3:

Math的幂函数,但是底层还是用了乘法

public class Solution {
    public int Sum_Solution(int n) {
        n = (int) (Math.pow(n, 2)+n)>>1;
        return n;
    }
}
View Code

 

48.不做加减乘除做加法

首先看十进制是如何做的: 5+7=12,三步走

第一步:相加各位的值,不算进位,得到2。

第二步:计算进位值,得到10. 如果这一步的进位值为0,那么第一步得到的值就是最终结果。

第三步:重复上述两步,只是相加的值变成上述两步的得到的结果2和10,得到12。

同样我们可以用三步走的方式计算二进制值相加: 5-101,7-111

第一步:相加各位的值,不算进位,得到010,二进制每位相加就相当于各位做异或操作,101^111。

第二步:计算进位值,得到1010,相当于各位做与操作得到101,再向左移一位得到1010,(101&111)<<1。

第三步重复上述两步, 各位相加 010^1010=1000,进位值为100=(010&1010)<<1。 继续重复上述两步:1000^100 = 1100,进位值为0,跳出循环,1100为最终结果。

public class Solution {
 public int Add(int num1,int num2) {
        while (num2!=0) {
            int temp = num1^num2;
            num2 = (num1&num2)<<1;
            num1 = temp;
        }
        return num1;
    }
}
View Code

 

49.将字符串转换成整数

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

思路:

1.首先判断字符串第一个字符是不是‘-’‘+’号,如果是,则从第二个字符开始判断是不是数字;如果是‘-’,记录负数的标记

2.遍历每一个数字的asc码,if (a[i] < 48 || a[i] > 57return 0

public class Solution {
   public int StrToInt(String str) {
            if (str.equals("") || str.length() == 0)
                    return 0;
                char[] a = str.toCharArray();
                int sum = 0;
                int fuhao = 0;
                boolean fushu = false;
                if(a[0] == '-'){
                    fuhao = 1;
                    fushu = true;
                 }else if (a[0] == '+'){
                    fuhao = 1;
                }
                
                for (int i = fuhao; i < a.length; i++)
                {
                    if (a[i] < 48 || a[i] > 57)
                        return 0;
                    sum = sum * 10 + a[i] - 48;
                }
                return fushu == false ? sum : sum * (-1);
            }
}
View Code

犯规写法:

public int StrToInt(String str) {
        int result;
        if (str == "" || str == null ) {
            return 0;
        }
        try {
           result = Integer.valueOf(str);
        } catch (NumberFormatException e) {
            return 0;
        }
        return result;
    }
View Code

 

50.数组中重复的数字

题目描述

在一个长度为n的数组里的所有数字都在0到n-1的范围内。 数组中某些数字是重复的,但不知道有几个数字是重复的。也不知道每个数字重复几次。请找出数组中任意一个重复的数字。 例如,如果输入长度为7的数组{2,3,1,0,2,5,3},那么对应的输出是第一个重复的数字2。
思路:
构造一个长度为n的boolean数组,思想类似hash算法取地址存放,根据输入数组中每个数字大小决定将boolean数组哪个位置的值置位为true。
public class Solution {

     public boolean duplicate(int numbers[],int length,int [] duplication) {
            boolean [] a = new boolean[length];
            for(int i=0;i<length; i++){
                if(a[numbers[i]] == true){
                    duplication[0] = numbers[i];
                    return true;
                }
                a[numbers[i]] = true;
            }
            return false;
        }
}
View Code

使用Array.sort()或者HashSet(hashSet.add方法返回的是boolean值),但是补不能保证题目条件不能保证题目条件:如果输入长度为7的数组{ 7 5 6 7 5 3 1},那么对应的输出是第一个重复的数字2。

链接:https://www.nowcoder.com/questionTerminal/623a5ac0ea5b4e5f95552655361ae0a8
来源:牛客网

import java.util.*;
public class Solution {   
public boolean duplicate(int numbers[],int length,int [] duplication) {
//方法1:
     if(numbers == null || numbers.length == 0) return false;
        Arrays.sort(numbers);
        int flag = 0;//做标记
        for(int i=0;i<length-1;i++) {
            if(numbers[i] == numbers[i+1]) {
                duplication[0] = numbers[i];
                flag = 1;
                break;
            }
        }
        return flag == 1? true:false;
//方法2:
        HashSet<Integer> hs = new HashSet<>();
        for(int i=0;i<length;i++) {
            if(!hs.add(numbers[i])) {
                duplication[0]=numbers[i];
                return true;
            }
        }
        return false;
    }
}
View Code

 

 51.构建乘积数组
 
剑指的思路:
B[i]的值可以看作下图的矩阵中每行的乘积。
下三角用连乘可以很容求得,上三角,从下向上也是连乘。
因此我们的思路就很清晰了,先算下三角中的连乘,即我们先算出B[i]中的一部分,然后倒过来按上三角中的分布规律,把另一部分也乘进去。
import java.util.ArrayList;
public class Solution {
    public int[] multiply(int[] A) {
        int length = A.length;
        int [] B = new int[length];
        B[0] = 1;
        //计算下三角连乘
        for(int i = 1;i<length;i++){
            B[i] = B[i-1]*A[i-1];
        }
        int temp = 1;
         //计算上三角
        for(int j=length -2;j>=0;j--){
           temp *= A[j+1];
           B[j] *= temp;
        }
        return B;
    }
}
View Code

 

 52.正则表达式的匹配

思路:

当模式中的第二个字符不是“*”时:
1、如果字符串第一个字符和模式中的第一个字符相匹配,那么字符串和模式都后移一个字符,然后匹配剩余的。
2、如果 字符串第一个字符和模式中的第一个字符相不匹配,直接返回false。

而当模式中的第二个字符是“*”时:
如果字符串第一个字符跟模式第一个字符不匹配,则模式后移2个字符,继续匹配。如果字符串第一个字符跟模式第一个字符匹配,可以有3种匹配方式:
1、模式后移2字符,相当于x*被忽略;
2、字符串后移1字符,模式后移2字符;
3、字符串后移1字符,模式不变,即继续匹配字符下一位,因为*可以匹配多位;

这里需要注意的是:Java里,要时刻检验数组是否越界。
public class Solution {
 public boolean match(char[] str, char[] pattern) {
    if (str == null || pattern == null) {
        return false;
    }
    int strIndex = 0;
    int patternIndex = 0;
    return matchCore(str, strIndex, pattern, patternIndex);
}
  
public boolean matchCore(char[] str, int strIndex, char[] pattern, int patternIndex) {
    //有效性检验:str到尾,pattern到尾,匹配成功
    if (strIndex == str.length && patternIndex == pattern.length) {
        return true;
    }
    //pattern先到尾,匹配失败
    if (strIndex != str.length && patternIndex == pattern.length) {
        return false;
    }
    //模式第2个是*,且字符串第1个跟模式第1个匹配,分3种匹配模式;如不匹配,模式后移2位
    if (patternIndex + 1 < pattern.length && pattern[patternIndex + 1] == '*') {
        if ((strIndex != str.length && pattern[patternIndex] == str[strIndex]) || (pattern[patternIndex] == '.' && strIndex != str.length)) {
            return matchCore(str, strIndex, pattern, patternIndex + 2)//模式后移2,视为x*匹配0个字符
                    || matchCore(str, strIndex + 1, pattern, patternIndex + 2)//视为模式匹配1个字符
                    || matchCore(str, strIndex + 1, pattern, patternIndex);//*匹配1个,再匹配str中的下一个
        } else {
            return matchCore(str, strIndex, pattern, patternIndex + 2);
        }
    }
    //模式第2个不是*,且字符串第1个跟模式第1个匹配,则都后移1位,否则直接返回false
    if ((strIndex != str.length && pattern[patternIndex] == str[strIndex]) || (pattern[patternIndex] == '.' && strIndex != str.length)) {
        return matchCore(str, strIndex + 1, pattern, patternIndex + 1);
    }
    return false;
    }
}
View Code

 

53.表示数值的字符串

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

犯规写法1:使用正则表达式

public class Solution {

 public boolean isNumeric(char[] str) {
        String string = String.valueOf(str);
        return string.matches("[\\+-]?[0-9]*(\\.[0-9]*)?([eE][\\+-]?[0-9]+)?");
    }
}
View Code

 犯规写法2:使用自带API

public class Solution {
    public boolean isNumeric(char[] str) {
        try {
            double re = Double.parseDouble(new String(str));
        } catch (NumberFormatException e) {
            return false;
        }
        return true;
    }
}
View Code

正规解法:

参考剑指offer

分成A[.[B]][e|EC]或者.B[e|EC]

分表验证ABC三部分,AC都是可带正负符号的整数,B为无符号整数

//参见剑指offer
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; //是否存在整数
    }
}
View Code

 

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

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

思路1:

使用LinkedHashMap实现,注意不能使用hashmap,因为题目要求得到的是第一个不重复的字符,所以必须有序。

import java.util.LinkedHashMap;
public class Solution {
     LinkedHashMap<Character,Integer> map =new LinkedHashMap<Character,Integer>(); 
         public void Insert(char ch)
        {
            if(map.containsKey(ch)){
                map.put(ch, map.get(ch)+1);
            }else{
                map.put(ch, 1);
            }
        }
      //return the first appearence once char in current stringstream
        public char FirstAppearingOnce()
        {
            for(char key:map.keySet()){
                if(map.get(key) == 1)
                    return key;
                
            }
            return '#';
        }
}
View Code

思路2:

一个256大小的数组来实现一个简易的哈希表

public class Solution
{    
    int[] hashtable=new int[256];
    StringBuffer s=new StringBuffer();
    //Insert one char from stringstream
    public void Insert(char ch)
    {
        s.append(ch);
        if(hashtable[ch]==0)
            hashtable[ch]=1;
        else hashtable[ch]+=1;
    }
  //return the first appearence once char in current stringstream
    public char FirstAppearingOnce()
    {
      char[] str=s.toString().toCharArray();
      for(char c:str)
      {
          if(hashtable[c]==1)
              return c;
      }
      return '#';
    }
}
View Code

 思路3:

同样使用哈希表,不过非常啊巧妙的借助队列

private int[] cnts = new int[256];
private Queue<Character> queue = new LinkedList<>();

public void Insert(char ch)
{
    cnts[ch]++;
    queue.add(ch);
    while (!queue.isEmpty() && cnts[queue.peek()] > 1)
        queue.poll();
}

public char FirstAppearingOnce()
{
    return queue.isEmpty() ? '#' : queue.peek();
}
View Code

 

55.链表中环的入口节点

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

 思路1:

设起点到相遇点距离为x,起点到入口点距离为y,环长度为r,则快慢针相遇时,满足2x-x=nr,n为快针在环中转的圈数。--> x=nr
快慢针相遇点距环入口点距离x-y
相遇后,快针从起点重新开始以步长为1速度开始走,经过距离y到达环入口点,慢针走y步后距离环入口点距离为x-y+y=x=nr,即走到了环入口点,两个指针相遇

public class Solution {
 
    public ListNode EntryNodeOfLoop(ListNode pHead)
    {
        ListNode fast = pHead;
        ListNode slow = pHead;
        while(fast !=null && fast.next !=null) {
            fast = fast.next.next;
            slow = slow.next;
            if(fast == slow) {
                ListNode p = pHead;
                while( p != slow) {
                    p = p.next;
                    slow = slow.next;
                }
                return p;
            }
        }
        return null;
    }
}
View Code

思路2:

碉堡的解法:利用HashSet,遍历添加链表中的元素,若遇到重复添加则返回哪个元素。

import java.util.HashSet;
public class Solution {
   public ListNode EntryNodeOfLoop(ListNode pHead)
        {
            HashSet<ListNode> set = new HashSet<ListNode>();
            while(pHead != null){
                if(!set.add(pHead))
                    return pHead;
                pHead = pHead.next;
            }
            return null;
        }
}
View Code

 使用ArrayList

List<ListNode> list = new ArrayList<ListNode>();
        while(!list.contains(pHead))
        {
            list.add(pHead);
            if(pHead.next!=null)
                pHead = pHead.next;
            else
                break;
        }
        if(pHead.next == null)return null;
        return pHead;
View Code

 

56.删除链表中重复的节点

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

 1.递归方法(推荐)

public class Solution {
 public ListNode deleteDuplication(ListNode pHead) {
        if (pHead == null || pHead.next == null) { // 只有0个或1个结点,则返回
            return pHead;
        }
        if (pHead.val == pHead.next.val) { // 当前结点是重复结点
            ListNode pNode = pHead.next;
            while (pNode != null && pNode.val == pHead.val) {
                // 跳过值与当前结点相同的全部结点,找到第一个与当前结点不同的结点
                pNode = pNode.next;
            }
            return deleteDuplication(pNode); // 从第一个与当前结点不同的结点开始递归
        } else { // 当前结点不是重复结点
            pHead.next = deleteDuplication(pHead.next); // 保留当前结点,从下一个结点开始递归
            return pHead;
        }
    }
}
View Code

2.非递归方法:

public static ListNode deleteDuplication(ListNode pHead) {
         
        ListNode first = new ListNode(-1);//设置一个trick
 
        first.next = pHead;
 
        ListNode p = pHead;
        ListNode last = first;
        while (p != null && p.next != null) {
            if (p.val == p.next.val) {
                int val = p.val;
                while (p!= null&&p.val == val)
                    p = p.next;
                last.next = p;
            } else {
                last = p;
                p = p.next;
            }
        }
        return first.next;
    }
View Code

 

57.二叉树的下一个节点

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

思路:

① 如果一个节点的右子树不为空,那么该节点的下一个节点是右子树的最左节点;

② 否则,向上找第一个左链接指向的树包含该节点的祖先节点。

 

/*
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.right !=null){
            TreeLinkNode node = pNode.right;
            while (node.left != null) //如果有右子树,则找右子树的最左节点
                node = node.left;
            return node;
        }else{
            while(pNode.next != null){
                TreeLinkNode parent = pNode.next;
                if(parent.left == pNode) //没右子树,则找第一个当前节点是父节点左孩子的节点
                    return parent;
                pNode = pNode.next;
            }
        }
        return null;  //退到了根节点仍没找到,则返回null
    }
}
View Code

 

58.对称的二叉树

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

 思路:

1.递归算法

1).只要pRoot.left和pRoot.right是否对称
2).左右节点的值相等且对称子树left.left, right.right ;left.rigth,right.left也对称
3 ).注意要把null作为一个节点也考虑进去
public class Solution {
    boolean isSymmetrical(TreeNode pRoot)
    {
        if(pRoot == null)
            return true;
         return isSymmetrical(pRoot.left,pRoot.right);
    }
    
    boolean isSymmetrical(TreeNode t1,TreeNode t2){
        if(t1 == null && t2 == null)
            return true;
        if(t1 == null) return t2 == null;
        if(t2 == null) return false;
        if(t1.val != t2.val) return false;
        return isSymmetrical(t1.left,t2.right) && isSymmetrical(t1.right,t2.left);
    }
}
View Code

2.使用栈

使用stack来保存成对的节点

 1).出栈的时候也是成对成对的 ,
                1.若都为空,继续;
                2.一个为空,返回false;
                3.不为空,比较当前值,值不等,返回false;

 2).确定入栈顺序,每次入栈都是成对成对的,如left.left, right.right ;left.rigth,right.left

boolean isSymmetricalDFS(TreeNode pRoot)
    {
        if(pRoot == null) return true;
        Stack<TreeNode> s = new Stack<>();
        s.push(pRoot.left);
        s.push(pRoot.right);
        while(!s.empty()) {
            TreeNode right = s.pop();//成对取出
            TreeNode left = s.pop();
            if(left == null && right == null) continue;
            if(left == null || right == null) return false;
            if(left.val != right.val) return false;
            //成对插入
            s.push(left.left);
            s.push(right.right);
            s.push(left.right);
            s.push(right.left);
        }
        return true;
    }
View Code

 

59.按照只字形顺序打印二叉树

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

重点学习:使用堆栈实现层级遍历,然后借助一个标记变量实现反向排序是之字形遍历。

每当使下一个层级的节点入堆栈,记录堆栈中节点的个数,然后取出相应个数的节点。

注意:堆栈中是可以存放null值的,当遍历到null时,直接返回,当null也全部从堆栈中弹出后堆栈为空就代表全部遍历完结束。

import java.util.ArrayList;

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

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

    }

}
*/
import java.util.ArrayList;
import java.util.Collections;
import java.util.LinkedList;
import java.util.Queue;
public class Solution {
    public ArrayList<ArrayList<Integer>> Print(TreeNode pRoot) {
            
          ArrayList<ArrayList<Integer>> al = new ArrayList<ArrayList<Integer>>();
         Queue <TreeNode> q = new LinkedList<>();
         q.add(pRoot);
         boolean reverse = false;
         while(!q.isEmpty()){
             ArrayList<Integer> ll = new  ArrayList<Integer>();
             int cnt = q.size();
             while(cnt-- > 0){
                 TreeNode node = q.poll();
                 if(node == null)
                     continue;
                 ll.add(node.val);
                 q.add(node.left);
                 q.add(node.right);
             }
             if(reverse)
                 Collections.reverse(ll);
             reverse = !reverse;
             if(ll.size() != 0)
                 al.add(ll);
         }
         return al;
        }

}
View Code

 

60.把二叉树打印成多行

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

 思路:跟前面那个类似,只是不用翻转顺序

import java.util.ArrayList;


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

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

    }

}
*/
import java.util.ArrayList;
import java.util.Collections;
import java.util.LinkedList;
import java.util.Queue;
public class Solution {
    ArrayList<ArrayList<Integer> > Print(TreeNode pRoot) {
        ArrayList<ArrayList<Integer>> ret = new ArrayList<>();
    Queue<TreeNode> queue = new LinkedList<>();
    queue.add(pRoot);
    while (!queue.isEmpty()) {
        ArrayList<Integer> list = new ArrayList<>();
        int cnt = queue.size();
        while (cnt-- > 0) {
            TreeNode node = queue.poll();
            if (node == null)
                continue;
            list.add(node.val);
            queue.add(node.left);
            queue.add(node.right);
        }
        if (list.size() != 0)
            ret.add(list);
    }
    return ret;
    }
    
}
View Code

 

 61.序列化二叉树

请实现两个函数,分别用来序列化和反序列化二叉树

思路:

序列化:用前序遍历递归,递归的终止条件

if(root == null)
return "#";

反序列化:同样以前序遍历的书序递归构造

递归的终止条件

if(strr[index].equals("#"))
return null;

用一个位置变量index记录遍历到达的位置

public class Solution {

   private int index = -1;
String Serialize(TreeNode root) {
            if(root == null)
                return "#";
            return root.val +" "+ Serialize(root.left)+" " + Serialize(root.right);
      }
        
TreeNode Deserialize(String str) {
         index++;
        String[] strr = str.split(" ");
        
        if(strr[index].equals("#"))
            return null;
        TreeNode  node = new TreeNode(Integer.valueOf(strr[index]));
        node.left = Deserialize(str);
        node.right = Deserialize(str);
         
        return node;
        
      }
}
View Code

遍历的时候传入未遍历到不同的字符串

private String deserializeStr;

public String Serialize(TreeNode root)
{
    if (root == null)
        return "#";
    return root.val + " " + Serialize(root.left) + " " + Serialize(root.right);
}

public TreeNode Deserialize(String str)
{
    deserializeStr = str;
    return Deserialize();
}

private TreeNode Deserialize()
{
    if (deserializeStr.length() == 0)
        return null;
    int index = deserializeStr.indexOf(" ");
    String node = index == -1 ? deserializeStr : deserializeStr.substring(0, index);
    deserializeStr = index == -1 ? "" : deserializeStr.substring(index + 1);
    if (node.equals("#"))
        return null;
    int val = Integer.valueOf(node);
    TreeNode t = new TreeNode(val);
    t.left = Deserialize();
    t.right = Deserialize();
    return t;
}
View Code

 

62.二叉搜索树的第k个节点

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

思路:利用二叉搜索数中序遍历有序的特点,递归的中序遍历第k个节点就是。

public class Solution {
    
     private TreeNode ret =null;
     private int cnt = 0;
    TreeNode KthNode(TreeNode pRoot, int k)
    {
       inorder(pRoot,k);
        return ret;
    }
    
    void inorder(TreeNode pRoot, int k){
        if(pRoot == null || cnt > k)
            return;
        inorder(pRoot.left,k);
        cnt++;
        if(cnt == k)
            ret = pRoot;
        inorder(pRoot.right,k);
    }


}
View Code

 

 63.数据流中的中位数

如何得到一个数据流中的中位数?如果从数据流中读出奇数个数值,那么中位数就是所有数值排序之后位于中间的数值。如果从数据流中读出偶数个数值,那么中位数就是所有数值排序之后中间两个数的平均值。

 思路:

将数据流分成了两个部分,从中间切分(想象一下数据流有序时的样子),大顶堆里面是小的一半,小顶堆大的一半,当求中位数时只用关心中间的一个数或者两个数,这时关心的数就是堆顶的数

import java.util.PriorityQueue;
public class Solution {

        private int count = 0;
        private PriorityQueue<Integer>  max = new PriorityQueue<Integer>((o1,o2)->(o2-o1));
        private PriorityQueue<Integer>  min = new PriorityQueue<Integer>();
        public void Insert(Integer num) {
            if(count%2 ==0){
        //当数据总数为偶数时,新加入的元素,应当进入小根堆
        //(注意不是直接进入小根堆,而是经大根堆筛选后取大根堆中最大元素进入小根堆)
        //1.新加入的元素先入到大根堆,由大根堆筛选出堆中最大的元素
                max.add(num);
        //2.筛选后的【大根堆中的最大元素】进入小根堆
                min.add(max.poll());
            }else {
        //当数据总数为奇数时,新加入的元素,应当进入大根堆
        //(注意不是直接进入大根堆,而是经小根堆筛选后取小根堆中最大元素进入大根堆)
        //1.新加入的元素先入到小根堆,由小根堆筛选出堆中最小的元素
                min.add(num);
        //2.筛选后的【小根堆中的最小元素】进入大根堆
                max.add(min.poll());
            }
            count++;
        }

         public Double GetMedian() {
            if(count%2 ==0)
                return (min.peek()+max.peek())/2.0;
            else
                return (double)min.peek();
        }



}
View Code

 

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]}。

思路1:朴素的解法:

import java.util.ArrayList;
public class Solution {
    public ArrayList<Integer> maxInWindows(int [] num, int size)
        {
            ArrayList<Integer> al = new ArrayList<>();
            if(num.length == 0 || size==0)
                return al;
            for(int i = 0; i<num.length-size+1;i++){
                int [] a = new int[size];
                for(int j=0;j<size;j++){
                    a[j] = num[i+j];
                }
                al.add(max(a));
            }
            return al;
        }
      
      public Integer max(int[] num){
          int max = num[0];
          for(int i = 1;i<num.length;i++){
              if(num[i]>max)
                  max = num[i];
          }
          return max;
      }

}
View Code

 思路2:大顶堆解法

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

 

65.矩阵中的路径

请设计一个函数,用来判断在一个矩阵中是否存在一条包含某字符串所有字符的路径。路径可以从矩阵中的任意一个格子开始,每一步可以在矩阵中向左,向右,向上,向下移动一个格子。如果一条路径经过了矩阵中的某一个格子,则之后不能再次进入这个格子。 例如 a b c e s f c s a d e e 这样的3 X 4 矩阵中包含一条字符串"bcced"的路径,但是矩阵中不包含"abcb"路径,因为字符串的第一个字符b占据了矩阵中的第一行第二个格子之后,路径不能再次进入该格子。

思路:回溯算法,回溯的思想体现在体现在那个很长的if语句后面的,flag标记的恢复

public class Solution {
public boolean hasPath(char[] matrix, int rows, int cols, char[] str) {
        int flag[] = new int[matrix.length];
        for (int i = 0; i < rows; i++) {
            for (int j = 0; j < cols; j++) {
                if (helper(matrix, rows, cols, i, j, str, 0, flag))
                    return true;
            }
        }
        return false;
    }
 
    private boolean helper(char[] matrix, int rows, int cols, int i, int j, char[] str, int k, int[] flag) {
        int index = i * cols + j;
        if (i < 0 || i >= rows || j < 0 || j >= cols || matrix[index] != str[k] || flag[index] == 1)
            return false;
        if(k == str.length - 1) return true;
        flag[index] = 1;
        if (helper(matrix, rows, cols, i - 1, j, str, k + 1, flag)
                || helper(matrix, rows, cols, i + 1, j, str, k + 1, flag)
                || helper(matrix, rows, cols, i, j - 1, str, k + 1, flag)
                || helper(matrix, rows, cols, i, j + 1, str, k + 1, flag)) {
            return true;
        }
        flag[index] = 0;
        return false;
    }


}
View Code

 

66.机器人的运动范围

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

思路:从0,0开始通过递归遍历上下左右加起来的结果就是最大的连通区域

public class Solution {
public int movingCount(int threshold, int rows, int cols) {
        int flag[][] = new int[rows][cols]; //记录是否已经走过
        return helper(0, 0, rows, cols, flag, threshold);
    }
 
    private int helper(int i, int j, int rows, int cols, int[][] flag, int threshold) {
        if (i < 0 || i >= rows || j < 0 || j >= cols || numSum(i) + numSum(j)  > threshold || flag[i][j] == 1) return 0;    
        flag[i][j] = 1;
        return helper(i - 1, j, rows, cols, flag, threshold)
            + helper(i + 1, j, rows, cols, flag, threshold)
            + helper(i, j - 1, rows, cols, flag, threshold)
            + helper(i, j + 1, rows, cols, flag, threshold)
            + 1;
    }
 
    private int numSum(int i) {
        int sum = 0;
        do{
            sum += i%10;
        }while((i = i/10) > 0);
        return sum;
    }
}
View Code

 

 

posted @ 2018-06-27 22:49  开拖拉机的蜡笔小新  阅读(734)  评论(0编辑  收藏  举报