剑指offer-专项突破版(1-25)

1. 整数除法

package com.lxh.special;

/* 题目介绍
*
* 输入2个int型整数,他们进行除法计算并返回商,要求不得使用乘号'*',
* 除号'/'以及求余符号'%'.当发生溢出时,返回最大的整数值.假设除数
* 不为0.
*
* */

/*
* divide 划分
* dividend 股利 红利
* divisor 除数
* core 核心
* */
public class IntegerDivision01 {

    public int divide(int dividend, int divisor) {
        /*
        * int型整数的除法只有一种情况会导致溢出,即(-2的31次方)/(-1)
        * 因为最大的正数为(2的31次方-1),(2的31次方)超出了正数的范围.
        *
        * */

        //对溢出的情况进行单独处理,0x80000000为最小的int型整数,即(-2的31次方)
        if (dividend == 0x80000000 && divisor == -1) {
            return Integer.MAX_VALUE;
        }
        //利用negative来判断结果正负性
        int negative = 2;

        //使被除数和除数都变为负数,因为从正数变为负数
        //并利用negative来记录 从而对结果正负性进行判断
        if (dividend > 0) {
            negative--;
            dividend = -dividend;
        }
        if (divisor > 0) {
            negative--;
            divisor = -divisor;
        }

        int result = divideCore(dividend, divisor);
        return negative == 1 ? -result : result;

    }

    private int divideCore(int dividend, int divisor) {
        int result = 0;
        //除非被除数小于除数了 否则继续减下去
        while (dividend <= divisor) {
            int value = divisor;
            //quotient 商
            int quotient = 1;
            //0xc0000000是0x80000000的一半,即(-2的30次方)
            //注意此时dividend和value都是负数 所以是 <= 符号
            while (value >= 0xc0000000 && dividend <= value + value) {
                quotient += quotient;
                value += value;
            }

            result += quotient;
            dividend -= value;
        }
        return result;
    }

    public static void main(String[] args) {
        IntegerDivision01 id = new IntegerDivision01();
        System.out.println(id.divide(-225, -4));
    }
}

2. 二进制加法

package com.lxh.special;

public class BinaryAddition02 {
    public String addBinary (String a, String b) {

        /*StringBuilder

        在程序开发过程中,我们常常碰到字符串连接的情况,方便和直接的方式是通过"+"符号来实现,
        但是这种方式达到目的的效率比较低,且每执行一次都会创建一个String对象,即耗时,又浪费空间。
        使用StringBuilder类就可以避免这种问题的发生,下面就Stringbuilder的使用做个简要的总结:

        */

        StringBuilder result = new StringBuilder();

        int i = a.length() - 1;
        int j = b.length() - 1;
        //carry表示要进位值
        int carry = 0;
        //不知道哪个字符串长度更长,以最长的字符串长度为准,短的前面补0即可.
        while (i >= 0 || j >= 0) {
            //char.At()获得当前字符串中该位置字符
            //将字符串的每一位通过 -'0' 变成整型
            int digitA = i >= 0 ? a.charAt(i--) - '0' : 0;
            int digitB = j >= 0 ? b.charAt(j--) - '0' : 0;
            int sum = digitA + digitB + carry;
            carry = sum >= 2 ? 1 : 0;
            sum = sum >=2 ? sum - 2 : sum;
            //append按顺序依次连接字符
            result.append(sum);
        }

        //如果最后进位值还有1,要加入到字符串中,勿忘!!
        if (carry == 1)
            result.append(1);

        //reverse()用以字符串翻转,toString()用于字符串的输出.
        return result.reverse().toString();
    }

    public static void main(String[] args) {
        BinaryAddition02 ba = new BinaryAddition02();
        System.out.println(ba.addBinary("11", "01"));
    }
}



StringBuilder的常用方法:
一、创建Stringbuilder对象
StringBuilder strB = new StringBuilder();

1、append(String str)/append(Char c):字符串连接
System.out.println(“StringBuilder:”+strB.append(“ch”).append(“111”).append(‘c’));
//return “StringBuilder:ch111c”

2、toString():返回一个与构建起或缓冲器内容相同的字符串
System.out.println(“String:”+strB.toString());
//return “String:ch111c”

3、appendcodePoint(int cp):追加一个代码点,并将其转换为一个或两个代码单元并返回this
System.out.println(“StringBuilder.appendCodePoint:”+strB.appendCodePoint(2));
//return “StringBuilder.appendCodePoint:ch111c”

4、setCharAt(int i, char c):将第 i 个代码单元设置为 c(可以理解为替换)
strB.setCharAt(2, ‘d’);
System.out.println(“StringBuilder.setCharAt:” + strB);
//return “StringBuilder.setCharAt:chd11c”

5、insert(int offset, String str)/insert(int offset, Char c):在指定位置之前插入字符(串)
System.out.println(“StringBuilder.insertString:”+ strB.insert(2, “LS”));
//return “StringBuilder.insertString:chLSd11c”
System.out.println(“StringBuilder.insertChar:”+ strB.insert(2, ‘L’));
//return “StringBuilder.insertChar:chLLSd11c”

6、delete(int startIndex,int endIndex):删除起始位置(含)到结尾位置(不含)之间的字符串
System.out.println(“StringBuilder.delete:”+ strB.delete(2, 4));
//return “StringBuilder.delete:chSd11c”

3. 前n个数字二进制形式中1的个数

package com.lxh.special;

/* 题目介绍 求前n个数字二进制形式中1的个数
* 输入一个非负数n,请计算0到n之间每个数字的二进制形式中1的个数,并输出一个数组。
* */

import java.util.Arrays;

public class NumberBinary03 {

    /* 1.简单计算每个整数的二进制形式中1的个数
     * 计算整数i的二进制形式中1的个数有多种不同的方法,其中一种比较高效的方法是
     * 每次用“i & (i-1)”将整数i的最右边的1变成0。整数i减去1,那么它最右边的1变成0。
     * 如果它的右边还有0,则右边所有的0都变成1,而其左边所有位都保持不变。
     * 以二进制的1100为例,它减去1的结果是1011。1100和1011的位与运算的结果正好是1000。
     * 二进制的1100最右边的1变为0,结果刚好就是1000。
     * 时间复杂度为O(nk)
     * */
    /*
    public int[] countBits(int num) {
        int result[] = new int[num + 1];
        for (int i = 0; i <= num; ++i) {
            int j = i;
            while (j != 0) {
                result[i] ++;
                j = j & (j-1);
            }
        }
        return result;
    }*/

    /* 2.根据“i & (i-1)”计算i的二进制形式中1的个数
    * “i & (i-1)”将i的二进制形式中最右边的1变成0,也就是说,
    * 整数i的二进制形式中1的个数比“i & (i-1)”的二进制形式中1的个数多1.
    *时间复杂度为O(n)。
    * */
    /*public int[] countBits(int num) {
        int[] result = new int[num + 1];
        for (int i = 1; i <= num; i++) {
            result[i] = result[i & (i-1)] + 1;
        }
        return result;
    }*/
    
    /* 根据"i/2"计算i的二进制形式中1的个数
    * 
    * 
    * 
    * */
    public int[] countBits(int num) {
        int[] result = new int[num + 1];
        for (int i=1; i <= num; ++i) {
            //i >> 1 计算 i/2 ; i & 1 计算 i%2
            result[i] = result[i >> 1] + (i & 1);
        }
        return result;
    }

    public static void main(String[] args) {
        NumberBinary03 nb = new NumberBinary03();
        int[] result = nb.countBits(5);
        System.out.println(Arrays.toString(result));
    }
}

PS:需要注意数组的输出方式,要用Arrays.toString(result)输出。

4. 面试题4:

package com.lxh.special;
/* 面试题4:只出现一次的数字
* 1.需进一步加强理解
* 
* */
public class ANumberAppearsOnce04 {
    public int singleNumber(int[] nums) {
        int[] bitSums = new int[32];
        for (int num : nums) {
            for (int i = 0; i < 32; i++) {
                bitSums[i] += (num >> (31 - i)) & 1; 
            }
        }
        
        int result = 0;
        for (int i = 0; i < 32; i++) {
            result = (result << 1) + bitSums[i] % 3;
        }
        return result;
    }
}

5. 面试题5:单词长度的最大乘积
用哈希表完成
toCharArray()将字符串转换为字符数组

package com.lxh.special;
/* 面试题5: 单词长度的最大乘积
* 用哈希表记录字符串中出现的字符
*
* */

public class MaximumWord05 {
    public int maxProduct(String[] words) {
        boolean[][] flags = new boolean[words.length][26];
        for (int i = 0; i < words.length; i++) {
            //注意对于字符的foreach中,要遍历的是个字符数组,要先转换为字符数组。
            for (char c: words[i].toCharArray()) {
                //使得在当前字母出现的位置由false变为true,有了true的位置就可以比较当前字符有没有重复出现
                flags[i][c - 'a'] = true;
            }
        }

        int result = 0;
        for (int i = 0; i < words.length; i++) {
            for (int j = i+1; j < words.length; j++) {
                int k = 0;
                for (; k < 26; k++) {
                    if (flags[i][k] && flags[j][k]) {
                        break;
                    }
                }

                if (k == 26) {
                    int prod = words[i].length() * words[j].length();
                    //用Math.max()来更好的取得最大值。
                    result = Math.max(result, prod);
                }
            }
        }
        return result;
    }

    public static void main(String[] args) {
        MaximumWord05 mm05 = new MaximumWord05();
        String[] words = {"abcw", "foo", "bar", "fxyz", "abcdef"};
        System.out.println(mm05.maxProduct(words));
    }
}

6. 面试题6:排序数组中的两个数字之和
可以这样直接返回数组
return new int[] {i, j};

package com.lxh.array02;

import java.util.Arrays;

public class SortSumArray06 {
    public int[] twoSum(int[] numbers, int target) {
        int i = 0;
        int j = numbers.length - 1;
        while (i < j && numbers[i] + numbers[j] != target) {
            if (numbers[i] + numbers[j] < target) {
                i++;
            } else
                j--;
        }
        //可以这样直接返回数组
        return new int[] {i, j};
    }
    
    public static void main(String[] args) {
        SortSumArray06 ssa06 = new SortSumArray06();
        int[] arrays = {1,2,4,6,10};
        int[] result = ssa06.twoSum(arrays, 8);
        System.out.println(Arrays.toString(result));
    }
}

7. 面试题7:数组中和为0的3个数字 LinkedList
学习链表的使用

package com.lxh.array02;
/* 面试题7:数组中和为0的3个数字
* 学习链表的使用
* */

import java.util.Arrays;
import java.util.LinkedList;
import java.util.List;

public class ArraySumThree07 {
    public List<List<Integer>> threeSum(int[] nums) {
        // 三元数组采用链表来存储和输出
        List<List<Integer>> result = new LinkedList<>();

        if (nums.length >= 3) {
            Arrays.sort(nums);

            int i = 0;
            while (i < nums.length - 2) {
                twoSum(nums, i, result);
                // 此时如果nums[i]重复,那么整个三元数组就会重复,因为在前面相同的数字时,
                // 所有跟相同数字有关的三元数组都已经确定而且保存在了链表中了,故要跳过
                int temp = nums[i];
                while (i < nums.length && nums[i] == temp){
                    ++i;
                }
            }
        }
        return result;
    }

    private void twoSum(int[] nums, int i, List<List<Integer>> result) {
        int j = i + 1;
        int k = nums.length - 1;
        while (j < k) {
            if (nums[i] + nums[j] + nums[k] == 0) {
                //将需要的值保存在链表中
                result.add(Arrays.asList(nums[i], nums[j], nums[k]));

                //此时如果nums[j]重复,那么整个三元数组就会重复,故要跳过
                int temp = nums[j];
                while (nums[j] == temp && j < k) {
                    ++j;
                }
            } else if (nums[i] + nums[j] + nums[k] < 0) {
                ++j;
            } else {
                --k;
            }
        }
    }

    public static void main(String[] args) {
        ArraySumThree07 ast07 = new ArraySumThree07();
        int[] nums = {-1, 0, 1, 2, -1, -4};
        List<List<Integer>> last = ast07.threeSum(nums);
        System.out.println(last);
    }
}

关于链表知识的相关总结:

8. 面试题8:和大于或等于K的最短子数组
有Math.max();和Math.min();就可以直接使用,省去三目运算符

package com.lxh.array02;
/* 面试题8:和大于或等于k的最短子数组
* 在解题前先思考,注意双指针的妙处
* 
* */
public class GreatEqualArray08 {
    public int minSubArrayLen (int k, int[] nums) {
        int left = 0;
        int sum = 0;
        int minLength = Integer.MAX_VALUE;
        for (int right = 0; right < nums.length; right++) {
            sum += nums[right];
            while (left <= right && sum >= k) {
                minLength = Math.min(minLength, right - left + 1);
                sum -= nums[left++];
            }
        }
        return minLength == Integer.MAX_VALUE ? 0 : minLength;
    }

    public static void main(String[] args) {
        GreatEqualArray08 gea08 = new GreatEqualArray08();
        int[] nums = {5,1,4,3};
        System.out.println(gea08.minSubArrayLen(7, nums));
    }
}

9. 面试题9:乘积小于K的子数组

package com.lxh.array02;
/* 面试题9: 乘积小于K的子数组
* 注意对代码更深的理解 最后的求数量的三目运算符那里很巧妙
* 
* */
public class LessThan09 {
    public int numSubarrayProductLessThanK (int[] nums, int k) {
        long product = 1;
        int left = 0;
        int count = 0;
        for (int right = 0; right < nums.length; ++right) {
            product *= nums[right];
            while (left <= right && product >= k) {
                product /= nums[left++];
            }
            //因为自己本身也是,故用right - left + 1就可以表示出当前这个子数组下有几种可能
            count += right >= left ? right - left + 1 : 0;
        }

        return count;
    }

    public static void main(String[] args) {
        LessThan09 lt09 = new LessThan09();
        int[] nums = {10, 5, 2, 6};
        System.out.println(lt09.numSubarrayProductLessThanK(nums, 100));
    }
}

10. 面试题10:和为K的子数组 HashMap

package com.lxh.array02;
/* 再敲一遍
* 
* 
* */

import java.util.HashMap;
import java.util.Map;

public class SubarraySumK10 {
/*    public int subarraySum (int[] nums, int k) {
        Map sumToCount = new HashMap<>();
        sumToCount.put(0, 1);
        int sum = 0;
        int count = 0;
        for (int num : nums) {
            sum += num;
            //getOrDefault()获取指定key对应的value,如果找不到key,则返回设置的默认值。
            count += sumToCount.getOrDefault(sum - k, 0);
            sumToCount.put(sum, sumToCount.getOrDefault(sum, 0) + 1);
        }
        return count;
    }*/
    public int subarraySum(int[] nums, int k) {
        int sum=0,count=0;
        Map<Integer,Integer> map=new HashMap<>();
        for (int i=0;i<nums.length;i++){
            sum+=nums[i];
            if(sum==k){
                count++;
            }
            if(map.containsKey(sum-k)){
                count+=map.get(sum-k);
            }
            if(!map.containsKey(sum)){
                map.put(sum,1);
            }else {
                map.put(sum,map.get(sum)+1);
            }
        }
        return count;
    }


    public static void main(String[] args) {
        SubarraySumK10 ssk10 = new SubarraySumK10();
        int[] nums = {-1,2,2,4,5};
        System.out.println(ssk10.subarraySum(nums, 4));
    }

}


关于HashMap的相关总结:
更详细的内容:Java HashMap
1)containsKey:测试是否存在于此映射中的键

package demo;
 
import java.util.HashMap;
import java.util.Map;
 
public class fordemo
{
public static void main(String[] args)
{
Map<String, String> paramMap=new HashMap<String, String>();
	paramMap.put("bc", "aa");
	paramMap.put("a", "bb");
	System.out.println(paramMap.containsKey("b"));--返回false
	System.out.println(paramMap.containsKey("a"));--返回true
}
}

2)containsValue:用来验证是否存在此Value值,Value值必须全部符合,包含也是返回false

package demo;
 
import java.util.HashMap;
import java.util.Map;
 
public class fordemo
{
public static void main(String[] args)
{
Map<String, String> paramMap=new HashMap<String, String>();
	paramMap.put("1", "b");
	paramMap.put("2", "b");
	paramMap.put("3", "ab");
	paramMap.put("4", "cc");
	System.out.println(paramMap.containsValue("b"));--返回true
	System.out.println(paramMap.containsValue("a"));--返回false
	System.out.println(paramMap.containsValue("cc"));--返回true
}
}

11. 面试题11: 0和1个数相同的子数组

package com.lxh.array02;
/* 哈希表的键是从第一个数字开始累加到当前扫描到的数字之和,而值是当前扫描的数字的下标。
* 思想太妙了!
*
* */
import java.util.HashMap;
import java.util.Map;

public class SameArray11 {
    public int findMaxLength (int[] nums) {
        Map<Integer, Integer> sumToIndex = new HashMap<>();
        sumToIndex.put(0, -1);
        int sum = 0;
        int maxLength = 0;
        for (int i = 0; i < nums.length; i++) {
            sum += nums[i] == 0 ? -1 : 1;
            if (sumToIndex.containsKey(sum)) {
                maxLength = Math.max(maxLength, i - sumToIndex.get(sum));
            } else
                sumToIndex.put(sum, i);
        }
        return maxLength;
    }
}

12. 面试题12:左右两边子数组的和相等

package com.lxh.array02;
/* 面试题12:左右两边子数组的和相等 2021.10.28
* 主要体会解题思路,举一反三,解题思路很巧妙。
*
* */

public class LeftRightEqual12 {
    public int pivotIndex(int[] nums) {

        int total = 0;
        int leftTotal = 0;

        for (int i : nums) {
            total += i;
        }

        for (int i = 0; i < nums.length; i++) {
            leftTotal += nums[i];
            //如果该下标左边的和等于右边则返回当前下标
            if (leftTotal - nums[i] == total - leftTotal) {
                return i;
            }
        }
        return -1;
    }
}

13. 面试题13:左右两边子数组的和相等

package com.lxh.array02;
/* 面试题13:二维子矩阵的数字之和
 *  此题怎么运行?
 * (11.04 已解决 注意类名要保持一致,这里只不过将SumSubmatrix13设置成了一个容器,在main方法中调用时方法一致)
 *
 * */
import java.util.Arrays;
class SumSubmatrix13 {
    private int[][] sums;
    //SumSubmatrix13的构造函数用来做预处理,根据输入矩阵生成辅助矩阵sums
    public SumSubmatrix13(int[][] matrix) {
        //matrix.length表示的是行数,matrix[0].length表示的是列数。
        if (matrix.length == 0 || matrix[0].length == 0) {
            return;
        }

        sums = new int[matrix.length + 1][matrix[0].length + 1];
        for (int i = 0; i < matrix.length; ++i) {
            int rowSum = 0;
            for (int j = 0; j < matrix[0].length; ++j) {
                rowSum += matrix[i][j];
                sums[i + 1][j + 1] = sums[i][j + 1] + rowSum;
            }
        }
    }
    //用来求子矩阵的数字之和,该函数只是从数组中读取几个数字做加减法,时间复杂度O(1)。
    public int sumRegion(int row1, int col1, int row2, int col2) {
        return sums[row2 + 1][col2 + 1] - sums[row1][col2 + 1] - sums[row2 + 1][col1] + sums[row1][col1];
    }


    public static void main(String[] args) {

        int[][] nums = {{3,0,1,4,2},{5,6,3,2,1},{1,2,0,1,5},{4,1,0,1,7},{1,0,3,0,5}};
        SumSubmatrix13 ss13 = new SumSubmatrix13(nums);
        System.out.println(ss13.sumRegion(2, 1, 4, 3));
    }
}

14. 面试题14:字符串中的变位词

package com.lxh.string03;
/* 面试题14:字符串中的变位词 2021.10.28
* 变位词具有以下几个特点:首先,一组变位词的长度一定相同;其次,组成变位词的字母集合一定相同,并且每个字母出现的次数也相同。
* 利用数组模拟哈希表 数组值的加与减很巧妙 其中一个有就加 另一个有就减 记住这种思想点
* */
public class ConjugationString14 {
    public boolean checkInclusion(String s1, String s2) {
        if (s1.length() > s2.length()) {
            return false;
        }

        int[] counts = new int[26];
        for (int i = 0; i < s1.length(); i++) {
            counts[s1.charAt(i) - 'a']++;
            counts[s2.charAt(i) - 'a']--;
        }

        if (arrAllZero(counts)) {
            return true;
        }

        for (int i = s1.length(); i < s2.length(); i++) {
            counts[s2.charAt(i) - 'a']--;
            counts[s2.charAt(i - s1.length()) - 'a']++;
            if (arrAllZero(counts)) {
                return true;
            }
        }

        return false;
    }

    //用于判断当前数组中所有的值是不是为0,若为0说明存在这样的变位词
    private boolean arrAllZero(int[] counts) {
        for (int count : counts) {
            if (count != 0)
                return false;
        }
        return true;
    }

    public static void main(String[] args) {
        ConjugationString14 cs14 = new ConjugationString14();
        String s1 = "ac";
        String s2 = "dgcaf";
        System.out.println(cs14.checkInclusion(s1, s2));
    }
}

15. 面试题15:字符串中的所有变位词

package com.lxh.string03;
/* 面试题15:字符串中的所有变位词 2021.10.28
* 跟面试题14思想类似
*
* */
import java.util.LinkedList;
import java.util.List;

public class ConjugationAllString15 {
    public List<Integer> findAnagrams(String s1, String s2) {
        List<Integer> indices = new LinkedList<>();

        if (s1.length() < s2.length())
            return indices;

        int[] counts = new int[26];
        int i = 0;
        for (; i < s2.length(); i++) {
            counts[s2.charAt(i) - 'a']++;
            counts[s1.charAt(i) - 'a']--;
        }
        if (areZeroAll(counts)) {
            indices.add(0);
        }

        for (; i < s1.length(); i++) {
            counts[s1.charAt(i) - 'a']--;
            counts[s1.charAt(i - s2.length()) - 'a']++;
            if (areZeroAll(counts)) {
                indices.add(i-s2.length() + 1);
            }
        }
        return indices;
    }

    private boolean areZeroAll(int[] counts) {
        for (int count : counts) {
            if (count != 0) {
                return false;
            }
        }
        return true;
    }

    public static void main(String[] args) {
        ConjugationAllString15 cs14 = new ConjugationAllString15();
        String s1 = "cbadabacg";
        String s2 = "abc";
        System.out.println(cs14.findAnagrams(s1, s2));
    }
}

16. 面试题16:不含重复字符的最长子字符串

package com.lxh.string03;
/* 面试题16:不含重复字符的最长子字符串
* 1.同样以数组模拟哈希表的思想来做的 建立一个数组存储字符的ascii值
* */
public class LongestSubstring16 {
    public int lengthOfLongestSubstring(String s) {
/* 方案一:会每次都遍历一遍哈希表
       if (s.length() == 0) {

            return 0;
        }

        int[] counts = new int[256];
        int i = -1;
        int j = 0;
        int longest = 1;

        for (; j < s.length(); j++) {
            counts[s.charAt(j)]++;

            while (hasGreater(counts)) {
                ++i;
                counts[s.charAt(i)]--;
            }

            longest = Math.max(j - i, longest);
        }
        return longest;
    }

    private boolean hasGreater(int[] counts) {
        for (int count : counts) {
            if (count > 1) {
              return true;
            }
        }
        return false;
    }*/

/* 方案二 避免多次遍历整个哈希表 和前面的解法相比时间效率有所提高
*
*
* */
        if (s.length() == 0) {
            return 0;
        }

        int[] counts = new int[256];
        int i = -1;
        int j = 0;
        int longest = 1;
        int countDup = 0;
        for (; j < s.length(); ++j) {
            counts[s.charAt(j)]++;
            if (counts[s.charAt(j)] == 2) {
                countDup++;
            }

            while (countDup > 0) {
                ++i;
                counts[s.charAt(i)]--;
                if (counts[s.charAt(i)] == 1) {
                    countDup--;
                }
            }

            longest = Math.max(j - i, longest);
        }
        return longest;
    }



    public static void main(String[] args) {
        LongestSubstring16 ls16 = new LongestSubstring16();
        String s = "babcca";
        System.out.println(ls16.lengthOfLongestSubstring(s));
    }
}

17. 面试题17:包含所有字符的最短字符串

package com.lxh.string03;
/* 面试题17:包含所有字符的最短字符串 2021.10.28
* 1. 学会利用哈希表对其中一个字符串加1,另一个字符串中如果出现了相同的字符就减1的思想
* 2. 对于字符串的双指针操作要熟练
* */
import java.util.HashMap;

public class ShortestStringContaining17 {
    public String minWindow(String s, String t) {
        HashMap<Character, Integer> charToCount = new HashMap<>();
        for (char ch : t.toCharArray()) {
            charToCount.put(ch, charToCount.getOrDefault(ch, 0) + 1);
        }

        int count = charToCount.size();
        int start = 0, end = 0, minStart = 0, minEnd = 0;
        int minLength = Integer.MAX_VALUE;
        while (end < s.length() || (count == 0 && end == s.length())) {
            if (count > 0) {
                char endCh = s.charAt(end);
                if (charToCount.containsKey(endCh)) {
                    charToCount.put(endCh, charToCount.get(endCh) - 1);
                    if (charToCount.get(endCh) == 0) {
                        count--;
                    }
                }
                end++;
            }else {
                if (end - start < minLength) {
                    minLength = end - start;
                    minStart = start;
                    minEnd = end;
                }
                char startCh = s.charAt(start);
                if (charToCount.containsKey(startCh)) {
                    charToCount.put(startCh, charToCount.get(startCh) + 1);
                    if (charToCount.get(startCh) == 1) {
                        count++;
                    }
                }
                start++;
            }
        }
        return minLength < Integer.MAX_VALUE ? s.substring(minStart, minEnd) : "";
    }
}

18. 面试题18:有效的回文

package com.lxh.string03;
/* 面试题18:有效的回文 2021.10.29
* 1.对 Character.isLetterOrDigit(ch1) 和 Character.toLowerCase(ch2) 记忆
* 2.本题也是利用双指针来进行计算的
* */
public class ValidPalindrome18 {
    public boolean isPalindrome(String s) {
        int i = 0;
        int j = s.length() - 1;
        while (i < j) {
            char ch1 = s.charAt(i);
            char ch2 = s.charAt(j);
            //java.lang.Character.isLetterOrDigit(char ch) 确定指定的字符是否为字母或数字。
            if (!Character.isLetterOrDigit(ch1)) {
                i++;
            }else if (!Character.isLetterOrDigit(ch2)) {
                j--;
            }else {
                //Character.toLowerCase将字符变为小写
                ch1 = Character.toLowerCase(ch1);
                ch2 = Character.toLowerCase(ch2);
                if (ch1 != ch2) {
                    return false;
                }
                i++;
                j--;
            }
        }
        return true;
    }
}

19. 面试题19:最多删除一个字符得到回文

package com.lxh.string03;
/* 面试题19:最多删除一个字符得到回文
* 1.本题很巧妙的使得每种出现的情况都进行了判断 即当出现两个字符不相等的时候,要对删除任意一个都要进行判断。
* 
* */
public class DeleteGetPalindrome19 {
    public boolean validPalindrome(String s) {
        int start = 0;
        int end = s.length() - 1;
        for (; start < s.length() / 2; ++start, --end) {
            if (s.charAt(start) != s.charAt(end)) {
                break;
            }
        }
        //三者满足其一即 返回 true 或者 返回 false 此写法可以很好的解决删除其中一种只要有一个满足条件就可以返回true
        return start == s.length() / 2 || isPalindrome(s, start, end - 1) || isPalindrome(s, start + 1, end);
    }

    private boolean isPalindrome(String s, int start, int end) {
        while (start < end) {
            if (s.charAt(start) != s.charAt(end)) {
                break;
            }

            start++;
            end--;
        }
        return start >= end;
    }

    public static void main(String[] args) {
        DeleteGetPalindrome19 dgp19 = new DeleteGetPalindrome19();
        String s = "abca";
        System.out.println(dgp19.validPalindrome(s));
    }
}

20. 面试题20:回文字符串的个数

package com.lxh.string03;
/* 面试题20:回文字符串的个数
* 
* 
* */
public class SumPalindrome20 {
    public int countSubstrings(String s) {
        /* 其中,输入值为字符串、数组或者链表时,有两种需要判断的情况,此处以String s 为例
        * 1.s == null; 也就是s还没有占用存储空间。
        * 2.s.length == 0; s占用存储空间,但是其中还没有元素。
        * 在 s == null 时,s.length()会报错NullPointerException
        * 所以按照下面的写法进行判断
        * */
        if (s == null || s.length() == 0) {
            return 0;
        }

        int count = 0;
        for (int i = 0; i < s.length(); i++) {
            //第i个字符本身可以成为长度为奇数的回文子字符串的对称中心
            //第i个字符和第i+1个字符可以一起成为长度为偶数的回文子字符串的对称中心
            count += countPalindrome(s, i, i);
            count += countPalindrome(s, i, i+1);
        }
        return count;
    }

    private int countPalindrome(String s, int start, int end) {
        int count = 0;
        while (start >= 0 && end < s.length() && s.charAt(start) == s.charAt(end)) {
            count++;
            start--;
            end++;
        }
        return count;
    }
}

21. 面试题21:删除倒数第k个节点

package com.lxh.list;

/**
 * @Author: Tiger
 * @Description: 面试题21:删除倒数第K个节点
 * 1. JAVA就规定一个源文件只能有一个public类,而且文件名必须与它相同,这样一个文件形成了一个功能单元
 * @Date: 下午 16:31 2021/10/31 0031
 **/

public class DeleteTheKthNode21 {

    ListNode head;

    public ListNode removeNthFromEnd(ListNode head, int n) {
        //哨兵节点
        ListNode dummy = new ListNode(0);
        dummy.next = head;
        // 此时front是否应该等于 dummy 或者back也等于head 这样才能保证进行while循环时二者之间的间隔为k
        ListNode front = head, back = dummy;
        for (int i = 0; i < n; i++) {
            front = front.next;
        }

        while (front != null) {
            front = front.next;
            back = back.next;
        }

        back.next = back.next.next;
        return dummy.next;
    }

    public ListNode addNode(int data){
        ListNode newNode = new ListNode(data);
        if(head == null){
            head = newNode;
            return newNode;
        }
        ListNode temp = head;
        while(temp.next != null){
            temp = temp.next;
        }
        temp.next = newNode;
        return newNode;
    }

    public void printLink(ListNode curNode){
        curNode = head;
        while(curNode !=null){
            System.out.print(curNode.val+" ");
            curNode = curNode.next;
        }
        System.out.println();
    }

    public static void main(String[] args) {

        DeleteTheKthNode21 dtkn21 = new DeleteTheKthNode21();

        ListNode node = dtkn21.addNode(9);

        // dtkn21.addNode(8);
        // dtkn21.addNode(6);
        // dtkn21.addNode(3);
        // dtkn21.addNode(5);



        dtkn21.printLink(node);
        // ListNode result = dtkn21.removeNthFromEnd(node1, 2);
        // dtkn21.printLink(result);

    }
}

22. 面试题22:链表中环的入口节点

package com.lxh.list;

/**
 * @Author: Tiger
 * @Description: 面试题22:链表中环的入口节点(解法一:需要知道环中节点数目的解法和解法二:不需要知道)
 * 1. 其中解法二省略了得到环中节点的数目的步骤
 * @Date: 下午 19:37 2021/10/31 0031
 **/
public class TheEntryNode22 {

    private ListNode getNodeInLoop(ListNode head) {
        if (head == null || head.next == null) {
            return null;
        }

        ListNode slow = head.next;
        ListNode fast = slow.next;

        while (slow != null && fast != null) {
            if (slow == fast) {
                return slow;
            }

            slow = slow.next;
            fast = fast.next;

            //多走一步 相当于速度每次比slow快1
            if (fast != null) {
                fast = fast.next;
            }
        }
        return null;
    }

/*    public ListNode detectCycle(ListNode head) {
        //调用上面的getNodeInLoop()函数,获得位于环中的一个节点
        ListNode inLoop = getNodeInLoop(head);
        int loopCount = 1;
        //求出环的节点数目
        for (ListNode n = inLoop; n.next != inLoop; n = n.next) {
            loopCount++;
        }

        //利用双指针找到环的入口点
        ListNode fast = head;
        for (int i = 0; i < loopCount; i++) {
            fast = fast.next;
        }

        ListNode slow = head;
        while (fast != slow) {
            fast = fast.next;
            slow = slow.next;
        }
        return slow;
    }*/

    public ListNode detectCycle(ListNode head) {
        ListNode inLoop = getNodeInLoop(head);
        if (inLoop == null) {
            return null;
        }

        ListNode node = head;
        while (node != inLoop) {
            node = node.next;
            inLoop = inLoop.next;
        }

        return node;
    }
}

23. 面试题23:两个链表的第一个重合节点

package com.lxh.list;

/**
 * @Author: Tiger
 * @Description: 面试题23:两个链表的第一个重合节点
 * @Date: 下午 14:01 2021/11/2 0002
 **/
public class FirstDuplicateNode23 {
    public ListNode getIntersectionNode(ListNode headA, ListNode headB) {
        int count1 = countList(headA);
        int count2 = countList(headB);

        int differ = Math.abs(count1 - count2);
        ListNode longer = count1 > count2 ? headA : headB;
        ListNode shorter = count1 > count2 ? headB : headA;
        ListNode node1 = longer;
        for (int i = 0; i < differ; i++) {
            node1 = node1.next;
        }

        ListNode node2 = shorter;
        while (node1 != node2) {
            node1 = node1.next;
            node2 = node2.next;
        }
        return node1;
    }

    private int countList(ListNode head) {
        int count = 0;
        while (head != null) {
            count++;
            head = head.next;
        }
        return count;
    }
}

24. 面试题24:反转链表

package com.lxh.list;

/**
 * @Author: Tiger
 * @Description: 面试题24:反转链表
 * @Date: 下午 14:52 2021/11/2 0002
 **/
public class ReverseLinkedList24 {
    public ListNode reverseList(ListNode head) {

        ListNode pre = null;
        ListNode cur = head;
        while (cur != null) {
            ListNode next = cur.next;
            cur.next = pre;
            pre = cur;
            cur = next;
        }
        return pre;
    }
}

25. 面试题25:链表中的数字相加

package com.lxh.list;

import java.util.List;

/**
 * @Author: Tiger
 * @Description: 面试题25:链表中的数字相加
 * @Date: 下午 15:12 2021/11/2 0002
 **/
public class AddNumbersInList25 {
    public ListNode addTwoNumbers(ListNode headA, ListNode headB) {
        headA = reverseList(headA);
        headB = reverseList(headB);
        ListNode reverseHead = addReversed(headA, headB);
        return reverseList(reverseHead);
    }

    private ListNode reverseList(ListNode head) {
        ListNode pre = null;
        ListNode cur = head;
        ListNode next = null;
        while (cur != null) {
            next = cur.next;
            cur.next = pre;
            pre = cur;
            cur = next;
        }
        return pre;
    }

    private ListNode addReversed(ListNode headA, ListNode headB) {
        ListNode dummy = new ListNode(0);
        ListNode sumNode = dummy;
        int carry = 0;

        while (headA != null || headB != null) {
            int sum = (headA == null ? 0 : headA.val) + (headB == null ? 0 : headB.val) + carry;
            carry = sum >= 10 ? 1 : 0;
            sum = sum >= 10 ? sum - 10 : sum;
            ListNode newNode = new ListNode(sum);

            sumNode.next = newNode;
            sumNode = sumNode.next;

            headA = headA == null ? null : headA.next;
            headB = headB == null ? null : headB.next;
        }
        if (carry > 0) {
            sumNode.next = new ListNode(carry);
        }
        return dummy.next;
    }
}

你可能感兴趣的:(offer,java,算法)