牛客剑指offer66题汇总(三)

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

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

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

public class Solution {
    public boolean isNumeric(char[] str) {
        String regex = "([+-]?[\\d]+)?([+-]?[\\d]*[\\.][\\d]+)?([+-]?[\\d]+[Ee][+-]?[\\d]+)?";
        return String.valueOf(str).matches(regex);
    }
}

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

public class Solution {
    //Insert one char from stringstream
   int [] temp = new int [256];
    int index = 1;
    public void Insert(char ch)
    {
       if(temp[ch] == 0){
           temp[ch]=index++;
       } else{
           temp[ch]=-1;
       }
    }
  //return the first appearence once char in current stringstream
    public char FirstAppearingOnce()
    {
       char c = '#';
        int max = Integer.MAX_VALUE;
        for(int i = 0;i<256;i++){
            if(temp[i]!=0&&temp[i]!=-1&&temp[i]<max){
                max = temp[i];
              c =(char)i;
                
            }
        }
        return c;
    }
}

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

public class Solution {
 
    public ListNode EntryNodeOfLoop(ListNode pHead)
    {
      if(pHead == null) return pHead;
        ListNode p = pHead;
        ListNode q = pHead;
         
        while(p.next!=null){
            p= p.next.next;
            q= q.next;
            if(p == q){
                p = pHead;
                while(p!=q){
                    p = p.next;
                    q = q.next;
                }
                return p;
            }
        }
        return null;
    }
}

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

public class Solution {
    public ListNode deleteDuplication(ListNode pHead)
    {
        if(pHead == null || pHead.next == null) return pHead;
        ListNode n = new ListNode(0);
        ListNode p = pHead;
        ListNode pre = n;
        n.next = p;
        boolean flag = true;
        while(p!=null){
            ListNode q = p.next;
            if(q == null) break;
            if(p.val == q.val){
                while(q!=null&&q.val == p.val){
                    q = q.next;
                }
                pre.next = q;
                p = q;
            }else{
                if(flag){
                   n.next = p;
                    flag = false;
                }
                pre = p;
                p = q;
            }
        }
        return n.next;
    }
}

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

public class Solution {
    public TreeLinkNode GetNext(TreeLinkNode pNode)
    {
        if(pNode == null) return pNode;
        if(pNode.right!=null){
            pNode = pNode.right;
            while(pNode.left!=null)
                pNode = pNode.left;
            return pNode;
        }
        while(pNode.next!=null){
            if(pNode.next.left ==pNode) return pNode.next;
            pNode = pNode.next;
        }
        return null;
    }
}

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

public class Solution {
    boolean isSymmetrical(TreeNode pRoot)
    { if(pRoot  == null)return true;
       TreeNode node = mirror(pRoot);
      return  isSymmetrical( pRoot, node) ;
    }
    boolean isSymmetrical(TreeNode pRoot,TreeNode node){
        if(pRoot==null&&node==null)return true;
        if((pRoot==null&&node!=null)||(pRoot!=null&&node==null)) return false;
        if(pRoot.val != node.val) return false;
         
        return isSymmetrical(pRoot.left,node.left)&&isSymmetrical(pRoot.right,node.right);
    }
     
   TreeNode mirror(TreeNode pRoot){
    if(pRoot == null) return null;
     TreeNode node = new TreeNode(pRoot.val);
       node.left =  mirror(pRoot.right);
       node.right = mirror(pRoot.left);
        
       return node;
        
   }
}

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

public class Solution {
    public ArrayList<ArrayList<Integer> > Print(TreeNode pRoot) {
      ArrayList<ArrayList<Integer>> list = new ArrayList<ArrayList<Integer>>();
        ArrayList<Integer> list2 = new ArrayList();
        if(pRoot == null) return list;
      Deque<TreeNode> que = new LinkedList();
      boolean flag = true;
       que.add(pRoot);
        int size = 1;
        int count = 0;
        while(!que.isEmpty()){
            if(flag){
                TreeNode p = que.poll();
                count++;
                list2.add(p.val);
                if(p.left!=null){
                    que.add(p.left);
                }
                if(p.right!=null){
                    que.add(p.right);
                }
            }else{
                TreeNode p = que.pollLast();
                count++;
                list2.add(p.val);
                if(p.right!=null){
                    que.addFirst(p.right);
                }
                if(p.left!=null){
                    que.addFirst(p.left);
                }
            }
        if(count == size){
            list.add(list2);
            list2 = new ArrayList();
            flag = (flag == true)?false:true;
            count = 0;
            size = que.size();
        }
         
        }
        return list;
         
    }
 
}

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

public class Solution {
    ArrayList<ArrayList<Integer> > Print(TreeNode pRoot){
        ArrayList<ArrayList<Integer>> list = new ArrayList<ArrayList<Integer>>();
        ArrayList<Integer> list2 = new ArrayList();
        if(pRoot == null) return list;
        Deque<TreeNode> que = new LinkedList();
        int count  =0;
        int size = 1;
        que.add(pRoot);
        while(!que.isEmpty()){
         TreeNode p = que.poll();
            list2.add(p.val);
            count++;
            if(p.left!=null){
                que.add(p.left);
            }
            if(p.right!=null){
                que.add(p.right);
            }
            if(count == size){
                list.add(list2);
                list2 = new ArrayList();
                size = que.size();
                count = 0;
            }
        }
       return list;
    }
     
}

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

public class Solution {
    String Serialize(TreeNode root) {
        if(root == null) return "";
        StringBuilder sb = new StringBuilder();
        Serialize2(root,sb);
        return sb.toString();
  }
    public void Serialize2(TreeNode root,StringBuilder sb){
        if(root == null) {
            sb.append("#,");
            return;
        }
         sb.append(root.val).append(",");
        Serialize2(root.left,sb);
  
        Serialize2(root.right,sb);
    }
    int index = -1;
    TreeNode Deserialize(String str) {
       if(str =="" || str.length()==0) return null;
        String [] strs = str.split(",");
      return  Deserialize2(strs);
    }
    public TreeNode Deserialize2(String [] strs){
         index++;
        if(!strs[index].equals("#")){
            TreeNode node = new TreeNode(Integer.parseInt(strs[index]));
            node.left = Deserialize2(strs);
            node.right = Deserialize2(strs);
            return node;
        }
        return null;
    }
}

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

public class Solution {
    TreeNode KthNode(TreeNode pRoot, int k)
    {
        if(pRoot == null ||k<=0) return null;
        TreeNode[] result = new TreeNode[1];
        KthNode(pRoot,k,new int[1],result);
        return result[0];
    }
    void KthNode(TreeNode pRoot,int k,int []count,TreeNode[] result){
     if(result[0]!=null || pRoot == null) return ;
        KthNode(pRoot.left,k,count,result);
        count[0]++;
        if(count[0]==k){
            result[0] = pRoot;
        }
        KthNode(pRoot.right,k,count,result);
 }
 
}

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

import java.util.*;
public class Solution {
    private int count = 0;
private PriorityQueue<Integer> minHeap = new PriorityQueue<>();
private PriorityQueue<Integer> maxHeap = new PriorityQueue<Integer>(15, new Comparator<Integer>() {
    @Override
    public int compare(Integer o1, Integer o2) {
        return o2 - o1;
    }
});
  
public void Insert(Integer num) {
    if (count %2 == 0) {//当数据总数为偶数时,新加入的元素,应当进入小根堆
        //(注意不是直接进入小根堆,而是经大根堆筛选后取大根堆中最大元素进入小根堆)
        //1.新加入的元素先入到大根堆,由大根堆筛选出堆中最大的元素
        maxHeap.offer(num);
        int filteredMaxNum = maxHeap.poll();
        //2.筛选后的【大根堆中的最大元素】进入小根堆
        minHeap.offer(filteredMaxNum);
    } else {//当数据总数为奇数时,新加入的元素,应当进入大根堆
        //(注意不是直接进入大根堆,而是经小根堆筛选后取小根堆中最大元素进入大根堆)
        //1.新加入的元素先入到小根堆,由小根堆筛选出堆中最小的元素
        minHeap.offer(num);
        int filteredMinNum = minHeap.poll();
        //2.筛选后的【小根堆中的最小元素】进入大根堆
        maxHeap.offer(filteredMinNum);
    }
    count++;
}
  
public Double GetMedian() {
    if (count %2 == 0) {
        return new Double((minHeap.peek() + maxHeap.peek())) / 2;
    } else {
        return new Double(minHeap.peek());
    }
}
 
}

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

import java.util.*;
public class Solution {
    public ArrayList<Integer> maxInWindows(int [] num, int size)
    {
        ArrayList<Integer> list = new ArrayList<Integer>();
        if(num ==null || num.length == 0 || size<=0) return list;
        ArrayDeque<Integer> deque = new ArrayDeque();
        int begin;
        for(int i = 0;i<num.length;i++){
            begin = i - size +1;
            if(deque.isEmpty()){
                deque.add(i);
            }else if(begin > deque.getFirst()){
                deque.pollFirst();
            }
            while(!deque.isEmpty()&& num[deque.getLast()]<=num[i]){
                deque.pollLast();
            }
            deque.add(i);
            if(begin>=0){
                list.add(num[deque.peekFirst()]);
            }
        }
      return list;
    }
}

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

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

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

public class Solution {
    public int movingCount(int threshold, int rows, int cols)
    {
      if(threshold<0|| rows<= 0||cols<=0) return 0;
      boolean [] visited = new boolean[rows*cols];
        int count = 0;
       return movingCount(threshold,rows,cols,0,0,visited);
         
    }
    public int movingCount(int threshold,int rows,int cols,int row,int col,boolean [] visited){
        if(row>=0&&row<rows&&col>=0&&col<cols&&!visited[row*cols+col]&&checked(threshold,row,col)){
                visited[row*cols+col] = true;
            return 1+movingCount(threshold,rows,cols,row+1,col,visited)+
                movingCount(threshold,rows,cols,row-1,col,visited)+
                movingCount(threshold,rows,cols,row,col+1,visited)+
                movingCount(threshold,rows,cols,row,col-1,visited);
        }
        return 0;
    }
    public boolean checked(int threshold,int row,int col){
        int sum = 0;
        while(row!=0){
            sum+= row%10;
            row = row/10;
        }
        while(col!=0){
            sum+=col%10;
            col=col/10;
        }
        if(sum>threshold) return false;
        return true;
    }
}

66.给你一根长度为n的绳子,请把绳子剪成m段(m、n都是整数,n>1并且m>1),每段绳子的长度记为k[0],k[1],…,k[m]。请问k[0]xk[1]x…xk[m]可能的最大乘积是多少?例如,当绳子的长度是8时,我们把它剪成长度分别为2、3、3的三段,此时得到的最大乘积是18。
输入描述:
输入一个数n,意义见题面。(2 <= n <= 60)
输出答案。

示例1
输入
8
输出
18

class Solution {
public:
    int cutRope(int number) {
        if(number==2)
            return 1;
        if(number==3)
            return 2;
         
        int x=number%3;
        int y=number/3;
        if(x==0)
            return pow(3,y);
        else if(x==1)
            return 2*2*(int)pow(3,y-1);
        else
            return 2*pow(3,y);
         
    }
};

你可能感兴趣的:(算法学习)