1.数组/顺序表
概念:用一段物理地址连续的存储单元依次存储数据元素的线性结构,一般情况下采用数组存储
常见笔试面试题
1.1旋转数组
1.1.1 模拟旋转-时间复杂度O(n),空间复杂度O(n)
cpp
void rotate(vector<int>& nums, int k) {
int n = nums.size();
vector<int> ret(n);
for (int i = 0; i < n; i++) {
ret[(i + k) % n] = nums[i];
}
nums = ret;
}
1.1.2 数组翻转--时间复杂度O(n),空间复杂度O(1)

cpp
void reserve(vector<int>& nums, int begin, int end) {
while (begin < end) {
swap(nums[begin], nums[end]);
begin += 1;
end -= 1;
}
}
void rotate(vector<int>& nums, int k) {
k %= nums.size();
reserve(nums, 0, nums.size() - 1);
reserve(nums, 0, k - 1);
reserve(nums, k, nums.size() - 1);
}
1.1.3 删除排序数组中的重复项
bash
int removeDuplicates(vector<int>& nums) {
int n = nums.size();
int slow = 1, fast = 1;
if (n == 0)
return 0;
while (fast < n) {
if(nums[fast]!=nums[fast-1]){
nums[slow++]=nums[fast];
}
fast++;
}
return slow;
}

1.1.4 数组形式的整形加法
方法一:逐位相加
思路
让我们逐位将数字加在一起。例如计算 123+912,我们从低位到高位依次计算 3+2、2+1 和 1+9。任何时候,若加法的结果大于等于 10,把进位的 1 加入到下一位的计算中,所以最终结果为 1035。
cpp
vector<int> addToArrayForm(vector<int>& num, int k) {
int flag = 0;//进位标志位
int index = num.size() - 1;//索引位数
while (index >= 0) {
int a = k % 10;
k /= 10;
num[index] = num[index] + a + flag;
flag = num[index] / 10;
num[index] = num[index] % 10;
index--;
}
if (flag != 0) {
k += flag;
}
while (k != 0) {
int a = k % 10;
k /= 10;
num.insert(num.begin(), a);
}
return num;
}
1.1.5 乘积最大子数组

cpp
int maxProduct(vector<int>& nums) {
int n = nums.size();
vector<int> f(n);
vector<int> g(n);
f[0] = g[0] = nums[0];
int ans = 0;
for (int i = 1; i < n; i++) {
f[i] = max(g[i - 1] * nums[i], max(nums[i], f[i - 1] * nums[i]));
g[i] = min(g[i - 1] * nums[i], min(nums[i], f[i - 1] * nums[i]));
ans = max(f[i], ans);
}
return ans;
}
1.1.5 众数
1.利用哈希表统计次数与n/2进行比较
cpp
int majorityElement(vector<int>& nums) {
unordered_map<int, int> counts;
int len = nums.size() / 2;
for (int num : nums) {
counts[num]++;
if (counts[num] > len) {
return num;
}
}
return 0;
}
时间复杂度O(n),空间复杂度O(n)
2. 排序
由于众数是大于2/n的,说明排完序后,在nums[n/2]的位置就是众数的值
cpp
int majorityElement(vector<int>& nums) {
sort(nums.begin(), nums.end());
return nums[nums.size() / 2];
}
3. 利用随机值
随机一个值,在通过计数器比较,这个数很有可能就是众数,但有可能一直跳不出循环,但这是最坏的情况
cpp
int majorityElement(vector<int>& nums) {
while (true) {
int ret = nums[rand() % nums.size()];
int count = 0;
for (int num : nums)
if (num == ret)
++count;
if (count > nums.size() / 2)
return ret;
}
return -1;
}
时间复杂度:理论上最坏情况下的时间复杂度为 O(∞),因为如果我们的运气很差,这个算法会一直找不到众数,随机挑选无穷多次,所以最坏时间复杂度是没有上限的。然而,运行的期望时间是线性的。为了更简单地分析,先说服你自己:由于众数占据 超过 数组一半的位置,期望的随机次数会小于众数占据数组恰好一半的情况。因此,我们可以计算随机的期望次数(下标为 prob 为原问题,mod 为众数恰好占据数组一半数目的问题):

计算方法为:当众数恰好占据数组的一半时,第一次随机我们有1/2的概率找到众数,如果没有找到,则第二次随机时,包含上一次我们有1/4的概率找到众数,以此类推。因此期望的次数为i*1/(2^i)的和,可以计算出这个和为 2,说明期望的随机次数是常数。每一次随机后,我们需要 O(n) 的时间判断这个数是否为众数,因此期望的时间复杂度为 O(n)。
1.1.6 分治

cpp
int count_in_range(vector<int>& nums, int target, int left, int right) {
int count = 0;
for (int i = left; i <= right; i++) {
if (nums[i] == target)
count++;
}
return count;
}
int majority_element_rec(vector<int>& nums, int left, int right) {
if (left == right)
return nums[left];
int mid = (right - left) / 2 + left;
int left_majority = majority_element_rec(nums, left, mid);
int right_majority = majority_element_rec(nums, mid + 1, right);
if (count_in_range(nums, left_majority, left, right) >
((right - left + 1) / 2))
return left_majority;
if (count_in_range(nums, right_majority, left, right) >
((right - left + 1) / 2))
return right_majority;
return -1;
}
int majorityElement(vector<int>& nums) {
return majority_element_rec(nums, 0, nums.size() - 1);
}
时间复杂度O(nlogn) 空间复杂度O(logn)
Boyer-Moore 投票算法
如果我们把众数记为 +1,把其他数记为 −1,将它们全部加起来,显然和大于0,从结果本身我们可以看出众数比其他数多。

cpp
int majorityElement(vector<int>& nums) {
int candidate = -1;
int count = 0;
for (int num : nums) {
if (num == candidate)
count++;
else if (--count < 0) {
candidate = num;
count = 1;
}
}
return candidate;
}
1.1.7 存在重复元素
这题一眼看上去就是遍历数组,判断数量是否>1,但结果很不尽人意,**nums的值可能为负数,**这时候我们就像想到了哈希表,哈希表中我们使用set还是map呢,都可以的,map的话则与前文一样,这里我选用set,**set在插入时会返回一个pair<iterator,bool>,**迭代器为哈希表中与这个值相同的迭代器,bool则是是否插入成功(是否相同),我们就可以利用这个条件
-109 <= nums[i] <= 109
cpp
int hashi[10010];
bool containsDuplicate(vector<int>& nums) {
for (int num : nums) {
hashi[num]++;
if (hashi[num] > 1) {
return true;
}
}
return false;
}
判断条件我们可以计算最后的哈希表的长度是否一样,也可以给nums排序,进行便利,也是很好的,解题方法是多样的
1.1.8 探索二维数组
cpp
bool searchMatrix(vector<vector<int>>& matrix, int target) {
int n = matrix.size(), m = matrix[0].size();
for (int i = 0; i < n; i++) {
for (int j = 0; j < m; j++) {
if (i < n && j < m && matrix[i][j] == target)
return true;
if (i < n && j < m && matrix[i][j] > target) {
j = -1;
i++;
}
}
}
return false;
}
不定时更新中......