欢迎来我的博客 http://www.blackblog.tech,我的简书 https://www.jianshu.com/u/55a1bc4688c6
这是一篇笔记型Blog,主要存一下最近练的代码的笔记。LeetCode的代码,在云端,复习起来麻烦,就这样存下来。
目前的练习为LeetCode中级算法与每日模拟赛.
没事刷一刷LeetCode还是可以提高一下基本的代码能力的。
给定一个包含 n 个整数的数组 nums,判断 nums 中是否存在三个元素 a,b,c ,使得 a + b + c = 0 ?找出所有满足条件且不重复的三元组。
注意:答案中不可以包含重复的三元组。
例如, 给定数组 nums = [-1, 0, 1, 2, -1, -4],
满足要求的三元组集合为:
[
[-1, 0, 1],
[-1, -1, 2]
]
class Solution {
public:
vector<vector<int>> threeSum(vector<int>& nums) {
vector<vector<int>> vec;
sort(nums.begin(),nums.end());
for(int k=0;kif(k>0 && nums[k]==nums[k-1])
continue;
int i,j;
for(i=k+1,j=nums.size()-1;iif(i>k+1 && nums[i]==nums[i-1])
{
i++;
continue;
}
if(j1 && nums[j]==nums[j+1])
{
j--;
continue;
}
int sum = nums[i]+nums[j]+nums[k];
if(sum==0)
{
vector<int> m_vec;
m_vec.push_back(nums[k]);
m_vec.push_back(nums[i]);
m_vec.push_back(nums[j]);
vec.push_back(m_vec);
j--;
i++;
}
else if(sum<0) i++;
else if(sum>0) j--;
}
}
return vec;
}
};
此题直接穷举一定爆时间
采用暴力+双指针解法
首先对数据进行排序。
第一次遍历确定一个数字n,则剩下两个数字的和必须是-n,这样才满足条件。确定两个指针i、j,从左至右,从右至左依次遍历。
单个指针遍历的过程中,如果重复直接跳过,防止结果中出现重复的数字。
如果sum为0时,得到答案,存储,继续遍历。
如果sum小于0,则证明当前情况的左指针小了,左指针++。
如果sum大于0,则证明当前情况的右指针大了,右指针–。
此题尝试了二重暴力+二分,时间没问题,但是结果总出错,不知道为什么。
给定一个 m x n 的矩阵,如果一个元素为 0,则将其所在行和列的所有元素都设为 0。请使用原地算法。
样例1:
输入:
[
[1,1,1],
[1,0,1],
[1,1,1]
]
输出:
[
[1,0,1],
[0,0,0],
[1,0,1]
]
样例2:
输入:
[
[0,1,2,0],
[3,4,5,2],
[1,3,1,5]
]
输出:
[
[0,0,0,0],
[0,4,5,0],
[0,3,1,0]
]
class Solution {
public:
void setZeroes(vector<vector<int>>& matrix) {
bool Row[matrix.size()]={false};
bool Col[matrix[0].size()]={false};
if (matrix.size()==1 && matrix[0].size()==1) return ;
for(int i=0;ifor(int j=0;j0].size();j++)
{
if(matrix[i][j]==0){
Row[i]=true;
Col[j]=true;
}
}
}
for(int i=0;ifor(int j=0;j0].size();j++)
{
if(Row[i]) matrix[i][j]=0;
else if(Col[j]) matrix[i][j]=0;
}
}
return ;
}
};
这里使用的算法,空间复杂度O(m + n) ,并不是最优算法。
最优算法空间复杂度为常数级别
注意几个细节就好
if (matrix.size()==1 && matrix[0].size()==1) return ;
这句话用来判断[[1]]这样的特殊情况
用于存储0位的数字这样开辟
bool Row[matrix.size()]={false};
bool Col[matrix[0].size()]={false};
不要用vec去存储0,不然难以匹配坐标,用两个数组一一对应就好。
常数级别空间复杂度的算法并不复杂
就需要巧妙地利用原来矩阵的空间,这里利用第一行和第一列保存额外信息:
1 先判断号第一行和第一列是否需要全部置零
2 有任何一个该行或者该列的元素为零那么这个第一行或者第一列的元素必然是零,就保存这个零,最后用来判断整个矩阵的这一行或者这一列是否需要置零。
3 最后再根据前面判断,决定是否把这个第一行和第一列置零。
给定一个字符串数组,将字母异位词组合在一起。字母异位词指字母相同,但排列不同的字符串。
输入: ["eat", "tea", "tan", "ate", "nat", "bat"],
输出:
[
["ate","eat","tea"],
["nat","tan"],
["bat"]
]
说明:
所有输入均为小写字母。
不考虑答案输出的顺序。
class Solution {
public:
vector<vector<string>> groupAnagrams(vector<string>& strs) {
map<string,vector<string>> str_map;
vector<vector<string>> res;
for(auto str : strs)
{
string tmp=str;
sort(tmp.begin(),tmp.end());
str_map[tmp].push_back(str);
}
for(auto val : str_map)
{
//sort(val.second.begin(),val.second.end());
res.push_back(val.second);
}
return res;
}
};
题目逻辑很清晰
自己手动实现了一个算法,但是最后一组数据没有过啊!!!难受!!!等一下附上代码。
这个题目的思路很清晰,利用Map,将每次排序过的字符串作为Key,将剩下的字符串作为Val存放进去,最后整体存放在一个二维Vector里面就可以了。
附一下差一组数据没过的代码
class Solution {
public:
vector<vector<string>> groupAnagrams(vector<string>& strs) {
vector<vector<string>> res;
vector<string> fir_str;
fir_str.push_back(strs[0]);
res.push_back(fir_str);
for(int i=1;istring tmp = strs[i];
sort(tmp.begin(),tmp.end());
bool flag=false;
for (int j = 0; j < res.size(); j++) {
string m_tmp = res[j][0];
sort(m_tmp.begin(), m_tmp.end());
if (tmp == m_tmp) {
res[j].push_back(strs[i]);
flag = true;
break;
}
}
if(!flag)
{
vector<string> new_str;
new_str.push_back(strs[i]);
res.push_back(new_str);
}
sort(res.begin(),res.end());
}
return res;
}
};
给定一个包含红色、白色和蓝色,一共 n 个元素的数组,原地对它们进行排序,使得相同颜色的元素相邻,并按照红色、白色、蓝色顺序排列。
此题中,我们使用整数 0、 1 和 2 分别表示红色、白色和蓝色。
注意:
不能使用代码库中的排序函数来解决这道题。
示例:
输入: [2,0,2,1,1,0]
输出: [0,0,1,1,2,2]
进阶:
一个直观的解决方案是使用计数排序的两趟扫描算法。
首先,迭代计算出0、1 和 2 元素的个数,然后按照0、1、2的排序,重写当前数组。
你能想出一个仅使用常数空间的一趟扫描算法吗?
骚操作法:
void sortColors(vector<int>& nums) {
map<int,vector<int>> m;
for(int i=0;iint k=nums[i];
m[k].push_back(k);
}
nums.erase(nums.begin(),nums.end());
for(auto val : m)
{
for(int i=0;i
快速排序:
class Solution {
public:
void qsort(vector<int>& nums,int l,int r)
{
if(l>r) return;
int mid = nums.size()/2;
int i=l;
int j=r;
int flag = nums[l];
while(i!=j)
{
while(i=flag)
j--;
while(iint tmp;
tmp = nums[i];
nums[i] = nums[j];
nums[j] = tmp;
}
nums[l] =nums[i];
nums[i] = flag;
qsort(nums,i+1,r);
qsort(nums,l,i-1);
}
void sortColors(vector<int>& nums) {
qsort(nums,0,nums.size()-1);
}
};
用map可以直接排序有点意思
熟练一下快排
给定一个非空的整数数组,返回其中出现频率前 k 高的元素。
例如,
给定数组 [1,1,1,2,2,3] , 和 k = 2,返回 [1,2]。
注意:
你可以假设给定的 k 总是合理的,1 ≤ k ≤ 数组中不相同的元素的个数。
你的算法的时间复杂度必须优于 O(n log n) , n 是数组的大小。
class Solution {
public:
vector<int> topKFrequent(vector<int>& nums, int k) {
map<int,int> m;
vector<int> res;
for(int i=0;i1;
}
vectorint ,int>> vtMap;
for(auto it=m.begin();it!=m.end();it++)
vtMap.push_back(make_pair(it->first,it->second));
sort(vtMap.begin(),vtMap.end(),cmp_by_val);
for(int i=0;ireturn res;
}
static bool cmp_by_val(pair<int,int> &a,pair<int,int> &b)
{
return a.second>b.second;
}
};
一道说难不难,说简单不简单的题。
熟练使用stl很重要。
这道题利用到了map通过val排序的方法,将map转化成vector
在未排序的数组中找到第 k 个最大的元素。请注意,你需要找的是数组排序后的第 k 个最大的元素,而不是第 k 个不同的元素。
示例 1:
输入: [3,2,1,5,6,4] 和 k = 2
输出: 5
示例 2:
输入: [3,2,3,1,2,4,5,5,6] 和 k = 4
输出: 4
说明:
你可以假设 k 总是有效的,且 1 ≤ k ≤ 数组的长度。
class Solution {
public:
int partition(vector<int>&nums, int low, int high)//找枢纽
{
int first = low;
int last = high;
int key = nums[first];//用字表的第一个记录作为枢轴
while (first != last)
{
while (nums[last] >= key && first < last)
last--;
swap(nums[first], nums[last]);
while (nums[first] <= key && first < last)
first++;
swap(nums[first], nums[last]);
}
return first;//返回一个枢纽
}
int find_k(vector<int>& nums,int l,int r,int k)
{
int index=partition(nums,l,r);
int length = r-index+1;
if(length == k) //计算后面一段的长度,如果等于k表示找到了第k大
return nums[index];
else if(length > k)//如果后面的一段的长度大于k,证明第k大的数在后面一段
return find_k(nums,index+1,r,k);
else if(length //如果后面的一段的长度小于k,证明第k大的数在前面一段
return find_k(nums,l,index-1,k-length);//k-length 去掉已经被划分的数字
}
int findKthLargest(vector<int>& nums, int k) {
return find_k(nums,0,nums.size()-1,k);
}
};
利用快排思想查找第k大的数字,注意把patition和find_k分开写,写到一起容易出错。
时间复杂度:
该算法的平均时间复杂度为O(N)(详细的推导过程看算法导论9.2节),最坏情况为N^2,即每次划分把数组变为为(n-1) 和1的两断。
给定一个仅包含数字 2-9 的字符串,返回所有它能表示的字母组合。
给出数字到字母的映射如下(与电话按键相同)。注意 1 不对应任何字母。
大概是这样的:
base[10]={“”,”“,”abc”,”def”,”ghi”,”jkl”,”mno”,”pqrs”,”tuv”,”wxyz”};
示例:
输入:"23"
输出:["ad", "ae", "af", "bd", "be", "bf", "cd", "ce", "cf"].
说明:
尽管上面的答案是按字典序排列的,但是你可以任意选择答案输出的顺序。
class Solution {
public:
vector<string> letterCombinations(string digits) {
if(digits.length()<=0)
{
vector<string> v;
return v;
}//此处为题目要求
vector<string> res;
int index=0;
string str="";
letterCombinationsSearch(digits,str,index,res);
return res;
}
void letterCombinationsSearch(string digits,string str,int index, vector<string> &res)//注意啊,这里一定是取地址!!!
{
if(index==digits.size())
{
res.push_back(str);//搜索触底 将当前的字符串存入结果中
return;
}
string base[10]={"","","abc","def","ghi","jkl","mno","pqrs","tuv","wxyz"};
for(int i=0;i< base[digits[index]-'0'].size();i++)//自行领悟
{
str +=base[digits[index]-'0'][i];//将base数组对应位置的每个字母都放进字符串。
letterCombinationsSearch(digits,str,index+1,res);//递归,寻找下一个字母。
str.pop_back();//ad找完之后,弹出d,准备放入e
}
}
};
整个问题就是一个dfs+回溯。
举个例子,比如“234”,先一口气找到adg,然后弹出g,找到adh,弹出h,找到adi,弹出i,弹出d,找到aeg,以此类推。
给定一个没有重复数字的序列,返回其所有可能的全排列。
示例:
输入: [1,2,3]
输出:
[
[1,2,3],
[1,3,2],
[2,1,3],
[2,3,1],
[3,1,2],
[3,2,1]
]
class Solution {
public:
void permute_search(vector<int>&nums,vector<bool>&used,vector<int>&tmp,vector<vector<int>>&res)
{
if(tmp.size()==nums.size())
{
res.push_back(tmp);
return;
}
for(int i=0;iif(!used[i])
{
tmp.push_back(nums[i]);//存入一个没有用过的数字
used[i]=true;//用过的数字标记为true
permute_search(nums,used,tmp,res);
tmp.pop_back();//弹出刚刚用过的数字
used[i]=false;//将所对应的使用状态改为false
}
}
}
vector<vector<int>> permute(vector<int>& nums) {
vector<vector<int>> res;
int index=-1;
vector<int> tmp;
vector<bool> used(nums.size(), false);
permute_search(nums,used,tmp,res);
return res;
}
};
一道非常简单dfs+回溯题目,创建一个used数组用来存储当前每个数字的使用状态。核心部分都写了注释,这里就不赘述了。
给定一组不含重复元素的整数数组 nums,返回该数组所有可能的子集(幂集)。
说明:解集不能包含重复的子集。
示例:
输入: nums = [1,2,3]
输出:
[
[3],
[1],
[2],
[1,2,3],
[1,3],
[2,3],
[1,2],
[]
]
class Solution {
public:
void subsets_search(vector<int>& nums,vector<int>&tmp,int index,vector<vector<int>> &res)
{
if(index==nums.size())
{
return ;
}
for(int i=index;i//将临时数组增加一个数字
res.push_back(tmp);//将临时数组放入答案数组中
subsets_search(nums,tmp,i+1,res);//递归调用,注意这里是i+1,不是index+1,因为使用index会出现1 3已被放入,index=2,递归index=3,将3也放入tmp,这样会出现1 3 3 的情况。
tmp.pop_back();//将临时数组减少一个数字,用于回溯
}
}
vector<vector<int>> subsets(vector<int>& nums) {
vector<int> tmp;//创建一个临时数组用于增加删除数字
vector<vector<int>>res;//结果数组
res.push_back(tmp);
int index=0;
subsets_search(nums,tmp,index,res);
return res;
}
};
简单回溯题,重点已经在注释中写的很清晰了。
这个题比较有意思的地方就在于每次都要把tmp都放入res中。
注意递归调用参数是i+1,不是index+1,因为使用index会出现1 3已被放入,index=2,递归index=3,将3也放入tmp,这样会出现1 3 3 的情况。
编写一个算法来判断一个数是不是“快乐数”。
一个“快乐数”定义为:对于一个正整数,每一次将该数替换为它每个位置上的数字的平方和,然后重复这个过程直到这个数变为 1,也可能是无限循环但始终变不到 1。如果可以变为 1,那么这个数就是快乐数。
示例:
输入: 19
输出: true
解释:
12 + 92 = 82
82 + 22 = 68
62 + 82 = 100
12 + 02 + 02 = 1
骚操作法:
class Solution {
public:
bool isHappy(int n) {
while(1)
{
int k=0;
while(n)
{
k+=(n%10)*(n%10);
n/=10;
}
if(k==1) return true;
if(k==4) return false;
n = k;
}
}
};
常规解法:
class Solution {
public:
bool isHappy(int n) {
vector<int> vec;
while(1)
{
int k=0;
while(n)
{
k+=(n%10)*(n%10);
n/=10;
}
if(k==1) return true;
for(int i=0;iif(k==vec[i]) return false;
}
vec.push_back(k);
n = k;
}
}
};
有意思的一道题!
先说常规解法:
所有的不开心数最后都无法得到1,意味着他们一定会陷入一个有重复数字出现的循环,所以只要使用一个vector存储一下所有出现的数字,有相同的就返回false。
再来一波骚操作:
不是快乐数的数称为不快乐数(unhappy number),所有不快乐数的数位平方和计算,最後都会进入 4 → 16 → 37 → 58 → 89 → 145 → 42 → 20 → 4 的循环中。
所以只需要判断一下4就可以了。