zoukankan      html  css  js  c++  java
  • 剑指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
  • 相关阅读:
    联系我们
    RCMTM _百度百科
    DotNetCore跨平台~Dockerfile的解释
    DotNetCore跨平台~服务总线_事件总线的重新设计
    DotNetCore跨平台~问题~NETCoreAPP, Version=v1.0' compatible with one of the target runtimes: 'win10-x64
    DotNetCore跨平台~EFCore连接Mysql的方式
    何时可以开启透明数据加密(TDE)?
    通信系统概论---传输介质
    struts2.x中因变量命名错误不被注入到值栈的问题
    【面向代码】学习 Deep Learning(三)Convolution Neural Network(CNN)
  • 原文地址:https://www.cnblogs.com/xiangkejin/p/9211081.html
Copyright © 2011-2022 走看看