搞定leetcode面试经典150题之数组

系列博客目录



理论知识

数组是我们接触到的第一种数据结构,也是最常用的,虽然结构很普通,但是因为是连续内存,长度空间可知,所在在考虑效率的场景中效率较高,由于数组没有使用额外的索引,处理上我们一般使用遍历来对其进行加工,一些方法可以将数组中的前后关系或者下标的位置作为辅助记录数据的一种手段。

例题

88.合并两个有序数组

链接

class Solution {
    public void merge(int[] nums1, int m, int[] nums2, int n) {
        int[] res = new int[m + n];
        int i = 0;
        int j = 0;
        int ret = 0;
        while(i < m || j < n){
            if(i == m){
                res[ret++] = nums2[j++];
            } else if (j == n) {
                res[ret++] = nums1[i++];
            } else if (nums1[i] < nums2[j]) {
                res[ret++] = nums1[i++];
            }else {
                res[ret++] = nums2[j++];
            }
        }
        for (int k = 0; k < m + n ; k++) {
            nums1[k] = res[k];
        }
    }
}

27.移除元素

链接
注意if语句中的代码,我们不能写nums[left] = nums[right --];这样会导致最后right -- 所在位置的数字没有与val进行比较。我们要把右指针留在比较完毕也就是一定不是val值得位置上,这也是为什么right初始值为nums.length

class Solution {
    public int removeElement(int[] nums, int val) {
        int left = 0;
        int right = nums.length ;
        while(left < right){
            if(nums[left] == val){
                nums[left] = nums[right - 1];
                right --;
            }else {
                left ++;
            }
        }
        return left;
    }
}

26.删除有序数组中的重复项

链接
思路:先把slow置为1,也就是把0默认放入,因为自己不能与自己重复,然后我们放入的条件是,如果和slow上一个已经放入的值不重复的话nums[slow - 1]!=nums[fast],我们就放入。

class Solution {
    public int removeDuplicates(int[] nums) {
        int slow = 1;
        int fast = 1;
        while(fast < nums.length){
            if(nums[slow - 1]!=nums[fast]){
                nums[slow] = nums[fast];
                slow ++;
            }
            fast ++;
        }
        return slow ;
    }
}

80.删除有序数组中的重复项 II

链接
思路和上面很相似,但是我们是不和slow往上数第2个已经放入的值不重复的话就放入,因为重复一次没有事。

class Solution {
    public int removeDuplicates(int[] nums) {
        if(nums.length <= 2) return nums.length;
        int slow = 2;
        int fast = 2;
        while(fast < nums.length){
            if(nums[slow - 2]!=nums[fast]){
                nums[slow] = nums[fast];
                slow ++;
            }
            fast ++;
        }
        return slow ;
    }
}

169.多数元素

链接
思路:题干中有“你可以假设数组是非空的,并且给定的数组总是存在多数元素”。所以直接返回排序后的中间值即可。

class Solution {
    public int majorityElement(int[] nums) {
        Arrays.sort(nums);
        return nums[nums.length/2];
    }
}

189.轮转数组

链接

class Solution {
    public void rotate(int[] nums, int k) {
        k = k % nums.length;
        reverse(nums,0,nums.length-1);
        reverse(nums,0,k-1);
        reverse(nums,k,nums.length -1);
    }
    public void reverse(int nums[],int start,int end){
        while(start < end){
            int temp = nums[start];
            nums[start] = nums[end];
            nums[end] = temp;
            start ++;
            end --;
        }
    }
}

121.买卖股票的最佳时机

链接
思路:遍历寻找最小值,并且针对现有的最小值,更新最大利润。注意ret初始值不能是Integer的MIN,而是0,因为ret最小为0,我们在赋初始值时候,要注意不能一味的赋值Integer的MIN。

class Solution {
    public int maxProfit(int[] prices) {
        int ret = 0;
        int lowest = Integer.MAX_VALUE;
        for (int i = 0; i < prices.length; i++) {
            if(prices[i] < lowest){
                lowest = prices[i];
            }
            ret = Math.max(ret,prices[i] - lowest);
        }
        return ret;
    }
}

122.买卖股票的最佳时机 II

链接
思路:其实就是把每天相对于昨天盈利(相减大于0)的钱加起来。

class Solution {
    public int maxProfit(int[] prices) {
        int ret = 0;
        for (int i = 1; i < prices.length; i++) {
            ret += Math.max(0,prices[i] - prices[i-1]);
        }
        return ret;
    }
}

55.跳跃游戏

链接
思路:使用贪心算法,每一次都找能够走一步后加上走完的一步位置上的值所能到达的最远位置。
注意最后的两个if语句,我们要先检测是否到达了nums.length - 1的位置,再判断nextIndex是否为零,因为存在一开始就达标的情况,它不用走(nextIndex==0)就已经达标。

class Solution {
    public boolean canJump(int[] nums) {
        int index = 0;
        while(true){
            int length = nums[index];
            int nextIndex = 0;
            int longest = Integer.MIN_VALUE;
            for (int i = index + 1; i <= index + length && i < nums.length ; i++) {
                if(nums[i] + i >longest){
                    longest = nums[i] + i;
                    nextIndex = i;
                }
            }

            if(nextIndex + nums[nextIndex] >= nums.length - 1) return true;
            if(nextIndex == 0) return false;
            index = nextIndex;
        }
    }
}

45.跳跃游戏 II

链接
思路:利用上一题的思路进行

class Solution {
    public int jump(int[] nums) {
        int index =0;
        int step = 0;
        while(index < nums.length - 1){
            step ++;
            int length = nums[index];
            int nextIndex =0;
            int max = Integer.MIN_VALUE;
            for (int i = index + 1; i <= index + length ; i++) {
                if(i >= nums.length - 1) return step;
                if(nums[i] + i > max){
                    max = nums[i] + i;
                    nextIndex = i;
                }
            }
            index = nextIndex;
        }
        return step;
    }
}

思路:首先判断如果nums只有一个数,那就是0步,如果不是只有一个数字,那至少走一步,所以开始循环后可以先step++,如果达到或者超过nums.lenght - 1的位置就return。

class Solution {
    public  int jump(int[] nums) {
        int index = 0;
        int step = 0;
        if(nums.length ==1) return 0;
        while(true){
            step ++;
            int nextIndex = 0;
            int length = nums[index];
            int longest = Integer.MIN_VALUE;
            for (int i = index + 1; i <= length + index; i++) {
                if(i>=nums.length-1) return step;
                if(longest < i + nums[i]){
                    nextIndex = i;
                    longest = i + nums[i];
                }
            }
            index =nextIndex;
        }
    }
}

274.H指数

以论文的数量为基准,论文的数量H慢慢增加,就要找到H篇引用大于等于H的。我们可以先对引用量排序,然后从引用量大的一边往另一边找,比如引用量为1,2,3,5,那么H=2时候,从后面找两篇论文满足条件肯定比从前面找好,如果后面都找不到,那前面更不可能,所以for循环应该从大往小循环。(如何知道针对某个引用量,有多少篇论文大于这个引用量呢,经过排序后,citiation[i]可以代表i右边的论文引用都大于citation[i]。)

class Solution {
    public int hIndex(int[] citations) {
        Arrays.sort(citations);
        int res = 0;
        for(int i = citations.length - 1 ; i >= 0 ; i --){
            if(citations[i] >= citations.length - i){
                res = citations.length - i;
            }
        }
        return res;
    }
}

238.除自身以外数组的乘积

链接
思路:我们先通过 answer数组保留每个位置i左边所有数字的乘积,也就是左边位置(i-1)的answer*nums。然后再从右往左遍历一遍answer使其存放最后结果,因为其第i个位置的值已经是左边所有数的乘积,现在我们只需要记录右边所有数字的乘积即可。

class Solution {
    public int[] productExceptSelf(int[] nums) {
        int length = nums.length;
        int[] answer = new int[nums.length];
        answer[0] = 1;
        for (int i = 1; i < nums.length; i++) {
            answer[i] = answer[i - 1] * nums[i - 1];
        }
        int R = 1;
        for (int i = length - 1; i >= 0; i--) {
            answer[i] = answer[i] * R;
            R *= nums[i];
        }
        return answer;
    }
}

134.加油站

链接
思路:我们需要知道如果从第x到第y发现无法继续往下走到第y+1,那么从x到y中间任意再选择一个起点都无法走到y+1,我们只能通过y+1作为新起点,让他继续往下走。注意我们要沿着顺序绕环路行驶一周,这是个环路。我们需要设置一个参数来表示我们走过的距离,如果这个距离能够到达数组的长度,那说明我们走了一圈了。这时候起点就是要返回的答案。

class Solution {
    public int canCompleteCircuit(int[] gas, int[] cost) {
        int length = gas.length;
        int i = 0;
        while(i < length){
            int sumOfCost = 0;
            int sumOfGas = 0;
            int cnt = 0;
            while(cnt < length){
                int j = (i + cnt) % length;
                sumOfGas += gas[j];
                sumOfCost += cost[j];
                if(sumOfGas < sumOfCost){
                    break;
                }
                cnt ++;//注意不能在break前面我们下面代码中有针对cnt的+1操作。
            }
            if(cnt == length) return i;
            else {
                i = i + cnt + 1;
            }
        }
        return -1;
    }
}

13.罗马数字转整数

链接
思路:若存在小的数字在大的数字的左边的情况,根据规则需要减去小的数字。对于这种情况,我们也可以将每个字符视作一个单独的值,若一个数字右侧的数字比它大,则将该数字的符号取反。

class Solution {
    public int romanToInt(String s) {
        HashMap<Character,Integer> map = new HashMap<>();
        map.put('I',1);
        map.put('V',5);
        map.put('X',10);
        map.put('L',50);
        map.put('C',100);
        map.put('D',500);
        map.put('M',1000);
        char[] chars = s.toCharArray();
        int sum = 0;
        for (int i = 0; i < chars.length - 1; i++) {
            if(map.get(chars[i]) >= map.get(chars[i + 1])){
                sum += map.get(chars[i]);
            }else {
                sum -= map.get(chars[i]);
            }
        }
        return sum + map.get(chars[chars.length - 1]);
    }
}

12.整数转罗马数字

链接

class Solution {
    public String intToRoman(int num) {
        int[] values = {1000, 900, 500, 400, 100, 90, 50, 40, 10, 9, 5, 4, 1};
        String[] symbols = {"M", "CM", "D", "CD", "C", "XC", "L", "XL", "X", "IX", "V", "IV", "I"};
        StringBuffer roman = new StringBuffer();
        for (int i = 0; i < symbols.length; i++) {
            while(num>=values[i]){
                num -= values[i];
                roman.append(symbols[i]);
            }
            if(num == 0)
                return roman.toString();
        }
        return roman.toString();
    }
}

58.最后一个单词的长度

链接
思路:s.split(" ");中的参数是空格,而不是空。这样就能完美防止s的开头或者结尾是空格的情况。

class Solution {
    public int lengthOfLastWord(String s) {
        String[] result = s.split(" ");
        return result[result.length - 1].length();
    }
}

14.最长公共前缀

链接
思路,用第一个string的每个char去挨个判断。注意i==strs[j].length()。最后如果for循环中没有返回值,那就返回strs[0]。

class Solution {
    public String longestCommonPrefix(String[] strs) {
        for (int i = 0; i < strs[0].length(); i++) {
            for (int j = 1; j < strs.length; j++) {
                if(i==strs[j].length()||strs[j].charAt(i) != strs[0].charAt(i)){
                    return strs[0].substring(0, i);
                }
            }
        }
        return strs[0];
    }
}

151.反转字符串中的单词

链接

class Solution {
    public String reverseWords(String s) {
        // 除去开头和末尾的空白字符
        s = s.trim();
        // 正则匹配连续的空白字符作为分隔符分割
        List<String> wordList = Arrays.asList(s.split("\\s+"));
        Collections.reverse(wordList);
        return String.join(" ", wordList);
    }
}

作者:力扣官方题解
链接:https://leetcode.cn/problems/reverse-words-in-a-string/solutions/194450/fan-zhuan-zi-fu-chuan-li-de-dan-ci-by-leetcode-sol/
来源:力扣(LeetCode)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

6.Z字形变换

链接

class Solution {
    public String convert(String s, int numRows) {
        if(numRows == 1) return s;
        StringBuffer stringBuffer = new StringBuffer();
        char[] chars = s.toCharArray();
        for (int i = 0; i < s.length(); i += 2*(numRows-1)) {
            stringBuffer.append(chars[i]);
        }
        for (int i = 1; i < numRows - 1; i++) {
            int label = 0;
            for (int j = i; j < s.length(); j += label == 1?((numRows - 1 - i)*2):i*2) {
                stringBuffer.append(chars[j]);
                label = label == 0? 1:0;
            }
        }
        for (int i = numRows - 1; i < s.length(); i += 2*(numRows-1)) {
            stringBuffer.append(chars[i]);
        }
        return  stringBuffer.toString();

    }
}

28.找出字符串中第一个匹配项的下标

链接

class Solution {
    public int strStr(String haystack, String needle) {
        int start = -1;
        int end = -1;
        start = haystack.indexOf(needle);//字串首次出现位置
        end = haystack.lastIndexOf(needle);//字串最后出现位置
        if(end!=-1){
            return start;
        }
        return -1;
    }
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值