hot100-一刷-17技巧(共5道题)

136. 只出现一次的数字

题目链接

题目描述

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

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

示例 1 :

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

示例 2 :

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

示例 3 :

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

提示:

1 <= nums.length <= 3 * 104
-3 * 104 <= nums[i] <= 3 * 104
除了某个元素只出现一次以外,其余每个元素均出现两次。

代码实现

分析:
异或:

  • 0 ^ x = x
  • x ^ x = 0;

代码:

class Solution {
    public int singleNumber(int[] nums) {
        // 0 ^ x = x    x ^ x = 0;
        int ans = 0;
        for (int x : nums) {
            ans ^= x;
        }

        return ans;
    }
}

169. 多数元素

题目链接

题目描述

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

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

示例 1:

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

示例 2:

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

提示:

n == nums.length
1 <= n <= 5 * 104
-109 <= nums[i] <= 109

进阶:尝试设计时间复杂度为 O(n)、空间复杂度为 O(1) 的算法解决此问题。

代码实现

分析:

代码:

class Solution {
    public int majorityElement(int[] nums) {
        int votes = 0;
        int ans = 0;
        for(int n : nums){
            if(votes == 0){
                ans = n; // 假设当前元素为“众数”
            }
            if(n == ans){
                votes++;
            }else{
                votes--;
            }
        }
        return ans;
    }

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

75. 颜色分类

题目链接

题目描述

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

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

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

示例 1:

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

示例 2:

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

提示:

n == nums.length
1 <= n <= 300
nums[i] 为 0、1 或 2

进阶:

你能想出一个仅使用常数空间的一趟扫描算法吗?

代码实现

分析:

代码:

class Solution {
    public void sortColors(int[] nums) {
        int red = -1;
        int blue = nums.length - 1;
        // i <= blue          i = blue 时还有一个元素还没有看,还需要比较一次
        for(int i = 0; i <= blue; i++){
            // System.out.println(i);
            if(nums[i] == 0){
                red++;
                swap(nums, i, red);
            }else if(nums[i] == 1){
                continue;
            }else{
                swap(nums, i, blue);
                blue--;
                i--; // 回退一步,重新看下第i个是1还是0
            }
            
            // for(int n : nums){
            //     System.out.print(n);
            // }
            // System.out.print("\n");
        }
    }

    private void swap(int[] nums, int index1, int index2){
        int t = nums[index1];
        nums[index1] = nums[index2];
        nums[index2] = t;
        // System.out.println("swap" +": " + t + "<--->" + nums[index1] );
    }
}

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]

提示:

1 <= nums.length <= 100
0 <= nums[i] <= 100

代码实现

分析:

代码:

class Solution {
    public void nextPermutation(int[] nums) {
        int len = nums.length;
        for(int i = len - 2; i >= 0; i--){
            // 从后往前找到第一个升序排列, 目的是为了找到「小数」
            if (nums[i] < nums[i+1]){
                // 找到小数 nums[i], 因为从后往前找的第一个升序排列,
                // 所以此时[i+1, end) 为降序排列, 降序排列中有可能大于这个「小数」
                // 需要在这里从后往前找到第一个大于这个「小数」的「大数」,后面是降序,这个「大数」尽可能小
                for(int j = len - 1; j > i; j--){
                    if(nums[i] < nums[j]){
                        // 「大数」nums[j]    「小数」nums[i]
                        //  交换, 同时让「小数」原来位置i,往后的地方,升序排列, 因为原来是降序, 所以翻转一下就是升序了。
                        // 注意交换之后,i后面也是降序的, 因为找的是第一个比nums[i]大的
                        swap(nums, i, j);
                        reverse(nums, i+1, len-1);
                        return;
                    }
                }
            }
        }
        //如果没有提前退出,那么就是没碰到逆序遍历是降序的,全是升序,说明最大了
        reverse(nums,0,len-1);
    }

    private void swap(int[] nums, int i,int j){
        int temp = nums[i];
        nums[i] = nums[j];
        nums[j] = temp;
    }

    //由于nums[i] < nums[i+1],我们可以知道,到满足这个条件的前提,是必须存在递增,
    //从后面往前,遍历了很多个才碰到第一个符合条件的,
    //那么说明从第一个符合条件开始往后,后面的肯定都是递减,是降序的,所以逆置即可最快重新排序。
    // []
    private void reverse(int[] nums, int left, int right){
        while (left < right){
            swap(nums, left, right);
            left++;
            right--;
        }
    }
}

287. 寻找重复数

题目链接

题目描述

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

假设 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

提示:

1 <= n <= 105
nums.length == n + 1
1 <= nums[i] <= n
nums 中 只有一个整数 出现 两次或多次 ,其余整数均只出现 一次

进阶:

如何证明 nums 中至少存在一个重复的数字?
你可以设计一个线性级时间复杂度 O(n) 的解决方案吗?

代码实现

分析:
image

代码:

class Solution {
    public int findDuplicate(int[] nums) {
        int slow = 0;
        int fast = 0;
        slow = nums[slow];
        fast = nums[nums[fast]];
        while(fast != slow){
            slow = nums[slow];
            fast = nums[nums[fast]];
        }
        int head = 0;
        int point = slow;
        while(head != point){
            head = nums[head];
            point = nums[point];
        }
        return head;
        
    }
}
posted @ 2024-12-31 18:32  chendsome  阅读(4)  评论(0编辑  收藏  举报