算法-使用技巧

1. 只出现一次的数字

leetcode题目链接:136. 只出现一次的数字

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

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

示例 1 :

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

**输出:**1

示例 2 :

**输入:**nums = [4,1,2,1,2]

**输出:**4

示例 3 :

**输入:**nums = [1]

**输出:**1

解法:

java 复制代码
class Solution {
    public int singleNumber1(int[] nums) {
        Set<Integer> set = new HashSet<>();

        // 对于每一个num,第一次遍历到的时候加入set,第二次再遇到就从set中删除,最后剩下的就是只出现一次的元素
        for(int num : nums){
            if(set.contains(num)){
                set.remove(num);
            }else{
                set.add(num);
            }
        }

        // 获取set中的唯一元素
        if(set.size() == 1){
            return set.iterator().next();
        }

        return -1;
    }

    /**
        * 找出数组中只出现一次的元素
        * 使用异或运算的性质:相同数异或为0,任何数与0异或为自身
        * @param nums 非空整数数组,其中除一个元素外,其余均出现两次
        * @return 只出现一次的元素
        *
        * 示例1:输入 [2,2,1],计算过程为 2 ^ 2 ^ 1 = 0 ^ 1 = 1。
        * 示例2:输入 [4,1,2,1,2],计算过程为 4 ^ 1 ^ 2 ^ 1 ^ 2 = 4 ^ (1^1) ^ (2^2) = 4 ^ 0 ^ 0 = 4。
        */
    public int singleNumber(int[] nums) {
        int result = 0; // 初始结果为0
        for (int num : nums) {
            result ^= num; // 遍历数组,将所有元素进行异或运算
        }
        return result; // 最终结果即为只出现一次的元素
    }

}

2. 多数元素

leetcode题目链接:169. 多数元素

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

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

示例 1:

复制代码
输入:nums = [3,2,3]
输出:3

示例 2:

复制代码
输入:nums = [2,2,1,1,1,2,2]
输出:2

解法:

java 复制代码
class Solution {
    public int majorityElement1(int[] nums) {

        int n = nums.length;
        Map<Integer, Integer> map = new HashMap();
        // 统计每一个num出现的次数,并返回出现次数大于n/2的元素
        for(int num : nums){
            map.put(num, map.getOrDefault(num, 0)+1);
            if(map.get(num) > n/2){
                return num;
            }
        }

        return -1;
    }

    public int majorityElement(int[] nums) {
        int candidate = nums[0]; // 初始化候选元素为第一个元素
        int count = 1; // 计数器初始化为1
        for (int i = 1; i < nums.length; i++) { // 从第二个元素开始遍历
            if (count == 0) { // 如果计数器为0,更新候选元素为当前元素
                candidate = nums[i];
                count = 1;
            } else if (nums[i] == candidate) { // 当前元素与候选元素相同,计数器加1
                count++;
            } else { // 当前元素与候选元素不同,计数器减1
                count--;
            }
        }
        return candidate; // 遍历结束后,候选元素即为多数元素
    }

}

3. 颜色分类

leetcode题目链接:75. 颜色分类

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

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

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

示例 1:

复制代码
输入:nums = [2,0,2,1,1,0]
输出:[0,0,1,1,2,2]

示例 2:

复制代码
输入:nums = [2,0,1]
输出:[0,1,2]

解法:

java 复制代码
class Solution {
    public void sortColors(int[] nums) {
        int n = nums.length;
        int left = 0;
        
        // 双指针,固定left指针,用right指针按照顺序先后去找0、1、2,找到对应的值就放在left指针位置上,找完0,找1,最后找2
        for(int i = 0; i <= 2; i++){
            int right = left;
            while(right < n){
                if(nums[right] == i){
                    // 交换left和right的元素,left向前走一步
                    int temp = nums[left];
                    nums[left] = nums[right];
                    nums[right] = temp;

                    left++;
                }
                
                right++;
            }
        }
    }
}

4. 下一个排列

leetcode题目链接:31. 下一个排列

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

  • 例如,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 的下一个排列。

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

示例 1:

复制代码
输入:nums = [1,2,3]
输出:[1,3,2]

示例 2:

复制代码
输入:nums = [3,2,1]
输出:[1,2,3]

示例 3:

复制代码
输入:nums = [1,1,5]
输出:[1,5,1]

解法:

java 复制代码
class Solution {
    /**
     * 1. 寻找转折点:从数组末尾开始向前查找第一个满足nums[i] < nums[i+1]的位置i。这个位置是下一个排列需要调整的起点。
     * 2. 交换元素:如果找到了这样的i,再从末尾开始找到第一个比nums[i]大的元素nums[j],并交换它们的位置。这一步确保了后续的排列是更大的。
     * 3. 反转后续部分:将i之后的元素反转,使其变为升序排列,这样可以得到最小的下一个排列。如果整个数组已经是降序排列(即i未找到),则直接反转整个数组得到最小排列。
     */
    public void nextPermutation(int[] nums) {
        // 从后向前找到第一个相邻的升序对
        int i = nums.length - 2;
        while (i >= 0 && nums[i] >= nums[i + 1]) {
            i--;
        }
        
        // 如果找到这样的i,则找到右边比nums[i]大的最小元素进行交换
        if (i >= 0) {
            int j = nums.length - 1;
            while (j >= 0 && nums[j] <= nums[i]) {
                j--;
            }
            swap(nums, i, j);
        }
        // 反转i之后的元素,使其升序排列,得到最小的下一个排列
        reverse(nums, i + 1);
    }
    
    private void reverse(int[] nums, int start) {
        int left = start, right = nums.length - 1;
        while (left < right) {
            swap(nums, left, right);
            left++;
            right--;
        }
    }
    
    private void swap(int[] nums, int i, int j) {
        int temp = nums[i];
        nums[i] = nums[j];
        nums[j] = temp;
    }
}

5. 寻找重复数

leetcode题目链接:287. 寻找重复数

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

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

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

示例 1:

复制代码
输入:nums = [1,3,4,2,2]
输出:2

示例 2:

复制代码
输入:nums = [3,1,3,4,2]
输出:3

示例 3 :

复制代码
输入:nums = [3,3,3,3,3]
输出:3

解法:

java 复制代码
class Solution {

    /**
     * 1. 初始化指针:left 初始化为 1,right 初始化为数组长度减 1(即 n)。
     * 2. 二分查找循环:当 left 小于 right 时,计算中间值 mid。
     * 3. 统计数量:遍历数组,统计小于等于 mid 的元素数量 count。
     * 4. 调整区间:根据 count 和 mid 的比较结果调整左右指针,缩小搜索范围。
     * 5. 返回结果:当 left 和 right 相遇时,left 即为重复数。
     */
    public int findDuplicate(int[] nums) {
        int left = 1;
        int right = nums.length - 1; // n = nums.length - 1
        
        while (left < right) {
            int mid = left + (right - left) / 2;
            int count = 0;
            for (int num : nums) {
                if (num <= mid) {
                    count++;
                }
            }
            // 根据鸽巢原理,如果count > mid,说明重复数在左半部分
            if (count > mid) {
                right = mid;
            } else {
                left = mid + 1;
            }
        }
        return left;
    }

    /**
     * 使用索引来标记已经出现过的数字
     * 但是这个解法改变了原是数组中的值,不满足题目要求
     */
    public int findDuplicate2(int[] nums) {
        int n = nums.length;
        int result = nums[0];

        // 把result即数组中对应的值,找到nums中索引为result的地方设置为0
        while(result != 0){
            // 如果result索引对应的值已经是0,说明result已经出现过了,原始数组中的值没有0
            if(nums[result] == 0){
                return result;
            }

            // 索引result的值设置为0
            int temp = nums[result];
            nums[result] = 0;
            result = temp;
            
        }

        return result;
    }
}
相关推荐
0x7F7F7F7F2 小时前
数学知识——博弈论
数学·算法
爱学习的小仙女!2 小时前
顺序表定义、特点和基本操作(含C代码详细讲解)及时间复杂度
数据结构·算法
芥子沫2 小时前
《人工智能基础》[算法篇5]:SVM算法解析
人工智能·算法·机器学习·支持向量机·svm
BigerBang2 小时前
LoRA 全方位指南:从底层原理到 Qwen-Image-Edit 实战
人工智能·pytorch·深度学习·算法
passxgx2 小时前
11.3 迭代法和预条件子
线性代数·算法·矩阵
X在敲AI代码3 小时前
【无标题】
算法·leetcode·职场和发展
bubiyoushang8883 小时前
NSGA-II 带精英策略的双目标遗传算法
算法
qq_430855883 小时前
线代第二章矩阵第八节逆矩阵、解矩阵方程
线性代数·算法·矩阵
月明长歌3 小时前
【码道初阶】Leetcode136:只出现一次的数字:异或一把梭 vs HashMap 计数(两种解法完整复盘)
java·数据结构·算法·leetcode·哈希算法