刷题第一题题解

115 阅读3分钟

第一题 找单独的数 刷题心得分享

问题描述

在一个班级中,每位同学都拿到了一张卡片,上面有一个整数。有趣的是,除了一个数字之外,所有的数字都恰好出现了两次。现在需要你帮助班长小C快速找到那个拿了独特数字卡片的同学手上的数字是什么。

要求:

  1. 设计一个算法,使其时间复杂度为 O(n),其中 n 是班级的人数。
  2. 尽量减少额外空间的使用,以体现你的算法优化能力。

解题思路

该题目给了一个数组,其中有一个数字只出现了一次,其它数字均恰好出现了两次,要求找到那个只出现了一次的数字。

单独看这道题目其实并不难,但是它还有一个要求,就是时间复杂度为O(n),因此无法使用排序比较相邻元素的方法。在这里我想到了哈希表,因为从哈希表中查找一个元素只需要O(1)的时间。所以我首先创建了一个哈希表mp。接着,使用一个for循环遍历整个数组,当发现该元素未曾出现在哈希表中时,将该元素添加到哈希表中;如果该元素出现在哈希表中,则从哈希表中删除掉该元素。

最终哈希表中只会剩余一个元素,而这个元素对应的键值就是我们所需要的最终答案。

代码

#include <iostream>
#include <unordered_map>
#include <vector>

int solution(std::vector<int> cards) {
  // Edit your code here
  std::unordered_map<int, bool> mp;
  for (int i = 0; i < cards.size(); i++) {
    int key = cards[i];
    if (mp.count(key) == 0) {
      // 原来不存在,加入哈希表
      mp[key] = true;
    } else {
      // 原来存在,从哈希表中删除元素
      mp.erase(key);
    }
  }
  auto it = mp.begin();
  return it->first;
}

int main() {
  // Add your test cases here

  std::cout << (solution({1, 1, 2, 2, 3, 3, 4, 5, 5}) == 4) << std::endl;
  std::cout << (solution({0, 1, 0, 1, 2}) == 2) << std::endl;

  return 0;
}

复杂度分析

  • 时间复杂度:O(n) 哈希表中查找一个元素的函数mp.count()的时间复杂度为O(1),删除一个元素的时间复杂度为O(1),一个for循环,遍历整个数组,时间复杂度为O(n)。
  • 空间复杂度:使用了哈希表存储元素,最多存储n/2个元素,一共n个元素,除要找的元素以外的每个元素出现两次,哈希表不可以存储键值重复的元素,因此空间复杂度为O(n)。

改进

由于每个元素最多出现两次,因此可以进一步优化空间复杂度,可以使用位运算中的异或操作进一步优化空间复杂度。

异或操作的知识:0^x=x, x^x=0, 0和任何一个数异或得到的还是那个数,两个相同的数异或得到的结果为0。且异或运算可以多次计算,满足交换律、结合律等多个运算法则。a^b^c^b^a=c。

对于这个题目,由于每个元素最多出现两次,且仅有一个元素只出现一次。因此,可以使用异或运算。另所求的结果result最开始为0,让其依次和数组中每个元素异或,相同的元素最后会抵消掉,最后剩下的就是要求的那个元素的值。

对于该方法时间复杂度为O(n),空间复杂度为O(1)。

#include <iostream>
#include <vector>

int solution(std::vector<int> cards) {
  // Edit your code here
  int result = 0;
  for (int card : cards) {
      result ^= card;
  }
  return result;
}

int main() {
  // Add your test cases here

  std::cout << (solution({1, 1, 2, 2, 3, 3, 4, 5, 5}) == 4) << std::endl;
  std::cout << (solution({0, 1, 0, 1, 2}) == 2) << std::endl;

  return 0;
}