数据结构
学习资料:LeetCode 算法笔记
数据结构(Data Structure):带有结构特性的数据元素的集合。
数据结构分类:
- 按逻辑结构分类
- 集合结构 数据元素同属于一个集合,除此之外无其他关系。
- 线性结构 数据元素之间是「一对一」关系。 包括数组、链表、以及衍生的栈、队列、哈希表
- 树形结构 数据元素之间是「一对多」的层次关系。 包括树、二叉树、多叉树、字典树等
- 图形结构 数据元素之间是「多对多」的关系。 包括无向图、有向图、连通图等
- 按物理结构分类
- 顺序存储结构 包括数组
- 链式存储结构 包括链表
算法
### 特性
- 输入 2. 输出 3. 有穷性 4.确定性 5. 可行性
### 目标
- 时间复杂度低 2. 空间复杂度低 3. 正确性 4. 可读性 5. 健壮性
算法复杂度
在问题的输入规模为 n n n的条件下,程序的时间使用情况和空间使用情况。包括**「时间复杂度」和「空间复杂度」,用来分析算法执行效率与输入问题规模 n n n的增长关系。通常采用「渐进符号」**的形式来表示「算法复杂度」。
- 时间复杂度
- 空间复杂度
刷题记录
描述:给定一个整数数组 nums
和一个整数目标值 target
,
要求:请你在该数组中找出 和为目标值 target
的那 两个 整数,并返回它们的数组下标。
你可以假设每种输入只会对应一个答案。但是,数组中同一个元素在答案里不能重复出现。你可以按任意顺序返回答案。
思路 1:枚举算法
- 使用两重循环枚举数组中每一个数 𝑛𝑢𝑚𝑠[𝑖]、𝑛𝑢𝑚𝑠[𝑗],判断所有的 𝑛𝑢𝑚𝑠[𝑖]+𝑛𝑢𝑚𝑠[𝑗] 是否等于 𝑡𝑎𝑟𝑔𝑒𝑡。
- 如果出现 𝑛𝑢𝑚𝑠[𝑖]+𝑛𝑢𝑚𝑠[𝑗]==𝑡𝑎𝑟𝑔𝑒𝑡,则说明数组中存在和为 𝑡𝑎𝑟𝑔𝑒𝑡 的两个整数,将两个整数的下标 𝑖、𝑗 输出即可。
class Solution {
public:
vector<int> twoSum(vector<int>& nums, int target) {
int n = nums.size();
for (int i = 0; i < n; ++i) {
for (int j = i + 1; j < n; ++j) {
if (nums[i] + nums[j] == target) {
return {i, j};
}
}
}
return{};
}
};
思路 1:复杂度分析
- 时间复杂度: O ( n 2 ) O(n^2) O(n2),其中 𝑛 是数组 𝑛𝑢𝑚𝑠 的元素数量。
- 空间复杂度: O ( 1 ) O(1) O(1)。
思路 2:哈希表
哈希表中键值对信息为 t a r g e t − n u m s [ i ] : i target-nums[i]: i target−nums[i]:i,其中 i i i 为下标。
- 遍历数组,对于每一个数
n
u
m
s
[
i
]
nums[i]
nums[i]:
- 先查找字典中是否存在 t a r g e t − n u m s [ i ] target - nums[i] target−nums[i],存在则输出 t a r g e t − n u m s [ i ] target - nums[i] target−nums[i] 对应的下标和当前数组的下标 i i i。
- 不存在则在字典中存入 t a r g e t − n u m s [ i ] target - nums[i] target−nums[i] 的下标 i i i。
class Solution {
public:
vector<int> twoSum(vector<int>& nums, int target) {
unordered_map<int, int> numDict;
for(int i = 0; i < nums.size(); i++){
auto it = numDict.find(target - nums[i]);
if(it != numDict.end())
return {it->second, i}
numDict[nums[i]] = i;
}
return {};
}
};
思路 2:复杂度分析
- 时间复杂度: O ( n ) O(n) O(n),其中 𝑛 是数组 𝑛𝑢𝑚𝑠 的元素数量。
- 空间复杂度: O ( n ) O(n) O(n)。
2235. 两整数相加
描述:给定两个整数 𝑛𝑢𝑚1 和 𝑛𝑢𝑚2。
要求:返回这两个整数的和。
思路 1:直接相加
class Solution {
public:
int sum(int num1, int num2) {
return num1 + num2;
}
};
思路2 :位运算
class Solution {
public:
int sum(int num1, int num2) {
while (num2 != 0){
int temp = num1^num2;
num2 = (num1&num2) << 1;
num1 = temp;
}
return num1;
}
};
1929. 数组串联
描述: 给定一个长度为 𝑛 的整数数组 𝑛𝑢𝑚𝑠。
要求:构建一个长度为 2×𝑛 的答案数组 𝑎𝑛𝑠,答案数组下标从 0 开始计数 ,对于所有 0≤𝑖<𝑛 的 𝑖 ,满足下述所有要求:
- 𝑎𝑛𝑠[𝑖]==𝑛𝑢𝑚𝑠[𝑖]。
- 𝑎𝑛𝑠[𝑖+𝑛]==𝑛𝑢𝑚𝑠[𝑖]。
class Solution {
public:
vector<int> getConcatenation(vector<int>& nums) {
int n = nums.size();
vector<int> ans(2 * n);
std::copy(nums.begin(), nums.end(), ans.begin());
std::copy(nums.begin(), nums.end(), ans.begin() + n);
return ans;
}
};
0771. 宝石与石头
描述: 给定一个字符串 𝑗𝑒𝑤𝑒𝑙𝑠 代表石头中宝石的类型,再给定一个字符串 𝑠𝑡𝑜𝑛𝑒𝑠 代表你拥有的石头。𝑠𝑡𝑜𝑛𝑒𝑠 中每个字符代表了一种你拥有的石头的类型。
要求: 计算出拥有的石头中有多少是宝石。
思路一: 类似于两数之和构建hash表
class Solution {
public:
int numJewelsInStones(string jewels, string stones) {
unordered_set<char> set;
int count = 0;
for(char c : jewels)
set.insert(c);
for(char c : stones){
if(set.count(c))
count++;
}
return count;
}
};
复杂度: 线性时间,常数空间
思路二:位运算 具体思路见 771. 宝石与石头 - 力扣(LeetCode)
class Solution {
public:
int numJewelsInStones(string jewels, string stones) {
long long mask = 0;
for (char c: jewels)
mask |= 1LL << (c & 63);
//对于当前字符c,先执行按位与操作(c & 63),获取c的低6位。
//然后进行右左移操作(mask << ...),将掩码mask左移由(c & 63)确定的位数。
int ans = 0;
for (char c: stones)
ans += mask >> (c & 63) & 1;
// mask >> (c & 63) 获取c的低6位,将mask右移由(c & 63)确定的位数。
//最后与1与运算,只保留最低位的值(0或1)。判断c是否在mask中。
return ans;
}
};
复杂度: 线性时间,常数空间
1480. 一维数组的动态和
描述:给定一个数组 𝑛𝑢𝑚𝑠。
要求:返回数组 𝑛𝑢𝑚𝑠 的动态和。
说明:动态和:数组前 𝑖 项元素和构成的数组,计算公式为 runningSum[i] = ∑ x = 0 i ( n u m s [ i ] ) \text{runningSum[i]} = \sum_{x=0}^i (nums[i]) runningSum[i]=∑x=0i(nums[i])。
思路一: 累加
class Solution {
public:
vector<int> runningSum(vector<int>& nums) {
for(int i = 1; i <nums.size(); i++){
nums [i] += nums[i-1];
}
return nums;
}
};
思路二:STL函数
class Solution {
public:
vector<int> runningSum(vector<int>& nums) {
//STL API
std::partial_sum(nums.begin(), nums.end(), nums.begin());
return nums;
}
};
0709. 转换成小写字母
描述:给定一个字符串 𝑠。
要求:将该字符串中的大写字母转换成相同的小写字母,返回新的字符串。
思路一:小写字母比相应大写字母的ASSIC码大32(32而不是26是因为可以直接进行位运算),位运算比’‘加’'更快。
class Solution {
public:
string toLowerCase(string s) {
for(char &c : s){
if (c<='Z' && c>='A')
c |= 32;
}
return s;
}
};
// 时间复杂度O(n)
思路二:tolower函数
class Solution {
public:
string toLowerCase(string s) {
for (char& ch: s) {
ch = tolower(ch);
}
return s;
}
};
// 时间复杂度O(n)
1672. 最富有客户的资产总量
描述:给定一个 𝑚×𝑛 的整数网格 𝑎𝑐𝑐𝑜𝑢𝑛𝑡𝑠,其中 𝑎𝑐𝑐𝑜𝑢𝑛𝑡𝑠[𝑖][𝑗] 是第 𝑖 位客户在第 𝑗 家银行托管的资产数量。
要求:返回最富有客户所拥有的资产总量。
思路:遍历
class Solution {
public:
int maximumWealth(vector<vector<int>>& accounts) {
int MaxWealth = 0;
for (auto &account : accounts ){
MaxWealth = std::max(MaxWealth, std::accumulate(account.begin(), account.end(), 0));
}
return MaxWealth;
}
};
// 时间复杂度O(mn)