leetcode面试算法题

1.数组/字符串

合并两个有序数组

class Solution {
    public void merge(int[] nums1, int m, int[] nums2, int n) {
        int p1 = 0;//遍历数组1
        int p2 = 0;//遍历数组2

        int[] nums3 = new int[m + n];
        int p = 0;
        while (p1 < m && p2 < n) {//当数组1、2都没遍历完
            nums3[p++] = nums1[p1] <= nums2[p2] ? nums1[p1++] : nums2[p2++];
        }

        while (p1 < m) {
            nums3[p++] = nums1[p1++];
        }

        while (p2 < n) {
            nums3[p++] = nums2[p2++];
        }

        for (int i = 0; i < m + n; i++) {
            nums1[i] = nums3[i];
        }
    }
}

移除元素

class Solution {
     public int removeElement(int[] nums, int val) {

        if(nums.length<1) return 0;

        int l = 0;
        int r = nums.length - 1;
        int res = 0;

        while (l < r) {
            if (nums[l] == val) {
                //换到最末尾的元素一定是和val相同,但换过来的元素还没比较,因此不能直接l--
                swap(l, r--, nums);
            } else {
                res++;//数组有效长度+1
                l++;
            }
        }

         if (nums[l] != val) {
            res++;
        }

        return res;
    }
     public void swap(int a, int b, int[] nums) {
        int temp = nums[a];
        nums[a] = nums[b];
        nums[b] = temp;
    }
}

最长回文串

最长回文串长度 = 出现偶数次 + 出现奇数次 - 1(变成偶数次) + 1(如果存在奇数次出现次数,就把其中一个放中间)

class Solution {
    public int longestPalindrome(String s) {
        int res = 0;

        HashMap<Character, Integer> map = new HashMap<>();
        for (int i = 0; i < s.length(); i++) {//统计每个字符出现的次数
            char c = s.charAt(i);
            if (!map.containsKey(c)) {
                map.put(c, 1);
            } else {
                map.put(c, map.get(c) + 1);
            }
        }

        boolean flag = false;//用于判断是否有奇数次字符出现
        for (Integer value : map.values()) {
            if ((value & 1) == 0) {//说明该数是偶数
                res += value;//出现偶数次的字符一定可以用来构建回文字符串
            } else {//奇数次就取奇数次-1位偶数次
                flag = true;
                res += value - 1;
            }
        }

        if (flag) {//选一个奇数放中间,长度+1
            res += 1;
        }
        return res;
    }
}

找出与数组相加的整数II

题解参考:

class Solution {
     public int minimumAddedInteger(int[] nums1, int[] nums2) {

        //先给两个数组排序
        Arrays.sort(nums1);
        Arrays.sort(nums2);

        //由于只能在nums1中删除两个,因此排序后的nums1前三(最小的三个元素)中必定有一个和nums2中最小的一个匹配
        // 倒着枚举是因为 nums1[i] 越大答案越小(因为排序了),第一个满足的就是答案
        for (int i = 2; i > 0; i--) {
            //nums1每个元素的偏移量
            int offset = nums2[0] - nums1[i];
            //给nums1中的每个元素加上这个偏移量,判断nums2是不是nums1的一个子序列
            // (因为nums1要删除两个元素,所以nums1更长)
            int a = 0;//遍历nums2
            //k用来遍历nums1,从i开始遍历就行,i之前的元素是不用的
            for (int k = i; k < nums1.length; k++) {
                if (nums1[k] + offset == nums2[a]//nums1中当前元素+偏移量和nums2中当前指向的元素相同
                        &&
                        ++a == nums2.length) {//遍历nums2的指针a就后移
                    //如果指针a把nums2全部遍历完了,说明当前偏移量满足条件,直接返回offset
                    return offset;
                }
            }
        }
        //由于题目说明了一定存在解
        //因此当检查了nums1[1]和nums1[2]后,nums1[0]就不用检查了,直接返回就行
        return nums2[0] - nums1[0];
    }
}

合并区间

        这里引用leetcode上灵神的解析(本人只感觉到了要排序,至于为什么要以左区间排序,说不太清除...)

class Solution {
     public int[][] merge(int[][] intervals) {
        Arrays.sort(intervals, (p, q) -> p[0] - q[0]); // 按照左端点从小到大排序
        List<int[]> ans = new ArrayList<>();
        for (int[] p : intervals) {
            int m = ans.size();
            //如果list集合中有数组,并且集合中最后一个数组的右区间比当前数组的左区间大
            if (m > 0 && p[0] <= ans.get(m - 1)[1]) { // 可以合并
                //更新集合中最后一个数组元素的范围(右区间扩大)
                ans.get(m - 1)[1] = Math.max(ans.get(m - 1)[1], p[1]);
            } else { // 不相交,无法合并
                ans.add(p); // 直接把当前数组加入新的合并区间
                //注意:由于数组intervals已经根据每个数组左区间进行了排序,
                //因此加入list集合的顺序也是根据左区间从小到大的顺序
            }
        }
        return ans.toArray(new int[ans.size()][]);
    }
}

加一

class Solution {
    public int[] plusOne(int[] digits) {
        for (int i = digits.length - 1; i >= 0; i--) {
            digits[i]++;
            digits[i] = digits[i] % 10;
            //说明不用进位,如果要进位,就会往前遍历,把digits++
            if (digits[i] % 10 != 0) {
                return digits;//直接返回就行
            }
        }
        //如果遍历完了digits还有进位,就表示答案是1000....
        //最前面一个1,后面跟着digits.length个0
        digits = new int[digits.length + 1];
        digits[0] = 1;//其他默认为0
        return digits;
    }
}

2.哈希表

有效的字母异位词

class Solution {
       public boolean isAnagram(String s, String t) {

        if (s.length() != t.length()) return false;

        int[] num = new int[26];

        for (int i = 0; i < s.length();i++){
            num[s.charAt(i)-'a']++;
            num[t.charAt(i)-'a']--;
        }

        for (int i : num) {
            if (i!=0){
                return false;
            }
        }

        return true;
    }
}

快乐数

        解释:由于我们知道int的最大值为2147483647,而在这个数字之内的最大的next(next 为各位数字的平方和)。是1999_999_999 (1999_999_999 = 1*1 + 9*9*9(一共有9位数位9))。

        根据以上表格,我们可以发现,即使数字很大,next 范围也会跌下来,很快陷入 [1,243] 这个范围内,这是因为即使是1999_999_999,在经过一次运算以后,也会变成730,而730在[100,999]这个范围之内,说明之后进行快乐数运算,一定会在[1,243]这个范围之内!

        所以在有限次循环之内,就会出现相同的值。接下来面临循环或者达到 1 退出。这是典型的环形链表问题,我们可以通过快慢指针来解决。

class Solution {
    public boolean isHappy(int n) {
        int m = getNext(getNext(n));//快指针,每次进行两次运算
        while (m != n) {
            m = getNext(getNext(m));
            n = getNext(n);
        }
        if (m == 1) {//比较最后相遇的值
                return true;
        }
        return false;
    }

    public int getNext(int n) {//获取当前n的下一个数
        int sum = 0;
        while (n > 0) {
            int m = n % 10;
            n = n / 10;
            sum += m * m;
        }
        return sum;
    }
}

3.栈和队列

数据流中的中位数

思路

1.准备大小两个堆。

2.开始时,第一个数放大根堆。

3.后面放的每一个数字,都和大根堆堆顶元素比较。

        若:大于等于堆顶元素,放入小根堆;小于堆顶元素,放入大根堆。

       每放完一次数,进行一次判断。如果此时大小根堆的长度差绝对值等于2。从元素更多的那个堆的堆顶,弹出一个元素到另一个堆的堆顶。

4.简单来说,就是保证两个堆,各占n/2个元素!


class MedianFinder {

    //小根堆,堆顶元素最小
    PriorityQueue<Integer> minHeap = new PriorityQueue<Integer>(
            new Comparator<Integer>() {
                @Override
                public int compare(Integer o1, Integer o2) {
                    return o1 - o2;
                }
            }
    );

    //大根堆,堆顶元素最大
    PriorityQueue<Integer> maxHeap = new PriorityQueue<>(
            new Comparator<Integer>() {
                @Override
                public int compare(Integer o1, Integer o2) {
                    return o2 - o1;
                }
            }
    );

    public MedianFinder() {

    }

    public void addNum(int num) {
        //添加元素时,默认先跟大根堆堆顶元素比较

        if (maxHeap.size() == 0) {//如果大根堆没有元素,直接添加
            maxHeap.add(num);
            return;
        }
        if (num <= maxHeap.peek()) {//如果当前元素比大根堆堆顶元素小,添加到大根堆中
            maxHeap.add(num);
        } else {//反之添加到小根堆中
            minHeap.add(num);
        }
        //判断大小根堆中的元素之差如果等于2,就进行调整
        if (Math.abs(maxHeap.size() - minHeap.size()) == 2) {
            if (maxHeap.size() > minHeap.size()) {//大根堆元素多了
                minHeap.add(maxHeap.poll());
            } else {
                maxHeap.add(minHeap.poll());
            }
        }
    }

    public double findMedian() {

        if (maxHeap.size()>minHeap.size()){
            return maxHeap.peek();
        }
        if (maxHeap.size()<minHeap.size()){
            return minHeap.peek();
        }
        
        return (maxHeap.peek() + minHeap.peek()) / 2.00;
    }
}

  • 39
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值