217. 存在重复元素

250 阅读5分钟

Problem: 217. 存在重复元素

思路

要查找是否有重复的元素,有四种方法:

  • 暴力法,通过一一对比,查找是否有重复元素。

  • 排序法,如果元素是有序的,只需要一轮循环,将后一个元素与前一个元素对比即可。

  • 哈希表,遍历列表,在哈希表里面查找是否有当前元素,如果有,说明重复,反之,没有重复元素。

  • 使用 Set 的长度,利用 Set 中的元素是唯一的特点,将 Set 的元素与数组中的长度比较,如果长度一样,说明没有重复元素,如果数组的元素比 Set 中的元素多,说明有重复的元素。

方法一: 暴力法

步骤

  1. 使用两层嵌套循环遍历数组中的所有元素。
  2. 对于每个元素,检查它是否与数组中的其他元素重复。
  3. 如果找到重复元素,则返回 true
  4. 如果遍历完整个数组都没有找到重复元素,则返回 false

流程图

代码实现

/**
 * @param {number[]} nums
 * @return {boolean}
 */
var containsDuplicate = function(nums) {
    for (let i = 0; i < nums.length; i++) {
        for (let j = i + 1; j < nums.length; j++) {
            if (nums[i] === nums[j]) {
                return true;
            }
        }
    }
    return false;
};
function containsDuplicate(nums: number[]): boolean {
  for (let i = 0; i < nums.length; i++) {
    for (let j = i + 1; j < nums.length; j++) {
      if (nums[i] === nums[j]) {
        return true;
      }
    }
  }
  return false;
}
class Solution {
    public boolean containsDuplicate(int[] nums) {
        for (int i = 0; i < nums.length; i++) {
            for (int j = i + 1; j < nums.length; j++) {
                if (nums[i] == nums[j]) {
                    return true;
                }
            }
        }
        return false;
    }
}
bool containsDuplicate(int* nums, int numsSize) {
    for(int i = 0; i < numsSize; i++) {
        for(int j = i + 1; j < numsSize; j++) {
            if (nums[i] == nums[j]) {
                return true;
            }
        }
    }
    return false;
}

复杂度

  • 时间复杂度:O(n^2),其中 n 是数组的长度。使用两层嵌套循环遍历数组中的所有元素。
  • 空间复杂度:O(1),因为没有使用额外的数据结构。

方法二:排序法

步骤

  1. 对数组进行排序,可以使用快速排序或其他排序算法。
  2. 遍历排序后的数组,检查相邻元素是否相等。
  • 如果存在相等的相邻元素,返回 true,表示存在重复元素。
  • 如果所有相邻元素都不相等,返回 false,表示没有重复元素。

流程图

代码实现

function containsDuplicate(nums) {
  nums.sort();
  for (let i = 1; i < nums.length; i++) {
    if (nums[i] === nums[i - 1]) {
      return true;
    }
  }
  return false;
}
function containsDuplicate(nums: number[]): boolean {
  nums.sort();
  for (let i = 1; i < nums.length; i++) {
    if (nums[i] === nums[i - 1]) {
      return true;
    }
  }
  return false;
}
public class Solution {
    public boolean containsDuplicate(int[] nums) {
        Arrays.sort(nums);
        for (int i = 1; i < nums.length; i++) {
            if (nums[i] == nums[i - 1]) {
                return true;
            }
        }
        return false;
    }
}
int cmp(const void* _a, const void* _b) {
    int a = *(int*)_a, b = *(int*)_b; // 将 void 指针转换为 int 指针并引用
    return a - b; // 返回两个整数的差值,用于排序
}

bool containsDuplicate(int* nums, int numsSize) {
    qsort(nums, numsSize, sizeof(int), cmp); // qsort 是 C 语言标准库中用于排序数组的函数。它实现了快速排序算法,能够对任意类型的数组进行排序。
    for(int i = 0; i < numsSize - 1; i++) {
        if (nums[i] == nums[i + 1]) {
            return true;
        }
        
    }
    return false;
}

复杂度分析

  • 时间复杂度:O(NlogN),使用了排序, N 为数组的长度。
  • 空间复杂度:O(logN)

方法三:哈希表

步骤

  1. 创建一个空的哈希表 visited
  2. 遍历数组中的每个元素:
  • 如果当前元素已经存在于 visited 哈希表中,返回 true,表示存在重复元素。
  • 如果当前元素不存在于 visited 哈希表中,则将当前元素添加到 visited 哈希表中。
  1. 遍历完数组后,返回 false,表示没有重复元素。

流程图

代码实现

function containsDuplicate(nums) {
  const visited = new Set();
  for (let num of nums) {
    if (visited.has(num)) {
      return true;
    }
    visited.add(num);
  }
  return false;
}
function containsDuplicate(nums: number[]): boolean {
  const visited: Set<number> = new Set();
  for (let num of nums) {
    if (visited.has(num)) {
      return true;
    }
    visited.add(num);
  }
  return false;
}
public class Solution {
    public boolean containsDuplicate(int[] nums) {
        Set<Integer> visited = new HashSet<>();
        for (int num : nums) {
            if (!visited.add(num)) {
                return true;
            }
        }
        return false;
    }
}

// `visited.add()` 方法用于将元素添加到集合中,它会返回一个 boolean 值来表示添加元素是否成功:

// 如果元素已经存在于集合中,添加失败,返回 `false`。
// 如果元素不存在于集合中,添加成功,返回 `true`。
struct hashTable {
    int key; // 存储键值
    UT_hash_handle hh; // uthash 提供的句柄,用于哈希表管理
};

bool containsDuplicate(int* nums, int numsSize) {
    struct hashTable* set = NULL; // 初始化哈希表
    for (int i = 0; i < numsSize; i++) {
        struct hashTable* tmp;
        HASH_FIND_INT(set, nums + i, tmp); // 查找当前数字是否存在于哈希表中
        if (tmp == NULL) {
            tmp = malloc(sizeof(struct hashTable)); 
            tmp->key = nums[i];
            HASH_ADD_INT(set, key, tmp); // 将新元素添加到哈希表中
        } else {
            return true;
        }
    }
    return false;
}

复杂度分析

  • 时间复杂度:O(N)N 为数组的长度。
  • 空间复杂度:O(N)

方法四:Set 长度

步骤

  • 创建一个新的 Set 对象,Set 是一种数据结构,它只存储唯一的值。
  • nums 数组传递给 Set,这会自动去除数组中的重复元素。
  • 检查大小
    • Setsize 属性返回集合中元素的数量。
    • 比较 Set 的大小与原数组的长度。
    • 如果集合的大小小于数组的长度,说明数组中存在重复元素,返回 true;否则返回 false

流程图

复杂度分析

  • 时间复杂度:O(N)N 为数组的长度。
  • 空间复杂度:O(N)

代码实现

var containsDuplicate = function(nums) {
    return new Set(nums).size < nums.length;
};
class Solution {
    public boolean containsDuplicate(int[] nums) {
        return Arrays.stream(nums).distinct().count() < nums.length;
    }
}

敲黑板

使用暴力法,本题会超出时间限制。复杂度方面,排序法时间复杂度是 O(NlogN),空间复杂度是 O(1),而使用哈希表的方法,时间复杂度是 O(1),空间复杂度是 O(N),因此,两者比较,排序法的空间复杂度更优,而哈希表的时间复杂度更优。