CleanCodeHandbook Chapter 1: Array/String(1-16)

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/u011732358/article/details/84112507

Array/String

leetcode1.TwoSum

题目链接
题目:给定一个整数数组和一个目标值,找出数组中和为目标值的两个数。
你可以假设每个输入只对应一种答案,且同样的元素不能被重复利用。
给定 nums = [2, 7, 11, 15], target = 9
因为 nums[0] + nums[1] = 2 + 7 = 9
所以返回 [0, 1]

思路:暴力两重循环遍历数组,两两组合直到找到满足条件的数组中的数。时间复杂度为 O ( n 2 ) O(n^{2}) ;空间复杂度为 O ( 1 ) O(1)

class Solution {
    public int[] twoSum(int[] nums, int target) {
        int a[] = new int[2] ;
        for(int i=0;i<nums.length;i++){
            for(int j=i+1;j<nums.length;j++){
                if(nums[i]+nums[j]==target){
                    a[0] = i ;
                    a[1] = j ;
                    return a ;
                }
            }
        }
        return a ;
    }
}

显然时间复杂度有点高了,换种解法 优化一下代码
别人思路:一次遍历直接OK,利用了map的存储结构,数值为键,索引为值,遍历数组,查看target-num[i]是否在数组中,如果在直接通过map查找其索引值,返回结果。时间复杂度为 O ( n ) O(n) ;空间复杂度为 O ( n ) O(n) 。空间换时间,哈哈,可以接受。

class Solution {
    public int[] twoSum(int[] nums, int target) {
        Map<Integer,Integer> map = new HashMap<>() ;
        for(int i=0;i<nums.length;i++){
            int x = nums[i] ;
            if(map.containsKey(target-x)){
                return new int[]{map.get(target-x), i} ;
            }
            map.put(x, i) ;
        }
        // return new int[]{1,1} ;
        throw new IllegalArgumentException("No two sum solution");
    }
}

leetcode167. Two Sum II - Input array is sorted

题目链接
题目:给定一个已按照升序排列 的有序数组,找到两个数使得它们相加之和等于目标数。
函数应该返回这两个下标值 index1 和 index2,其中 index1 必须小于 index2。
说明:
返回的下标值(index1 和 index2)不是从零开始的。
你可以假设每个输入只对应唯一的答案,而且你不可以重复使用相同的元素。
示例:
输入: numbers = [2, 7, 11, 15], target = 9
输出: [1,2]
解释: 2 与 7 之和等于目标数 9 。因此 index1 = 1, index2 = 2 。

思路:既然是有序数组,还是查找问题,自然想到了二分查找的时间复杂度 O ( l o g n ) O(logn) 还是很6的,所以就确定numbers[i],然后循环遍历进行二分查找(target-numbers[i])啦。时间复杂度为 O ( n l o g n ) O(nlogn) ;空间复杂度为 O ( 1 ) O(1)

class Solution {
    public int[] twoSum(int[] numbers, int target) {
        int len = numbers.length ;
        int j = 0 ;
        for(int i=0;i<len;i++){
            j = bsearch(numbers, target-numbers[i], i) ;
            if(j!=-1){
                return new int[]{i+1,j+1} ;
            }
        }
        throw new IllegalArgumentException("old brother,stable") ;
    }
    public int bsearch(int []numbers, int key, int start){
        int L = start +1;
        int R = numbers.length -1 ;
        int M = 0 ;
        while(L<=R){
            M = (L+R)/2 ;
            if(numbers[M] < key){
                L = M+1 ;
            }else if(numbers[M] > key){
                R = M-1 ;
            }else{
                break ;
            }
        }
        if(L>R){
            return -1 ;
        }
        return M ;
    }
}

那么问题来了,可不可以继续优化呢?Of course
别人思路:既然是有序数组,直接从两头开始找满足条件的两个数即可。从最小的和最大的开始相加,如果比目标数大,最大数则换成次大的数;如果比目标数小,最小数则换成次小的数;以此类推,知道找到满足条件的俩数为止。时间复杂度为 O ( n ) O(n) ;空间复杂度为 O ( 1 ) O(1)

class Solution {
    public int[] twoSum(int[] numbers, int target) {
        int start = 0 ; 
        int end = numbers.length-1 ;
        while (start<end){
            if(numbers[start]+numbers[end]<target){
                start ++ ;
            }else if(numbers[start]+numbers[end]>target){
                end -- ;
            }else{
                return new int[]{start+1,end+1} ;
            }
        }
        throw new IllegalArgumentException("old brother stable") ;
    }
}

leetcode170. Two Sum III - Data structure design($)

题目链接(由于是道付费题目,就没有贴出来原文链接,这是篇别人的博客)
题目:设计和实现一个TwoSum类,支持add和find操作。
add—将数字添加到内部的数据结构中
find—找出是否存在一对数字,它们的和等于这个值。
示例:
add(1); add(3); add(5);

find(4) -> true
find(7) -> false

思路:使用map结构存储数据。
add:key为输入的值,value为该元素的个数。
find:遍历map,如果val-key == key,判断key的个数是否至少存在两个,若存在两个返回ture,否则返回false;如果val-key!=key,那么看看map中是否包含以val-key的键,有则返回true,否则返回false。
add时间复杂度为 O ( 1 ) O(1) ,find时间复杂度为 O ( n ) O(n) ,空间复杂度为 O ( n ) O(n)

public class TwoSum{
	Map<Integer,Integer> map = new HashMap<>() ;
	    int count = 0 ;
	    public void add(int val){
	        if(!map.containsKey(val)){
	            map.put(val, 0) ;
	        }else{
	            count = map.get(val) ;
	            map.put(count,count+1) ;
	        }
	    }
	    public boolean find(int val){
	        for(Map.Entry<Integer,Integer> entry : map.entrySet()){
	            int x = entry.getKey() ;
	            int y = val - x ;
	            if(y == x){
	                if(entry.getValue()>=2){
	                    return true ;
	                }
	            }else {
	                if(map.containsKey(y)){
	                    return true ;
	                }
	            }
	            return false ;
	        }
	        return false ;
	    }
}

leetcode125. Valid Palindrome

题目链接
题目:给定一个字符串,验证它是否是回文串,只考虑字母和数字字符,可以忽略字母的大小写。
说明:本题中,我们将空字符串定义为有效的回文串。
示例 1:
输入: “A man, a plan, a canal: Panama”
输出: true
示例 2:
输入: “race a car”
输出: false

思路:两个指针,左边一个从头到尾走,右边一个从尾向头走,如果左边的等于右边的数值,左边继续向右走,右边继续向左走,直至二者相遇,返回true 否则返回false。
O ( n ) O(n) runtime ; O ( 1 ) O(1) space

class Solution {
    public boolean isPalindrome(String s) {
        s = s.toLowerCase() ;
        if("".equals(s)||s.isEmpty()){
            return true ;
        }
        int L = 0 ;
        int R = s.length()-1 ;
        while(L<R){
            while(!isValid(s.charAt(L))&&L<R){
                L ++ ;
            }
            while(!isValid(s.charAt(R))&&L<R){
                R -- ;
            }
            if(s.charAt(L) == s.charAt(R)){
                L ++ ;
                R -- ;
            }else {
                return false ;
            }
        }
        return true ;
    }
    public boolean isValid(char ch){
        if(ch>='0'&&ch<='9'||ch>='a'&&ch<='z'){
            return true ;
        }
        return false ;
    }
}

完事看来下别人解法,思路基本一样,只是不知道java中还有自带判断字符是否为数字或者字母的方法,孤陋寡闻了,看来还是站得高才能尿的远。

class Solution {
    public boolean isPalindrome(String s) {
        s = s.toLowerCase() ;
        int i = 0 ;
        int j = s.length() - 1 ;
        while(i < j){
            while(i<j && !Character.isLetterOrDigit(s.charAt(i))){
                i ++ ;
            }
            while(i<j && !Character.isLetterOrDigit(s.charAt(j))){
                j -- ;
            }
            if(s.charAt(i) != s.charAt(j)){
                return false ;
            }
            i ++ ;
            j -- ;
        }
        return true ;
    }
}

leetcode28. Implement strStr()

题目链接
题目:实现 strStr() 函数。
给定一个 haystack 字符串和一个 needle 字符串,在 haystack 字符串中找出 needle 字符串出现的第一个位置 (从0开始)。如果不存在,则返回 -1。

示例 1:
输入: haystack = “hello”, needle = “ll”
输出: 2
示例 2:
输入: haystack = “aaaaa”, needle = “bba”
输出: -1
说明:
当 needle 是空字符串时,我们应当返回什么值呢?这是一个在面试中很好的问题。
对于本题而言,当 needle 是空字符串时我们应当返回 0 。这与C语言的 strstr() 以及 Java的 indexOf() 是一致的。

思路:求一个子串是否在字符串中,遍历字符串,直到字母与子串的首字母相同为止,记录下该字母在字符串中的位置start,然后从该字母依次向后比较字符串中的每个字母是否与子串的每个字母相同,只要有一个不同,立马跳出循环,继续遍历字符串,直到字母与子串的首字母相同为止,继续刚才的操作。若每个字母都和子串中字母相同,则跳出循环,返回start。若遍历完整个字符串,都未找到子串,返回-1。 其中,若子串为空,返回0;若字符串为空,返回-1。$O(nm) $runtime, O ( 1 ) O(1) space

class Solution {
    public int strStr(String haystack, String needle) {
        int start = 0 ;
        int j ;
        if("".equals(needle)||needle.isEmpty()){
            return 0 ;
        }
        if("".equals(haystack)||haystack.isEmpty()){
            return -1 ;
        }
        
        for(int i=0;i<haystack.length();i++){
            while(i<haystack.length()&&haystack.charAt(i)!=needle.charAt(0)){
                i ++ ;
            }
            start = i ;
            for(j=0;j<needle.length()&&i+j<haystack.length();j++){
                if(haystack.charAt(i+j) != needle.charAt(j)){
                    break ;
                }
            }
            if(j<needle.length()){
                start = -1 ;
            }else{
                break ;
            }
        }
        return start ;
    }
}

看来波别人的代码。也是用的暴力方法求解,不过代码写起来很整洁,值得学习一波。

class Solution {
    public int strStr(String haystack, String needle) {
        for (int i = 0; ; i++) {
            for (int j = 0; ; j++) {
                 if (j == needle.length()) {
                     return i;
                 }
                 if (i + j == haystack.length()){
                     return -1;
                 }
                 if (needle.charAt(j) != haystack.charAt(i + j)) {
                     break ;
                 }
             }
         }
    }
}

leetcode151. Reverse Words in a String

题目链接
题目:给定一个字符串,逐个翻转字符串中的每个单词。
示例:
输入: “the sky is blue”,
输出: “blue is sky the”.
说明:
无空格字符构成一个单词。
输入字符串可以在前面或者后面包含多余的空格,但是反转后的字符不能包括。
如果两个单词间有多余的空格,将反转后单词间的空格减少到只含一个。
进阶: 请选用C语言的用户尝试使用 O(1) 时间复杂度的原地解法。

思路:利用空格作为分隔符划分单词,然后倒序输出即可。 O ( n ) O(n) 的时间复杂度,但是需要额外的空间,还有进一步优化的空间。

public class Solution {
    public String reverseWords(String s) {
        //去除字符串前后的空格
        s = s.trim() ;
        String []words = s.split(" ") ;
        if(s.length() == 0 || words.length == 0){
            return "" ;
        }
        String result = "" ;
        for(int i=words.length-1;i>=0;i--){
            if(words[i].length()!=0){
                result += words[i] + " " ;
            }
        }
        return result.trim() ;
    }
}

从尾向前遍历字符串,遇到空格时,就截取子串,然后加到结果之后。这样就不用额外的数组了,老哥稳 不过需要额外保存子串的StringBuilder

public class Solution {
    public String reverseWords(String s) {
        StringBuilder reversed = new StringBuilder() ;
        int j = s.length() ;
        for(int i=s.length()-1;i>=0;i--){
            if(s.charAt(i) == ' '){
                j = i ;
            }else if(i == 0 || s.charAt(i-1) == ' ') {//判断是否要截取单词
                if (reversed.length() != 0) {//如果不是第一个单词,要在后面加空格隔开
                    reversed.append(" ");
                }
                reversed.append(s.substring(i, j));
            }
        }
        return reversed.toString() ;
    }
}

leetcode186. Reverse Words in a String II

题目链接
题目:Given an input string, reverse the string word by word. A word is defined as a sequence of non-space characters.
The input string does not contain leading or trailing spaces and the words are always separated by a single space.
For example,
Given s = “the sky is blue”,
return “blue is sky the”.
Could you do it in-place without allocating extra space?
其实跟上一道题目相比,就是这个单词之间只有一个空格而已,然后不利用其它空间来解决此问题。

别人思路:把每个单词进行反转,然后再把顺序进行反转或者调换个顺序,都一样。即(abc,def)->(cba,fed)->(def,abc)

public class Solution {
    public String reverseWords(String s) {
        char [] ss = s.toCharArray() ;
        for(int i=0,j=0;j<=s.length();j++){
            if(j == ss.length || ss[j] == ' '){
                reverse(ss, i, j);
                i = j + 1 ;
            }
        }
        reverse(ss, 0, s.length()) ;
        return String.valueOf(ss) ;
    }
    public void reverse(char s[], int begin, int end){
        char temp ;
        for(int i=0;i<(end-begin)/2;i++){
            temp = s[end-i-1] ;
            s[end-i-1] = s[begin+i] ;
            s[begin+i] = temp ;
        }
    }
}

leetcode8. String to Integer (atoi)

题目:实现 atoi,将字符串转为整数。

在找到第一个非空字符之前,需要移除掉字符串中的空格字符。如果第一个非空字符是正号或负号,选取该符号,并将其与后面尽可能多的连续的数字组合起来,这部分字符即为整数的值。如果第一个非空字符是数字,则直接将其与之后连续的数字字符组合起来,形成整数。

字符串可以在形成整数的字符后面包括多余的字符,这些字符可以被忽略,它们对于函数没有影响。

当字符串中的第一个非空字符序列不是个有效的整数;或字符串为空;或字符串仅包含空白字符时,则不进行转换。
若函数不能执行有效的转换,返回 0。

说明:

假设我们的环境只能存储 32 位有符号整数,其数值范围是 [−231, 231 − 1]。如果数值超过可表示的范围,则返回 INT_MAX (231 − 1) 或 INT_MIN (−231) 。

示例 1:
输入: “42”
输出: 42
示例 2:
输入: " -42"
输出: -42
解释: 第一个非空白字符为 ‘-’, 它是一个负号。
我们尽可能将负号与后面所有连续出现的数字组合起来,最后得到 -42 。
示例 3:
输入: “4193 with words”
输出: 4193
解释: 转换截止于数字 ‘3’ ,因为它的下一个字符不为数字。
示例 4:
输入: “words and 987”
输出: 0
解释: 第一个非空字符是 ‘w’, 但它不是数字或正、负号。
因此无法执行有效的转换。
示例 5:
输入: “-91283472332”
输出: -2147483648
解释: 数字 “-91283472332” 超过 32 位有符号整数范围。
因此返回 INT_MIN (−231) 。

别人思路:
从头开始遍历字符串,直到遇见第一个非空格字符,如果是“+”或者“-”,用sign标记下来,如果是数字,直接进入正题。
继续往下遍历,按位进行计算,直到遇到非数字为止跳出。其中要注意处理溢出部分,如果num还差一位与MAX到达同一数量级别,提前进行判断,如果此时num较大,则可以返回MAX ;如果相等,则来判断digit是否比MAX的最后一位大,如果大于等于,也返回MAX就完事。

class Solution {
    public int myAtoi(String str) {
        int i = 0 ;
        int n = str.length() ;
        //正数负数标志位
        int sign = 1 ;
        //直到遇见第一个非空格字符
        while(i < n && Character.isWhitespace(str.charAt(i))){
            i ++ ;
        }
        if(i < n && str.charAt(i) == '+'){
            i ++ ;
        }else if(i < n && str.charAt(i) == '-'){
            sign = -1 ;
            i ++ ;
        }
        int num = 0 ;
        while(i < n && Character.isDigit(str.charAt(i))){
            int digit = Character.getNumericValue(str.charAt(i)) ;
            //关键部分:溢出判断
            if(num > Integer.MAX_VALUE/10 || (num == Integer.MAX_VALUE/10 && digit >=8)){
                return sign == 1 ? Integer.MAX_VALUE :Integer.MIN_VALUE ;
            }
            num = num * 10 +digit ;
            i ++ ;
        }
        return sign * num ;
    }
}

leetcode65. Valid Number

题目链接
题目:例如:
“0” => true
" 0.1 " => true
“abc” => false
“1 a” => false
“2e10” => true
“.”=>true
“…”=>false
说明: 我们有意将问题陈述地比较模糊。在实现代码之前,你应当事先思考所有可能的情况。

别人思路:其实主要是处理两个特殊的符号".“和"e”
首先考虑输入字符串首位空格,然后考虑遇到.和e的情形
第一步去除字符串前面的空格,直到遇见第一个不是空格的字符,,如果该字符是代表正负号的符号"+“和”-",可以直接往后继续判断。
当遇到的一直是数字的时候,可以使是否为数字标志位置为true,然后继续往后走,直到在遇见.或者e
当遇到.的时候,判断其后是否有数字,有的话可以使标志位为true
当遇到e的时候,判断此标志位是否为true,若是,先令标志位为false,然后判断其后是否有"+""-",若有继续往后走,判断是否有数字,若有数字则标志位置为true。
最后判断末尾处的空格即可,然后返回isNumeric && i == length

class Solution {
    public boolean isNumber(String s) {
        int i = 0;
        int n = s.length();
        while(i<n && Character.isWhitespace(s.charAt(i))){
            i++;
        }
        if(i<s.length() && (s.charAt(i) == '+' || s.charAt(i) == '-')){
            i++; 
        }
        boolean isNumeric = false ;
        while(i<s.length() && Character.isDigit(s.charAt(i))){
            isNumeric = true ;
            i++;
        }
        if(i<s.length()&&s.charAt(i) == '.'){
            i++;
            while(i < s.length() && Character.isDigit(s.charAt(i))){
                isNumeric = true ;
                i++;
            }
        }
        if(isNumeric && i<n && s.charAt(i) == 'e'){
            isNumeric = false ;
            i++;
            if(i < n && (s.charAt(i) == '+' || s.charAt(i) == '-')){
                i++;
            }
            while(i < n && Character.isDigit(s.charAt(i))){
                isNumeric = true;
                i++;
            }
        }
        while(i<n && Character.isWhitespace(s.charAt(i))){
            i++;
        }
        return isNumeric && i==s.length() ;
    }
}

leetcode3. Longest Substring Without Repeating Characters

题目链接
题目:给定一个字符串,找出不含有重复字符的最长子串的长度。

示例:

给定 “abcabcbb” ,没有重复字符的最长子串是 “abc” ,那么长度就是3。

给定 “bbbbb” ,最长的子串就是 “b” ,长度是1。

给定 “pwwkew” ,最长子串是 “wke” ,长度是3。请注意答案必须是一个子串,“pwke” 是 子序列 而不是子串。

思路:利用map来存储每个字符对应的索引编号,index表示起始子串的位置,每当遇到重复的字符的时候,更新index和map。然后每次更新最大长度就可以了。

class Solution {
    public int lengthOfLongestSubstring(String s) {
        int index = 0; 
        int maxLength = 0;
        int curLength = 0;
        Map<Character,  Integer> map = new HashMap<>() ;
        for(int i = 0; i < s.length(); i++){
            if(map.get(s.charAt(i)) == null){
                map.put(s.charAt(i), i) ;
                curLength ++;
            }else{
                int n = map.get(s.charAt(i));
                index = index > n ? index : n ;
                curLength = i - index;
                map.put(s.charAt(i), i);
            }
            maxLength = maxLength > curLength ? maxLength : curLength;
        }
        return maxLength;
    }
}

别人思路:跟自己的思路差不多,就是用个标志位来记录子串的起始位置,然后不断更新。

class Solution {
    public int lengthOfLongestSubstring(String s) {
        int[] charMap = new int[256];//各类字符ascii码
        int i = 0;
        int maxLen = 0;//记录最大长度
        Arrays.fill(charMap, -1);
        for(int j = 0; j < s.length(); j++){
        //这个很重要,一着不慎满盘皆输
            if(charMap[s.charAt(j)] >= i){
                i = charMap[s.charAt(j)] + 1;
            }
            charMap[s.charAt(j)] = j;
            maxLen = Math.max(j - i + 1, maxLen);
        }
        return maxLen;
    }
}

leetcode11. Longest Substring with At Most Two Distinct Characters($)

题目链接
题目:给出一个字符串,找到最长子串,这个子串中最多含有两类不同的字符。
示例:
输入:S = “eceba”
输出:3
解释:“ece” 是最长的符合要求的子串,其长度为 3。

思路:由于无法提交,所以我也不知道自己的是否正确,思路说一下吧:

用map来存储,key->字符,value->字符出现的个数(不过我好像没有用到value,尴尬)。

然后遍历字符串,开始先判断,map中是否有该字符,如果有,直接把该字符对应的value+1。如果没有该字符,判断map大小是否小于2,如果小于2,直接加到map中,并且value初始化为1。
这一手很关键
用index来保存最近种类字符的起始位置,用j来保存前一个种类字符的起始位置。
如果map大小大于等于2了,需要把前一种类字符从map中去掉,然后加入新的字符进来。
每次比价一下maxLen与i-j+1的大小,更新maxLen就可以了。

public int lengthOfLongestSubstringTwoDistinct(String s) {
        Map<Character, Integer> charMap = new HashMap<>();
        int maxLen = 0;
        //存储前一种类的字符起始位置
        int j = 0;
        //存储后一种类的字符起始位置
        int index = 0;
        for(int i = 0; i < s.length(); i++){
            if(charMap.containsKey(s.charAt(i))){
                int num = charMap.get(s.charAt(i));
                charMap.put(s.charAt(i), num + 1);
                if(i > 0 && s.charAt(i) != s.charAt(i-1)){
                    index = i;
                }
            }else{
                if(charMap.size() < 2){
                    charMap.put(s.charAt(i), 1);
                    index = i;
                }else{
                    //charMap.clear();
                    charMap.remove(s.charAt(j));
                    j = index;
                    charMap.put(s.charAt(j), i - index + 1);
                    charMap.put(s.charAt(i), 1);
                    index = i;
                }
            }
            maxLen = Math.max(i - j + 1, maxLen);
        }
        return maxLen;
    }

别人思路:利用滑动窗口技术,动态地去调整这个窗口

public static int lengthOfLongestSubstringTwoDistinct(String s) {
        //i代表第一类字符的起始位置,
        // 如果只有两类字符时,j代表与当前类不同的上一类的末尾位置
        //如果又出现一类字符时,此时j代表当前类不同的上上类的末尾位置,j+1则为当前类不同的上一类的起始位置
        int i = 0, j = -1;
        int len = s.length();
        int maxLen = 0;
        for(int k = 1; k < len; k++){
            if(s.charAt(k-1) == s.charAt(k)){
                continue;
            }
            //如果只有一类字符,j=-1
            //遇到不止一类字符的时候,j >=0 j会成为与当前类不同的上一类的末尾位置
            if(j >= 0 && s.charAt(k) != s.charAt(j)){
                maxLen = Math.max(k - i, maxLen);
                i = j + 1;
            }
            j = k - 1;
        }
        return Math.max(len - i, maxLen);
    }

思维拓展:如果求至少含有三种或者四种……字符的长度呢?
别人思路: 用一个变量来记录字符的种类numDistinct,然后如果字符种类大于2时,头指针开始往后走,直至只剩下两种字符,,,这种思路很好理解,比上面的滑动窗口要好 哈哈 也可能不同的思路在不同场景下表现不同吧

public int lengthOfLongestSubstringTwoDistinct(String s) {
        int i = 0;//每个符合要求的字符串的记录起始位置
        int len = s.length();
        int numDistinct = 0;
        int[] charMap = new int[256];
        int maxLen = 0;
        for(int j = 0; j < len; j++){
            if(charMap[s.charAt(j)] == 0){
                numDistinct++;
            }
            charMap[s.charAt(j)]++;
            while(numDistinct > 2){
                charMap[s.charAt(i)]--;
                if(charMap[s.charAt(i)] == 0){
                    numDistinct--;
                }
                i++;
            }
            maxLen = Math.max(j - i + 1, maxLen);
        }
        return maxLen;
    }

leetcode163. Missing Ranges

题目链接
题目:给出一个数组和一个范围,输出在此范围内,结合数组中的元素,返回缺少的数。
示例:
输入:[0, 1, 3, 50, 75], lower = 0 and upper = 99
输出:[“2”, “4->49”, “51->74”, “76->99”]
思路:从头向后遍历,如果后一个元素比前一个元素大于等于2,则加入到返回的列表中。

public List<String> findMissingRanges(int vals[], int start, int end){
        List<String> list = new ArrayList<>();
        int len = vals.length;
        int prev = start - 1;
        int cur;
        for(int i = 0; i <= len; i++){
            cur = (i == len) ? end + 1 : vals[i] ;
            if(cur - prev >= 2){
                list.add(getRange(prev + 1, cur - 1));
            }
            prev = cur;
        }
        return list;
    }
public String getRange(int from, int to){
	return from == to ? String.valueOf(from) : from + "->" + to;
}

leetcode5. Longest Palindromic Substring

题目链接
题目:给定一个字符串 s,找到 s 中最长的回文子串。你可以假设 s 的最大长度为 1000。

示例 1:
输入: “babad”
输出: “bab”
注意: “aba” 也是一个有效答案。
示例 2:
输入: “cbbd”
输出: “bb”
别人思路: 中心扩展的方法,从一个元素为中心,往左往右扩展,看看长度为多少时一直保持为回文子串,并记录最大长度的回文子串的起始位置和长度。因为回文子串的长度要么为偶数要么为奇数,所以要从(i,i)和(i,i+1)都分别扩展。

class Solution {
    int start = 0, maxLen = 0;
    public String longestPalindrome(String s) {
        int len = s.length();
        if(len < 2){
            return s;
        }
        for(int i = 0; i < len - 1; i++){
            //回文子串长度要么为奇数要么为偶数,都试着求一下
            expandAroundCenter(s, i, i);
            expandAroundCenter(s, i, i + 1);
        }
        return s.substring(start, start + maxLen);
    }
    public void expandAroundCenter(String s, int left, int right){
        int len = s.length();
        while(left >=0 && right < len && s.charAt(left) == s.charAt(right)){
            left--;
            right++;
        }
        if(maxLen < right - left - 1){// right-1-(left-1)+1  返回子串长度
            maxLen = right - left - 1;
            start = left + 1;
        }
    }
}

在这里插入图片描述

leetcode161. One Edit Distance$

题目链接
题目大意:给出两个字符串,判断他们之间是否存在可编辑的距离

示例:
i. Modify operation – Modify a character to X in S.
S = “abcde”
T = “abXde”
ii. Insert operation – X was inserted before a character in S.
S = “abcde”
T = “abcXde”
iii. Append operation – X was appended at the end of S.
S = “abcde”
T = “abcdeX”

别人思路:1. 两个字符串的长度之差大于1,那么直接返回False
2. 两个字符串的长度之差等于1,那么长的那个字符串去掉一个字符,剩下的应该和短的字符串相同
3. 两个字符串的长度之差等于0,那么两个字符串对应位置的字符只能有一处不同。

public static boolean isOneEditDistance(String s, String t){
        int m = s.length();
        int n = s.length();
        //跳转子串,使s长度小于等于t的长度
        if(m > n){
            return isOneEditDistance(t, s);
        }
        //如果一个子串比另一个子串长度大于1,直接返回false
        if(n - m > 1){
            return false;
        }
        //两个子串长度要么相等,要么相差1
        int i = 0, shift = n - m;
        while(i < m && s.charAt(i) == t.charAt(i)){
            i++;
        }
        //若i==m,说明s和t在前m位完全相同,如果shift为0 说明二者等长,即一模一样,不可编辑,返回false
        //如果shift不为0,其实那就是1啦,二者有一处不同,可以编辑,返回true
        if(i == m){
            return shift > 0;
        }
        //shift为0  说明两子串长度相等,只要有一处不等即可编辑
        if(shift == 0){
            i++;
        }
        while(i < m && s.charAt(i) == t.charAt(i + shift)){
            i++;
        }
        return i == m;
}

leetcode157. Read N Characters Given Read4$

题目链接
参考
题目大意:给了我们一个Read4函数,每次可以从一个文件中最多读出4个字符,如果文件中的字符不足4个字符时,返回准确的当前剩余的字符数。现在让我们实现一个最多能读取n个字符的函数。
别人思路:
用一个临时数组,存放每次read4读到字符,再用一个指针标记buf数组目前存储到的位置,然后将这个临时数组的内容存到buf相应的位置就行了。这里需要注意两个corner case:
1.如果本次读到多个字符,但是我们只需要其中一部分就能完成读取任务时,我们要拷贝的长度是本次读到的个数和剩余所需个数中较小的
2.如果read4没有读满4个,说明数据已经读完,这时候对于读到的数据长度,因为也可能存在我们只需要其中一部分的情况,所以要返回总所需长度和目前已经读到的长度的较小的。

题目链接

public class Solution extends Reader4 {
    public int read(char[] buf, int n) {
        for(int i = 0; i < n; i += 4){
            char[] tmp = new char[4];
            // 将数据读入临时数组
            int len = read4(tmp);
            // 将临时数组拷贝至buf数组,这里拷贝的长度是本次读到的个数和剩余所需个数中较小的
            System.arraycopy(tmp, 0, buf, i, Math.min(len, n - i));
            // 如果读不满4个,说明已经读完了,返回总所需长度和目前已经读到的长度的较小的
            if(len < 4) return Math.min(i + len, n);
        }
        // 如果循环内没有返回,说明读取的字符是4的倍数
        return n;
    }
}
public class Solution extends Reader4 {
        /**
         * @param buf Destination buffer
         * @param n Maximum number of characters to read
         * @return The number of characters read
         */
        public int read(char[] buf, int n) {
            char[] buffer = new char[4];
            int readBytes = 0;
            boolean eof = false;
            while (!eof && readBytes < n) {
                int sz = read4(buffer);
                if (sz < 4) eof = true;
                int bytes = Math.min(n - readBytes, sz);
                System.arraycopy(buffer /* src */, 0 /* srcPos */,
                        buf /* dest */, readBytes /* destPos */, bytes /* length */);
                readBytes += bytes;
            }
            return readBytes;
        }
    }

leetcode158. Read N Characters Given Read4 II - Call multiple times$

题目链接
题目大意:那道题说read函数只能调用一次,而这道题说read函数可以调用多次,那么难度就增加了,为了更简单直观的说明问题,我们举个简单的例子吧,比如:

buf = “ab”, [read(1),read(2)],返回 [“a”,“b”]

那么第一次调用read(1)后,从buf中读出一个字符,那么就是第一个字符a,然后又调用了一个read(2),想取出两个字符,但是buf中只剩一个b了,所以就把取出的结果就是b。再来看一个例子:

buf = “a”, [read(0),read(1),read(2)],返回 ["",“a”,""]

第一次调用read(0),不取任何字符,返回空,第二次调用read(1),取一个字符,buf中只有一个字符,取出为a,然后再调用read(2),想取出两个字符,但是buf中没有字符了,所以取出为空。

别人思路:次读与一次读的主要不同在于read4()函数中的buffer相当于global的,每次read()的时候前面read4()里读进buffer的剩下来字符的还要继续用,不够才又调用read4()往buffer里新增加内容供read读取。
所以我们只要存一个global的针对read4()的buffer的起始位置和终止位置即可。每次read()先读上次buffer里没读完的字符,不够才又调用read4(). 当然,越界问题还是得注意,跟上一道题一样。

public class Solution extends Reader4 {
    char[] buffer = new char[4];
    int start = 0; // inclusive
    int end = 0; // exclusive
    /**
     *@param buf Destination buffer
     *@param n   Maximum number of characters to read
     *@return    The number of characters read
     */
    public int read(char[] buf, int n) {
        int i = 0;
        while (i < n) {
            
            // 之前read4()读进buffer里的字符已全部读完
            if (start == end) {
                end = read4(buffer);
                start = 0;
            }
            
            // 依次把buffer里的字符读进buf里
            while (i < n && start < end) {
                buf[i++] = buffer[start++];
            }
            
            // 判断是否到达文件末尾,是的话跳出循环
            if (end != 4)
                break;
        }
        return i;
    }
}
public class Solution extends Reader4 {
        private char[] buffer = new char[4];
        int offset = 0, bufsize = 0;
        /**
         * @param buf Destination buffer
         * @param n Maximum number of characters to read
         * @return The number of characters read
         */
        public int read(char[] buf, int n) {
            int readBytes = 0;
            boolean eof = false;
            while (!eof && readBytes < n) {
                int sz = (bufsize > 0) ? bufsize : read4(buffer);
                if (bufsize == 0 && sz < 4) eof = true;
                int bytes = Math.min(n - readBytes, sz);
                System.arraycopy(buffer, offset,buf, readBytes, bytes);
                offset = (offset + bytes) % 4;
                bufsize = sz - bytes;
                readBytes += bytes;
            }
            return readBytes;
        }
    }

猜你喜欢

转载自blog.csdn.net/u011732358/article/details/84112507