牛客网剑指Offer67题及题解(Java实现)

数组

1 二维数组中的查找

题目描述
在一个二维数组中(每个一维数组的长度相同),每一行都按照从左到右递增的顺序排序,每一列都按照从上到下递增的顺序排序。请完成一个函数,输入这样的一个二维数组和一个整数,判断数组中是否含有该整数。

思路
由于数组从左至右,从上至下都是有序的,可以先判断每行最右边的数字即每行最大的数字是否小于目标值,如果小于的话则跳到下一行,如果在下一行没有找到则继续跳到下一行,依次类推。
其中在每一行使用二分查找查找目标值。


public class Solution {
    public boolean Find(int target, int [][] array) {
    	//如果数组为空返回false
        if(array.length==0||array[0].length==0)
            return false;
        int row=0;//从第一行开始
        while(row<array.length){//循环到最后一行
            if(array[row][array[0].length-1]<target){//如果该行最后一列小于目标值
                row++;//跳到下一行
            }
            else {//在行中使用二分查找
                int low=0;
                int high=array[0].length-1;
                while(low<=high){
                    int mid=low+(high-low)/2;
                    if(array[row][mid]<target)
                        low=mid+1;
                    else if(array[row][mid]>target)
                        high=mid-1;
                    else
                        return true;
                }//如果没找到 跳到下一行 不然会无限循环
                row++;
            }
        }
        return false;
    }
}

2 数组中重复的数字

题目描述
在一个长度为n的数组里的所有数字都在0到n-1的范围内。 数组中某些数字是重复的,但不知道有几个数字是重复的。也不知道每个数字重复几次。请找出数组中任意一个重复的数字。 例如,如果输入长度为7的数组{2,3,1,0,2,5,3},那么对应的输出是第一个重复的数字2。
思路
使用哈希表,判断当前遍历的元素是否已存在,若存在返回true。


public class Solution {
    public boolean duplicate(int numbers[],int length,int [] duplication) {
        if(numbers!=null){
            HashMap<Integer,Integer> map=new HashMap<>();
            for(int num:numbers){
                if(map.containsKey(num)){
                    duplication[0]=num;
                    return true;
                }else
                    map.put(num,1);
            }
        
        duplication[0]=-1;
        return false;
    }
}

3 构建乘积数组

题目描述
给定一个数组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]。不能使用除法。
思路
按照题目描述构建算法即可。


public class Solution {
    public int[] multiply(int[] A) {
        int[] B=new int[A.length];//长度和A一样
        for(int i=0;i<A.length;i++){//依次计算乘积并赋值
            int mul=1;
            for(int j=0;j<i;j++)//计算0-i-1的乘积
                mul*=A[j];
            for(int j=i+1;j<A.length;j++)//计算i+1到n-1的乘积
                mul*=A[j];
            B[i]=mul;//赋值
        }
        return B;
    }
}

字符串

1 替换空格

题目描述
请实现一个函数,将一个字符串中的每个空格替换成“%20”。例如,当字符串为We Are Happy.则经过替换之后的字符串为We%20Are%20Happy。
思路
构建一个stringbuffer,如果遇到空格则附加%20,否则附加原字符。


public class Solution {
    public String replaceSpace(StringBuffer str) {
        StringBuffer sb=new StringBuffer();
    	for(int i=0;i<str.length();i++){
            if(str.charAt(i)!=' ')
                sb.append(str.charAt(i));
            else
                sb.append("%20");
        }
        return sb.toString();
    }
}

2 正则表达式匹配

题目描述
请实现一个函数用来匹配包括.*的正则表达式。模式中的字符’.‘表示任意一个字符,而’*'表示它前面的字符可以出现任意次(包含0次)。 在本题中,匹配是指字符串的所有字符匹配整个模式。例如,字符串aaa与模式a.aab*ac*a匹配,但是与aa.aab*a均不匹配
思路
利用递归判断,定义两个指针分别遍历字符串和匹配字符串,若可以同时到达末尾说明可以完全匹配,否则说明不能完全匹配。


public class Solution {
    public boolean match(char[] str, char[] pattern){
        //任意一个为空,则匹配失败
        if(str==null||pattern==null)
            return false;
        return match(str,0,pattern,0);
    }
    public boolean match(char[] str, int s,char[] pattern,int p){
        //如果字符串和匹配字符串都到达了末尾,则说明匹配成功
        if(s==str.length&&p==pattern.length)
            return true;
        //如果字符串未遍历结束,但匹配字符串已结束,匹配失败
        if(s<str.length&&p==pattern.length)
            return false;
        //如果匹配字符串的下一个字符是*
        if(p<pattern.length-1&&pattern[p+1]=='*'){
            //如果当前字符串字符和匹配字符串字符相同或者匹配字符为.
            if(s<str.length&&(str[s]==pattern[p]||pattern[p]=='.'))
                return match(str, s+1, pattern, p)//.*可以匹配多个字符 如aa匹配a*
                        ||match(str, s, pattern, p+2);//忽略当前匹配字符串的2个字符 如a不匹配b*,可跳过
            else
                return match(str, s, pattern, p+2);//匹配失败
        }
        if(s<str.length&&(str[s]==pattern[p]||pattern[p]=='.')){
            return match(str,s+1,pattern,p+1);
        }
        return false;
    }
}

3 字符流中第一个不重复的字符

题目描述
请实现一个函数用来找出字符流中第一个只出现一次的字符。例如,当从字符流中只读出前两个字符"go"时,第一个只出现一次的字符是"g"。当从该字符流中读出前六个字符“google"时,第一个只出现一次的字符是"l"。
输出描述
如果当前字符流没有存在出现一次的字符,返回#字符。
思路
定义一个ArrayList集合存储输入流的字符,一个哈希表记录各字符的出现次数,遍历当前集合返回第一个出现次数为1的字符,如果没有就返回#


import java.util.*;
public class Solution {
    //Insert one char from stringstream
    HashMap<Character,Integer> map=new HashMap<>();
    ArrayList<Character> list=new ArrayList<>();
    
    public void Insert(char ch){
        if(map.containsKey(ch))//如果存在就加一
            map.put(ch,map.get(ch)+1);
        else//不存在就放入map,并赋初值1
            map.put(ch,1);
        list.add(ch);//保存到集合中
    }
  //return the first appearence once char in current stringstream
    public char FirstAppearingOnce(){
        //遍历集合返回在哈希表中出现次数为1的字符
        for(char c:list){
            if(map.get(c)==1)
                return c;
        }
        return '#';//如果没有则返回#
    }
}

4 表示数值的字符串

题目描述
请实现一个函数用来判断字符串是否表示数值(包括整数和小数)。例如,字符串"+100",“5e2”,"-123",“3.1416"和”-1E-16"都表示数值。 但是"12e",“1a3.14”,“1.2.3”,"±5"和"12e+4.3"都不是。
思路
^表示以…开头
[+-]表示有0或1个正负号
[0-9]*表示任意个数字
(\\.[0-9]*)?表示0或1个 (小数点+任意个数字) 如.1.123
([eE][+-]?[0-9]+)? 表示0或1个( e/E 0或1个正负号 一或多个数字) 如e+1e-12e2
$表示以…结尾


public class Solution {
    public boolean isNumeric(char[] str) {
        String s="^[+-]?[0-9]*(\\.[0-9]*)?([eE][+-]?[0-9]+)?$";
        String line=new String(str);
        return line.matches(s);
    }
}

链表

1 从尾到头打印链表

题目描述
输入一个链表,按链表从尾到头的顺序返回一个ArrayList。
思路
先遍历链表存储值到集合中,再对换链表前半部分和后半部分的值


import java.util.ArrayList;
public class Solution {
    public ArrayList<Integer> printListFromTailToHead(ListNode listNode) {
        ArrayList<Integer> list=new ArrayList<>();
        while(listNode!=null){//遍历链表,存值到集合
            list.add(listNode.val);
            listNode=listNode.next;
        }
        //将集合前后部分的值对换
        for(int i=0;i<list.size()/2;i++){
            int temp=list.get(i);
            list.set(i,list.get(list.size()-1-i));
            list.set(list.size()-1-i,temp);
        }
        return list;
    }
}

2 链表中环的节点入口

题目描述
给一个链表,若其中包含环,请找出该链表的环的入口结点,否则,输出null。
思路
设链表入口到环入口的距离为a,设两个快慢指针slow和fast,slow每次一步,fast两步,相遇时距离环入口距离为b,剩下环距离为c。
由于fast的距离是slow的2倍,因此fast的路程a+n(b+c)+b是slow的路程a+b的2倍,化简得到a=(n-1)b+c,所以相遇时将如果同时从相遇点和链表入口出发一定在环入口相遇。


public class Solution {
    public ListNode EntryNodeOfLoop(ListNode pHead){
    	//如果链表长度小于2个,不可能存在环
        if(pHead==null||pHead.next==null)
            return null;
        //定义快慢指针
        ListNode slow=pHead;
        ListNode fast=pHead;
        while(fast.next!=null){//fast的下一个不为空
           slow=slow.next;//slow走一步
           fast=fast.next.next;//fast走两步  
           if(slow==fast){//相遇
                fast=pHead;//fast从环入口开始
                while(slow!=fast){//直到相遇
                    slow=slow.next;
                    fast=fast.next;
                } 
                return slow;//返回环入口
           }
        }
        return null;//不存在环
    }
}

3 删除链表中重复的结点

题目描述
在一个排序的链表中,存在重复的结点,请删除该链表中重复的结点,重复的结点不保留,返回链表头指针。 例如,链表1->2->3->3->4->4->5 处理后为 1->2->5
思路
添加一个头结点便于遍历,定义前后两个指针,让last.next指针只需非重复数字的结点。


import java.util.*;
public class Solution {
    public ListNode deleteDuplication(ListNode pHead){
        ListNode head=new ListNode(-1);//定义头结点
        head.next=pHead;
        ListNode pre=head;//定义前指针
        ListNode last=pre.next;//定义后指针
        while(last!=null){//后指针不为空
            if(last.next!=null&&last.val==last.next.val){//如果后指针的下一个和当前相同
                while(last.next!=null&&last.next.val==last.val)//遍历直到后指针的下一个是非重复数
                    last=last.next;
                pre.next=last.next;//前指针的下一个是非重复数,跳过重复数
                last=last.next;//更新后指针
            }else{//没遍历到重复数时前后指针都跳到下一个
                pre=pre.next;
                last=pre.next;
            }
        }
        return head.next;      
    }
}

wait

发布了66 篇原创文章 · 获赞 302 · 访问量 5万+

猜你喜欢

转载自blog.csdn.net/qq_41112238/article/details/103937376