力扣hot100——技巧

96.只出现一次的数字

题目描述

136. 只出现一次的数字

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

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

示例 1 :

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

**输出:**1

示例 2 :

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

**输出:**4

示例 3 :

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

**输出:**1

提示:

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

思路:异或

异或操作有以下性质:

  1. 任何数和 0 异或,结果仍然是它本身a ^ 0 = a
  2. 任何数和自身异或,结果是 0a ^ a = 0
  3. 异或操作满足交换律和结合律a ^ b ^ a = (a ^ a) ^ b = 0 ^ b = b

基于以上性质,我们可以将所有数字进行异或操作,最终的结果就是只出现一次的数字。

代码

int singleNumber(int* nums, int numsSize) {
    int ans=0;
    for(int i=0;i<numsSize;i++){
        ans^=nums[i];
    }
    return ans;
}

97.多数元素

题目描述

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 * 10^4
  • -10^9 <= nums[i] <= 10^9

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

思路: Boyer-Moore 投票算法

  • 初始化候选元素 candidate 和计数器 count
  • 遍历数组:
    • 如果 count == 0,则将当前元素设为候选元素。
    • 如果当前元素等于候选元素,则 count++,否则 count--
  • 最终候选元素就是多数元素。

有疑问的可以举例分析一遍,这里就不给证明了。

代码

#include <stdio.h>

int majorityElement(int* nums, int numsSize) {
    int candidate = nums[0]; // 初始化候选元素
    int count = 1; // 初始化计数器

    // 遍历数组
    for (int i = 1; i < numsSize; i++) {
        if (count == 0) {
            // 如果计数器为 0,更新候选元素
            candidate = nums[i];
            count = 1;
        } else if (nums[i] == candidate) {
            // 如果当前元素等于候选元素,计数器加 1
            count++;
        } else {
            // 否则计数器减 1
            count--;
        }
    }

    // 返回候选元素
    return candidate;
}

98.颜色分类

题目描述

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]

提示:

  • n == nums.length
  • 1 <= n <= 300
  • nums[i]012

进阶:

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

方法 1:计数排序

  • 统计数组中 012 的个数。
  • 根据统计结果,重新填充数组。

代码1

void sortColors(int* nums, int numsSize) {
    int hash[3];
    memset(hash,0,sizeof(int));
    for(int i=0;i<numsSize;i++){
        hash[nums[i]]++;
    }
    int index=0;
    for(int i=0;i<3;i++){
        int cnt=hash[i];
        while(cnt--){
            nums[index++]=i;
        }
    }

}

方法 2:双指针(荷兰国旗问题)

  • 使用三个指针:leftrightcurr
    • left:指向已排序的 0 的末尾。
    • right:指向已排序的 2 的开头。
    • curr:当前遍历的元素。
  • 遍历数组:
    • 如果 nums[curr] == 0,将其与 nums[left] 交换,left++curr++
    • 如果 nums[curr] == 2,将其与 nums[right] 交换,right--
    • 如果 nums[curr] == 1,直接 curr++

代码

#include <stdio.h>

void swap(int* a, int* b) {
    int temp = *a;
    *a = *b;
    *b = temp;
}

void sortColors(int* nums, int numsSize) {
    int left = 0;        // 指向已排序的 0 的末尾
    int right = numsSize - 1; // 指向已排序的 2 的开头
    int curr = 0;        // 当前遍历的元素

    while (curr <= right) {
        if (nums[curr] == 0) {
            // 如果当前元素是 0,将其与 nums[left] 交换
            swap(&nums[curr], &nums[left]);
            left++;
            curr++;
        } else if (nums[curr] == 2) {
            // 如果当前元素是 2,将其与 nums[right] 交换
            swap(&nums[curr], &nums[right]);
            right--;
        } else {
            // 如果当前元素是 1,直接跳过
            curr++;
        }
    }
}

99.下一个排列

题目描述

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

思路:双指针

  1. 从后向前查找第一个降序的位置
    • 找到第一个满足 nums[i] < nums[i+1] 的位置 i
    • 如果找不到这样的位置,说明整个数组是降序排列的,直接反转数组即可。
  2. 从后向前查找第一个大于 nums[i] 的位置
    • 找到第一个满足 nums[j] > nums[i] 的位置 j
  3. 交换 nums[i]nums[j]
    • 交换这两个元素。
  4. 反转 i+1 到末尾的部分
    • i+1 到末尾的部分反转,使其升序排列。

代码

#include <stdio.h>

// 交换两个元素
void swap(int* a, int* b) {
    int temp = *a;
    *a = *b;
    *b = temp;
}

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

void nextPermutation(int* nums, int numsSize) {
    int i = numsSize - 2;

    // 从后向前查找第一个降序的位置
    while (i >= 0 && nums[i] >= nums[i + 1]) {
        i--;
    }

    if (i >= 0) {
        int j = numsSize - 1;
        // 从后向前查找第一个大于 nums[i] 的位置
        while (j >= 0 && nums[j] <= nums[i]) {
            j--;
        }
        // 交换 nums[i] 和 nums[j]
        swap(&nums[i], &nums[j]);
    }

    // 反转 i+1 到末尾的部分
    reverse(nums, i + 1, numsSize - 1);
}

100.寻找重复数

题目描述

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

提示:

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

进阶:

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

思路:哈希

用hash表记录某个数是否出现过,先将hash表全部初始化为false,表示全部都没有出现过,接着遍历数组:

  • 对于每个数字 nums[i]
    • 如果 hash[nums[i]]true,说明该数字已经出现过,直接返回 nums[i]
    • 否则,将 hash[nums[i]] 设置为 true

代码

#include <stdio.h>
#include <stdbool.h>
#include <string.h>

int findDuplicate(int* nums, int numsSize) {
    bool hash[numsSize]; // 定义哈希表
    memset(hash, false, sizeof(hash)); // 初始化哈希表为 false

    for (int i = 0; i < numsSize; i++) {
        if (hash[nums[i]]) {
            // 如果当前数字已经出现过,返回该数字
            return nums[i];
        } else {
            // 否则,标记该数字为已出现
            hash[nums[i]] = true;
        }
    }

    return 0; // 如果没有找到重复数字,返回 0
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值