剑指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。考察树的递归思想

 1 /*
 2 struct TreeNode {
 3     int val;
 4     struct TreeNode *left;
 5     struct TreeNode *right;
 6     TreeNode(int x) :
 7             val(x), left(NULL), right(NULL) {
 8     }
 9 };*/
10 class Solution {
11 public:
12     bool HasSubtree(TreeNode* pRoot1, TreeNode* pRoot2)
13     {
14         bool ans=false;
15         if(pRoot1!=NULL&&pRoot2!=NULL)
16         {
17             if(pRoot1->val==pRoot2->val)
18                 ans=check(pRoot1,pRoot2);
19             if(!ans)
20                 ans=HasSubtree(pRoot1->left,pRoot2);
21             if(!ans)
22                 ans=HasSubtree(pRoot1->right,pRoot2);
23         }
24         return ans;
25     }
26     bool check(TreeNode* pRoot1, TreeNode* pRoot2)
27     {
28         if(pRoot2==NULL)return true;
29         if(pRoot1==NULL)return false;
30         if(pRoot1->val!=pRoot2->val)return false;
31         return check(pRoot1->left,pRoot2->left)&& check(pRoot1->right,pRoot2->right);
32     }
33 };

 顺时针打印矩阵:简单模拟,用左上和右下的坐标定位出一次要旋转打印的数据,一次旋转打印结束后,往对角分别前进和后退一个单位。

 1 class Solution {
 2 public:
 3     vector<int> v;
 4     vector<int> printMatrix(vector<vector<int> > matrix) 
 5     {
 6         int col = matrix[0].size();
 7         int row = matrix.size();
 8         if(col==0||row==0)return v;
 9         // 定义四个关键变量,表示左上和右下的打印范围
10         int left=0,right=col-1,top=0,bottom=row-1;
11         while(left<=right&&top<=bottom)
12         {
13             // left to right
14             for(int i=left;i<=right;i++)v.push_back(matrix[top][i]);
15             // top to bottom
16             for(int i=top+1;i<=bottom;i++)v.push_back(matrix[i][right]);
17             // right to left
18             if(top!=bottom) // 当形成单行时,不能重复
19             for(int i=right-1;i>=left;i--)v.push_back(matrix[bottom][i]);
20             // bottom to top
21             if(left!=right) // 形成单列
22             for(int i=bottom-1;i>top;i--)v.push_back(matrix[i][left]);
23             left++,right--,top++,bottom--;
24         }
25         return v;
26     }
27 };

 栈的压入,弹出序列:模拟

 1 class Solution {
 2 public:
 3     vector<int> s;
 4     bool IsPopOrder(vector<int> pushV,vector<int> popV) {
 5         if(pushV.size()==0)return false;
 6         for(int i=0,j=0;i<pushV.size();)
 7         {
 8             s.push_back(pushV[i++]);
 9             while(j<popV.size() && s.back()==popV[j])s.pop_back(),j++;
10         }
11         return s.empty();
12     }
13 };

 二叉搜索树的后序遍历序列:注意从右顶点出发,可以满足所有特殊情况

 1 class Solution {
 2 public:
 3     bool check(vector<int> v, int l, int r)
 4     {
 5         if(l>=r)return true;
 6         int i=r;
 7         while(i>l&&v[i-1]>v[r])i--;
 8         for(;i>l;i--)if(v[i-1]>v[r])return false;
 9         return check(v,l,i-1)&&check(v,i,r-1);
10     }
11     bool VerifySquenceOfBST(vector<int> sequence) {
12         if(sequence.size()==0)return false;
13         return check(sequence,0,sequence.size()-1);
14     }
15 };

 二叉树中和为某一值得路径

 1 /*
 2 struct TreeNode {
 3     int val;
 4     struct TreeNode *left;
 5     struct TreeNode *right;
 6     TreeNode(int x) :
 7             val(x), left(NULL), right(NULL) {
 8     }
 9 };*/
10 class Solution {
11 public:
12     vector<vector<int> > vv;
13     vector<int> v;
14     vector<vector<int> > FindPath(TreeNode* root,int expectNumber) {
15         if(root==NULL)return vv;
16         if(root->val<=expectNumber)
17         {
18             v.push_back(root->val);
19             if(expectNumber-root->val==0&&root->left==NULL&&root->right==NULL)
20             {
21                     vv.push_back(v);
22                     v.pop_back();// 回溯
23                     return vv;
24             }
25             FindPath(root->left,expectNumber-root->val);
26             FindPath(root->right,expectNumber-root->val);
27             v.pop_back(); // 回溯
28         }
29         return vv;
30     }
31 };

 数组中只出现一次的数字: 题目中要找两个出现一次的数字,出现一次的题会做吧,异或就OK,利用两个相同的数字异或后为0。同样从头到尾异或一遍,最后求得这两个数的异或,现在考虑如何分开他,还是利用异或的性质,找两数异或后这个数的末尾第一个1的位置,从而将拆分为两个数组,分别异或即可。

不用加减乘除做加法

二进制做加法:具体步骤1.不进位求和:a^b,2.计算进的位:(a&b)<<1, 然后迭代计算步骤1+步骤2的结果直到进位为0

表示数值的字符串:判断非法情况逻辑要清晰

 1 class Solution {
 2 public:
 3     bool isNumeric(char* str) {
 4         // 标记符号、小数点、e是否出现过
 5         bool sign = false, decimal = false, hasE = false;
 6         for (int i = 0; i < strlen(str); i++) {
 7             if (str[i] == 'e' || str[i] == 'E') {
 8                 if (i == strlen(str)-1) return false; // e后面一定要接数字
 9                 if (hasE) return false;  // 不能同时存在两个e
10                 hasE = true;
11             } else if (str[i] == '+' || str[i] == '-') {
12                 // 第二次出现+-符号,则必须紧接在e之后
13                 if (sign && str[i-1] != 'e' && str[i-1] != 'E') return false;
14                 // 第一次出现+-符号,且不是在字符串开头,则也必须紧接在e之后
15                 if (!sign && i > 0 && str[i-1] != 'e' && str[i-1] != 'E') return false;
16                 sign = true;
17             } else if (str[i] == '.') {
18               // e后面不能接小数点,小数点不能出现两次
19                 if (hasE || decimal) return false;
20                 decimal = true;
21             } else if (str[i] < '0' || str[i] > '9') // 不合法字符
22                 return false;
23         }
24         return true;
25     }
26 };

对称的二叉树: 递归 

 1 class Solution {
 2 public:
 3     bool isSymmetrical(TreeNode* pRoot)
 4     {
 5         if(pRoot==NULL)return true;
 6         return check(pRoot->left,pRoot->right);
 7     }
 8     
 9     bool check(TreeNode* left, TreeNode* right)
10     {
11         if(left==NULL)return right==NULL;
12         if(right==NULL)return false;
13         if(left->val!=right->val)return false;
14         return check(left->left,right->right)&&check(left->right,right->left);
15     }
16 };

序列化二叉树: NULL的位置补位,一种遍历方式也可以序列话,传参时注意引用的使用,递归遍历和建树

 1 class Solution {
 2 public:
 3     char* Serialize(TreeNode *root) {    
 4         if(root==NULL)return "#";
 5         string r = to_string(root->val);
 6         r.push_back(',');
 7         char* left = Serialize(root->left);
 8         char* right = Serialize(root->right);
 9         char* res = new char[strlen(left) + strlen(right) + r.size()];
10         strcpy(res,r.c_str());
11         strcat(res,left);
12         strcat(res,right);
13         return res;
14     }
15     TreeNode* Deserialize(char *str) {
16         return decode(str);
17     }
18     TreeNode* decode(char* & str)
19     {
20         if(*str=='#'){
21             str++;
22             return NULL;
23         }
24         int num=0;
25         while(*str!=',')
26         {
27             num=num*10+(*(str++)-'0');
28         }
29         str++;
30         TreeNode* node = new TreeNode(num);
31         node->left = decode(str);
32         node->right = decode(str);
33         return node;
34     }
35 };

 数据流中的中位数:维护两个平衡堆,大根堆个数==小根堆个数 或 大根堆个数+1==小根堆个数

 1 class Solution {
 2 public:
 3     priority_queue<int, vector<int>, less<int> > p;//大根堆
 4     priority_queue<int, vector<int>, greater<int> > q;//小根堆
 5     void Insert(int num)
 6     {
 7         if(p.empty()||num<=p.top())p.push(num);
 8         else q.push(num);
 9         if(p.size()==q.size()+2)q.push(p.top()),p.pop();
10         if(p.size()+1==q.size())p.push(q.top()),q.pop();
11     }
12 
13     double GetMedian()
14     { 
15         return p.size()==q.size()?(p.top()+q.top())/2.0:p.top();//返回double
16     }
17 
18 };

 滑动窗口的最大值: 有重复,deque版的尺取法,队首记录窗口内最大值的下标

 1 class Solution {
 2 public:
 3     vector<int> maxInWindows(const vector<int>& num, unsigned int size)
 4     {
 5         vector<int> res;
 6         deque<int> s;// 双端队列
 7         for(int i=0;i<num.size();i++)
 8         {
 9             while(s.size()&&num[s.back()]<num[i])s.pop_back();//队尾<当前值,出队
10             while(s.size()&&i-s.front()+1>size)s.pop_front();//队首出窗,出队
11             s.push_back(i);//每次都入队
12             if(size&&i+1>=size)//大于窗口时开始写入
13                 res.push_back(num[s.front()]);
14         }
15          return res;
16     }
17 };

 把数组排成最小的数: to_string比较x+y, y+x 排序

二叉搜索树与双向链表: 递归和非递归解法,相对简单,注意树的递归思想,只考虑一个单元的处理,以及出口的设置。

 1     TreeNode* Convert(TreeNode* pRootOfTree)
 2     {
 3         if(pRootOfTree==NULL)return NULL;
 4         TreeNode* leftRoot = Convert(pRootOfTree->left);
 5         TreeNode* root = leftRoot;
 6         while(leftRoot!=NULL&&leftRoot->right!=NULL)leftRoot=leftRoot->right;
 7         if(leftRoot==NULL){
 8             root = pRootOfTree;
 9             pRootOfTree->left = NULL;
10         }
11         else {
12             leftRoot->right=pRootOfTree;
13             pRootOfTree->left=leftRoot;
14         }
15         TreeNode* rightRoot = Convert(pRootOfTree->right);
16         pRootOfTree->right = rightRoot;
17         if(rightRoot!=NULL)rightRoot->left = pRootOfTree;
18         return root;
19     }
20 
21 //  非递归中序遍历记录pre结点
22     TreeNode* Convert(TreeNode* pRootOfTree)
23 {
24     if(pRootOfTree==NULL)return NULL;
25     stack<TreeNode*> s;
26     TreeNode* p = pRootOfTree;
27     TreeNode* pre = NULL;
28     TreeNode* root = NULL;
29     while(p||s.size())
30     {
31         while(p!=NULL)
32         {
33             s.push(p);
34             p = p->left;
35         }
36         p = s.top();
37         s.pop();
38         
39         if(pre==NULL)root = p;
40         else
41         {
42             pre->right = p;
43             p->left = pre;
44         }
45         pre = p;
46         p=p->right;
47     }
48     return root;
49 }

 整数中1出现的次数(从1到n整数中1出现的次数): 找规律,从个位到最高位一个一个来,统计出现次数,编程之美统计整数中x出现的次数

 1 class Solution {
 2 public:
 3     int NumberOf1Between1AndN_Solution(int n)
 4     {
 5         if(n<0)return 0;
 6         int high,low,cur,tmp,i=1;
 7         high=n;
 8         int tot=0;
 9         while(high!=0)
10         {
11             high = n/(int)pow(10,i);
12             tmp = n%(int)pow(10,i);
13             cur = tmp/(int)pow(10,i-1);
14             low = tmp%(int)pow(10,i-1);
15             if(cur==1)
16                 tot+=high*(int)pow(10,i-1)+low+1;
17             else if(cur<1)
18                 tot+=high*(int)pow(10,i-1);
19             else
20                 tot+=(high+1)*(int)pow(10,i-1);
21             i++;
22         }
23         return tot;
24     }
25 };

 平衡二叉树: 普通递归求解,需要维护一个求深度的递归函数,根据该结点的左右子树高度差判断是否平衡,然后递归地对左右子树进行判断

 1 class Solution {
 2 public:
 3 bool IsBalanced_Solution(TreeNode * root) {
 4     if(root==NULL)return true;
 5     return IsBalanced_Solution(root->left)&&IsBalanced_Solution(root->right)&&abs(depth(root->left)-depth(root->right))<=1;
 6 }
 7     int depth(TreeNode * root)
 8     {
 9         if(root==NULL)return 0;
10         return max(depth(root->left),depth(root->right))+1;
11     }
12 };

这种做法有很明显的问题,在判断上层结点的时候,会多次重复遍历下层结点,增加了不必要的开销。如果改为从下往上遍历,如果子树是平衡二叉树,则返回子树的高度;如果发现子树不是平衡二叉树,则直接停止遍历,这样至多只对每个结点访问一次

 1 bool IsBalanced_Solution(TreeNode * root) {
 2     if(root==NULL)return true;
 3     return depth(root)!=-1;
 4 }
 5 
 6 int depth(TreeNode * root)
 7 {
 8     if(root==NULL)return 0;
 9     int left = depth(root->left);
10     if(left==-1)return -1;
11     int right = depth(root->right);
12     if(right==-1)return -1;
13     return abs(left-right)>1?-1:max(left,right)+1;
14 }

丑数:构造第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的
 1 class Solution {
 2 public:
 3     int GetUglyNumber_Solution(int index) {
 4         if(index<7)return index;
 5         vector<int> res(index);
 6         res[0]=1;
 7         int t2=0,t3=0,t5=0;
 8         for(int i=1;i<index;i++)
 9         {
10             res[i]=min(res[t2]*2,min(res[t3]*3,res[t5]*5));
11             if(res[i]==res[t2]*2)t2++;
12             if(res[i]==res[t3]*3)t3++;
13             if(res[i]==res[t5]*5)t5++;
14         }
15         return res[index-1];
16     }
17 };

 扑克牌顺子:考虑顺子的特征,不要考虑细节,抓住主要特征,1.除了大小王没有重复的,2.max-min<5

正则表达式匹配:分为后一个为*或不为*,两种情况,不为*则一个一个比,为*考虑两种情况,匹配0,和匹配1(匹配多个在匹配1后递归中包含)

 1 class Solution {
 2 public:
 3     bool match(char* str, char* pattern)
 4     {
 5 
 6         if (*str == '\0' && *pattern == '\0')
 7             return true;
 8         if (*str != '\0' && *pattern == '\0')
 9             return false;
10         //if the next character in pattern is not '*'
11         if (*(pattern+1) != '*')
12         {
13             if (*str == *pattern || (*str != '\0' && *pattern == '.'))
14                 return match(str+1, pattern+1);
15             else
16                 return false;
17         }
18         //if the next character is '*'
19         else
20         {
21             if (*str == *pattern || (*str != '\0' && *pattern == '.'))
22                 return match(str, pattern+2) || match(str+1, pattern);
23             else
24                 return match(str, pattern+2);
25         }
26     }
27 };

孩子们的游戏(圆圈中最后剩下的数):

1    int index=-1;
2      while(c.size()>1)
3      {
4          index = (m+index)%c.size();
5          c.erase(c.begin()+index);
6          index--;
7      }

 

二叉树的下一个结点: 中序遍历的下一个结点

 1 class Solution {
 2 public:
 3     TreeLinkNode* GetNext(TreeLinkNode* pNode)
 4     {
 5         if(pNode==NULL)return NULL;
 6         if(pNode->right!=NULL) // 从右子树找后继结点
 7         {
 8             pNode=pNode->right;
 9             while(pNode->left!=NULL)
10                 pNode=pNode->left;
11             return pNode;
12         }
13         while(pNode->next!=NULL) // 向上回溯,该节点是其父节点的左孩子才行
14         {
15             if(pNode->next->left==pNode)return pNode->next;
16             pNode=pNode->next;
17         }
18         return NULL; //返回到root还没有找到
19     }
20 };
posted @ 2018-07-20 16:37  demianzhang  阅读(411)  评论(0编辑  收藏  举报