【Java完整版 面试必备】Leetcode Top100题目和答案--技巧篇

目录

以下摘自leetcode Top100精选题目--技巧篇

只出现一次的数字 

多数元素

颜色分类

下一个排列

寻找重复数


以下摘自leetcode Top100精选题目--技巧篇

只出现一次的数字 

给你一个 非空 整数数组 nums ,除了某个元素只出现一次以外,其余每个元素均出现两次。找出那个只出现了一次的元素。

你必须设计并实现线性时间复杂度的算法来解决此问题,且该算法只使用常量额外空间。

示例:

输入:nums = [2,2,1]
输出:1

Solution:

      可以使用异或运算来解决。异或运算有一个性质,就是任何数和自身异或的结果都是0,任何数和0异或的结果都是自身。可以对数组中的所有元素进行异或运算,最终的结果就是那个只出现一次的元素。

class Solution {
    public int singleNumber(int[] nums) {
        // 初始化结果为0
        int result = 0;
        // 遍历数组中的所有元素
        for (int num : nums) {
            // 将当前元素和result进行异或运算,然后将结果赋值给result
            result ^= num;
        }
        // 返回result,这就是那个只出现一次的元素
        return result;
    }
}

多数元素

给定一个大小为 n 的数组 nums ,返回其中的多数元素。多数元素是指在数组中出现次数 大于 ⌊ n/2 ⌋ 的元素。

你可以假设数组是非空的,并且给定的数组总是存在多数元素。

示例:

输入:nums = [3,2,3]
输出:3

输入:nums = [2,2,1,1,1,2,2]
输出:2

Solution:

使用摩尔投票法来解决。摩尔投票法是一种在O(n)时间复杂度和O(1)空间复杂度内找出候选的众数的算法。

class Solution {
    public int majorityElement(int[] nums) {
        // 初始化候选的众数和计数器
        int candidate = nums[0];
        int count = 1;

        // 遍历数组中的所有元素
        for (int i = 1; i < nums.length; i++) {
            // 如果计数器为0,那么将当前元素设为候选的众数,计数器设为1
            if (count == 0) {
                candidate = nums[i];
                count = 1;
            }
            // 如果当前元素等于候选的众数,那么计数器加1
            else if (nums[i] == candidate) {
                count++;
            }
            // 如果当前元素不等于候选的众数,那么计数器减1
            else {
                count--;
            }
        }

        // 返回候选的众数
        return candidate;
    }
}

颜色分类

给定一个包含红色、白色和蓝色、共 n 个元素的数组 nums ,原地对它们进行排序,使得相同颜色的元素相邻,并按照红色、白色、蓝色顺序排列。

我们使用整数 0、 1 和 2 分别表示红色、白色和蓝色。

必须在不使用库内置的 sort 函数的情况下解决这个问题。

示例:

输入:nums = [2,0,2,1,1,0]
输出:[0,0,1,1,2,2]

Solution:

可以使用三向切分的快速排序来解决。使用三个指针,low,mid和high,来将数组分为三个部分,nums[0..low-1]是0,nums[low..mid-1]是1,nums[mid..high]是未知的,nums[high+1..n-1]是2。

class Solution {
    public void sortColors(int[] nums) {
        // 定义三个指针,low,mid和high
        int low = 0, mid = 0, high = nums.length - 1;

        // 当mid小于等于high时,继续循环
        while (mid <= high) {
            // 根据nums[mid]的值进行不同的操作
            switch (nums[mid]) {
                case 0:
                    // 如果nums[mid]等于0,交换nums[low]和nums[mid]的值,然后将low和mid都向后移动一位
                    swap(nums, low++, mid++);
                    break;
                case 1:
                    // 如果nums[mid]等于1,只是将mid向后移动一位
                    mid++;
                    break;
                case 2:
                    // 如果nums[mid]等于2,交换nums[mid]和nums[high]的值,然后将high向前移动一位
                    swap(nums, mid, high--);
                    break;
            }
        }
    }

    // 交换数组中两个位置的元素
    private void swap(int[] nums, int i, int j) {
        int temp = nums[i];
        nums[i] = nums[j];
        nums[j] = temp;
    }
}

下一个排列

整数数组的一个 排列  就是将其所有成员以序列或线性顺序排列。

  • 例如,arr = [1,2,3] ,以下这些都可以视作 arr 的排列:[1,2,3][1,3,2][3,1,2][2,3,1] 。

整数数组的 下一个排列 是指其整数的下一个字典序更大的排列。更正式地,如果数组的所有排列根据其字典顺序从小到大排列在一个容器中,那么数组的 下一个排列 就是在这个有序容器中排在它后面的那个排列。如果不存在下一个更大的排列,那么这个数组必须重排为字典序最小的排列(即,其元素按升序排列)。

  • 例如,arr = [1,2,3] 的下一个排列是 [1,3,2] 。
  • 类似地,arr = [2,3,1] 的下一个排列是 [3,1,2] 。
  • 而 arr = [3,2,1] 的下一个排列是 [1,2,3] ,因为 [3,2,1] 不存在一个字典序更大的排列。

给你一个整数数组 nums ,找出 nums 的下一个排列。

必须 原地 修改,只允许使用额外常数空间。

示例:

输入:nums = [1,2,3]
输出:[1,3,2]

Solution:

class Solution {
    public void nextPermutation(int[] nums) {
        // 第一步:从右向左找到第一个比其下一个元素小的元素,我们称之为'支点'
        int i = nums.length - 2;
        while (i >= 0 && nums[i] >= nums[i + 1]) {
            i--;
        }

        // 如果没有找到'支点',说明数组已经是降序排列,我们需要反转整个数组
        if (i == -1) {
            reverse(nums, 0, nums.length - 1);
        } else {
            // 第二步:从右向左找到第一个比'支点'大的元素,我们称之为'后继'
            int j = nums.length - 1;
            while (nums[j] <= nums[i]) {
                j--;
            }

            // 第三步:交换'支点'和'后继'
            swap(nums, i, j);

            // 第四步:反转'支点'之后的所有元素
            reverse(nums, i + 1, nums.length - 1);
        }
    }

    // 反转数组的某一部分
    private void reverse(int[] nums, int start, int end) {
        while (start < end) {
            swap(nums, start, end);
            start++;
            end--;
        }
    }

    // 交换数组中的两个元素
    private void swap(int[] nums, int i, int j) {
        int temp = nums[i];
        nums[i] = nums[j];
        nums[j] = temp;
    }
}

  1. 从右到左遍历数组,找到第一个比其下一个元素小的元素,称这个元素为'pivot'。如果找不到这样的元素,那么数组已经是降序排列,需要将其反转为升序排列。

  2. 从右到左遍历数组,找到第一个比'pivot'大的元素,称这个元素为'successor'。

  3. 交换'pivot'和'successor'。

  4. 将'pivot'之后的所有元素反转,以确保得到的是字典序最小的排列。

寻找重复数

给定一个包含 n + 1 个整数的数组 nums ,其数字都在 [1, n] 范围内(包括 1 和 n),可知至少存在一个重复的整数。

假设 nums 只有 一个重复的整数 ,返回 这个重复的数 。

你设计的解决方案必须 不修改 数组 nums 且只用常量级 O(1) 的额外空间。

示例:

输入:nums = [1,3,4,2,2]
输出:2

Solution:

     可以使用Floyd's Tortoise and Hare (乌龟和兔子) 算法来解决,也称为Floyd's Cycle-Finding Algorithm(Floyd的循环查找算法)。这个算法通常用来检测链表中的循环,但是在这个问题中,可以看作是一个有向图,其中每个节点都有一个指向值相同的节点的箭头。例如,如果数组为[2, 3, 1],则它表示从1到2,从2到3,从3到1的箭头。

class Solution {
    public int findDuplicate(int[] nums) {
        // 第一步:使用Floyd's Tortoise and Hare (乌龟和兔子) 算法找到两个跑者的交点
        // 初始化两只跑者(乌龟和兔子)在起始位置
        int tortoise = nums[0];
        int hare = nums[0];

        // 乌龟每次移动一步,兔子每次移动两步
        // 由于数组中存在重复的数,所以一定会形成一个环,乌龟和兔子最终会在环中的某个点相遇
        do {
            tortoise = nums[tortoise];
            hare = nums[nums[hare]];
        } while (tortoise != hare);

        // 第二步:找到环的入口,也就是重复的数
        // 让乌龟回到起点,兔子留在交点
        tortoise = nums[0];

        // 两者都以相同的速度前进,当他们再次相遇时,就是环的入口,也就是重复的数
        while (tortoise != hare) {
            tortoise = nums[tortoise];
            hare = nums[hare];
        }

        // 返回重复的数
        return hare;
    }
}

      先使用Floyd's Tortoise and Hare (乌龟和兔子) 算法找到两个跑者的交点。初始化两只跑者(乌龟和兔子)在起始位置,乌龟每次移动一步,兔子每次移动两步。由于数组中存在重复的数,所以一定会形成一个环,乌龟和兔子最终会在环中的某个点相遇。

      找到环的入口,也就是重复的数。让乌龟回到起点,兔子留在交点,然后两者都以相同的速度前进,当再次相遇时,就是环的入口,也就是重复的数。

     这个算法利用数学中的一个有趣的现象:在一个有向图中,如果有两条路径从同一个起点出发,到达同一个终点,那么这两条路径的起点到交点的距离,一定等于交点到终点的距离。在这个问题中,起点是数组的第一个元素,终点是环中的某个点,两条路径是乌龟和兔子走过的路径。

  • 7
    点赞
  • 13
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

何遇mirror

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

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

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

打赏作者

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

抵扣说明:

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

余额充值