同另一篇文章,不过markdown用起来更方便。
1.1 反转字符串
知识点:就是双指针,对称进行交换,当p>q时结束。很简单。
坑:c++ STL库掌握不牢。
vector矢量的end()迭代器不是最后一个元素的指针,end()-1才是。
void reverseString(vector<char>& s) {
if(s.size()<1) return;
reverse(s.begin(),s.end());
}
void reverseString1(vector<char>& s) {
if(s.size()<1) return;
auto iter = s.begin();
//auto iter2 = s.end();//错误,需要-1
auto iter2 = s.begin()+s.size()-1;
while(iter<=iter2){
auto temp=*iter;
*iter=*iter2;
*iter2=temp;
iter++;
iter2--;
}
}
void reverseString2(vector<char>& s) {
int p=0;
int q=s.size()-1;
while(p<=q){
char temp = s[p];
s[p]=s[q];
s[q]=temp;
p++;
q--;
}
}
1.2 最大利润问题
记一笔,发现自己用了网易和微信两个leetcode账号;以后还是用微信号吧。
言归正传:3分钟想出算法;最大利润其实很简单,求连续两数的差分序列,将所有正数求和即得最大利润。
int maxProfit(vector<int>& prices) {
int maxProfit = 0;
if(prices.size()<1) return maxProfit;
for(int i=0,j=i+1;i<prices.size()-1;i++,j++){
if(prices[j]-prices[i]>0){
maxProfit+=prices[j]-prices[i];
}
}
return maxProfit;
}
今日就仅仅刷了一道题。
2020/2/29 晚
1.3 移动零
写这上边似乎有点浪费时间。还是记一下语法知识吧。
双指针;一个负责遍历,一个用于记录非零元素的个数k;将k个非零元素直接赋值到最前面,随后将k以后的元素全赋值为0。
void moveZeroes(vector<int>& nums) {
if(nums.size()<1) return;
int q=0;
for(int p=0;p<nums.size();p++){
if(nums[p]!=0){
nums[q]=nums[p];
q++;
}
}
for(;q<nums.size();q++){
nums[q]=0;
}
}
暴力法:遇到0则使用erase擦除,并在最后面补0会导致超时。
注意:erase擦除之后会自动指向下一个元素的位置,如果擦除的是最后一个元素,则擦除后会指向end();此时若再++则会越界,出现也指针。下面写法正确,没擦除则移动,擦除则不再移动。但以下代码依然超时。
void moveZeroes1(vector<int>& nums) {
if(nums.size()<1) return;
for(auto iter=nums.begin();iter<nums.end();){
if(*iter==0){
nums.erase(iter);
nums.push_back(0);
}else iter++;
}
}
1.4 加一
主要就是考虑进位问题。也就是说从末位开始进位flag=1,如果该位再产生进位,则flag不变,将该位设为0;否则将该位加flag就完了,并且再将flag设为0,break;即只要没产生进位,则退出,并且flag=0未能进位而结束。直到判断完第一位,如果第一位产生进位,则第一位将被设置成0;flag仍然为1,此后while结束。判断第一位是否产生进位,是的话在结果里面先弹入1,再接上改变后的数。Over,还是调了10分钟的。
注意:涉及到迭代器要倒着走完到第一位,此时不建议采用迭代器写法。
2020/3/3
vector<int> plusOne(vector<int>& digits) {
vector<int> result;
if(digits.size()<1) return result;
// 判断有无进位,进位则将该位设为0,进位设为1;
// 若无进位,则加1;并将flag设为0;直到走到第一位
int i = digits.size() -1;
int flag=1;
while(i>=0){
if(digits[i]+ flag <10){
digits[i] += flag;
flag=0;
break;
}else{
digits[i] = 0;
}
i--;
}
if(flag==1){
result.push_back(1);
}
for(auto ele:digits){
result.push_back(ele);
}
return result;
}
1.5 是否存在重复
EASY! 哈希map解决一切问题。不多比比直接上代码。
bool containsDuplicate(vector<int>& nums) {
if(nums.size()<2) return false;
// 建立一个哈希表,key为序号,value为值
map<int,int> hash;
for(int i=0;i<nums.size();i++){
if( hash.find(nums[i])!=hash.end() ){
// 找到了,则存在重复
return true;
}else {
// 没找到,插入
//hash[nums[i] ]++;
hash.insert(pair<int,int>(nums[i],i));
}
}
return false;
}
1.6 两个数组的交集
解答:暴力的搜索法。分别对两个数组建立直方图hist1,hist2;在hist2中查找hist1中的每一项,并求该项个数的最小值,输入结果即可。
感觉我是真的再怎么努力也赶不上YZT大神了,必须要清楚地认识到自己的境况。但是接下来的几个月,必须打起十二分精神来备战。
注意:统计直方图的map插入操作 map[key]++ 就够了。得仔细学一下STL库。
以下为解法,大概写了3分钟,基础不牢调了5分钟。
vector<int> intersect(vector<int>& nums1, vector<int>& nums2) {
vector<int> result;
// 1-构建两个直方图哈希表,
// 针对nums1中的每一个元素,直接在nums2中进行查找,如果找到则返回该数即其个数
map<int,int> hist1 = getHistgrom(nums1);
map<int,int> hist2 = getHistgrom(nums2);
map<int,int>::iterator iter = hist1.begin();
for(;iter!=hist1.end();iter++){
auto findIter = hist2.find( iter->first );
if( findIter != hist2.end() ){
int count = (iter->second<=findIter->second) ? iter->second : findIter->second;
for(int i=0;i< count;i++){
result.push_back( iter->first );
}
}
}
return result;
}
// 统计数组直方图
map<int,int> getHistgrom(vector<int> nums){
map<int,int> hash;
for(auto ele:nums){
hash[ele]++;
}
return hash;
}
写点儿牢骚话…
读研这一年半,感觉自己学的东西太多太杂了,倒是没能有非常擅长之领域,若非要说,怕是传统的图像处理了。做过MFC,不系统的学了c++,做过安卓开发,懂一点儿JAVA皮毛和android app开发,学过keras做过DL也做过无人机控制与SITL仿真,文章也看了不少。但是到头来吧,可以说一无所获,比较失败,学过的诸多知识慢慢被遗忘,对于以后毫无裨益。究其原因,纵然实验室课题繁多,可感觉还是自己的时间没规划好,剩下的时间不多,系统的学习一下若干个方面,选一方向钻研,要有深度而不再追求广度了。
- C++;
- 数据结构与算法;
- ROS开发;
- 沾点DL与SLAM
- 图像处理老本行。
不管走到哪一步,心态平和尽力就好,就这样吧。
2020/3/4晚于家中
1.7 有效数独
我的方法很简单粗暴,三个条件逐步判断。将每一行、每一列、每一格子的有效数字转化成数组,然后判断该数组是否存在重复,存在重复则无效。
注意:70行代码还是写了挺久的,主要是对字符串转数字用得少,查了半天用了个ss的方法,atoi失效,暂时不知道为啥(const??)。反正这题还是不难的。不过我这写法时间复杂度应该不低,老clear数组效率不咋地,可以用int[9],然后把’.'设置成0或者-1这样子,修改一下查重复函数。
bool isValidSudoku(vector<vector<char>>& board) {
// 分别按行、列、格子将区域划入vector<int>中,没有数就不加入
vector<int> temp;
// 1-按行
for(int i=0;i<9;i++){
// 将数存入temp
temp.clear();
for(int j=0;j<9;j++){
if(board[i][j]!='.'){
//int num =atoi( board[i][j] );//失效,未解
temp.push_back(char2int(board[i][j]));
}
}
// 判断该行有效数值是否成立?
if( !isValidSet(temp)) return false; // 不成立则退出
}
// 2-按列
for(int i=0;i<9;i++){
temp.clear();
for(int j=0;j<9;j++){
if(board[j][i]!='.'){
temp.push_back( char2int(board[j][i]) );
}
}
if( !isValidSet(temp)) return false; // 不成立则退出
}
// 3-按格子
temp.clear();
for(int i=0;i<7;i+=3){
for(int j=0;j<7;j+=3){
temp = getBlock(board,i,j);
if( !isValidSet(temp)) return false; // 不成立则退出
}
}
return true;
}
// 获取格子
vector<int> getBlock(vector<vector<char>>&board,int i,int j){
vector<int> block;
for(int m=0;m<3;m++){
for(int n=0;n<3;n++){
if(board[i+m][j+n]!='.'){
block.push_back( char2int(board[i+m][j+n]) );
}
}
}
return block;
}
// 字符转数字
int char2int(char s){
int result;
stringstream ss;
ss<<s;
ss>>result;
return result;
}
// 判断一个数组是否存在重复
bool isValidSet(vector<int> nums){
map<int,int> hash;
for(auto ele:nums){
auto findIter = hash.find(ele);
if(findIter!=hash.end()){
return false;//找到了存在重复
}else hash[ele]++;//没找到,则插入
}
return true;
}
颓废的一天,不能这样下去了。睡觉。明天早起好好干。2020/3/5 23:46
1.8 三数之和
记录一下双指针的用法。
class Solution {
public:
vector<vector<int>> threeSum(vector<int>& nums) {
vector<vector<int>> res;
if(nums.size() < 3) return res;
sort(nums.begin(),nums.end()); //先排序
for(vector<int>::iterator it = nums.begin(); it != nums.end()-2;){
int tmp = *it;
if(tmp > 0) break;
int target = 0 - tmp;
vector<int>::iterator left = it+1;
vector<int>::iterator right = nums.end()-1;
while(left < right){
if(*right < 0) break; //如果右边小于0,break
if(*left + *right < target){
int v= *left;
while(left != right && *left == v) left++; //跳过相等的元素
}else if(*left + *right > target){
int v= *right;
while(left != right && *right == v) right --;//跳过相等的元素
}else{
vector<int> tmp_res{tmp,*left,*right}; //保存结果
res.push_back(tmp_res);
int v= *left;
while(left != right && *left == v) left++;//跳过相等的元素
v= *right;
while(left != right && *right == v) right --;//跳过相等的元素
}
}
while(it != nums.end()-2 && *it == tmp) it++;//跳过相等的元素
}
return res;
}
};
//作者:hou-yong-sheng
//链接:https://leetcode-cn.com/problems/3sum/solution/c-shuang-zhi-zhen-by-hou-yong-sheng/
//来源:力扣(LeetCode)
//著作权归作者所有。侵删。
1.9 去重复数字
总结:这道题我开始以为得把所有重复数字全擦除掉,结果官方答案只要确定非重复数字的个数;区别显而易见。
注意:矢量迭代器擦除可能会导致迭代器失效,出现野指针。
//
// Created by wbzhang on 2020/2/27.
//
#ifndef ALGORITHMS_REMOVEDUPLICATES_H
#define ALGORITHMS_REMOVEDUPLICATES_H
#include "commonHeaders.h"
/** 原创;暴力法1--双指针
* 思路:鉴于数组已经排好顺序,重复数字必然连续,即对两位相邻数字擦除后者即可。
* 取指针p和q=p+1;若两数相等,则擦除q,此时下一轮循环时p将挪到下一位,因此需要将指针p向前挪一位(防止连续3个以上相同的串的情形失效)。
* 若两数不等,指针p向后挪一位。重复以上过程,直到指针p挪到倒数第二位。
*/
int removeDuplicates(vector<int>& nums) {
if(nums.size()<2) return nums.size();
for(vector<int>::iterator iter=nums.begin();iter!=nums.end()-1;++iter){
auto iter2=iter+1;
if (*iter==*(iter2)){
//cout<<"before:"<<*iter2<<endl;
nums.erase(iter2);
//cout<<"after:"<<*iter2<<endl;
iter--;// 删除后自动指向下一位,给复位
}
}
return nums.size();
}
// 暴力法2
int removeDuplicates1(vector<int>& nums) {
if(nums.size()<2) return nums.size();
cout<<*(nums.begin()+nums.size()-1)<<endl;
for(int i=0;i<nums.size()-1;++i){
int j=i+1;
if (nums[i]==nums[j]){
nums.erase(nums.begin()+j);
i--;
}
}
return nums.size();
}
// 该解存在冗余。
// 未完成,迭代器失效
int removeDuplicates11(vector<int>& nums) {
if(nums.size()<2) return nums.size();
for(auto iter=nums.begin();iter!=nums.end()-1;++iter){
//auto iter2=nums.end();
auto iter2 = nums.begin()+nums.size()-1;
while(iter2>iter){
if (*iter==*(iter2)){
nums.erase(iter2);//可能出现野指针?比如擦除了最后一项,iter2会变成野指针?
//iter2--;
}
iter2--;
}
}
return nums.size();
}
// 官方答案--双指针法
int removeDuplicates2(vector<int>& nums){
int p=0,q=1;
while(q!=nums.size()-1){
if(nums[p]==nums[q]){
q++;
}else{
nums[p+1]=nums[q];
p++;
q++;
}
}
return p+1;
}
#endif //ALGORITHMS_REMOVEDUPLICATES_H
1.10 数组旋转
知识点:
1)数组旋转k次,当k>数组长度时,与旋转k%n次是同样的
2)位置i旋转k次后;nums[(i+k)%n]等于最初的nums[i]
3)矢量逆向reverse操作的是前闭后开区间。
// 1-原创,暴力法
/* 注意旋转k次与旋转k%nums.size()是一样的,因此可以简化运算量;
* 首先将k%=nums.size();然后直接将末尾的弹出,并插入到开头
*/
void rotate2(vector<int>& nums, int k) {
if(k<1 || nums.size()<2) return;
k%=nums.size();
for(int i=1;i<=k;i++){
int temp = nums.back();
nums.pop_back();
nums.insert(nums.begin(),1,temp);
}
}
// 时间复杂度:O(k%nums.size()),空间复杂度O(1)
// 2-三次旋转
/* 将数组整体逆向排序;再将前k个数字逆向排序;再将后nums.size()-k个数字逆向排序
* 以上三步顺序可以适当调整。
*/
void rotate(vector<int>& nums, int k) {
int len=nums.size();
reverse(nums.begin(), nums.end() - k%len);
//reverse翻转的是[begin,end)区间
reverse(nums.end()- k%len,nums.end());
reverse(nums.begin(),nums.end());
}
};
void rotate(vector<int>& nums, int k) {
if(k<1 || nums.size()<2) return;
k%=nums.size();
reverse(nums.begin(),nums.end());
reverse(nums.begin(),nums.begin()+k);
reverse(nums.begin()+k,nums.end());
}
// 时间复杂度:O(n),空间复杂度O(1)
// 3-额外数组,直接赋值
void rotate(vector<int>& nums, int k) {
vector<int> nums2(nums);
int len=nums.size();
for(int i=0;i<len;i++){
nums[(i+k)%len]=nums2[i];
}
}
// 时间复杂度:O(n),空间复杂度O(n)
写点小记
感觉自己的刷题策略似乎出了问题。没有系统学过数据结构和算法设计,去刷初级题的数组和字符串处理一般都能搞定;但是也仅限于此,接下来或许应该把c++的数据结构花点时间搞一搞,然后继续刷题。今天下了个leetcode app。
本帖到此为止,后续直接记录在leetcode上。
2020/3/6
2.0 验证回文串

思路分析:如题,验证回文串很容易使用首尾双指针解决。但是需要判断该字符是否为字母或者数字,此时需要用到c函数库中
isalnum函数。isalnum参考链接,当isalnum(char c)当c为数字或字符时,返回非0值,否则返回0.

本文深入探讨了C++编程中的多种算法实现,包括字符串反转、最大利润问题、移动零、加一、存在重复、两个数组的交集、有效数独、三数之和、去重复数字、数组旋转等,通过实际代码示例解析了双指针、哈希表、直方图等技术的应用。
430

被折叠的 条评论
为什么被折叠?



