zoukankan      html  css  js  c++  java
  • 剑指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 == '' && *pattern == '')
     7             return true;
     8         if (*str != '' && *pattern == '')
     9             return false;
    10         //if the next character in pattern is not '*'
    11         if (*(pattern+1) != '*')
    12         {
    13             if (*str == *pattern || (*str != '' && *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 != '' && *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 };
  • 相关阅读:
    SignalR客户端和服务端编写,winfrom端
    SQL 发送邮件msdb.dbo.sp_send_dbmail
    Stimulsoft打印内容横向,变成竖向,解决方法
    Chrome浏览器所有页面崩溃
    SQL存储过程直接传表
    c#SignalR一次发送最大数据量
    c#USB扫描枪,防止输入框手动输入
    SQLPrompt_9.5.5.9830破解版含注册机_永久激活不掉线
    SQL快捷键设置
    USB HDI 通信
  • 原文地址:https://www.cnblogs.com/demian/p/9342282.html
Copyright © 2011-2022 走看看