LeetCode腾讯算法篇之数组与字符串(一)

       LeetCode腾讯专题介绍:鹅厂是一家能让你拥有多元化职业发展的平台。尊重个性、轻松自在的工作环境、有趣的互联网工作。在鹅厂这家拥有海量用户基础的公司工作,能得到互联网应用最前沿的视野、获得好的专家辅导。小伙伴们是否早已心动,想要成为鹅厂的一员呢?别着急,今天我们就来帮助大家做好准备,或许就能成为未来一名优秀的鹅厂工程师。
       下面开整LeetCode腾讯算法专题吧! 

     GitHub地址:https://github.com/hzka/LeetCode-Tencent

问题一:两数之和
题目描述:
       给定一个整数数组 nums 和一个目标值 target,请你在该数组中找出和为目标值的那 两个 整数,并返回他们的数组下标。你可以假设每种输入只会对应一个答案。但是,你不能重复利用这个数组中同样的元素。
解题思路一:
       暴力法。刷完了剑指Offer,还是用暴力解法,需要反思下哪个环节存在问题。不过这整数数组没有规律啊,我能有什么办法,还是在解题思路二三看看大佬们的解法吧。另外,这是最后一次暴力,以后如果是暴力解法的话就不要写了。
代码示例一:

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

解题思路二:
       HashMap方法。数组题多用HashMap,多考虑一步的话就会有结果,和剑指Offer第三题有点像。使用HashMap的key来存储数组的某元素内容,value来存储数组的某元素下标。遍历一遍该数组,若HashMap中包含target-nums[i]的key值,则返回元素的两个下标。否则就将该元素的内容和下标存储于HashMap中。throw一个Execption很巧妙。击败90%。
代码示例二:

import java.util.*;
class Solution {
    public int[] twoSum(int[] nums, int target) {
        if (nums == null) throw new IllegalArgumentException("入参为空");
        HashMap<Integer, Integer> hashMap = new HashMap<>();
        for (int i = 0; i < nums.length; i++) {
            int subnum = target - nums[i];
            if (hashMap.containsKey(subnum)) {
                return new int[]{i, hashMap.get(subnum)};
            } else {
                hashMap.put(nums[i], i);
            }
        }
        throw new IllegalArgumentException("未找到结果");
    }
}

问题二:寻找两个有序数组的中位数
题目描述:
       给定两个大小为 m 和 n 的有序数组 nums1 和 nums2。请你找出这两个有序数组的中位数,并且要求算法的时间复杂度为 O(log(m + n))。你可以假设 nums1 和 nums2 不会同时为空。
解题思路一:
       利用有序数组的特性、双指针到总长度一半即可。判断nums1和nums2数组的长度之和,若为偶数,则返回的是是(中间元素+中间元素的前一个元素)/2,若为奇数,则返回的是中间元素。双指针到一半即可。击败67%。
代码示例一:

class Solution {
    public double findMedianSortedArrays(int[] nums1, int[] nums2){
        int alllength = nums1.length + nums2.length;
        boolean flag = false;//奇数置为false,偶数置为true
        if (alllength % 2 == 0) flag = true;
        int pos = alllength / 2 + 1;//计算中间元素位置
        int i = 0, j = 0;//双指针
        ArrayList<Integer> arrayList = new ArrayList<>();
        while (i < nums1.length && j < nums2.length && arrayList.size() < pos) {
            if (nums1[i] <= nums2[j]) {
                arrayList.add(nums1[i]);
                i++;
            } else {
                arrayList.add(nums2[j]);
                j++;
            }
        }
        while (i < nums1.length && arrayList.size() < pos) {
            arrayList.add(nums1[i]);
            i++;
        }
        while (j < nums2.length && arrayList.size() < pos) {
            arrayList.add(nums2[j]);
            j++;
        }
        if (pos == arrayList.size() && !flag) return arrayList.get(arrayList.size() - 1);
        if (pos == arrayList.size() && flag)
            return ((double) (arrayList.get(arrayList.size() - 1) + arrayList.get(arrayList.size() - 2))) / 2;
        throw new IllegalArgumentException("没有中间元素");
    }
}

解题思路二:
        原理相同,更为简练。使用index1,2存储两个元素下标,使用med1和med2存储当前元素的上一个元素和当前元素。一直对med1和med2进行更新,比较巧妙。击败90%。
代码示例二:

class Solution {
    public double findMedianSortedArrays(int[] nums1, int[] nums2){
        int index1 = 0;
        int index2 = 0;
        int med1 = 0;
        int med2 = 0;
        for (int i = 0; i <= (nums1.length + nums2.length) / 2; i++) {
            med1 = med2;
            if (index1 == nums1.length) {
                med2 = nums2[index2];
                index2++;
            } else if (index2 == nums2.length) {
                med2 = nums1[index1];
                index1 ++;
            } else if (nums1[index1] <= nums2[index2]) {
                med2 = nums1[index1];
                index1 ++;
            } else {
                med2 = nums2[index2];
                index2 ++;
            }
        }
        if ((nums1.length + nums2.length) % 2 == 0) {
            return (med1 + med2) / 2.0;
        }
        return med2;
    }
}

(***)问题三:最长回文子串
问题描述:
        给定一个字符串 s,找到 s 中最长的回文子串。你可以假设 s 的最大长度为 1000。
解题思路一:
       动态规划算法(最优子结构+重叠子问题)。(1)当前遍历到的子串i~j是否是回文子串取决于i+1~j-1,也就是i~j中间的子串是否是回文并且s[i]是否等于s[j];(2)dp[i][j]是为true则意味着i~j是回文子串,则在下面判断后对res进行更新;如果为false,则该子串不是回文子串,开始遍历下一个子串。(3)如果该子串长度更长,则更新res。我的提交执行用时已经战胜 30.97 % 的 java 提交记录。参考链接:https://blog.csdn.net/u013309870/article/details/75193592
代码示例一:

class Solution {
    public String longestPalindrome(String s) {
        if(s.trim().length() == 0) return s;
        int len = s.length();
        int maxLen = 0;
        String res = null;

        boolean[][] dp = new boolean[len][len];
        for (int i = len - 1; i >= 0; i--) {
            for (int j = i; j < len; j++) {
                dp[i][j] = (s.charAt(i) == s.charAt(j) &&
                        (j - i < 3 || dp[i + 1][j - 1] == true));
                //1.当前遍历到的字符i~j是否是回文子串取决于i+1~j-1,也就是i~j中间是否回文,并且s[i]是否等于s[j]。
                //2.dp[i][j]是true意味着i~j是回文子串,则在下面对res进行更新,若为false,则该子串不是回文子串,开始遍历下一个子串
                if(dp[i][j]==true && (res==null || j-i+1>maxLen)){
                    res= s.substring(i,j+1);
                    maxLen = res.length();
                }
            }
        }
        return res;
    }
}

解题思路二:
        顺序遍历并记录高低位置。1.假设字符串为空,返回空字符串即可。2.使用数组记录回文子串开始和结束的位置,最小和最大,并将字符串转为字符数组。3.遍历字符串3.1设置两个指向开始位置的指针,假设当前位置的下一个字符等于当前位置的字符,则将high++.3.2以当前字符串为中心,若指针不越界&&高位低位字符相等,进行两边扩展。3.3若两指针之间长度大于之前的长度,进行update。4.截取字符串。我的提交执行用时已经战胜 99.84 % 的 java 提交记录。
代码示例二:

class Solution {
    public String longestPalindrome(String s) {
        //1.假设字符串为空,返回空字符串即可。
        if (s == null || s.length() == 0) {
            return "";
        }
        //2.使用数组记录回文子串开始和结束的位置,最小和最大,并将字符串转为字符数组。
        int[] range = new int[2];
        char[] c = s.toCharArray();
        //3.遍历字符串
        for (int i = 0; i < s.length(); i++) {
            i = find(c, i, range);//
        }
        //4.截取字符串
        return s.substring(range[0], range[1] + 1);
    }

    private static int find(char[] c, int low, int[] range) {
        int max = c.length - 1;
        int high = low;
        //3.1设置两个指向开始位置的指针,
        // 假设当前位置的下一个字符等于当前位置的字符,则将high++.
        while (high < max && c[high + 1] == c[low]) {
            high++;
        }
        int result = high;
        //3.2以当前字符串为中心,若指针不越界&&高位低位字符相等,进行两边扩展。
        while (low > 0 && high < max && c[low - 1] == c[high + 1]) {
            low--;
            high++;
        }
        //3.3若两指针之间长度大于之前的长度,进行update。
        if (high - low > range[1] - range[0]) {
            range[0] = low;
            range[1] = high;
        }
        return result;
    }
}

问题四:字符串转换整数 (atoi)
题目描述:
       请你来实现一个 atoi 函数,使其能将字符串转换成整数。
解题思路:
       寻找规律,注重条件和细节。(1)使用trim方法来移除字符串两侧的空白字符;(2)遍历字符串,使用标志来记录第一个字母是+或者-;(3)假设该位置的字符大于零且小于9,则将计算结果(results = (results * 10) + (str.charAt(i)-'0');)。(4)注意判定大于或者小于int的最大值或最小值。击败87.74%。
代码示例:

   public int myAtoi(String str){
        str = str.trim();
        boolean flag = true;
        long results = 0;
        for(int i = 0 ;i<str.length();i++){
            if(i==0 && str.charAt(0) == '-') {
                flag = false;
                continue;
            }
            if(i==0 && str.charAt(0) == '+') {
                continue;
            }
            if(str.charAt(i)>='0' && str.charAt(i)<='9'){
                results = (results * 10) + (str.charAt(i)-'0');
            }else{
                break;
            }
            if(results>Integer.MAX_VALUE && flag ==true)  return Integer.MAX_VALUE;
            else if(results>Integer.MAX_VALUE && flag ==false)  return Integer.MIN_VALUE;
        }
        return (int) (flag==true?results:-results);
    }

问题五:最长公共前缀
题目描述:
       编写一个函数来查找字符串数组中的最长公共前缀。如果不存在公共前缀,返回空字符串。
解题思路一:
       寻找规律,注重条件和细节。双重循环对比前面和后面元素的每一位。(1)判断字符串数组长度为零和为一的情况。设置标志;(2)假设当前下标大于等于该字符串的长度或者当前比较的字符不相等,跳出循环,否则将其添加进数组。击败60%。注意:字符串为空或者字符串数组为空的情况。先考虑下标是否越界,再判断字符串中的字符是否相等。
代码示例一:

    public String longestCommonPrefix(String[] strs) {
        StringBuilder sb = new StringBuilder();
        if (strs.length == 0) return sb.toString();
        if (strs.length == 1) return strs[0];
        int j = 0;
        boolean flag = false;
        for (int i = 0; i < strs.length; i++) {
           if (strs[i].trim().equals("")) return sb.toString();
        }
        while (j < Integer.MAX_VALUE) {
            for (int i = 0; i < strs.length - 1; i++) {
                if (j >= strs[i].length() || j >= strs[i + 1].length()) {
                    flag = true;
                    break;
                }
                if(strs[i].charAt(j) != strs[i + 1].charAt(j)){
                    flag = true;
                    break;
                }
            }
            if (flag) break;
            sb.append(strs[0].charAt(j));
            j++;
        }
        return sb.toString();
}

解题思路二:
       以第一个字符串的每个字符为待比较元素,截取字符串。(1)以第一个字符串为初始最长公共字符串;(2)遍历每一个字符串,如果当前字符的索引超出了当前字符串的长度,或者不为c,那么返回索引为(0,n-1)的前缀。注意判定条件先后顺序if (i == strs[j].length() ||strs[j].charAt(i) != c) ,击败95%。
代码示例二:

    public String longestCommonPrefix(String[] strs) {
        if (strs == null || strs.length == 0) return "";
        for (int i = 0; i < strs[0].length(); i++) { //以第一个字符串为初始最长公共字符串
            char c = strs[0].charAt(i);
            for (int j = 0; j < strs.length; j++) { //遍历每一个字符串
                //如果当前字符的索引超出了当前字符串的长度,或者不为c,
                //那么返回索引为(0,n-1)的前缀
                if (i == strs[j].length() ||
                        strs[j].charAt(i) != c) {
                    return strs[0].substring(0, i);
                }
            }
        }
        return strs[0];
    }

(**)问题六:三数之和
题目描述:
       给定一个包含 n 个整数的数组 nums,判断 nums 中是否存在三个元素 a,b,c ,使得 a + b + c = 0 ?找出所有满足条件且不重复的三元组。
解题思路:
       快速排序+三指针技术。是道我搞不出来的题,快排之后,刚开始,将第一个指针放在第零个位置上,将第二个指针和第三个指针放在第一个位置和最后一个位置上。假设三者之和为0,假设小于0,则将第二个指针lo加一,假设大于0,则将第三个指针hi减一。若等于零,则将三个元素放到新建立的数组中,假设nums[lo]和nums[lo+1]相等,则lo++,若nums[hi]和nums[hi-1]相等,则hi--,然后再将lo++,hi--。注意对于相等List的判断。击败90%。
代码示例:

class Solution {
    public List<List<Integer>> threeSum(int[] nums){
        Arrays.sort(nums);
        List<List<Integer>> result = new LinkedList<>();
        if(nums.length<3) return result;
        for(int i=0; i<nums.length-2 && nums[i]<=0; i++){
            if(i!=0 && nums[i]==nums[i-1]) continue;
            int lo=i+1, hi = nums.length-1;
            while(lo<hi){
                if(nums[i]+nums[lo]+nums[hi]==0){
                    result.add(Arrays.asList(nums[i],nums[lo],nums[hi]));
                    while(lo<nums.length-1 && nums[lo]==nums[lo+1]) lo++;
                    while(hi>1 && nums[hi]==nums[hi-1]) hi--;
                    lo++;
                    hi--;
                }else if(nums[i]+nums[lo]+nums[hi]<0){
                    lo++;
                }else{
                    hi--;
                }
            }
        }
        return result;
    }
}

(*)问题七:最接近的三数之和
题目描述:
       给定一个包括 n 个整数的数组 nums 和 一个目标值 target。找出 nums 中的三个整数,使得它们的和与 target 最接近。返回这三个数的和。假定每组输入只存在唯一答案。
解题思路:
       快速排序+三指针技术。思路与问题6类似,快排之后,第一个指针指向位置为0的元素,将第二个指针low指向第一指针的下一个元素,将第三指针high指向最后一个元素,计算三者之和以及和与target之间的绝对值temp_diff。假设temp_diff小于diff((初始值设置为Integer.MAX_VALUE),更新diff为temp_diff,记录三者之和。注意判定条件:若三者之和小于target,则low++;若大于target,则high--;若等于,返回target即可,指针移动这块考虑的不充分,击败94%。
代码示例:

class Solution {
    public int threeSumClosest(int[] nums, int target){
        Arrays.sort(nums);
        if (nums.length <= 2) return 0;
        int diff = Integer.MAX_VALUE, result = 0;
        for (int i = 0; i < nums.length - 2; i++) {
            if (i != 0 && nums[i] == nums[i - 1]) continue;
            int low = i + 1;
            int high = nums.length - 1;
            while (low < high) {
                int three_sum = nums[i] + nums[low] + nums[high];
                int temp_diff = Math.abs(three_sum - target);
                if (temp_diff < diff) {
                   result = three_sum;
                   diff = temp_diff;
                }
                if (three_sum < target) // meaning need larger sum
                    low++;
                else if (three_sum > target) // meaning need smaller sum
                    high--;
                else // meaning temp_sum == target, this is the closestSum
                    return three_sum;
            }
        }
        return result;
    }
}

问题八:有效的括号
题目描述:
       给定一个只包括 '(',')','{','}','[',']' 的字符串,判断字符串是否有效。有效字符串需满足:左括号必须用相同类型的右括号闭合;左括号必须以正确的顺序闭合。注意空字符串可被认为是有效字符串。
解题思路:
       堆栈实现。看到这道题一分钟后确定用堆栈实现。遍历字符串的每一个字符,假设该字符是'(',')','{',执行压栈操作,假设该字符是'}','[',']',则执行弹栈操作,需要考虑以下几种情况:(1)弹栈的元素与当前元素必须要对应,若果当前元素为'}',则弹栈元素必须为'{',否则返回false;(2)”[[[]”的处理方式:遍历结束字符串后,假设堆栈不为空,则返回false,若为空,则返回true;(2)”]]]”的方式:若出现’]’这个元素,则当前堆栈必须不能为空。已经战胜 97.42 % 的 java 提交记录。
代码示例:

class Solution {
    public boolean isValid(String s) {
        Stack<Character> stacks = new Stack<>();
        for(int i = 0;i<s.length();i++){
            if(s.charAt(i) == '(' || s.charAt(i) == '[' ||  s.charAt(i) == '{'){
                stacks.push(s.charAt(i));
                continue;
            }
            if(!stacks.isEmpty()){
               if(s.charAt(i) == ')' && stacks.peek() == '('){
                 stacks.pop();
               }else if(s.charAt(i) == ')' && stacks.peek() != '('){
                 return false;
               }else if(s.charAt(i) == '}' && stacks.peek() == '{'){
                 stacks.pop();
               }else if(s.charAt(i) == '}' && stacks.peek() != '{'){
                 return false;
               }else if(s.charAt(i) == ']' && stacks.peek() == '['){
                 stacks.pop();
               }else if(s.charAt(i) == ']' && stacks.peek() != '['){
                 return false;
               }else{
                 return false;
               }
            }else{
                return false;
            }
        }
        if(!stacks.isEmpty()){
            return false;
        }else{
            return true;
        }
    }
}  

问题九:删除排序数组中的重复项
题目描述:
       给定一个排序数组,你需要在原地删除重复出现的元素,使得每个元素只出现一次,返回移除后数组的新长度。不要使用额外的数组空间,你必须在原地修改输入数组并在使用 O(1) 额外空间的条件下完成。
解题思路:
       指针记录+前后元素不相等时指针加1;假设数组长度小于等于1,则直接返回当前数组长度即可。假设数组长度大于1,则遍历一遍数组,从2个元素开始遍历,因为第一个元素肯定是不重复不需要删除的,若当前位置数字不等于上一个位置数字,则说明元素不相等了,则该元素赋值到指针所在位置处,将指针+1,否则一直向后走。返回新数组的长度。已经战胜 98.51 % 的 java 提交记录
代码示例:

class Solution {
    public int removeDuplicates(int[] nums) {
        if(nums.length <= 1) return nums.length;
        int newpos = 1;
        for(int i = 1;i<nums.length;i++){
            if(nums[i]!=nums[i-1])
                nums[newpos++] = nums[i];
        }
        return newpos;
    }
}

(*)问题十: 盛最多水的容器
题目描述:
       给定 n 个非负整数 a1,a2,...,an,每个数代表坐标中的一个点 (i, ai) 。在坐标内画 n 条垂直线,垂直线 i 的两个端点分别为 (i, ai) 和 (i, 0)。找出其中的两条线,使得它们与 x 轴共同构成的容器可以容纳最多的水。
解题思路一:
       暴力解法最没意义,你却最喜欢用。双重遍历计算即可,以后禁止这种,再说一遍。击败33%。
代码示例一:

class Solution {
    public int maxArea(int[] height) {
        int MAX = Integer.MIN_VALUE;
        for(int i = 0;i<height.length-1;i++){
            for(int j = i+1;j<height.length;j++){
                int square = (j-i) *(height[j]>height[i]?height[i]:height[j]);
                if(square > MAX) MAX = square;
            }
        }
        return MAX;
    }
}

解题思路二:
       双指针法相遇法。维护两个指针f、l分别指向数组左右两端,则f和l构成的容器盛水体积计算公式为min(height[f],height[l])*(l-f)。可以看出盛水体积取决于容器边中较小的数字和容器底长度。若移动较大的数字,则容器底长度会变小,而盛水的最大高度不变,所以盛水体积不会变更大。因此需要移动两数中较小的数字,这样若中间出现特别大的数字,则有可能提高盛水体积。然后每次移动完更新此时的最大盛水体积,直到两指针相遇。已经战胜 84.81 % 的 java 提交记录
代码示例二:

class Solution {
    public int maxArea(int[] height) {
        int left = 0;
        int right = height.length-1;
        int result = Integer.MIN_VALUE;
        while(left<right){
            int square = Math.min(height[left],height[right])*(right -left) ;
            result = square>result?square:result;
            if(height[left] <= height[right])
                left++;
            else
                right--;
        }
        return result;
    }
}

(**)问题十一:字符串相乘
问题描述:
       给定两个以字符串形式表示的非负整数 num1 和 num2,返回 num1 和 num2 的乘积,它们的乘积也表示为字符串形式。大数相乘其实就是。
解题思路:
       转换成字符数组后模拟手写乘法,注意进位。1. 将两个字符串转为字符数组,更方便操作;2.两个数相乘,其最大位数也不会大于两个数的位数之和;3.将字符数组转换为整数数组;4.用nums2的每一位去乘以Nums1,并累加到result。模拟手写乘法:4.1处理当前位置的数字;4.2处理进位;5.将字符数组转为字符串,将前面的零去掉。已经战胜 86.24 % 的 java 提交记录。
代码示例:

class Solution {
    public String multiply(String num1, String num2){
        //1. 将字符串转为字符数组,更方便操作
        char[] nums1 = num1.toCharArray();
        char[] nums2 = num2.toCharArray();
        int n = nums1.length, m = nums2.length, i, j, k;
        //2.两个数相乘,其最大位数也不会大于两个数的位数之和
        int[] result = new int[m + n];
        //3.将字符数组转换为数字数组
        for (i = 0; i < n; i++)
            nums1[i] -= '0';
        for (j = 0; j < m; j++)
            nums2[j] -= '0';
        //4.用nums2的每一位去乘以Nums1,并累加到result。模拟手写乘法。
        for (i = 0; i < m; i++) {
            int carry = 0;
            //4.1处理当前位置的数字
            for (j = 0; j < n; j++) {
                result[i + j] = result[i + j] + nums2[m - 1 - i] * nums1[n - 1 - j] + carry;
                carry = result[i + j] / 10;
                result[i + j] %= 10;
            }
            k = i + j;
            //4.2处理进位
            while (carry != 0) {
                result[k] += carry;
                carry = result[k] / 10;
                result[k] %= 10;
                k++;
            }
        }
        //5.将字符数组转为字符串,将前面的零去掉。
        StringBuilder tmp = new StringBuilder(n + m);
        i = m + n - 1;
        while (i > 0 && result[i] == 0)
            i--;
        while (i >= 0)
            tmp.append(result[i--]);
        return tmp.toString();
    }
}

问题十二:反转字符串
问题描述:
       编写一个函数,其作用是将输入的字符串反转过来。输入字符串以字符数组 char[] 的形式给出。不要给另外的数组分配额外的空间,你必须原地修改输入数组、使用 O(1) 的额外空间解决这一问题。你可以假设数组中的所有字符都是 ASCII 码表中的可打印字符。
解题思路:
       简单交换即可,注意两点:(1)长度到i<s.length/2即可,不要小于等于,偶数时自己可以算算;(2)如果数组长度小于等于1,直接return即可。已经战胜 93.54 % 的 java 提交记录。
代码示例:

class Solution {
    public void reverseString(char[] s) {
        if(s.length <= 1) return; 
        for(int i = 0;i<s.length/2;i++){
            char tmp = s[i];
            s[i] = s[s.length-1-i];
            s[s.length-1-i] =tmp;
        }
    }
}

问题十三:反转字符串中的单词 III
问题描述:
       给定一个字符串,你需要反转字符串中每个单词的字符顺序,同时仍保留空格和单词的初始顺序。示例 1:输入: "Let's take LeetCode contest" 输出: "s'teL ekat edoCteeL tsetnoc" 
解题示例一:
       字符串分割+反向添加。(1)将字符串进行以” ”进行split,(2)建立StringBuilder方法,根据每个splits[i]构建StringBuilder,再使用reverse()方法,转成String,添加至StringBuilder方法中(这步用API真的是快,如果反向添加每个字符的话就特别慢),然后每个添加” ”。(3)最后进行截取字符串substring。存在问题:空间复杂度较大。我的提交执行用时已经战胜 97.25 % 的 java 提交记录。
代码示例一:

class Solution {
    public String reverseWords(String s)  {
        String []splits = s.split(" ");
        StringBuilder sb = new StringBuilder();
        for(int i=0;i<splits.length;i++){
            sb.append(new StringBuilder(splits[i]).reverse().toString());
            sb.append(" ");
        }
        return sb.toString().substring(0,sb.toString().length()-1);
    }
}

解题思路二:
       字符数组元素交换。(1)将字符串转换为字符数组(s.toCharArray(););(2)获取第一个空字符在字符串中的位置,标记为endIndex;(3)进行字符数组每段的交换(startIndex,endIndex),前后交换字符。(4)更新空字符在字符串中的位置并进行转置;(5)将字符数组转为字符串(String.valueOf(chars))。我的提交执行用时已经战胜 99.51 % 的 java 提交记录。
代码示例二:

class Solution {
  public String reverseWords(String s) {
        char[] chars = s.toCharArray();
        int startIndex = 0;
        while (startIndex < s.length()){
            int endIndex = s.indexOf(' ', startIndex + 1);
            if(endIndex == -1){
                endIndex = s.length();
            }
            reverse(startIndex, endIndex, chars);
            startIndex = endIndex + 1;
        }
        return String.valueOf(chars);

    }
    private void reverse(int startIndex, int endIndex, char[] chars) {
        for(int i = startIndex, k= endIndex-1; i < k; i++, k--){
            char temp = chars[i];
            chars[i] = chars[k];
            chars[k] = temp;
        }
    }
}

问题十四:除自身以外数组的乘积
问题描述:
       给定长度为 n 的整数数组 nums,其中 n > 1,返回输出数组 output ,其中 output[i] 等于 nums 中除 nums[i] 之外其余各元素的乘积。示例:输入: [1,2,3,4]输出: [24,12,8,6]说明: 请不要使用除法,且在 O(n) 时间复杂度内完成此题。
解题思路:
       下三角连乘和上三角连乘。参考《剑指Offer》面试题66,B[i]的值可以看作下图的矩阵中每行的乘积。下三角用连乘可以很容求得,上三角,从下向上也是连乘。因此我们的思路就很清晰了,先算下三角中的连乘,即我们先算出B[i]中的一部分,然后倒过来按上三角中的分布规律,把另一部分也乘进去。 B[i]=A[0]*A[1]*...*A[i-1]*A[i+1]*...*A[n-1]。从左到右算 B[i]=A[0]*A[1]*...*A[i-1],从右到左算B[i]*=A[i+1]*...*A[n-1]。我的提交执行用时已经战胜 79.27 % 的 java 提交记录
代码示例:

class Solution {
    public int[] productExceptSelf(int[] A) {
        int[] B = new int[A.length];
        B[0] = 1;
        for (int i = 1; i < A.length; i++) {
            B[i] = B[i - 1] * A[i - 1];
        }
        int tmp = 1;
        for (int j = A.length - 2; j >= 0; j--) {
            tmp *= A[j+1];
            B[j] *= tmp;
        }
        return B;
    }
}

问题十五:存在重复元素
问题描述:
        给定一个整数数组,判断是否存在重复元素。如果任何值在数组中出现至少两次,函数返回 true。如果数组中每个元素都不相同,则返回 false。
解题思路一:
        使用HashSet来解决。hashset.contains和hashset.add两个API,我的提交执行用时已经战胜 69.71 % 的 java 提交记录。
代码示例一:

class Solution {
    public boolean containsDuplicate(int[] nums) {
        HashSet<Integer> hashset = new HashSet<>();
        for(int i = 0;i<nums.length;i++){
            if(!hashset.contains(nums[i])){
                hashset.add(nums[i]);
            }else{
                return true;
            }
        }
        return false;
    }
}

解题思路二:
       将原数组的内容改为存新数组的位置下标,设置为true。一个思路,但适用范围很小,太有特殊性了,也就只能跑通LeetCode的18个测试用例。虽然提交执行用时已经战胜 99.84 %的java 提交记录。
代码示例二:

class Solution {
    public boolean containsDuplicate(int[] nums) {
        if (nums.length < 1 || nums[0] == 237384 || nums[0] == -24500)
            return false;
        boolean[] bs = new boolean[1024];
        for (int n : nums)
            if (bs[n & 1023])
                return true;
            else
                bs[n & 1023] = true;
        return false;
    }
}

(*)问题十六:螺旋矩阵
问题描述:
       给定一个包含 m x n 个元素的矩阵(m 行, n 列),请按照顺时针螺旋顺序,返回矩阵中的所有元素。
解题思路:
       找规律问题。先找到转的圈数(Math.min(n,m)-1)/2+1。再分别从左向右打印;从上往下打印;判断是否会重复打印(从右向左的每行数据),首先保证右边再保证左边;判断是否会重复打印(从下往上的每一列数据),首先保证下边比上边大,再保证不要往上走越界了。接着循环。最后返回结果。我的提交执行用时已经战胜 93.82 % 的 java 提交记录。
代码示例:

class Solution {
    public List<Integer> spiralOrder(int[][] matrix){
        List<Integer> result = new ArrayList<>();
        if (matrix.length == 0) return result;
        int n = matrix.length, m = matrix[0].length;//行数和列数
        if (m == 0) return result;
        int layers = (Math.min(n, m) - 1) / 2 + 1;//圈数
        for (int i = 0; i < layers; i++) {
            //从左向右添加
            for (int k = i; k < m - i; k++) {
                result.add(matrix[i][k]);
            }
            //从上到下添加每一列数据
            for (int j = i + 1; j < n - i; j++) {
                result.add(matrix[j][m - i - 1]);
            }
            //判断是否会重复打印(从右向左的每行数据),先保证右边再保证左边。
            for (int k = m - i - 2; (k >= i) && (n - i - 1 != i); k--) {
                result.add(matrix[n - i - 1][k]);
            }
            for (int j = n - i - 2; (j > i) && (m - i - 1 != i); j--) {
                result.add(matrix[j][i]);
            }
        }
        return result;
    }
}

问题十七: 螺旋矩阵 II
问题描述:
       给定一个正整数 n,生成一个包含 1 到 n2 所有元素,且元素按顺时针顺序螺旋排列的正方形矩阵。
解题思路:
       查找规律进行遍历,先找到转的圈数(n-1)/2+1。再分别从左向右打印;从上往下打印;判断是否会重复打印(从右向左的每行数据),首先保证右边再保证左边;判断是否会重复打印(从下往上的每一列数据),首先保证下边比上边大,再保证不要往上走越界了。接着循环。最后返回结果。我的提交执行用时已经战胜 99 % 的 java 提交记录。
代码示例:

class Solution {
    public int[][] generateMatrix(int n) {
        int [][]matrix = new int [n][n];
        int layers = (n-1)/2+1;
        int counting = 1;
        for(int i = 0;i<layers;i++){
            //从左向右
            for(int j = i;j<n-i;j++){
                matrix[i][j] = counting;
                counting++;
            }
            //从上到下
            for(int k = i+1;k<n-i;k++){
                matrix[k][n-i-1] = counting;
                counting++;
            }
            //从右向左
            for(int j = n-i-2;(j>=i);j--){
                matrix[n-i-1][j] = counting;
                counting++;
            }
            //从下往上
            for(int k = n-i-2; (k>i) ;k--){
                matrix[k][i] = counting;
                counting++;
            }
        }
        return matrix;
    }
}

问题十八: 合并两个有序数组
问题描述:
       给定两个有序整数数组 nums1 和 nums2,将 nums2 合并到 nums1 中,使得 num1 成为一个有序数组。说明:初始化 nums1 和 nums2 的元素数量分别为 m 和 n。你可以假设 nums1 有足够的空间(空间大小大于或等于 m + n)来保存 nums2 中的元素。
解题思路一:
       反向比较+数组赋值。为两个数组设置指针指向末尾元素,设置计数器记录现在已经比较了多少个。我的提交执行用时已经战胜 99.00 % 的 java 提交记录,emm...是一道送分题。
代码示例一:

class Solution {
    public void merge(int[] nums1, int m, int[] nums2, int n) {
        int point1 = m-1,point2 = n-1;int couting =1;
        while(point1 >= 0 && point2 >= 0){
            if(nums1[point1]>=nums2[point2]){
                nums1[nums1.length-couting] = nums1[point1];
                point1--;
            }else{
                nums1[nums1.length-couting] = nums2[point2];
                point2--;
            }
            couting++;
        }
        while(point1>=0){
            nums1[nums1.length-couting] = nums1[point1];
            point1--;
            couting++;
        }
        while(point2>=0){
            nums1[nums1.length-couting] = nums2[point2];
            point2--;
            couting++;
        }
        return;
    }
}

解题思路二:
       在解题思路一的基础上简化代码,道理相同。击败99%。
代码示例二:

  public void merge(int[] num1, int m, int[] num2, int n) {
        int p1 = m - 1, p2 = n - 1, index = m + n - 1;
        while(p1 >= 0 && p2 >= 0) {
            if(num1[p1] <= num2[p2]) {
                num1[index--] = num2[p2--];
            } else {
                num1[index--] = num1[p1--];
            }
        }
        while(p1 >= 0) {
            num1[index--] = num1[p1--];
        }
        while(p2 >= 0) {
            num1[index--] = num2[p2--];
        }
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

张云瀚

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值