赞
踩
最近,鉴于目前网上关于《剑指Offer》C++版的汇总资料特别少或者不规范,特整理了一版书中重要的考题,以备学习使用。
目录
题目:输入一个整数,输出该数二进制表示中1的个数,负数用补码表示。
思路:&运算通常用于二进制的取位操作,n&(n-1) 操作相当于把二进制表示中最右边的1变成0。
- int NumberOf1(int n)
- {
- int count = 0;
- while (n != 0)
- {
- n = n & (n-1);
- ++count;
- }
- return count;
- }
题目:输入一个整数,输出该二进制表示中0的个数,负数用补码表示。
思路:n&1操作相当于判断二进制最后一位数是否位1,左移相当乘2,右移除2,左移补0,有符号右移,正数补0,负数补1,无符号右移,均补0。每次无符号右移一位,同1做&运算,可以判断最后一位是否为1。
- int NumberOf0(int n)
- {
- int count = 0;
- while (n != 0)
- {
- if ((n&1) != 1)
- count++;
- n>>=1;
- }
- return count;
- }
题目:输入一个整数,输出该二进制中高位连续0的个数。
思路:0x80000000的二进制是1000 0000 0000 0000 0000 0000 0000 0000,仅最高位为1,每次左移一位,与最高位为1的二进制进行&操作,可以判断高位连续为0的个数。
- int numberOfLeading0(int n)
- {
- if (n == 0)
- return 32;
- int count = 0;
- int mask = 0x80000000;
- int m = n & mask;
- while (m == 0)
- {
- ++count;
- n <<= 1;
- m = n & mask;
- }
- return count;
- }
题目:输入一颗二叉搜索树,输出第k个节点。
思路:二叉搜索树是满足根节点值大于左节点,小于右节点的。中序遍历二叉搜索树可得到有序的,判断当前是否为k即可。
- struct treeNode {
- int val;
- treeNode * left;
- treeNode * right;
- treeNode(int x) : val(x), left(NULL), right(NULL) {}
- };
-
- treeNode *inorder(treeNode *root, int &k)
- {
- if (root == NULL) return NULL;
- treeNode * node = NULL;
-
- node = inorder(root->left,k);
-
- if (k-- == 1)
- node = root;
- if (k > 0)
- node = inorder(root->right, k);
- return node;
- }
-
- treeNode *KthNode(treeNode * pRoot, int k)
- {
- return inorder(pRoot, k);
- }

题目:输入一颗二叉树,输出从上到下,从左到右节点的值。
思路:可借助队列先进先出的特性,先层次遍历二叉树,依次存入队列中,最后再逐个弹出。
- struct treeNode {
- int val;
- treeNode * left;
- treeNode * right;
- treeNode(int x) : val(x), left(NULL), right(NULL) {}
- };
-
- vector<int> PrintFromTopToBottom(treeNode *root)
- {
- vector<int> vec;
- queue<treeNode*> queue;
-
- if (root == NULL) return vec;
- queue.push(root);
-
- while(!queue.empty())
- {
- vec.push_back(queue.front()->val);
- if (queue.front()->left != NULL)
- queue.push(queue.front()->left);
- if (queue.front()->right != NULL)
- queue.push(queue.front()->right);
- queue.pop();
- }
- }

题目:输入一颗二叉树,输出从上到下,从左到右节点的值,且每一层输出在一行。
思路:可借助队列先进先出的特性,先层次遍历二叉树,依次存入队列中,最后再逐个弹出。
- vector<vector<int>> Print(treeNode *root)
- {
- vector<vector<int>> vec;
- queue<treeNode*> queue;
-
- if (root == NULL) return vec;
- queue.push(root);
-
- while(!queue.empty())
- {
- int n = queue.size(); //n用于保存每一次队列的元素个数,即二叉树每一层节点的个数。
- vector<int> v;
- while(v.size() < n)
- {
- v.push_back(queue.front()->val);
- if(queue.front()->left)
- queue.push(queue.front()->left);
- if(queue.front()->right)
- queue.push(queue.front()->right);
- queue.pop();
- }
- vec.push_back(v);
- }
- }

题目:输入一个数据流,输出数据流中的中位数。
思路:最容易的办法是如果排序取中间值,显然这种不是面试官希望的答案。简单的办法是采取优先队列priority_queue,优先队列具有队列的所有特性,包括基本操作,只是在这基础上添加了内部的一个排序,它本质是一个堆实现的。把列表分成两半,分别用两个优先级队列来保存,左侧为数字越大优先级越高,右侧为数字越小优先级越高。那么取出两个优先级的top值即可。
- priority_queue<int, vector<int>, greater<int>> Rqueue; //最小堆
- priority_queue<int, vector<int>, less<int>> Lqueue; //最大堆
-
- void addNum(int num)
- {
- Lqueue.push(num);
- if ((Lqueue.size() - Rqueue.size())>1)
- {
- Rqueue.push(Lqueue.top());
- Lqueue.pop();
- }
- if(Rqueue.size() > 0 && Lqueue.top() > Rqueue.top())
- {
- int tmp = Lqueue.top();
- Lqueue.pop();
- Lqueue.push(Rqueue.top());
- Rqueue.top();
- Rqueue.push(tmp);
- }
- }
-
- double findMedian()
- {
- if (Lqueue.size() == Rqueue.size()) return 1.0*(Lqueue.top()+Rqueue.top())/2;
- else return Lqueue.top();
- }

题目:输入一颗二叉树的跟节点和一个整数,输出二叉树中结点值的和为输入整数的所有路径,返回的数组长度大的靠前。
思路:使用前序遍历的方式访问节点,使用二维向量vec存储全部路径,使用一维向量tmp存储当前路径。遍历二叉树的过程:按前序遍历顺序访问每一个节点。访问每个结点时,将结点添加到路径向量tmp中。如果当前结点是叶子结点,则判断当前路径是否是符合条件的路径,符合条件的路径存入到二维向量vec;如果当前结点不是叶子结点,则递归当前节点的左右子节点。
- struct treeNode {
- int val;
- treeNode * left;
- treeNode * right;
- treeNode(int x) : val(x), left(NULL), right(NULL) {}
- };
-
- vector<vector<int>> vec;
- vector<int> tmp;
-
- void search(treeNode* root, int target)
- {
- tmp.push_back(root->val);
-
- if (root->left == NULL && root->right == NULL) {
- if (root->val == target) {
- vec.push_back(tmp);
- }
- }
-
- if (root->left != NULL)
- search(root->left, target-root->val);
-
- if (root->right != NULL)
- search(root->right,target-root->val);
-
- if (!tmp.empty())
- tmp.pop_back();
-
- }
-
- void findTarget(treeNode* root, int target)
- {
- if (root == NULL) return;
-
- search(root,target);
- }

题目:输入某二叉树的前序遍历和中序遍历的结果,且前序遍历和中序遍历的结果中都不含重复的数字,输出重建二叉树。
思路:根据前序遍历找到根节点,再根据中序遍历找到左右子树,最后根据前序遍历得到左右子树的前序遍历和中序遍历,进行递归求解。
- struct treeNode {
- int val;
- treeNode * left;
- treeNode * right;
- treeNode(int x) : val(x), left(NULL), right(NULL) {}
- };
-
- treeNode* ReConstructTree(vector<int>& pre, vector<int>& mid)
- {
- if (pre.empty() || mid.empty())
- return NULL;
-
- treeNode *head = new treeNode(pre[0]);
-
- vector<int> leftpre, leftmid;
- vector<int> rightpre, rightmid;
-
- size_t root = 0;
-
- // 寻找根节点
- for (size_t i = 0;i < mid.size();++i)
- {
- if (mid[i] == pre[0])
- {
- root = i;
- break;
- }
- }
-
- // 左子树
- for (size_t i = 0;i < root;++i)
- {
- leftpre.push_back(pre[i+1]);
- leftmid.push_back(mid[i]);
- }
-
- // 右子树
- for (size_t i = root+1;i < mid.size();++i)
- {
- rightpre.push_back(pre[i]);
- rightmid.push_back(mid[i]);
- }
-
- head->left = ReConstructTree(leftpre,leftmid);
- head->right = ReConstructTree(rightpre,rightmid);
-
- return head;
- }

题目:输入两棵二叉树A,B,判断B是不是A的子结构。
思路:从树A的根节点开始寻找与B根节点相同的节点,如果相同,则继续比较其他节点,不相同,则继续。
- struct treeNode {
- int val;
- treeNode * left;
- treeNode * right;
- treeNode(int x) : val(x), left(NULL), right(NULL) {}
- };
-
- bool IsEqualTree(treeNode* pRoot1, treeNode* pRoot2)
- {
- if (pRoot2 == NULL)
- return true;
- if (pRoot1 == NULL)
- return false;
- if (pRoot1->val != pRoot2->val)
- return false;
-
- return IsEqualTree(pRoot1->left,pRoot2->left) && IsEqualTree(pRoot1->right,pRoot2->right);
- }
-
- bool HasSubTree(treeNode* pRoot1, treeNode* pRoot2)
- {
- bool result = false;
- if (pRoot2 != NULL && pRoot1 != NULL)
- {
- if (pRoot1->val == pRoot2->val) //头结点相同
- {
- result = IsEqualTree(pRoot1,pRoot2); //比较子节点
- }
- if (!result)
- {
- result = HasSubTree(pRoot1->left, pRoot2); //继续找头结点相同的节点
- }
- if (!result)
- {
- result = HasSubTree(pRoot1->right, pRoot2);
- }
- }
- return result;
- }

题目:输入一颗二叉树,输出源二叉树的镜像。
思路:递归转换二叉树即可。
- struct treeNode {
- int val;
- treeNode * left;
- treeNode * right;
- treeNode(int x) : val(x), left(NULL), right(NULL) {}
- };
-
- treeNode* imageTree(treeNode* pRoot)
- {
- if (!pRoot) return NULL;
-
- treeNode* iRoot = new treeNode(pRoot->val);
-
- iRoot->left = imageTree(pRoot->right);
- iRoot->right = imageTree(pRoot->left);
-
- return iRoot;
- }

题目:给定一个二叉树和其中的一个结点,请找出中序遍历顺序的下一个结点并且返回。注意,树中的结点不仅包含左右子结点,同时包含指向父结点的指针。
思路:1.如果存在右子节点,则右子节点的最左节点为下一个节点; 2.如果不存在右子节点,且该节点是父节点的左子节点,则父节点为下一个节点; 3.如果不存在右子节点,且该节点是父节点的右子节点,则向上找到一个节点是该其父节点的左孩子,则该其父节点为下一个节点。
- struct treeNode {
- int val;
- treeNode * left;
- treeNode * right;
- treeNode * next;
- treeNode(int x) : val(x), left(NULL), right(NULL), next(NULL) {}
- };
-
- treeNode* GetNext(treeNode *pNode)
- {
- if (pNode == NULL)
- return NULL;
- // 1.如果存在右子节点,则右子节点的最左节点为下一个节点
- if (pNode->right)
- {
- pNode = pNode->right;
- while(pNode->left)
- {
- pNode = pNode->left;
- }
- return pNode;
- }
- // 2.如果不存在右子节点,且该节点是父节点的左子节点,则父节点为下一个节点
- if (!pNode->right)
- {
- if(pNode->next && pNode->next->left == pNode)
- {
- return pNode->next;
- }
- }
- // 3.如果不存在右子节点,且该节点是父节点的右子节点,则向上找到一个节点是该其父节点的左孩子,则该其父节点为下一个节点
- if (!pNode->right)
- {
- if(pNode->next && pNode->next->right == pNode)
- {
- pNode = pNode->next;
- while (pNode->next)
- {
- if (pNode->next->left == pNode)
- return pNode->next;
- pNode = pNode->next;
- }
- }
- }
- return NULL;
- }

题目:输入一个字符串和其模式字符串,判断两者是否正则匹配。模式中的字符'.'表示任意一个字符,而'*'表示前一个字符重复出现任意次(包含0次)。
思路:按照pattern下个字符为'*'进行递归判断。
- bool match(const char* str, const char* pattern)
- {
- if (str == NULL || pattern == NULL)
- return false;
- // 字符串都匹配结束,返回true
- if (*str == '\0' && *pattern == '\0')
- return true;
- // pattern匹配结束,str还没匹配结束,返回false
- if (*str != '\0' && *pattern == '\0')
- return false;
- // pattern下个字符为'*'
- if (*(pattern+1) == '*')
- {
- // 1. 当前字符相等,则str后移一位或者pattern后移二位
- if (*str == *pattern || (*str != '\0' && *pattern == '.'))
- {
- return match(str+1, pattern) || match(str, pattern+2);
- }
- // 2. 当前字符不相等,则pattern后移二位
- else
- {
- return match(str, pattern+2);
- }
- }
- // pattern下个字符不是'*'
- else
- {
- // 当前字符相等,则str和pattern均后移一位,继续比较下一位
- if (*str == *pattern || (*str != '\0' && *pattern == '.'))
- return match(str+1, pattern+1);
- else
- return false;
- }
- }

题目:输入一个字符串,判断该字符串是否表示数值(包括整数、小数、正负数、科学计数法等)
思路:按照当前字符为正负号、小数点、指数e进行循环判断。
- bool IsNumeric(char * str)
- {
- if (str == NULL)
- return false;
- // 第一位为符号位
- if (*str == '+' || *str == '-')
- ++str;
- // 第一位不为数字,且不为'.'
- else if ((*str < '0' || *str > '9') && *str != '.')
- return false;
- bool flag = false; //是否出现过'.'或者'e'
- while (*str != '\0')
- {
- // 当前字符为符号,且前一个字符不为'e',不为最后一个数
- if ((*str == '+' || *str == '-') && *(str-1) != 'e' && *(str-1) != 'E' && *(str+1) >= '0' && *(str+1) <= '9')
- return false;
- // 当前字符为'.'
- if (*str == '.')
- {
- // 已经出现或者最后一位为'.'或者'e'或者'E'
- if (flag || *(str+1) == '\0')
- return false;
- else
- flag = true;
- }
- // 当前字符为'e'或者'E'
- else if (*str == 'e' || *str == 'E')
- {
- // 已经出现或者最后一位为'.'或者'e'或者'E'
- if (flag || *(str+1) == '\0')
- return false;
- else {
- flag = true;
- }
- }
- else if(*str < '0' || *str > '9')
- {
- return false;
- }
- ++str;
- }
- return true;
- }

题目:在一个字符串(0<=字符串长度<=10000,全部由字母组成)中找到第一个只出现一次的字符,并返回它的位置, 如果没有则返回 -1(需要区分大小写)。
思路:利用map形成键值对,用于存储字符串中每个字符出现的次数,最后再遍历一遍字符串,找到第一个map值为1的索引即可。
- int FirstNotRepeatingChar(string str)
- {
- std::map<char,int> map;
- for (int i=0;i < str.size();++i)
- {
- map[str[i]]++;
- }
- for (int i=0;i < str.size();i++)
- {
- if (map[str[i]] == 1)
- return i;
- }
- return -1;
- }
题目:对于一个给定的字符序列S,请你把其循环左移K位后的序列输出。例如,字符序列S=”abcXYZdef”,要求输出循环左移3位后的结果,即“XYZdefabc”。
思路:拼接两次字符序列,然后从第n位开始取字符序列长度的字符串即可。例如,SS=”abcXYZdefabcXYZdef”,左移3位的话,从SS的第3个字符开始取,则翻转后的字符串为“XYZdefabc”。
- string reversal(string str, int k)
- {
- string doubleS = str + str;
- string reverS;
- for (int i=k;i < str.size()+k;i++)
- {
- reverS += doubleS[i];
- }
- return reverS;
- }
题目:给定一个数组A[0,1,...,n-1],请构建一个数组B[0,1,...,n-1],其中B中的元素B[i]=A[0]*A[1]*...*A[i-1]*A[i+1]*...*A[n-1],不能使用除法。
思路:用矩阵的方式,先计算左下三角,再计算右上三角,将B[i]=A[0]*A[1]*...*A[i-1]*A[i+1]*...*A[n-1]看成两部分来计算,即:A[0]*A[1]*...*A[i-1]和 *A[i+1]*...*A[n-1]两部分的乘积就是B[i]。

- vector<int> multiply(vector<int> A) {
- int num = A.size();
- vector<int> B1(num,1);
- vector<int> B2(num,1);
- vector<int> B(num,1);
-
- // 计算左下角B1
- for (int i=1;i < num;++i)
- {
- B1[i] = B1[i-1]*A[i-1];
- }
- // 计算右上角B2
- for (int i=num-2;i >= 0;--i)
- {
- B2[i] = B2[i+1]*A[i+1];
- }
- // 计算矩阵B
- for (int i=0;i < num;i++)
- {
- B[i] = B1[i]*B2[i];
- }
- return B;
- }

题目:在一个二维数组中,每一行都按照从左到右递增的顺序排序,每一列都按照从上到下递增的顺序排序。请完成一个函数,输入这样的一个二维数组和一个整数,判断数组中是否含有该整数。
思路:从右上角开始,若小,向下走,删除一行,若大,向左走,删除一列。
- bool FindElement(vector<vector<int>> vecs, int target)
- {
- if (vecs.empty())
- return false;
- int m = 0;
- int n = vecs[0].size()-1;
- while (m < vecs.size() && n >= 0)
- {
- if (vecs[m][n] == target)
- return true;
- else if (vecs[m][n] < target)
- m++;
- else
- n--;
- }
- return false;
- }

题目:给一个数组,返回它的最大连续子序列的和。
思路:利用动态规划解决,dp[i] = max(vec[i],dp[i-1]+vec[i])
- int MaxContinuesSubarraySum(vector<int> vec)
- {
- vector<int> dp(vec.size(),0);
- for (int i = 1;i < vec.size();i++)
- {
- dp[i] = vec[i] > (dp[i-1]+vec[i])?vec[i]:(dp[i-1]+vec[i]);
- }
- int max = 0;
- for (int i = 0;i < dp.size();++i)
- {
- if (max < dp[i])
- max = dp[i];
- }
- return max;
- }
参考文献
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。