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

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;
    }

}
解析:见代码的注释




猜你喜欢

转载自blog.csdn.net/shl_shl/article/details/68921841
今日推荐