牛客网 剑指Offer,一些值得记住的小题(五)

来源:互联网 发布:一句话 晚安 知乎 编辑:程序博客网 时间:2024/05/17 04:53

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

public class Solution {    // Parameters:    //    numbers:     an array of integers    //    length:      the length of array numbers    //    duplication: (Output) the duplicated number in the array number,length of duplication array is 1,so using duplication[0] = ? in implementation;    //                  Here duplication like pointor in C/C++, duplication[0] equal *duplication in C/C++    //    这里要特别注意~返回任意重复的一个,赋值duplication[0]    // Return value:       true if the input is valid, and there are some duplications in the array number    //                     otherwise false    public boolean duplicate(int numbers[],int length,int [] duplication) {                for(int i=0;i<length;i++){            int index = numbers[i];            if(index>=length){                index-=length;            }            if(numbers[index]>=length){                duplication[0]=index;                return true;            }            numbers[index] = numbers[index]+length;        }        return false;    }}
解析:本题要利用好题中的条件以减少时间复杂度("在一个长度为n的数组里的所有数字都在0到n-1的范围内")首先取出一个数,以这个数作为角标的位置上的数+length,当遇到某个数比数组长度大时就存入duplication[]


26.给定一个数组A[0,1,...,n-1],请构建一个数组B[0,1,...,n-1],其中B中的元素B[i]=A[0]*A[1]*...*A[i-1]*A[i+1]*...*A[n-1]。不能使用除法。

import java.util.ArrayList;public class Solution {    public int[] multiply(int[] A) {                int [] B = new int [A.length];        B[0]=1;        if(A.length!=0){            for(int i=1;i<A.length;i++){            B[i]=B[i-1]*A[i-1];        }        int temp=1;        for(int i=A.length-2;i>=0;i--){            temp*=A[i+1];            B[i]*=temp;        }        }                return B;        /*if(A.length==0){            return null;        }        int [] B = new int [A.length];                for(int i=0;i<B.length;i++){            B[i]=1;            for(int j=0;j<A.length;j++){                if(j==i){                    continue;                }else{                    B[i]*=A[j];                }                            }             }        return B;*/    }}
解析:如下图,为了减少复杂度,思路是先算正方形的左下半部分,再算右上半部分



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

public class Solution {    public boolean match(char[] str, char[] pattern)    {        if(str==null||pattern==null){            return false;        }        int sindex=0;        int pindex=0;        return Match(str,sindex,pattern,pindex);    }    public boolean Match(char[] str,int sindex,char[] pattern,int pindex){        if(sindex==str.length&&pindex==pattern.length){            return true;        }        if(sindex!=str.length&&pindex==pattern.length){            return false;        }        if(pindex+1<pattern.length&&pattern[pindex+1]=='*'){当前要匹配的pattern中的字符的后面是"*"的情况            //如果当前匹配的字符是‘.’或者两个字符相等的情况,就会分为三种情况,sindex后挪,或者pindex后挪,或者dounuo            if((sindex!=str.length&&str[sindex]==pattern[pindex])||(sindex!=str.length&&pattern[pindex]=='.')){                return Match(str,sindex,pattern,pindex+2)||                    Match(str,sindex+1,pattern,pindex+2)||                    Match(str,sindex+1,pattern,pindex);            }            //如果不匹配的话就相当于把'*'用掉了,就直接pindex后挪            else{            return Match(str,sindex,pattern,pindex+2);            }        }        //如果当前正在验证的字符后面不是'*',如果当前字符匹配则都后挪,否则直接返回false        if((sindex!=str.length&&str[sindex]==pattern[pindex])||(str.length!=sindex&&pattern[pindex]=='.')){            return Match(str,sindex+1,pattern,pindex+1);        }        return false;    }}
解析:见代码中的注释。


28.一个链表中包含环,请找出该链表的环的入口结点。

/* public class ListNode {    int val;    ListNode next = null;    ListNode(int val) {        this.val = val;    }}*/public class Solution {    public ListNode EntryNodeOfLoop(ListNode pHead)    {        if(pHead==null||pHead.next==null){            return null;        }        ListNode p1=pHead;        ListNode p2=pHead;        int pp1=2;        int pp2=1;        p1=p1.next;        p1=p1.next;        p2=p2.next;        while(p1!=p2){            p1=p1.next;            p1=p1.next;            p2=p2.next;            pp1+=2;            pp2++;        }        int n = pp1-pp2;        p1=pHead;        p2=pHead;        for(int i=0;i<n;i++){            p1=p1.next;        }        while(p1!=p2){            p1=p1.next;            p2=p2.next;        }        return p1;    }}
解析:p1,p2都指向表头,pp1,pp2分别记录两者的步数,p1走两步,p2走一步,当p1,p2相遇时就说明p1,p2都在圈里并且p1已经比p2多走一圈了,这时候pp1-pp2就是一圈的长度,再重新指向表头,这次让p1先走一圈的长度,然后一起一步一步向后走,当到了相同节点是就是要找的节点。


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

/*public class TreeNode {    int val = 0;    TreeNode left = null;    TreeNode right = null;    public TreeNode(int val) {        this.val = val;    }}*/public class Solution {    boolean isSymmetrical(TreeNode pRoot)    {        if(pRoot==null){            return true;        }        return test(pRoot.left,pRoot.right);    }    boolean test(TreeNode left,TreeNode right){        if(left==null) return right==null;        if(right==null) return false;        if(left.val!=right.val) return false;        return test(left.right,right.left)&&test(left.left,right.right);    }}
解析:利用递归,注意test()中的参数就可以

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

import java.util.ArrayList;import java.util.Stack;/*public class TreeNode {    int val = 0;    TreeNode left = null;    TreeNode right = null;    public TreeNode(int val) {        this.val = val;    }}*/public class Solution {    public ArrayList<ArrayList<Integer> > Print(TreeNode pRoot) {                ArrayList<ArrayList<Integer>> listall=new ArrayList<ArrayList<Integer>>();        if(pRoot==null){            return listall;        }        ArrayList<Integer> list = new ArrayList<Integer>();        Stack<TreeNode> s1 = new Stack<TreeNode>();        Stack<TreeNode> s2 = new Stack<TreeNode>();        int flag=0;//flag=0从左往右输出,从右往左压栈        s1.push(pRoot);        while(s1.size()>0){            TreeNode temp = s1.pop();            list.add(temp.val);            if(flag==1){                if(temp.right!=null){                    s2.push(temp.right);                }                if(temp.left!=null){                    s2.push(temp.left);                }                            }else{                if(temp.left!=null){                    s2.push(temp.left);                }                if(temp.right!=null){                    s2.push(temp.right);                }            }        if(s1.size()==0){            flag=1-flag;            Stack<TreeNode> t = s1;            s1=s2;            s2=t;            //s1=s2;            //s2.clear();            listall.add(new ArrayList<Integer>(list));            list.clear();        }        }        return listall;    }}

解析:其实可以用reverse(),但是还是需要考虑复杂度的问题,所以这个用两个栈就可以解决,先把第一层压栈,因为第二层要从右往左输出,所以要先将左子树后右子树压s2栈,当s1空时,s1=s2,继续将s1中结点的字数压栈,通过flag来标记应该从左往右还是从右往左,以此类推。


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

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(Test(matrix,rows,cols,i,j,str,0,flag)){                    return true;                }            }        }        return false;    }    //matrix就是题中的[a b c e s f c s a d e e],rows,cols是行和列,str就是题中的"bcced",k是str的角标 ,flag用来标记该点走没走过    public boolean Test(char[] matrix,int rows,int cols,int i,int j,char[] str,int k,int[] flag){        int index = i*cols+j;        //如果走过或者值不相等就返回false        if(i<0||i>=rows||j<0||j>=cols||str[k]!=matrix[index]||flag[index]==1){            return false;        }        if(k==str.length-1){            return true;        }        //标记该点已经走完了        flag[index]=1;        //然后检查四周是否有和下个字符匹配的点        if(Test(matrix,rows,cols,i+1,j,str,k+1,flag)||          Test(matrix,rows,cols,i-1,j,str,k+1,flag)||          Test(matrix,rows,cols,i,j+1,str,k+1,flag)||          Test(matrix,rows,cols,i,j-1,str,k+1,flag)){            return true;        }        //递归结束后,要删除这个点,即标记这个点没有到过,是一种回溯思想,以为这个点并不是想要的点        flag[index]=0;        return false;    }}
解析:见代码的注释





0 0
原创粉丝点击