剑指offer错题记录
错误重点:
1. 传递vector参数时,如果调用函数改变了vector的内容,一定一定要&,传引用保持一致
旋转数组的最小数字:有重复数字情况,二分查找照样搞。情况考虑要周全,当a[mid]==a[l]==a[r]时,target在左右区间都可能出现,所以要枚举去搞,当a[mid]>a[r]在右边,当a[mid]<a[l]在左边,剩余为递增顺序,取左边。
矩形覆盖:找dp最优子结构啊,从小开始找规律,变成特殊的斐波那契了。
二进制中1的个数:上bitset啊,count啊。敲黑板,自己写居然出问题了,原因是负数补码移位操作会补1导致出现问题,解决方案两个,一个是外循环32次,另一个将1一步步左移,注意与操作不为0即可。
树的子结构: 在树的遍历过程中,找与模板root结点相同的结点,进一步子树check。考察树的递归思想
/*
struct TreeNode {
int val;
struct TreeNode *left;
struct TreeNode *right;
TreeNode(int x) :
val(x), left(NULL), right(NULL) {
}
};*/
class Solution {
public:
bool HasSubtree(TreeNode* pRoot1, TreeNode* pRoot2)
{
bool ans=false;
if(pRoot1!=NULL&&pRoot2!=NULL)
{
if(pRoot1->val==pRoot2->val)
ans=check(pRoot1,pRoot2);
if(!ans)
ans=HasSubtree(pRoot1->left,pRoot2);
if(!ans)
ans=HasSubtree(pRoot1->right,pRoot2);
}
return ans;
}
bool check(TreeNode* pRoot1, TreeNode* pRoot2)
{
if(pRoot2==NULL)return true;
if(pRoot1==NULL)return false;
if(pRoot1->val!=pRoot2->val)return false;
return check(pRoot1->left,pRoot2->left)&& check(pRoot1->right,pRoot2->right);
}
};
顺时针打印矩阵:简单模拟,用左上和右下的坐标定位出一次要旋转打印的数据,一次旋转打印结束后,往对角分别前进和后退一个单位。
class Solution {
public:
vector<int> v;
vector<int> printMatrix(vector<vector<int> > matrix)
{
int col = matrix[].size();
int row = matrix.size();
if(col==||row==)return v;
// 定义四个关键变量,表示左上和右下的打印范围
int left=,right=col-,top=,bottom=row-;
while(left<=right&&top<=bottom)
{
// left to right
for(int i=left;i<=right;i++)v.push_back(matrix[top][i]);
// top to bottom
for(int i=top+;i<=bottom;i++)v.push_back(matrix[i][right]);
// right to left
if(top!=bottom) // 当形成单行时,不能重复
for(int i=right-;i>=left;i--)v.push_back(matrix[bottom][i]);
// bottom to top
if(left!=right) // 形成单列
for(int i=bottom-;i>top;i--)v.push_back(matrix[i][left]);
left++,right--,top++,bottom--;
}
return v;
}
};
栈的压入,弹出序列:模拟
class Solution {
public:
vector<int> s;
bool IsPopOrder(vector<int> pushV,vector<int> popV) {
if(pushV.size()==)return false;
for(int i=,j=;i<pushV.size();)
{
s.push_back(pushV[i++]);
while(j<popV.size() && s.back()==popV[j])s.pop_back(),j++;
}
return s.empty();
}
};
二叉搜索树的后序遍历序列:注意从右顶点出发,可以满足所有特殊情况
class Solution {
public:
bool check(vector<int> v, int l, int r)
{
if(l>=r)return true;
int i=r;
while(i>l&&v[i-]>v[r])i--;
for(;i>l;i--)if(v[i-]>v[r])return false;
return check(v,l,i-)&&check(v,i,r-);
}
bool VerifySquenceOfBST(vector<int> sequence) {
if(sequence.size()==)return false;
return check(sequence,,sequence.size()-);
}
};
二叉树中和为某一值得路径:
/*
struct TreeNode {
int val;
struct TreeNode *left;
struct TreeNode *right;
TreeNode(int x) :
val(x), left(NULL), right(NULL) {
}
};*/
class Solution {
public:
vector<vector<int> > vv;
vector<int> v;
vector<vector<int> > FindPath(TreeNode* root,int expectNumber) {
if(root==NULL)return vv;
if(root->val<=expectNumber)
{
v.push_back(root->val);
if(expectNumber-root->val==&&root->left==NULL&&root->right==NULL)
{
vv.push_back(v);
v.pop_back();// 回溯
return vv;
}
FindPath(root->left,expectNumber-root->val);
FindPath(root->right,expectNumber-root->val);
v.pop_back(); // 回溯
}
return vv;
}
};
数组中只出现一次的数字: 题目中要找两个出现一次的数字,出现一次的题会做吧,异或就OK,利用两个相同的数字异或后为0。同样从头到尾异或一遍,最后求得这两个数的异或,现在考虑如何分开他,还是利用异或的性质,找两数异或后这个数的末尾第一个1的位置,从而将拆分为两个数组,分别异或即可。
不用加减乘除做加法
二进制做加法:具体步骤1.不进位求和:a^b,2.计算进的位:(a&b)<<1, 然后迭代计算步骤1+步骤2的结果直到进位为0
表示数值的字符串:判断非法情况逻辑要清晰
class Solution {
public:
bool isNumeric(char* str) {
// 标记符号、小数点、e是否出现过
bool sign = false, decimal = false, hasE = false;
for (int i = ; i < strlen(str); i++) {
if (str[i] == 'e' || str[i] == 'E') {
if (i == strlen(str)-) return false; // e后面一定要接数字
if (hasE) return false; // 不能同时存在两个e
hasE = true;
} else if (str[i] == '+' || str[i] == '-') {
// 第二次出现+-符号,则必须紧接在e之后
if (sign && str[i-] != 'e' && str[i-] != 'E') return false;
// 第一次出现+-符号,且不是在字符串开头,则也必须紧接在e之后
if (!sign && i > && str[i-] != 'e' && str[i-] != 'E') return false;
sign = true;
} else if (str[i] == '.') {
// e后面不能接小数点,小数点不能出现两次
if (hasE || decimal) return false;
decimal = true;
} else if (str[i] < '' || str[i] > '') // 不合法字符
return false;
}
return true;
}
};
对称的二叉树: 递归
class Solution {
public:
bool isSymmetrical(TreeNode* pRoot)
{
if(pRoot==NULL)return true;
return check(pRoot->left,pRoot->right);
} bool check(TreeNode* left, TreeNode* right)
{
if(left==NULL)return right==NULL;
if(right==NULL)return false;
if(left->val!=right->val)return false;
return check(left->left,right->right)&&check(left->right,right->left);
}
};
序列化二叉树: NULL的位置补位,一种遍历方式也可以序列话,传参时注意引用的使用,递归遍历和建树
class Solution {
public:
char* Serialize(TreeNode *root) {
if(root==NULL)return "#";
string r = to_string(root->val);
r.push_back(',');
char* left = Serialize(root->left);
char* right = Serialize(root->right);
char* res = new char[strlen(left) + strlen(right) + r.size()];
strcpy(res,r.c_str());
strcat(res,left);
strcat(res,right);
return res;
}
TreeNode* Deserialize(char *str) {
return decode(str);
}
TreeNode* decode(char* & str)
{
if(*str=='#'){
str++;
return NULL;
}
int num=;
while(*str!=',')
{
num=num*+(*(str++)-'');
}
str++;
TreeNode* node = new TreeNode(num);
node->left = decode(str);
node->right = decode(str);
return node;
}
};
数据流中的中位数:维护两个平衡堆,大根堆个数==小根堆个数 或 大根堆个数+1==小根堆个数
class Solution {
public:
priority_queue<int, vector<int>, less<int> > p;//大根堆
priority_queue<int, vector<int>, greater<int> > q;//小根堆
void Insert(int num)
{
if(p.empty()||num<=p.top())p.push(num);
else q.push(num);
if(p.size()==q.size()+)q.push(p.top()),p.pop();
if(p.size()+==q.size())p.push(q.top()),q.pop();
} double GetMedian()
{
return p.size()==q.size()?(p.top()+q.top())/2.0:p.top();//返回double
} };
滑动窗口的最大值: 有重复,deque版的尺取法,队首记录窗口内最大值的下标
class Solution {
public:
vector<int> maxInWindows(const vector<int>& num, unsigned int size)
{
vector<int> res;
deque<int> s;// 双端队列
for(int i=;i<num.size();i++)
{
while(s.size()&&num[s.back()]<num[i])s.pop_back();//队尾<当前值,出队
while(s.size()&&i-s.front()+>size)s.pop_front();//队首出窗,出队
s.push_back(i);//每次都入队
if(size&&i+>=size)//大于窗口时开始写入
res.push_back(num[s.front()]);
}
return res;
}
};
把数组排成最小的数: to_string比较x+y, y+x 排序
二叉搜索树与双向链表: 递归和非递归解法,相对简单,注意树的递归思想,只考虑一个单元的处理,以及出口的设置。
TreeNode* Convert(TreeNode* pRootOfTree)
{
if(pRootOfTree==NULL)return NULL;
TreeNode* leftRoot = Convert(pRootOfTree->left);
TreeNode* root = leftRoot;
while(leftRoot!=NULL&&leftRoot->right!=NULL)leftRoot=leftRoot->right;
if(leftRoot==NULL){
root = pRootOfTree;
pRootOfTree->left = NULL;
}
else {
leftRoot->right=pRootOfTree;
pRootOfTree->left=leftRoot;
}
TreeNode* rightRoot = Convert(pRootOfTree->right);
pRootOfTree->right = rightRoot;
if(rightRoot!=NULL)rightRoot->left = pRootOfTree;
return root;
} // 非递归中序遍历记录pre结点
TreeNode* Convert(TreeNode* pRootOfTree)
{
if(pRootOfTree==NULL)return NULL;
stack<TreeNode*> s;
TreeNode* p = pRootOfTree;
TreeNode* pre = NULL;
TreeNode* root = NULL;
while(p||s.size())
{
while(p!=NULL)
{
s.push(p);
p = p->left;
}
p = s.top();
s.pop(); if(pre==NULL)root = p;
else
{
pre->right = p;
p->left = pre;
}
pre = p;
p=p->right;
}
return root;
}
整数中1出现的次数(从1到n整数中1出现的次数): 找规律,从个位到最高位一个一个来,统计出现次数,编程之美统计整数中x出现的次数
class Solution {
public:
int NumberOf1Between1AndN_Solution(int n)
{
if(n<)return ;
int high,low,cur,tmp,i=;
high=n;
int tot=;
while(high!=)
{
high = n/(int)pow(,i);
tmp = n%(int)pow(,i);
cur = tmp/(int)pow(,i-);
low = tmp%(int)pow(,i-);
if(cur==)
tot+=high*(int)pow(,i-)+low+;
else if(cur<)
tot+=high*(int)pow(,i-);
else
tot+=(high+)*(int)pow(,i-);
i++;
}
return tot;
}
};
平衡二叉树: 普通递归求解,需要维护一个求深度的递归函数,根据该结点的左右子树高度差判断是否平衡,然后递归地对左右子树进行判断
class Solution {
public:
bool IsBalanced_Solution(TreeNode * root) {
if(root==NULL)return true;
return IsBalanced_Solution(root->left)&&IsBalanced_Solution(root->right)&&abs(depth(root->left)-depth(root->right))<=;
}
int depth(TreeNode * root)
{
if(root==NULL)return ;
return max(depth(root->left),depth(root->right))+;
}
};
这种做法有很明显的问题,在判断上层结点的时候,会多次重复遍历下层结点,增加了不必要的开销。如果改为从下往上遍历,如果子树是平衡二叉树,则返回子树的高度;如果发现子树不是平衡二叉树,则直接停止遍历,这样至多只对每个结点访问一次
bool IsBalanced_Solution(TreeNode * root) {
if(root==NULL)return true;
return depth(root)!=-;
} int depth(TreeNode * root)
{
if(root==NULL)return ;
int left = depth(root->left);
if(left==-)return -;
int right = depth(root->right);
if(right==-)return -;
return abs(left-right)>?-:max(left,right)+;
}
丑数:构造第index个丑数
文字转自:lizo,code转自:anybody
如果p是丑数,那么p=2^x * 3^y * 5^z
那么只要赋予x,y,z不同的值就能得到不同的丑数。
如果要顺序找出丑数,要知道下面几个特点。
对于任何丑数p:
(一)那么2*p,3*p,5*p都是丑数,并且2*p<3*p<5*p
(二)如果p<q, 那么2*p<2*q,3*p<3*q,5*p<5*q
算法思想:
由于1是最小的丑数,那么从1开始,把2*1,3*1,5*1,进行比较,得出最小的就是1
的下一个丑数,也就是2*1,
这个时候,多了一个丑数‘2’,也就又多了3个可以比较的丑数,2*2,3*2,5*2,
这个时候就把之前‘1’生成的丑数和‘2’生成的丑数加进来也就是
(3*1,5*1,2*2,3*2,5*2)进行比较,找出最小的。。。。如此循环下去就会发现,
每次选进来一个丑数,该丑数又会生成3个新的丑数进行比较。这样
暴力方法也能解决,但是如果在面试官用这种方法,估计面试官只会摇头吧。下面说一个O(n)的算法。
在上面的特点中,既然有p<q, 那么2*p<2*q,那么
“我”在前面比你小的数都没被选上,你后面生成新的丑数一定比“我”大吧,那么你乘2
生成的丑数一定比我乘2的大吧,那么在我选上之后你才有机会选上。
其实每次我们只用比较3个数:用于乘2的最小的数、用于乘3的最小的数,用于乘5的最小的
数。也就是比较(2*x , 3*y, 5*z) ,x>=y>=z的
class Solution {
public:
int GetUglyNumber_Solution(int index) {
if(index<)return index;
vector<int> res(index);
res[]=;
int t2=,t3=,t5=;
for(int i=;i<index;i++)
{
res[i]=min(res[t2]*,min(res[t3]*,res[t5]*));
if(res[i]==res[t2]*)t2++;
if(res[i]==res[t3]*)t3++;
if(res[i]==res[t5]*)t5++;
}
return res[index-];
}
};
扑克牌顺子:考虑顺子的特征,不要考虑细节,抓住主要特征,1.除了大小王没有重复的,2.max-min<5
正则表达式匹配:分为后一个为*或不为*,两种情况,不为*则一个一个比,为*考虑两种情况,匹配0,和匹配1(匹配多个在匹配1后递归中包含)
class Solution {
public:
bool match(char* str, char* pattern)
{ if (*str == '\0' && *pattern == '\0')
return true;
if (*str != '\0' && *pattern == '\0')
return false;
//if the next character in pattern is not '*'
if (*(pattern+) != '*')
{
if (*str == *pattern || (*str != '\0' && *pattern == '.'))
return match(str+, pattern+);
else
return false;
}
//if the next character is '*'
else
{
if (*str == *pattern || (*str != '\0' && *pattern == '.'))
return match(str, pattern+) || match(str+, pattern);
else
return match(str, pattern+);
}
}
};
孩子们的游戏(圆圈中最后剩下的数):
int index=-;
while(c.size()>)
{
index = (m+index)%c.size();
c.erase(c.begin()+index);
index--;
}
二叉树的下一个结点: 中序遍历的下一个结点
class Solution {
public:
TreeLinkNode* GetNext(TreeLinkNode* pNode)
{
if(pNode==NULL)return NULL;
if(pNode->right!=NULL) // 从右子树找后继结点
{
pNode=pNode->right;
while(pNode->left!=NULL)
pNode=pNode->left;
return pNode;
}
while(pNode->next!=NULL) // 向上回溯,该节点是其父节点的左孩子才行
{
if(pNode->next->left==pNode)return pNode->next;
pNode=pNode->next;
}
return NULL; //返回到root还没有找到
}
};
剑指offer错题记录的更多相关文章
- 剑指offer刷题记录
目录 二维数组中的查找 替换空格 从尾到头打印链表 反转链表 重建二叉树 用两个栈实现队列 旋转数组的最小数字 斐波拉切数列 跳台阶 变态跳台阶 矩形覆盖 二进制中1的个数 数值的整次方 链表中倒数第 ...
- 剑指 offer 第一题: 二维数组中的查找
打算写 图解剑指 offer 66 题 的系列文章,不知道大家有没有兴趣
- 剑指Offer编程题2——替换空格
剑指Offer编程题2——替换空格 题目描述 请实现一个函数,将一个字符串中的每个空格替换成“%20”.例如,当字符串为We Are Happy.则经过替换之后的字符串为We%20Are%20Happ ...
- 剑指Offer编程题1——二维数组中的查找
剑指Offer编程题1---------------二维数组中的查找 题目描述 在一个二维数组中(每个一维数组的长度相同),每一行都按照从左到右递增的顺序排序,每一列都按照从上到下递增的顺序排序.请完 ...
- LeetCode剑指Offer刷题总结(一)
LeetCode过程中值得反思的细节 以下题号均指LeetCode剑指offer题库中的题号 本文章将每周定期更新,当内容达到10题左右时将会开下一节. 二维数组越界问题04 public stati ...
- 剑指offer刷题
1.面试题43. 1-n整数中1出现的次数 输入一个整数 n ,求1-n这n个整数的十进制表示中1出现的次数. 例如,输入12,1-12这些整数中包含1 的数字有1.10.11和12,1一共出现了5次 ...
- 剑指offer编程题66道题 36-66
36.两个链表的第一个公共节点 题目描述 输入两个链表,找出它们的第一个公共结点. 1.具有重合节点的两个链表是一个Y字性,用两个堆栈放这两个链表,从尾部开始遍历,直到遍历到最后一个重合节点. 这种算 ...
- 牛客网剑指offer刷题总结
二维数组中的查找: 题目描述:在一个二维数组中,每一行都按照从左到右递增的顺序排序,每一列都按照从上到下递增的顺序排序.请完成一个函数,输入这样的一个二维数组和一个整数,判断数组中是否含有该整数. 两 ...
- 剑指offer编程题Java实现——面试题12相关题大数的加法、减法、乘法问题的实现
用字符串或者数组表示大数是一种很简单有效的表示方式.在打印1到最大的n为数的问题上采用的是使用数组表示大数的方式.在相关题实现任意两个整数的加法.减法.乘法的实现中,采用字符串对大数进行表示,不过在具 ...
随机推荐
- vc++基础班[27]---实现一个简单的任务管理器
因为任务管理器中涉及到进程的枚举操作,所以把两节课的知识点合并到一起来讲! ①.设计界面.以及列表控件变量的绑定: ②.列表控件样式的指定: m_TaskList.SetExtendedSty ...
- 获取图片的EXIF信息
对于专业的摄影师来说,Exif信息是很重要的信息,也包含了非常多的东西 1.EXIF EXIF(Exchangeable Image File)是“可交换图像文件”的缩写,当中包含了专门为数码相机的照 ...
- git与eclipse集成之保存快照
1.1. 保存快照 在个分支进行编码,然后需要紧急切换到另外一个分支进行快速修复一个问题,此时可以先将当前分支的修改进行保存快照. 在分支A进行编码,保存快照 切换到另外分支B进行修改 切换回A分支继 ...
- Chromium Embedded Framework (CEF)_3.2171.2069_v20170606_x86.tar.xz
CEF 为观看各个直播平台而特此修改的浏览器 可以单独提取 Flash 视频, 并可以修改视频的大小等功能 [增加了960x90% 和 1280x90%] 这次修改是主要针对 YY web 直播平台 ...
- Tomcat 部署项目的三种方法(转)
转自:https://www.cnblogs.com/ysocean/p/6893446.html#_label0 1.下载 Tomcat 服务器 ①.官网下载地址:http://tomcat.apa ...
- SSH localhost免密不成功 + 集群状态显示Configured Capacity: 0 (0 KB)
前一天运行hadoop一切安好,今天重新运行出现BUG.下面对遇到的bug.产生原因以及解决方法进行一下简单总结记录. [bug1]用ssh localhost免密登录时提示要输入密码. 原因分析:之 ...
- Github+HEXO FATAL bad indentation of a mapping entry at line 84
当配置玩_config.yml文件时,执行hexo g -d时出现错误如下: $ hexo g -d FATAL can not read a block mapping entry; a multi ...
- 用layui搭建的后台框架
<!doctype html> <html lang="en"> <head> <meta charset="UTF-8&quo ...
- MYSQL 获取当前星期方法
当前星期一: select subdate(curdate(),date_format(curdate(),'%w')-1) 当前星期日: select subdate(curdate(),date_ ...
- Android中播放音乐的几种方式
前言 前几天一直在研究RxJava2,也写了记录了几篇博客,但因为工作任务原因,需要研究音频相关的知识,暂时放下Rxjava,本文的demo中,MediaPalyer 部分使用RxJava编写一点逻辑 ...