剑指offer题目(所有题目解答)
面试题63:股票的最大利润
暴力搜索复杂度O(N^2),换个思路,遍历到某点,只需要记录改点之前最小的点即可,二者之差即为当前最大利润,时间复杂度O(N)
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 int MaxDiff(vector<int> numbers) 2 { 3 int len = numbers.size(); 4 if (len < 2) 5 return 0; 6 int min = numbers[0]; 7 int maxDiff = numbers[1] - min; 8 9 for (int i = 2; i < len; i++) 10 { 11 if (numbers[i - 1] < min) 12 min = numbers[i - 1]; 13 int currentDiff = numbers[i] - min; 14 if (currentDiff > maxDiff) 15 maxDiff = currentDiff; 16 } 17 return maxDiff; 18 }
面试题62:圆圈中最后剩下的数字
典型的约瑟夫环的问题,但是需要注意到达链表末尾时需要把指针移到头部
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 class Solution { 2 public: 3 int LastRemaining_Solution(int n, int m) 4 { 5 if(n < 1 || m < 1) 6 return -1; 7 8 unsigned int i = 0; 9 10 list<int> numbers; 11 for(i = 0; i < n; ++ i) 12 numbers.push_back(i); 13 14 auto current = numbers.begin(); 15 while(numbers.size() > 1) 16 { 17 for(int i = 1; i < m; ++ i) 18 { 19 current ++; 20 if(current == numbers.end()) 21 current = numbers.begin(); 22 } 23 24 auto next = ++ current; 25 if(next == numbers.end()) 26 next = numbers.begin(); 27 28 -- current; 29 numbers.erase(current); 30 current = next; 31 } 32 33 return *current; 34 } 35 };
面试题61:扑克牌中的顺子
三步走:排序;统计大小王数量;统计gap数量 如果后者小于前者,则可以凑成顺子,反之不行
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 class Solution { 2 public: 3 bool IsContinuous(vector<int> numbers) { 4 int len = numbers.size(); 5 if (len < 1) 6 return false; 7 sort(numbers.begin(), numbers.end()); 8 int numberOfZero = 0; 9 int numberOfGap = 0; 10 //统计数组中0 的个数 11 for (int i = 0; i < len && numbers[i] == 0; i++) 12 numberOfZero++; 13 int small = numberOfZero; 14 int big = small + 1; 15 while (big < len) 16 { 17 //如果有对子,肯定不是顺子 18 if (numbers[small] == numbers[big]) 19 return false; 20 numberOfGap += numbers[big] - numbers[small] - 1; 21 small = big; 22 big++; 23 } 24 return numberOfZero >= numberOfGap ? true : false; 25 } 26 };
面试题57-2:和为s的连续正数序列
方法与1类似,遍历然后根据数字求和之后的大小和sum大小之间的关系来确定指针的移动方向,注意while循环的边界条件,while中必须为small<middle,而不能是<=
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 class Solution { 2 public: 3 vector<vector<int> > FindContinuousSequence(int sum) { 4 vector<vector<int> > res; 5 if (sum < 3) 6 return res; 7 int small = 1; 8 int big = 2; 9 int middle = (sum + 1) / 2; 10 int curSum = small + big; 11 while (small < middle) // 这里千万不要写成 <=,否则在最后一次循环执行过程中,会陷入死循环 12 { 13 if (curSum == sum) 14 { 15 vector<int> temp; 16 for (int i = small; i <= big; i++) 17 temp.push_back(i); 18 res.push_back(temp); 19 } 20 //解释一下这里为何要将small往前而不是big往后,因为big往后的验证过了 21 22 while (curSum > sum &&small < middle) //这里需要注意,两处while条件中 small middle中的符号必须一致,都是< 23 { 24 curSum -= small; 25 small++; 26 if (curSum == sum) 27 { 28 vector<int> temp; 29 for (int i = small; i <= big; i++) 30 temp.push_back(i); 31 res.push_back(temp); 32 } 33 } 34 big++; 35 curSum += big; 36 } 37 return res; 38 39 } 40 }; 41 42 int main() { 43 Solution A; 44 vector<vector<int> > v; 45 v = A.FindContinuousSequence(3); 46 for (int i = 0; i < v.size(); i++) 47 { 48 for (int j = 0; j < v[i].size(); j++) 49 cout << v[i][j] << " "; 50 cout << endl; 51 52 } 53 //cout << "hhh" << endl; 54 system("pause"); 55 return 0; 56 }
面试题57:和为s的两个数字
经典的双指针为题,首尾开始往中间遍历
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 class Solution { 2 public: 3 vector<int> FindNumbersWithSum(vector<int> array, int sum) { 4 vector<int> res; 5 int ahead = 0; 6 int behind = array.size() - 1; 7 while (ahead < behind) 8 { 9 if (array[ahead] + array[behind] == sum) 10 { 11 res.push_back(array[ahead]); 12 res.push_back(array[behind]); 13 break; 14 } 15 else if (array[ahead] + array[behind] < sum) 16 ahead++; 17 else 18 behind--; 19 } 20 return res; 21 } 22 };
面试题56-1:数组中数字出现的次数
可以直接排序然后遍历搜索,时间复杂度O(nlogn)
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 class Solution { 2 public: 3 void FindNumsAppearOnce(vector<int> data,int* num1,int *num2) { 4 sort(data.begin(),data.end()); 5 int count = 0; 6 for (int i = 0; i < data.size();) 7 { 8 if (data[i] == data[i+1]) 9 i = i+2; 10 else 11 { 12 if (count == 0) 13 { 14 *num1 = data[i]; 15 count++; 16 i++; 17 } 18 else 19 { 20 *num2 = data[i]; 21 break; 22 } 23 } 24 } 25 } 26 };
更好的方式是利用数位的异或运算,详细内容请参考书本
面试题55:二叉树的深度
递归的典型应用
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
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 int TreeDepth(TreeNode* pRoot) 13 { 14 if (!pRoot) 15 return 0; 16 return 1 + max(TreeDepth(pRoot->left),TreeDepth(pRoot->right)); 17 } 18 };
面试题55-2:平衡二叉树
和1挺像,如果调用1中的求深度的函数来构造递归,可以运行,但是会多次重复遍历节点,会存在问题,会有节点多次重复调用
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 class Solution { 2 public: 3 bool IsBalanced_Solution(TreeNode* pRoot) { 4 if (pRoot == nullptr) 5 return true; 6 int left = TreeDepth(pRoot->left); 7 int right = TreeDepth(pRoot->right); 8 int diff = left - right; 9 if (diff > 1 || diff < -1) 10 return false; 11 return IsBalanced_Solution(pRoot->left) && IsBalanced_Solution(pRoot->right); 12 } 13 int TreeDepth(TreeNode* pRoot) 14 { 15 if (!pRoot) 16 return 0; 17 return 1 + max(TreeDepth(pRoot->left),TreeDepth(pRoot->right)); 18 } 19 20 };
其实可以有改进,做到每个节点只访问一次,也就是后序遍历二叉树时,遍历到某个节点之后记录下来它的深度,就可以一边遍历一遍判断每个节点是否平衡。注意:递归时一定要要传递指针(或者引用)而不是数值
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 class Solution { 2 public: 3 bool IsBalanced_Solution(TreeNode* pRoot) { 4 int depth = 0; 5 return isBalanced(pRoot, depth); 6 } 7 bool isBalanced(TreeNode* pRoot, int& depth ) 8 { 9 if (pRoot == nullptr) 10 { 11 depth = 0; 12 return true; 13 } 14 int left, right; 15 if (isBalanced(pRoot->left, left) && isBalanced(pRoot->right, right)) 16 { 17 int diff = left - right; 18 if (diff >= -1 && diff <= 1) 19 { 20 depth = 1 + max(left, right); 21 return true; 22 } 23 } 24 return false; 25 } 26 };
面试题54:二叉搜索树的第K大节点
就是个中序遍历,但是书上代码不是很好理解,其实可以中序遍历所有节点然后读取第k个就可以
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
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 */ 11 class Solution { 12 public: 13 TreeNode* KthNode(TreeNode* pRoot, int k) 14 { 15 if (pRoot == nullptr || k == 0) 16 return nullptr; 17 return KthNodeCore(pRoot,k); 18 } 19 TreeNode* KthNodeCore(TreeNode* pRoot, int &k) 20 { 21 TreeNode* temp = nullptr; 22 if (pRoot->left != nullptr) 23 temp = KthNodeCore(pRoot->left, k); 24 if (temp == nullptr) 25 { 26 if (k==1) 27 temp = pRoot; 28 k--; 29 } 30 if (temp == nullptr && pRoot->right != nullptr) 31 temp = KthNodeCore(pRoot->right, k); 32 return temp; 33 } 34 35 36 };
面试题53-3:数组中数值和下标相等的元素
思路与53-2类似,二分查找
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 int GetNumberSameAsIndex(vector<int> data) 2 { 3 if (data.empty()) 4 return -1; 5 int lo = 0, hi = data.size() - 1; 6 while (lo <= hi) 7 { 8 int mid = (lo + hi) >> 1; 9 if (data[mid] > mid) 10 hi = mid - 1; 11 else if (data[mid] < mid) 12 lo = mid + 1; 13 else 14 return mid; 15 } 16 if (lo == data.size()) 17 return data.size(); 18 return -1; 19 //return data.size(); 20 }
面试题53-2:0~n-1中缺失的数字
可以顺序遍历,但不是最优解法,没有利用数组递增特性,思路与53-1相似,寻找第一个数字与对应下标不一样的元素即可
我自己写的
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 //这种情我自己写的,也算是做出来了,但是感觉讨论的情况太多,而且很容易出现遗漏或者说数组访问越界 2 //还是应该学习《剑指offer》的思路 3 int GetMissingNumber(vector<int> data) 4 { 5 int lo = 0, hi = data.size() - 1; 6 while (lo <= hi) 7 { 8 if (data[0] != 0) 9 return 0; 10 if (data[data.size() - 1] == data.size() - 1) 11 return data.size(); 12 int mid = (lo + hi) / 2; 13 if (data[mid] != mid && data[mid - 1] == mid - 1) 14 return mid; 15 else if (data[mid] == mid && data[mid + 1] != mid + 1) 16 return mid + 1; 17 else if (data[mid] == mid && data[mid + 1] == mid + 1) 18 lo = mid + 1; 19 else if (data[mid] != mid && data[mid - 1] != mid - 1) 20 hi = mid - 1; 21 } 22 //return data.size(); 23 }
书上的
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 int GetMissingNumber(vector<int> data) 2 { 3 if (data.empty()) 4 return -1; 5 int lo = 0, hi = data.size() - 1; 6 while (lo <= hi) 7 { 8 int mid = (lo + hi) >> 1; 9 if (data[mid] != mid) 10 { 11 if (mid == 0 || data[mid - 1] == mid - 1) 12 return mid; 13 hi = mid - 1; 14 } 15 else 16 lo = mid + 1; 17 } 18 if (lo == data.size()) 19 return data.size(); 20 return -1; 21 //return data.size(); 22 }
面试题53-1:在排序数组中查找数字
可以从头到尾数,时间复杂度O(n)
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 class Solution { 2 public: 3 int GetNumberOfK(vector<int> data ,int k) { 4 int res = 0; 5 for (int i = 0; i < data.size(); i++) 6 if (data[i] == k) 7 res++; 8 return res; 9 } 10 };
但是没有利用排序特性,一般看见“排序”二字,就应该想到,二分查找,最后可以得到log(n)的复杂度,这道题的关键在于如何寻找第一个该数字和最后一个该数字的位置(牛客网上折腾至少一个半小时,自己写的死活过不去,还是粘贴官方代码然后修改,mmp!!!!)
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 class Solution { 2 public: 3 int GetNumberOfK(vector<int> data ,int k) { 4 int number = 0; 5 6 if(data.size() > 0) 7 { 8 int first = GetFirstK(data, k, 0, data.size() - 1); 9 int last = GetLastK(data, k, 0, data.size() - 1); 10 11 if(first > -1 && last > -1) 12 number = last - first + 1; 13 } 14 return number; 15 } 16 17 int GetFirstK(vector<int> data, int k, int start, int end) 18 { 19 if(start > end) 20 return -1; 21 22 int middleIndex = (start + end) / 2; 23 int middleData = data[middleIndex]; 24 25 if(middleData == k) 26 { 27 if((middleIndex > 0 && data[middleIndex - 1] != k) 28 || middleIndex == 0) 29 return middleIndex; 30 else 31 end = middleIndex - 1; 32 } 33 else if(middleData > k) 34 end = middleIndex - 1; 35 else 36 start = middleIndex + 1; 37 38 return GetFirstK(data, k, start, end); 39 } 40 41 // 找到数组中最后一个k的下标。如果数组中不存在k,返回-1 42 int GetLastK(vector<int> data, int k, int start, int end) 43 { 44 if(start > end) 45 return -1; 46 47 int middleIndex = (start + end) / 2; 48 int middleData = data[middleIndex]; 49 50 if(middleData == k) 51 { 52 if((middleIndex < data.size() - 1 && data[middleIndex + 1] != k) 53 || middleIndex == data.size() - 1) 54 return middleIndex; 55 else 56 start = middleIndex + 1; 57 } 58 else if(middleData < k) 59 start = middleIndex + 1; 60 else 61 end = middleIndex - 1; 62 63 return GetLastK(data, k, start, end); 64 } 65 };
次日早上重新学习之后自己编写完成上面的程序,一个循环,一个递归,调试一下居然通过了,开心,编程序就是这样,很多技巧书本无法交给我们,只能依靠自己一次次碰壁来获得,加油,希望这是一个好的开始,然后有一个坚持,就酱紫!——2018.06.16
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 class Solution { 2 public: 3 int GetNumberOfK(vector<int> data ,int k) { 4 int res = 0; 5 if (data.empty()) 6 return res; 7 int first = getFirstK(data,k,0,data.size()-1); 8 int last = getLastK(data,k,0,data.size()-1); 9 if (first != -1 && last != -1) 10 return last - first + 1; 11 return res; 12 13 } 14 //递归写法 15 int getFirstK(vector<int> data, int k, int lo, int hi) 16 { 17 if (lo > hi) //这里很重要,是递归的终止条件 18 return -1; 19 int mid = (lo + hi )/ 2; 20 if (data[mid] > k) 21 return getFirstK(data,k,lo,mid - 1); 22 // hi = mid - 1; 23 else if (data[mid] < k) 24 return getFirstK(data,k,mid+1,hi); 25 // lo = mid + 1; 26 else //if (data[mid] == k) 27 { 28 if (mid == 0 || data[mid - 1] != k) 29 return mid; 30 else 31 return getFirstK(data,k,lo,mid - 1); 32 } 33 return -1; 34 } 35 36 //循环写法 37 int getLastK(vector<int> data, int k, int lo, int hi) 38 { 39 int mid = (lo + hi) / 2; 40 while (lo <= hi) 41 { 42 if (data[mid] > k) 43 hi = mid - 1; 44 else if (data[mid] < k) 45 lo = mid + 1; 46 else //if (data[mid] == k) 47 { 48 if (mid == data.size()-1 || data[mid + 1] != k) 49 return mid; 50 else 51 lo = mid + 1; 52 } 53 mid = (lo + hi)/2; 54 } 55 return -1; 56 } 57 };
牛客评论区看见的更巧的解法,寻找k-0.5和k+0.5这俩数字的位置,然后相减即可
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 class Solution { 2 public: 3 int GetNumberOfK(vector<int> data ,int k) { 4 return biSearch(data, k+0.5) - biSearch(data, k-0.5) ; 5 } 6 private: 7 int biSearch(const vector<int> & data, double num){ 8 int s = 0, e = data.size()-1; 9 while(s <= e){ 10 int mid = (e - s)/2 + s; 11 if(data[mid] < num) 12 s = mid + 1; 13 else if(data[mid] > num) 14 e = mid - 1; 15 } 16 return s; 17 } 18 };
牛客网,循环与递归的写法
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 public class Solution { 2 public int GetNumberOfK(int [] array , int k) { 3 int length = array.length; 4 if(length == 0){ 5 return 0; 6 } 7 int firstK = getFirstK(array, k, 0, length-1); 8 int lastK = getLastK(array, k, 0, length-1); 9 if(firstK != -1 && lastK != -1){ 10 return lastK - firstK + 1; 11 } 12 return 0; 13 } 14 //递归写法 15 private int getFirstK(int [] array , int k, int start, int end){ 16 if(start > end){ 17 return -1; 18 } 19 int mid = (start + end) >> 1; 20 if(array[mid] > k){ 21 return getFirstK(array, k, start, mid-1); 22 }else if (array[mid] < k){ 23 return getFirstK(array, k, mid+1, end); 24 }else if(mid-1 >=0 && array[mid-1] == k){ 25 return getFirstK(array, k, start, mid-1); 26 }else{ 27 return mid; 28 } 29 } 30 //循环写法 31 private int getLastK(int [] array , int k, int start, int end){ 32 int length = array.length; 33 int mid = (start + end) >> 1; 34 while(start <= end){ 35 if(array[mid] > k){ 36 end = mid-1; 37 }else if(array[mid] < k){ 38 start = mid+1; 39 }else if(mid+1 < length && array[mid+1] == k){ 40 start = mid+1; 41 }else{ 42 return mid; 43 } 44 mid = (start + end) >> 1; 45 } 46 return -1; 47 } 48 }
面试题42:连续子数组最大和
动态规划的思想,线性复杂度,数组中某个数字之前的所有数字的和如果小于0,那就不用再考虑了,加上只有比本身还小,无意义
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 class Solution { 2 public: 3 int FindGreatestSumOfSubArray(vector<int> array) { 4 if (array.size() < 1) 5 return 0; 6 int nCurSum = 0; 7 int nGreatestSum = INT_MIN; 8 for (int i = 0; i < array.size(); i++) 9 { 10 if (nCurSum <= 0) 11 nCurSum = array[i]; 12 else 13 nCurSum += array[i]; 14 if (nCurSum > nGreatestSum) 15 nGreatestSum = nCurSum; 16 } 17 return nGreatestSum; 18 19 } 20 };
二刷:AC
面试题36:二叉搜索树与双向链表
这道题主要涉及中序遍历,以及指针方向的调整,难点在于递归的过程,个人认为递归并不需要知道细节,只需要卡准更简单的递归形式和递归的终止条件即可,根本不需要单步调试,详细过程参看书本
代码如下:
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
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 11 class Solution { 12 public: 13 TreeNode* Convert(TreeNode* pRootOfTree) 14 { 15 TreeNode *pLastNodeInList = nullptr; 16 ConvertCore(pRootOfTree,&pLastNodeInList); 17 TreeNode *head = pLastNodeInList; 18 while (head != nullptr && head->left != nullptr) 19 head = head->left; 20 return head; 21 } 22 23 void ConvertCore(TreeNode *pNode, TreeNode **pLastNodeInList) 24 { 25 if (pNode == nullptr) 26 return; 27 TreeNode *cur = pNode; 28 if (pNode->left != nullptr) 29 ConvertCore(pNode->left,pLastNodeInList); 30 cur->left = *pLastNodeInList; 31 if (*pLastNodeInList != nullptr) 32 (*pLastNodeInList)->right = cur; 33 *pLastNodeInList = cur; 34 if (cur->right != nullptr) 35 ConvertCore(cur->right,pLastNodeInList); 36 } 37 };
面试题35:复杂链表的赋值
思路很直接,三步走:1、赋值节点并连接在原节点后方;2、还原新节点的random指针;3、拆分链表
代码如下:
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 /* 2 struct RandomListNode { 3 int label; 4 struct RandomListNode *next, *random; 5 RandomListNode(int x) : 6 label(x), next(NULL), random(NULL) { 7 } 8 }; 9 */ 10 class Solution { 11 public: 12 RandomListNode* Clone(RandomListNode* pHead) 13 { 14 if (!pHead) 15 return nullptr; 16 RandomListNode *pNode = pHead; 17 while (pNode) 18 { 19 RandomListNode *pCloned = new RandomListNode(pNode->label); 20 pCloned->next = pNode->next; 21 pNode->next = pCloned; 22 pNode = pCloned->next; 23 } 24 pNode = pHead; 25 while (pNode) 26 { 27 if (pNode->random) 28 pNode->next->random = pNode->random->next; 29 pNode = pNode->next->next; 30 } 31 RandomListNode *newHead = pHead->next; 32 pNode = pHead; 33 while (pNode) 34 { 35 RandomListNode *l1 = pNode->next; 36 pNode->next = l1->next; 37 pNode = pNode->next; 38 if (l1->next) 39 l1->next = l1->next->next; 40 } 41 /*for (l1 = pHead; l1 != nullptr; l1 = l1->next) 42 { 43 l2 = l1->next; 44 l1->next = l2->next; 45 if (l2->next != nullptr) 46 l2->next = l2->next->next; 47 }*/ 48 return newHead; 49 } 50 };
2018-03-22二刷:前两步没问题,最后一步折腾我很久,最后才知道是处理最后一个节点的null时出问题了,我不小心修改了原链表末尾的null。链表类题目一定要小心头和尾部;另外,用while循环自己一定要处理好循环变量的增加与范围。
面试题33:二叉搜索树的后序遍历序列
书上的思路不难理解,利用递归进行,但是把书上的内容生搬硬套使用向量这让我很不爽,郁闷中……
代码如下:
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 class Solution { 2 public: 3 bool VerifySquenceOfBST(vector<int> sequence) { 4 int len = sequence.size(); 5 if (len == 0) 6 return false; 7 int* array = new int[len]; 8 for (int i = 0; i < len; i++) 9 array[i] = sequence[i]; 10 return myVerifySquenceOfBST(array,len); 11 } 12 bool myVerifySquenceOfBST(int sequence[], int length) 13 { 14 if (sequence == nullptr || length <= 0) 15 return false; 16 int root = sequence[length - 1]; 17 //二叉搜索树中左子树节点值小于根节点的值 18 int i = 0; 19 for (; i < length - 1; i++) 20 if (sequence[i]>root) 21 break; 22 int j = i; 23 for (; j < length - 1; j++) 24 if (sequence[j] < root) 25 return false; 26 //判断左子树是否为二叉树; 27 bool left = true; 28 if (i > 0) 29 left = myVerifySquenceOfBST(sequence, i); 30 //判断右子树是否为二叉树; 31 bool right = true; 32 if (i < length - 1) 33 right = myVerifySquenceOfBST(sequence + i, length - 1 - i); 34 return left&&right; 35 36 37 } 38 39 };
面试题32-3:之字形打印二叉树
与32-1类似,可以看做是层序遍历的变种,牛客网上的解答仍然是利用层序遍历,只是在偶数层的vector需要调用reverse函数进行翻转;《剑指offer》书上提供的解法则是是用来两个栈
代码如下:
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 struct TreeNode { 2 int val; 3 struct TreeNode *left; 4 struct TreeNode *right; 5 TreeNode(int x) : 6 val(x), left(NULL), right(NULL) { 7 } 8 }; 9 10 //这道题本质上也是一个层序遍历,只是偶数层需要反序;书上给的方法是两个栈 11 //我个人觉得有点绕,牛客网上的解答有的是用层序遍历,调用reverse函数反转偶数层 12 //但是有人说自己面试时用reverse被面试官批评,海量数据用这个会很慢 13 //我最开始的思路是用双端队列来做,因为双端队列本身就兼具队列和栈的特性 14 //实践之后证明双端队列并不好用,尽量少用,不伦不类,容易混乱 15 //这里队列存的是指针而非数值,涉及到访问左右子节点的问题,必须仔细,这也是用双端队列不方便的地方 16 class Solution 17 { 18 public: 19 vector<vector<int> > Print(TreeNode* pRoot) 20 { 21 vector<vector<int>> res; 22 if (pRoot == nullptr) 23 return res; 24 queue<TreeNode*> nodes; 25 bool even = false; 26 nodes.push(pRoot); 27 while (!nodes.empty()) 28 { 29 vector<int> vec; 30 int size = nodes.size(); 31 for (int i = 0; i < size; i++) 32 { 33 TreeNode* pNode = nodes.front(); 34 nodes.pop(); 35 vec.push_back(pNode->val); 36 if (pNode->left != nullptr) 37 nodes.push(pNode->left); 38 if (pNode->right != nullptr) 39 nodes.push(pNode->right); 40 } 41 if (even) 42 reverse(vec.begin(), vec.end()); 43 even = !even; 44 res.push_back(vec); 45 } 46 return res; 47 } 48 49 };
面试题32-2:分行从上到下方打印二叉树(层序遍历/广度优先遍历)
与32-1类似,但是细节方面略有差异,实现上是用的向量的向量
代码如下:
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
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 */ 11 class Solution { 12 public: 13 vector<vector<int> > Print(TreeNode* pRoot) { 14 vector<vector<int> > result; //类似于二维数组 15 if (pRoot == nullptr) 16 return result; 17 queue<TreeNode*> nodes; //建立一个队列,存放层序遍历的节点 18 nodes.push(pRoot); 19 while (!nodes.empty()) 20 { 21 vector<int> nodesInLevel; //存放每一层的节点,其实是局部变量,每次循环结束之后会自动释放的 22 int lo = 0, hi = nodes.size();//队列中元素始末位置,要求队列中元素是二叉树中一层的元素 23 while (lo < hi) 24 { 25 TreeNode* temp = nodes.front(); //这里其实应该考虑要不要释放的 26 nodesInLevel.push_back(temp->val); 27 nodes.pop(); 28 if (temp->left) nodes.push(temp->left); 29 if (temp->right) nodes.push(temp->right); 30 lo++; 31 } 32 result.push_back(nodesInLevel); 33 } 34 return result; 35 } 36 37 };
面试题32-1:从上到下方打印二叉树(层序遍历/广度优先遍历)
先访问的节点其子节点也会被先访问,符合队列的特征
代码如下:
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
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 11 class Solution { 12 public: 13 vector<int> PrintFromTopToBottom(TreeNode* root) { 14 vector<int> result; 15 if (root == nullptr) //这里必须加上对空指针的处理,否则牛客网会报“段错误” 16 return result; 17 deque<TreeNode*> dequeTreeNode;//利用队列来实现; 18 dequeTreeNode.push_back(root); 19 while (dequeTreeNode.size()) 20 { 21 TreeNode* pNode = dequeTreeNode.front(); 22 result.push_back(pNode->val); 23 dequeTreeNode.pop_front(); 24 if (pNode->left) 25 dequeTreeNode.push_back(pNode->left); 26 if (pNode->right) 27 dequeTreeNode.push_back(pNode->right); 28 } 29 return result; 30 } 31 };
面试题31:栈的压入、弹出序列
利用辅助栈,重现栈的压入弹出操作。
代码如下:
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 class Solution { 2 public: 3 bool IsPopOrder(vector<int> pushV, vector<int> popV) 4 { 5 if (pushV.empty() || popV.empty() || pushV.size() != popV.size()) 6 return false; 7 std::stack<int> sta; //辅助栈 8 int pPop = 0;//用来记录弹出序列的读取位置 9 //读取栈的压入序列,压入sta,当读到的数字与弹出序列pPop对应 10 //的数字相同,弹出该数字,pPop+1 11 //重复以上过程,若sta最后为空,则该序列是压栈序列的弹出序列,否则不是 12 for (int i = 0; i < pushV.size(); i++) 13 { 14 sta.push(pushV[i]); 15 while (!sta.empty() && sta.top() == popV[pPop]) 16 { 17 sta.pop(); 18 pPop++; 19 } 20 } 21 if (sta.empty()) 22 return true; 23 return false; 24 } 25 };
面试题30:包含min函数的栈
这道题应当学会把抽象问题具体化。可以画图分析。关键点在于利用辅助栈处理,辅助栈中包含的是当前情况下的最小的元素
代码如下:
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 class Solution { 2 public: 3 void push(int value) { 4 m_data.push(value); 5 if (m_min.empty() || value < m_min.top()) 6 m_min.push(value); 7 else 8 m_min.push(m_min.top()); 9 10 } 11 void pop() { 12 m_data.pop(); 13 m_min.pop(); 14 } 15 int top() { 16 return m_data.top(); 17 } 18 int min() { 19 return m_min.top(); 20 } 21 private: 22 std::stack<int> m_data; 23 std::stack<int> m_min; 24 };
面试题29:顺时针打印矩阵
主要是两部分,一是确定打印的圈数,二是确定打印一圈的操作,注意打印一圈时,需要判断什么情况下需要打印第二、第三、第四步
一刷:没控制好打印一圈的条件,测试用例显示出现重复打印
代码如下:
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 //牛客网 2 class Solution { 3 public: 4 vector<int> printMatrix(vector<vector<int> > matrix) { 5 int rows = matrix.size(); //矩阵行数 6 int cols = matrix[0].size(); //矩阵列数 7 int start = 0; //左上角的起始标号(start,start) 8 vector<int> result; 9 //打印一圈的操作 10 while (rows > start * 2 && cols > start * 2) 11 { 12 int endX = cols - 1 - start; //终止行号 13 int endY = rows - 1 - start; //终止列号 14 15 //第一次写这部分代码连续写了4个for循环,最后只通过了部分测试用例 16 //原因在于第一步是所有的矩阵都需要的,但是后面三步却未必,需要判断是否需要打印,否则会有重复 17 //从左到右打印一行 18 for (int i = start; i <= endX; i++) 19 result.push_back(matrix[start][i]); //像访问数组一样访问向量的向量 20 //从上到下打印一列 21 if (start < endY) 22 { 23 for (int i = start + 1; i <= endY; i++) 24 result.push_back(matrix[i][endX]); 25 } 26 //从右到左打印一行 27 if (start < endX&&start < endY) 28 { 29 for (int i = endX - 1; i >= start; i--) 30 result.push_back(matrix[endY][i]); 31 } 32 //从下到上打印一列 33 if (start < endX&&start < endY - 1) 34 { 35 for (int i = endY - 1; i >= start + 1; i--) 36 result.push_back(matrix[i][start]); 37 } 38 ++start; 39 } 40 return result; 41 } 42 };
面试题26:树的子结构
递归思想,主要是确定递归终止情况,并且确定具有更简单参数的递归调用
一刷:DoesTree1HaveTree2函数中return后面是 递归函数,参数写错了,应该是左右子树分别匹配;
代码如下:
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
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 //由于val是int类型的,所以可以用“==”直接判断是否相等 15 //否则就需要去写个equal函数来判断double或者float是否相等 16 17 bool result = false; 18 //初次运行HasSubtree函数,两个指针必须都是非空,否则直接返回false 19 if (pRoot1 != nullptr&&pRoot2 != nullptr) 20 { 21 //若根节点相同,继续判断树1中以R为根节点的子树是否包含树2 22 if (pRoot1->val == pRoot2->val) 23 result = DoesTree1HaveTree2(pRoot1, pRoot2); 24 //当以R为根节点的树1不包含树2时,继续从R的左子树寻找 25 if (!result) 26 result = HasSubtree(pRoot1->left, pRoot2); 27 //如果仍然找不到,从R的右子树寻找 28 if (!result) 29 result = HasSubtree(pRoot1->right, pRoot2); 30 } 31 //返回结果 32 return result; 33 } 34 //该函数的作用是判断以pRoot1为根节点的树是否包含以pRoot2为根节点的树 35 bool DoesTree1HaveTree2(TreeNode* pRoot1, TreeNode *pRoot2) 36 { 37 //如果tree2遍历完,所有节点在tree1中都找得到,返回true 38 if (pRoot2 == nullptr) 39 return true; 40 //如果tree2没遍历完但是tree1遍历完了,返回false 41 if (pRoot1 == nullptr) 42 return false; 43 //如果根节点数值不相等,返回false 44 if (pRoot1->val != pRoot2->val) 45 return false; 46 //若根节点相等了,那么左右子树也必须都相等才可以,所以用&& 47 return DoesTree1HaveTree2(pRoot1->left, pRoot2->left) && 48 DoesTree1HaveTree2(pRoot1->right, pRoot2->right); 49 } 50 };
面试题25:合并两个排序链表
递归思路,每次两个链表头部数值较小的就是新链表的头
一刷:if判决语句中pMergedHead的next指向错了,一定注意处理好链表结构体中next指针的指向
代码如下:
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 /* 2 struct ListNode { 3 int val; 4 struct ListNode *next; 5 ListNode(int x) : 6 val(x), next(NULL) { 7 } 8 };*/ 9 class Solution { 10 public: 11 ListNode* Merge(ListNode* pHead1, ListNode* pHead2) 12 { 13 if (pHead1 == nullptr) 14 return pHead2; 15 else if (pHead2 == nullptr) 16 return pHead1; 17 18 ListNode *pMergedHead = nullptr; 19 if (pHead1->val < pHead2->val) 20 { 21 pMergedHead = pHead1; 22 pMergedHead->next = Merge(pHead1->next, pHead2); //MD错了,应该是pMerged->next 23 } 24 else 25 { 26 pMergedHead = pHead2; 27 pMergedHead->next = Merge(pHead1, pHead2->next); 28 } 29 return pMergedHead; 30 } 31 };
面试题21:调整数组顺序使奇数位于偶数前面
牛客网不仅要求分开奇数偶数,还要求相对位置不变,我新开辟了一个向量,两次扫描原来向量。时间空间复杂度均是O(n)
代码如下:
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 //原来书上 的解题思路不保证顺序,只是让奇数位于前半部分,偶数位于后半部分 2 //这里既然要求顺序,可以考虑再开辟一个相同大小的vector 3 class Solution { 4 public: 5 void reOrderArray(vector<int> &array) { 6 int length = array.size(); 7 //vector<int> result(length, 0);//初始化可以不需要的 8 vector<int> result; 9 for (int i = 0; i < length; i++) 10 if ((array[i] & 0x1) != 0) 11 result.push_back(array[i]); 12 for (int i = 0; i < length; i++) 13 if ((array[i] & 0x1) == 0) 14 result.push_back(array[i]); 15 array = result; 16 } 17 };
面试题19:正则表达式匹配
分类讨论的关键点在于——正则表达式中第二个字符是否是“*”。若第二个字符不是*,字符与模式都后移一位;若第二个字符是*,则有一下几种可能:1、字符串后移一位,模式不变(对应于*以及它前面的字符多次出现);2、字符串后移一个,模式后移两个(对应于*和它前面的字符认为只出现一次);3、字符串不动,模式后移2个(对应于*前面的字符一次都不出现)
代码如下:
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 class Solution { 2 public: 3 bool match(char* str, char* pattern) 4 { 5 if (str == nullptr || pattern == nullptr) 6 return false; 7 return matchCore(str, pattern); 8 } 9 10 bool matchCore(char* str, char* pattern) 11 { 12 if (*str == '\0'&&*pattern == '\0') 13 return true; 14 if (*str != '\0'&&*pattern == '\0') 15 return false; 16 if (*(pattern + 1) == '*') 17 { 18 if (*pattern == *str || (*str != '\0'&&*pattern == '.')) //第一个字符匹配上了 19 return matchCore(str + 1, pattern + 2) 20 || matchCore(str + 1, pattern) 21 || matchCore(str, pattern + 2); 22 else //第一个字符没对上 23 return matchCore(str, pattern + 2); 24 } 25 if (*pattern == *str || (*str != '\0'&&*pattern == '.')) 26 return matchCore(str + 1, pattern + 1); 27 return false; 28 } 29 };
面试题16:数值的整数次方
难度一般,一遍AC,注意底数和指数为0的特殊情况,同时还要注意题目中是否让调用库函数
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 class Solution {
2 public:
3 double Power(double base, int exponent) {
4 double epsilon=1e-6;
5 double result=1.0;
6 int absExponent;
7 if(fabs(base-0.0)<epsilon)//认为base为零
8 return 0.0;
9 if(exponent==0)
10 return 1;
11 if(exponent<0)
12 absExponent=-exponent;
13 else
14 absExponent=exponent;
15 for(int i=1;i<=absExponent;i++)
16 result*=base;
17 if(exponent<0)
18 return 1.0/result;
19 return result;
20 }
21 };
按照《剑指offer》上的说法,还是可以做进一步优化的,计算乘方时可以使用递归,将复杂度降为O(logn),代码如下:
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 bool g_InvalidInput = false;
2 bool equal(double num1, double num2);
3 double PowerWithUnsignedExponent(double base, unsigned int exponent);
4
5 double Power(double base, int exponent)
6 {
7 g_InvalidInput = false;
8
9 if (equal(base, 0.0) && exponent < 0)
10 {
11 g_InvalidInput = true;
12 return 0.0;
13 }
14
15 unsigned int absExponent = (unsigned int) (exponent);
16 if (exponent < 0)
17 absExponent = (unsigned int) (-exponent);
18
19 double result = PowerWithUnsignedExponent(base, absExponent);
20 if (exponent < 0)
21 result = 1.0 / result;
22
23 return result;
24 }
25 double PowerWithUnsignedExponent(double base, unsigned int exponent)
26 {
27 if (exponent == 0)
28 return 1;
29 if (exponent == 1)
30 return base;
31
32 double result = PowerWithUnsignedExponent(base, exponent >> 1);
33 result *= result;
34 if ((exponent & 0x1) == 1)
35 result *= base;
36
37 return result;
38 }
39
40 bool equal(double num1, double num2)
41 {
42 if ((num1 - num2 > -0.0000001) && (num1 - num2 < 0.0000001))
43 return true;
44 else
45 return false;
46 }
面试题15:二进制中1的个数
能用移位运算就别用乘除,移位的运算速度更快。
为了避免讨论原理数字是正是负,这里选择用数1与原来数字做位与运算,再将1左移,32位的整数需要移动32次
代码如下:
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 class Solution {
2 public:
3 int NumberOf1(int n) {
4 int count = 0;
5 unsigned int flag = 1;
6 while (flag)
7 {
8 if (n&flag)
9 count++;
10 flag = flag << 1;
11 }
12 return count;
13 }
14 };
其实可以做简单的优化,优化之后的可以变为“有几个1,就进行几次循环”,这是根据“把一个整数减去1,再和原整数做与运算,会把该整数最右边一个1变成0”
代码如下:
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 class Solution {
2 public:
3 int NumberOf1(int n) {
4 int count = 0;
5 while(n)
6 {
7 count++;
8 n = n&(n-1);
9 }
10 return count;
11 }
12 };
面试题10:斐波那契数列
使用递归虽然简单,但是会导致栈溢出,循环的代码鲁棒性更好一些。
代码如下:
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 class Solution {
2 public:
3 int Fibonacci(int n) {
4
5 if(n<=0)
6 return 0;
7 if(n==1)
8 return 1;
9 int f1=0;
10 int f2=1;
11 int f;
12 for(int i=2;i<=n;i++)
13 {
14 f=f1+f2;
15 f1=f2;
16 f2=f;
17 }
18 return f;
19 }
20 };
青蛙跳台阶问题,青蛙一次只能跳一个或者两个台阶,本质上还是斐波那契数列
补充:变态跳台阶问题,青蛙一次可以调1,2,3,……,n个台阶,经过推导,可以发现通向公式f(n) = 2^(n-1);
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 class Solution { 2 public: 3 int jumpFloorII(int number) { 4 if(number<=0) 5 return 0; 6 if(number==1) 7 return 1; 8 return 2*jumpFloorII(number-1); 9 } 10 };
面试题9:两个栈实现一个队列
栈1负责进,栈2负责出
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 class Solution
2 {
3 public:
4 void push(int node) {
5 stack1.push(node);
6 }
7
8 int pop() {
9 if(stack2.size()>0)
10 {
11 int a = stack2.top();
12 stack2.pop();
13 return a;
14 }
15 else //默认不考虑为负数的的情况,此处只考虑为0的情况
16 {
17 while(stack1.size())
18 {
19 int temp = stack1.top();
20 stack1.pop();
21 stack2.push(temp);
22 }
23 int temp2 = stack2.top();
24 stack2.pop();
25 return temp2;
26 }
27 }
28
29 private:
30 stack<int> stack1;
31 stack<int> stack2;
32 };
33
34 /* 一种更高效的解法
35 class Solution
36 {
37 public:
38 void push(int node) {
39 stack1.push(node);
40 }
41
42 int pop() {
43 int a;
44 if (stack2.empty())
45 {
46 while (!stack1.empty())
47 {
48 a = stack1.top();
49 stack1.pop();
50 stack2.push(a);
51 }
52 }
53 a = stack2.top();
54 stack2.pop();
55 return a;
56 }
57
58 private:
59 stack<int> stack1;
60 stack<int> stack2;
61 };
62 */
面试题8:二叉树的下一个节点
思路不难,注意条件的的划分
代码如下:
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 /*
2 struct TreeLinkNode {
3 int val;
4 struct TreeLinkNode *left;
5 struct TreeLinkNode *right;
6 struct TreeLinkNode *next;
7 TreeLinkNode(int x) :val(x), left(NULL), right(NULL), next(NULL) {
8
9 }
10 };
11 */
12 class Solution {
13 public:
14 TreeLinkNode* GetNext(TreeLinkNode* pNode)
15 {
16 //《剑指offer》书本思路,最好还是先按照自己的思路写,然后再去按照书上精简的答案去做
17 if (pNode == nullptr)
18 return nullptr;
19 TreeLinkNode* pNext = nullptr; //将要作为结果返回的节点
20 if (pNode->right != nullptr)
21 {
22 TreeLinkNode* pRight = pNode->right;
23 while (pRight->left != nullptr)
24 pRight = pRight->left;
25 pNext = pRight;
26 }
27 else if (pNode->next != nullptr) //else if 改成else可以吗,可以改,但是运行速度变慢,占用内存增多(牛客测试结果)
28 {
29 TreeLinkNode* pCurrent = pNode;
30 TreeLinkNode* pParent = pNode->next;
31 while (pParent != nullptr&&pCurrent == pParent->right)
32 {
33 pCurrent = pParent;
34 pParent = pParent->next;
35 }
36 pNext = pParent;
37 }
38 return pNext;
39 }
40 };
面试题7:重建二叉树
用递归的方法重建二叉树,凡是递归类的问题,注意三点:1、分析问题;2、找到递归终止条件;3、递归调用函数时,一定要注意参数的取值,否则很容易出错而且错误比较难查找,我第一次做就是参数取值写错,结果函数进入死循环
代码如下:
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 BinaryTreeNode* ConstructCore(int* startPreorder, int* endPreorder, int* startInorder, int* endInorder);
2
3 BinaryTreeNode* Construct(int* preorder, int* inorder, int length)
4 {
5 if(preorder == nullptr || inorder == nullptr || length <= 0)
6 return nullptr;
7
8 return ConstructCore(preorder, preorder + length - 1,
9 inorder, inorder + length - 1);
10 }
11
12 BinaryTreeNode* ConstructCore
13 (
14 int* startPreorder, int* endPreorder,
15 int* startInorder, int* endInorder
16 )
17 {
18 // 前序遍历序列的第一个数字是根结点的值
19 int rootValue = startPreorder[0];
20 BinaryTreeNode* root = new BinaryTreeNode();
21 root->m_nValue = rootValue;
22 root->m_pLeft = root->m_pRight = nullptr;
23
24 if(startPreorder == endPreorder)
25 {
26 if(startInorder == endInorder && *startPreorder == *startInorder)
27 return root;
28 else
29 throw std::exception("Invalid input.");
30 }
31
32 // 在中序遍历中找到根结点的值
33 int* rootInorder = startInorder;
34 while(rootInorder <= endInorder && *rootInorder != rootValue)
35 ++ rootInorder;
36
37 if(rootInorder == endInorder && *rootInorder != rootValue)
38 throw std::exception("Invalid input.");
39
40 int leftLength = rootInorder - startInorder;
41 int* leftPreorderEnd = startPreorder + leftLength;
42 if(leftLength > 0)
43 {
44 // 构建左子树
45 root->m_pLeft = ConstructCore(startPreorder + 1, leftPreorderEnd,
46 startInorder, rootInorder - 1);
47 }
48 if(leftLength < endPreorder - startPreorder)
49 {
50 // 构建右子树
51 root->m_pRight = ConstructCore(leftPreorderEnd + 1, endPreorder,
52 rootInorder + 1, endInorder);
53 }
54
55 return root;
56 }
牛客网在线对应题目,注意构建左右子树是,for循环中vector的index,比较容易写错。
代码如下:
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 class Solution {
2 public:
3 TreeNode* reConstructBinaryTree(vector<int> pre, vector<int> vin) {
4 int size1 = pre.size();
5 int size2 = vin.size();
6 if (size1 <= 0 || size2 <= 0 || size1 != size2)
7 return nullptr;
8 vector<int> left_pre,right_pre, left_vin, right_vin;
9 TreeNode* root = new TreeNode(pre[0]);
10 int pos;
11 for (int i = 0; i < size1;i++)
12 if (vin[i] == pre[0])
13 {
14 pos = i;
15 break;
16 }
17 for (int i = 0; i < pos; i++)
18 {
19 left_pre.push_back(pre[i+1]); //此处小心
20 left_vin.push_back(vin[i]);
21 }
22 for (int i = pos + 1; i < size1; i++)
23 {
24 right_pre.push_back(pre[i]);
25 right_vin.push_back(vin[i]);
26 }
27 root->left = reConstructBinaryTree(left_pre, left_vin);
28 root->right=reConstructBinaryTree(right_pre, right_vin);
29 return root;
30 }
31 };
面试题6:翻转链表
牛客网题目,思路为先将链表顺序压入栈中,在逐个出栈放入另一个vector,完成逆序
代码如下:
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 class Solution {
2 public:
3 vector<int> printListFromTailToHead(ListNode* head) {
4 vector<int> temp;
5 stack<int> sta;
6 ListNode* pNode = head; //这里注意一定要把头指针赋值,而不要直接去修改头指针,否则会报“段错误”
7 while(pNode != nullptr) //只需要判断pNode 而不是它的next
8 {
9 sta.push(pNode->val);
10 pNode = pNode->next;
11 }
12 while(!sta.empty())
13 {
14 temp.push_back(sta.top());
15 sta.pop();
16 }
17 return temp;
18 }
19
20 };
1、一刷失误在while循环中,不应该直接用了头指针,也不应该改变头指针的取值。
《剑指offer》书上提供了两种思路,分别表示如下:
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 //循环
2 void PrintListReversingly_Iteratively(ListNode* pHead)
3 {
4 std::stack<ListNode*> nodes;
5
6 ListNode* pNode = pHead;
7 while(pNode != nullptr)
8 {
9 nodes.push(pNode);
10 pNode = pNode->m_pNext;
11 }
12 while(!nodes.empty())
13 {
14 pNode = nodes.top();
15 printf("%d\t", pNode->m_nValue);
16 nodes.pop();
17 }
18 }
19
20 //递归
21 void PrintListReversingly_Recursively(ListNode* pHead)
22 {
23 if(pHead != nullptr)
24 {
25 if (pHead->m_pNext != nullptr)
26 {
27 PrintListReversingly_Recursively(pHead->m_pNext);
28 }
29
30 printf("%d\t", pHead->m_nValue);
31 }
32 }
面试题5:替换空格
从后向前查找,复杂度为O(n)
代码如下:
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 class Solution {
2 public:
3 void replaceSpace(char *str, int length) {
4 if (str == nullptr || length <= 0)
5 return;
6 /*originalLength 为字符串str的实际长度*/
7 int originalLength = 0;
8 int numberOfBlank = 0;
9 int i = 0;
10 while (str[i] != '\0')
11 {
12 if (str[i]==' ') //一定注意是单引号而不是双引号
13 numberOfBlank++;
14 originalLength++;
15 i++;
16 }
17
18 /*newLenght为把空格替换成%20之后的长度*/
19 int newLength = originalLength + numberOfBlank * 2;
20 if (newLength > length)
21 return;
22 int indexOfOriginal = originalLength;
23 int indexOfNew = newLength;
24 while (indexOfOriginal >= 0 && indexOfNew > indexOfOriginal)
25 {
26 if (str[indexOfOriginal] == ' ')
27 {
28 str[indexOfNew--] = '0';
29 str[indexOfNew--] = '2';
30 str[indexOfNew--] = '%';
31
32 }
33 else
34 str[indexOfNew--] = str[indexOfOriginal];
35 indexOfOriginal--;
36 }
37
38 }
39 };
面试题4:二维数组的查找
这道题思路是查找时每次要么去除一行,要么去除一列,逐步缩小查找范围。牛客网在线做题函数用的是两重vector传递函数,但是其实我觉得用个一重指针就行
两重vector的初始化方式可以看这个链接:http://blog.csdn.net/oNever_say_love/article/details/50763238
代码如下:
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 class Solution {
2 public:
3 bool Find(int target, vector<vector<int> > array) { //数据结构为vector的vector,略微有些复杂,访问时和二维数组一样的方式
4 int row = array.size(); //获得数组的行数
5 int col = array[0].size(); //获得数组的列数
6 int rowIndex = 0; //行索引
7 int colIndex = col - 1; //列索引
8 bool found = false;
9 if (row > 0 && col > 0)
10 {
11 while (rowIndex < row&&colIndex >= 0) //从矩阵的右上角开始遍历,该数字大于target,去掉这一列,否则去掉这一行
12 { //依次往复,每次都可以缩小查找范围
13 if (array[rowIndex][colIndex] == target)
14 {
15 found = true;
16 break;
17 }
18 else if (array[rowIndex][colIndex]>target)
19 colIndex--;
20 else
21 rowIndex++;
22 }
23 }
24 return found;
25 }
26
27 };
面试题3:数组中重复的数字
数组中如果有重复的数字,返回true,否则返回false
第一遍:出错,很傻,交换元素时出错了,请小心
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
class Solution {
public:
// Parameters:
// numbers: an array of integers
// length: the length of array numbers
// duplication: (Output) the duplicated number in the array number
// Return value: true if the input is valid, and there are some duplications in the array number
// otherwise false
bool duplicate(int numbers[], int length, int* duplication)
{
if (numbers == nullptr || length <= 0)
return false;
for (int i = 0; i < length; i++)
if (numbers[i]<0 || numbers[i]>length - 1)
return false;
for (int i = 0; i < length; i++)
{
while (numbers[i] != i)
{
if (numbers[i] == numbers[numbers[i]])
{
*duplication = numbers[i];
return true;
}
int temp = numbers[i]; //交换数字,容易出错
numbers[i] = numbers[temp];
numbers[temp] = temp; //千万别写成 numbers[numbers[i]] = temp;
}
}
return false;
}
};
面试题3,题目2:数组中重复的数字(不能改变数组元素的位置)
这道题和上一道题目很像,只是要求不能改动数组,基本思路类似二分查找
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 class Solution {
2 public:
3 // Parameters:
4 // numbers: an array of integers
5 // length: the length of array numbers
6 // duplication: (Output) the duplicated number in the array number
7 // Return value: the repetitive number otherwise -1
8 int countRange(const int* numbers, int length, int start, int end);
9
10 // 参数:
11 // numbers: 一个整数数组
12 // length: 数组的长度
13 // 返回值:
14 // 正数 - 输入有效,并且数组中存在重复的数字,返回值为重复的数字
15 // 负数 - 输入无效,或者数组中没有重复的数字
16 int getDuplication(const int* numbers, int length)
17 {
18 if (numbers == nullptr || length <= 0)
19 return -1;
20
21 int start = 1;
22 int end = length - 1;
23 while (end >= start)
24 {
25 int middle = ((end - start)>>1) + start;
26 int count = countRange(numbers, length, start, middle);
27 if (end == start)
28 {
29 if (count > 1)
30 return start;
31 else
32 break;
33 }
34 if (count > (middle - start + 1))
35 end = middle;
36 else
37 start = middle + 1;
38 }
39 return -1;
40 }
41
42 int countRange(const int* numbers, int length, int start, int end)
43 {
44 if (numbers == nullptr)
45 return 0;
46
47 int count = 0;
48 for (int i = 0; i < length; i++)
49 if (numbers[i] >= start && numbers[i] <= end)
50 ++count;
51
52 return count;
53 }
54 };