[关闭]
@ShawnNg 2018-07-25T05:05:13.000000Z 字数 32453 阅读 1369

剑指 offer面试题

面试


1. 二维数组中的查找

题目描述

在一个二维数组中,每一行都按照从左到右递增的顺序排序,每一列都按照从上到下递增的顺序排序。请完成一个函数,输入这样的一个二维数组和一个整数,判断数组中是否含有该整数。

解题思路

从矩阵的左下角开始搜索,因为左下角的元素往右是递增,往左是递减。如果遇到比target大,就往上找,如果比tagert小就往右走,如果找到了则直接输出。时间复杂度是O(n+m)。

代码

  1. class Solution{
  2. public:
  3. bool Find(int target, vector<vector<int> > array){
  4. if(array.size()==0) return false;
  5. int n=array.size(), m=array[0].size();
  6. int i=n-1, j=0;
  7. while(i>=0 && j<m){
  8. if(target==array[i][j]) return true;
  9. else if(target>array[i][j]) ++j;
  10. else --i;
  11. }
  12. return false;
  13. }
  14. };

2. 替换空格

题目描述

请实现一个函数,将一个字符串中的空格替换成“%20”。例如,当字符串为We Are Happy.则经过替换之后的字符串为We%20Are%20Happy。

解题思路

先计算好替换后的字符串长度,然后将字符串从后开始拷贝到替换后的位置,遇到空格就替换%20,当两个指针相遇的时候就是替换成功。

代码

  1. class Solution{
  2. public:
  3. void replaceSpace(char *str, int length){
  4. if(length==0) return;
  5. int countSpace=0;
  6. for(int i=0; i<length; ++i){
  7. if(str[i]==' ') ++countSpace;
  8. }
  9. int j=length+countSpace*2-1, i=length-1;
  10. while(i!=j){
  11. if(str[i]==' ') {
  12. str[j--]='0';
  13. str[j--]='2';
  14. str[j--]='%';
  15. }else{
  16. str[j--]=str[i];
  17. }
  18. --i;
  19. }
  20. }
  21. };

3.从尾到头打印链表

题目描述

输入一个链表,从尾到头打印链表每个节点的值。

解题思路

方法1:直接用递归,从最后一个节点开始打印起。
方法2: 用栈来存储,然后全部弹出,放入数组。

代码

方法1

  1. class Solution{
  2. public:
  3. vector<int> printListFromTailToHead(ListNode* head){
  4. vector<int> result;
  5. printListFromTailToHeadCore(head, result);
  6. return result;
  7. }
  8. void printListFromTailToHeadCore(ListNode* head, vector<int> &array){
  9. if(head==NULL) return ;
  10. printListFromTailToHeadCore(head->next, array);
  11. array.push_back(head->val);
  12. }
  13. };

方法2

  1. class Solution{
  2. public:
  3. vector<int> printListFromTailToHead(ListNode* head){
  4. vector<int> array;
  5. if(head==NULL) return array;
  6. stack<int> stk;
  7. while(head){
  8. stk.push(head->val);
  9. }
  10. while(!stk.empty()){
  11. array.push_back(stk.top());
  12. stk.pop();
  13. }
  14. return array;
  15. }
  16. };

4.重建二叉树

题目描述

输入某二叉树的前序遍历和中序遍历的结果,请重建出该二叉树。假设输入的前序遍历和中序遍历的结果中都不含重复的数字。例如输入前序遍历序列{1,2,4,7,3,5,6,8}和中序遍历序列{4,7,2,1,5,3,8,6},则重建二叉树并返回。

解题思路

前序的第一个节点为根结点,按照根节点将中序一分为二,再根据中序左右节点数将前序一分为二。然后将左右部分递归,返回根节点即可。

代码

  1. class Solution {
  2. public:
  3. TreeNode* reConstructBinaryTree(vector<int> pre,vector<int> vin) {
  4. return reConstructCore(pre, 0, pre.size()-1, vin, 0, vin.size()-1);
  5. }
  6. TreeNode* reConstructCore(vector<int> pre, int preLeft, int preRight, vector<int> vin, int vinLeft, int vinRight){
  7. if(preLeft>preRight) return NULL;
  8. // find the split point of vin.
  9. int vinMid = vinLeft;
  10. for(;vinMid<=vinRight; ++vinMid){
  11. if(pre[preLeft]==vin[vinMid]) break;
  12. }
  13. // construct the tree.
  14. TreeNode *root = new TreeNode(pre[preLeft]);
  15. root->left = reConstructCore(pre, preLeft+1, preLeft+vinMid-vinLeft, vin, vinLeft, vinMid-1);
  16. root->right = reConstructCore(pre, preLeft+vinMid-vinLeft+1, preRight, vin, vinMid+1, vinRight);
  17. return root;
  18. }
  19. };

5.用两个栈实现队列

题目描述

用两个栈来实现一个队列,完成队列的Push和Pop操作。 队列中的元素为int类型。

解题思路

用一个栈来接受push,一次性pop出来,一次性push到另外一个栈。

代码

  1. class Solution
  2. {
  3. public:
  4. void push(int node) {
  5. stack1.push(node);
  6. }
  7. int pop() {
  8. if(stack2.empty()){
  9. while(!stack1.empty()){
  10. stack2.push(stack1.top());
  11. stack1.pop();
  12. }
  13. }
  14. int result = stack2.top();
  15. stack2.pop();
  16. return result;
  17. }
  18. private:
  19. stack<int> stack1;
  20. stack<int> stack2;
  21. };

6. 旋转数组的最小数字

题目描述

把一个数组最开始的若干个元素搬到数组的末尾,我们称之为数组的旋转。 输入一个非递减排序的数组的一个旋转,输出旋转数组的最小元素。 例如数组{3,4,5,1,2}为{1,2,3,4,5}的一个旋转,该数组的最小值为1。 NOTE:给出的所有元素都大于0,若数组大小为0,请返回0。

解题思路

由于数组旋转部分永远在后面,所以我们只需要找到原数组和旋转部分的交界处即可。用二分法,中间元素不断跟最右的元素比较。如果大于最右,则交界处在右边,如果小于最右,交界处在左边,如果等于最右,则最右往左走一步。

代码

  1. class Solution {
  2. public:
  3. int minNumberInRotateArray(vector<int> rotateArray) {
  4. int low=0, high=rotateArray.size()-1;
  5. while(low<high){
  6. int mid = low + (high-low)/2;
  7. if(rotateArray[mid]>rotateArray[high]){
  8. low=mid+1;
  9. }else if(rotateArray[mid]<rotateArray[high]){
  10. high=mid;
  11. }else {
  12. --high;
  13. }
  14. }
  15. return rotateArray[low];
  16. }
  17. };

7.斐波那契数列

题目描述

大家都知道斐波那契数列,现在要求输入一个整数n,请你输出斐波那契数列的第n项。
n<=39

解题思路

斐波那契数列,当时,。所以直接用递推公式写出结果。不过需要思考一下停止条件。

代码

  1. class Solution {
  2. public:
  3. int Fibonacci(int n) {
  4. if(n<2) return n;
  5. int f0=0, f1=1;
  6. for(int i=2; i<=n; ++i){
  7. int f2=f0+f1;
  8. f0=f1;
  9. f1=f2;
  10. }
  11. return f1;
  12. }
  13. };

8.跳台阶

题目描述

一只青蛙一次可以跳上1级台阶,也可以跳上2级。求该青蛙跳上一个n级的台阶总共有多少种跳法。

解题思路

这题就是斐波那契数列。

9.矩形覆盖

题目描述

我们可以用2*1的小矩形横着或者竖着去覆盖更大的矩形。请问用n个2*1的小矩形无重叠地覆盖一个2*n的大矩形,总共有多少种方法?

解题思路

还是一个斐波那契数列。

10. 变态跳台阶

题目描述

一只青蛙一次可以跳上1级台阶,也可以跳上2级……它也可以跳上n级。求该青蛙跳上一个n级的台阶总共有多少种跳法。

解题思路

递推公式为,可以推出,由于,可以退出等比数列。需要注意n=0的情况。

代码

  1. class Solution {
  2. public:
  3. int jumpFloorII(int number) {
  4. if(number==0) return 0;
  5. return pow(2, number-1);
  6. }
  7. };

11. 二进制中1的个数

题目描述

输入一个整数,输出该数二进制表示中1的个数。其中负数用补码表示。

解题思路

每次的操作都会让x少一个1,重复此操作直到x为0。但是要注意x是负数的情况,要将其转为正数。

代码

  1. class Solution {
  2. public:
  3. int NumberOf1(int n) {
  4. unsigned int x = n;
  5. int count=0;
  6. while(x){
  7. x &= x-1;
  8. count ++;
  9. }
  10. return count;
  11. }
  12. };

12. 数值的整数次方

题目描述

给定一个double类型的浮点数base和int类型的整数exponent。求base的exponent次方。

解题思路

要考虑base=0的情况,exponent=0的情况,还有exponent正负数的情况。用二进制去移位,得到结果。

代码

  1. class Solution {
  2. public:
  3. double Power(double base, int exponent) {
  4. if(base==0) return 0;
  5. if(exponent==0) return 1;
  6. double result=1;
  7. int exp=abs(exponent);
  8. while(exp){
  9. if(exp&1) result*=base;
  10. base *=base;
  11. exp >>=1;
  12. }
  13. return exponent>0?result:1/result;
  14. }
  15. };

13. 调整数组顺序使奇数位于偶数前面

题目描述

输入一个整数数组,实现一个函数来调整该数组中数字的顺序,使得所有的奇数位于数组的前半部分,所有的偶数位于位于数组的后半部分,并保证奇数和奇数,偶数和偶数之间的相对位置不变。

解题思路

因为要求数字的相对位置不能变,如果可以变的话直接用快排的思想,O(n)的时间复杂度,O(1)的空间复杂度。
方法1: O(n)的时间复杂度和O(n)的空间复杂度,用另外一个数组存储。
方法2: O(n^2)的时间复杂度和O(1)的空间复杂度,用冒泡排序,遇到一个偶数后面是奇数就交换。

代码

方法1:

  1. class Solution {
  2. public:
  3. void reOrderArray(vector<int> &array) {
  4. if(array.size()<2) return;
  5. vector<int> result(array.size());
  6. int low=0, high=array.size()-1;
  7. for(int i=0, j=array.size()-1;i<array.size(); ++i, --j){
  8. if(array[i]%2==1) result[low++]=array[i];
  9. if(array[j]%2==0) result[high--]=array[j];
  10. }
  11. for(int i=0; i<array.size(); ++i) array[i] = result[i];
  12. }
  13. };

方法2:

  1. class Solution {
  2. public:
  3. void reOrderArray(vector<int> &array) {
  4. int length = array.size();
  5. for(int i=0; i<length-1; ++i){
  6. for(int j=0; j<length-1-i; ++j){
  7. if(array[j]%2==0&&array[j+1]%2==1)
  8. swap(array[j], array[j+1]);
  9. }
  10. }
  11. }
  12. };

14.链表中倒数第k个结点

题目描述

输入一个链表,输出该链表中倒数第k个结点。

解题思路

先用一个快指针往前走k步,然后头指针跟快指针一起走,快指针到尾的时候头指针就是倒数第
k个。但是要注意少于k个结点的情况。

代码

  1. class Solution {
  2. public:
  3. ListNode* FindKthToTail(ListNode* pListHead, unsigned int k) {
  4. ListNode* fast=pListHead;
  5. while(fast&&k>0){
  6. fast=fast->next;
  7. --k;
  8. }
  9. if(k>0) return NULL;
  10. while(fast){
  11. fast=fast->next;
  12. pListHead=pListHead->next;
  13. }
  14. return pListHead;
  15. }
  16. };

15.反转链表

题目描述

输入一个链表,反转链表后,输出链表的所有元素。

解题思路

要注意一个结点的情况

代码

  1. class Solution {
  2. public:
  3. ListNode* ReverseList(ListNode* pHead) {
  4. if(pHead==NULL||pHead->next==NULL) return pHead;
  5. ListNode* pre=pHead, *mid=pHead->next, *next;
  6. pre->next=NULL;
  7. while(mid){
  8. next=mid->next;
  9. mid->next=pre;
  10. pre=mid;
  11. mid=next;
  12. }
  13. return pre;
  14. }
  15. };

16. 合并两个排序得链表

题目描述

输入两个单调递增的链表,输出两个链表合成后的链表,当然我们需要合成后的链表满足单调不减规则。

解题思路

用两个指针指向未扫描的位置,一个指针指向新链表最后一个。要注意得是,要新建一个辅助头实体。

代码

  1. class Solution {
  2. public:
  3. ListNode* Merge(ListNode* pHead1, ListNode* pHead2)
  4. {
  5. if(pHead1==NULL) return pHead2;
  6. if(pHead2==NULL) return pHead1;
  7. ListNode *newHead=new ListNode(-1);
  8. ListNode *p = newHead;
  9. while(pHead1&&pHead2){
  10. if(pHead1->val < pHead2->val) {
  11. p->next=pHead1;
  12. pHead1=pHead1->next;
  13. }else{
  14. p->next=pHead2;
  15. pHead2=pHead2->next;
  16. }
  17. p=p->next;
  18. }
  19. while(pHead1){
  20. p->next=pHead1;
  21. pHead1=pHead1->next;
  22. }
  23. while(pHead2){
  24. p->next=pHead2;
  25. pHead2=pHead2->next;
  26. }
  27. return newHead->next;
  28. }
  29. };

17.树的子结构

题目描述

输入两棵二叉树A,B,判断B是不是A的子结构。(ps:我们约定空树不是任意一个树的子结构)

解题思路

先将两棵树序列化,然后判断小的树是否在大的树里面。要注意把序列化后的最后的空结点剪掉。并且空树不是子结构,要特殊处理。

代码

  1. class Solution {
  2. public:
  3. bool HasSubtree(TreeNode* pRoot1, TreeNode* pRoot2)
  4. {
  5. if(pRoot2==NULL) return false;
  6. string str1=cutEnd(serialize(pRoot1));
  7. string str2=cutEnd(serialize(pRoot2));
  8. return str1.find(str2) != string::npos;
  9. }
  10. string serialize(TreeNode* root){
  11. if(root==NULL) return "#";
  12. string rootStr=to_string(root->val);
  13. string leftStr=serialize(root->left);
  14. string rightStr=serialize(root->right);
  15. return rootStr + "," + leftStr + "," + rightStr;
  16. }
  17. string cutEnd(string str){
  18. int i=str.size()-1;
  19. for(; i>=0; --i){
  20. if(str[i]!='#'&&str[i]!=',') break;
  21. }
  22. return str.substr(0, i+1);
  23. }
  24. };

18.二叉树的镜像

题目描述

操作给定的二叉树,将其变换为源二叉树的镜像。
输入描述:

二叉树的镜像定义:源二叉树 
            8
           /  \
          6   10
         / \  / \
        5  7 9 11
        镜像二叉树
            8
           /  \
          10   6
         / \  / \
        11 9 7  5

解题思路

后序遍历,将二叉树的左右结点交换。

代码

  1. class Solution {
  2. public:
  3. void Mirror(TreeNode *pRoot) {
  4. if(pRoot==NULL) return;
  5. Mirror(pRoot->left);
  6. Mirror(pRoot->right);
  7. swap(pRoot->left, pRoot->right);
  8. }
  9. };

19. 顺时针打印矩阵

题目描述

输入一个矩阵,按照从外向里以顺时针的顺序依次打印出每一个数字,例如,如果输入如下矩阵: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 则依次打印出数字1,2,3,4,8,12,16,15,14,13,9,5,6,7,11,10.

解题思路

对于0的情况先处理,然后得到宽、高和层数。按照层数做四个循环,边界条件一定要注意,而且要注意的是只有一行或者一竖的情况。

代码

  1. class Solution {
  2. public:
  3. vector<int> printMatrix(vector<vector<int> > matrix) {
  4. vector<int> result;
  5. if(matrix.size()==0) return result;
  6. if(matrix[0].size()==0) return result;
  7. int n=matrix.size(), m=matrix[0].size(), level = (min(n,m)+1)/2;
  8. for(int i=0; i<level; ++i){
  9. int left=i, right=m-1-i, top=i, bottom=n-1-i;
  10. int j=left, k=top;
  11. for(;j<=right; ++j) result.push_back(matrix[k][j]);
  12. for(--j, ++k;k<=bottom; ++k) result.push_back(matrix[k][j]);
  13. for(--k, --j;top!=bottom&&j>=left; --j) result.push_back(matrix[k][j]);
  14. for(++j, --k;left!=right&&k>=top+1; --k) result.push_back(matrix[k][j]);
  15. }
  16. return result;
  17. }
  18. };

20. 包含min函数的栈

题目描述

定义栈的数据结构,请在该类型中实现一个能够得到栈最小元素的min函数。

解题思路

每次push用另外一个栈来记录min。一定要注意栈为空的时候要做判断。

代码

  1. class Solution {
  2. public:
  3. stack<int> stk;
  4. stack<int> minStk;
  5. void push(int value) {
  6. stk.push(value);
  7. if(minStk.empty()||minStk.top()>value)
  8. minStk.push(value);
  9. else
  10. minStk.push(minStk.top());
  11. }
  12. void pop() {
  13. stk.pop();
  14. minStk.pop();
  15. }
  16. int top() {
  17. return stk.top();
  18. }
  19. int min() {
  20. return minStk.top();
  21. }
  22. };

21. 栈的压入、弹出序列

题目描述

输入两个整数序列,第一个序列表示栈的压入顺序,请判断第二个序列是否为该栈的弹出顺序。假设压入栈的所有数字均不相等。例如序列1,2,3,4,5是某栈的压入顺序,序列4,5,3,2,1是该压栈序列对应的一个弹出序列,但4,3,5,1,2就不可能是该压栈序列的弹出序列。(注意:这两个序列的长度是相等的)

解题思路

用一个栈来模拟push,每次栈顶遇到弹出序列的未弹出首元素时,就将栈顶弹出,最终如果栈为空,则说明是存在该弹出序列的。

代码

  1. class Solution {
  2. public:
  3. bool IsPopOrder(vector<int> pushV,vector<int> popV) {
  4. if(pushV.size()==0||pushV.size()!=popV.size()) return false;
  5. stack<int> stk;
  6. int i=0, j=0;
  7. while(i<popV.size()){
  8. if(stk.empty()||stk.top()!=popV[i]){
  9. stk.push(pushV[j]);
  10. ++j;
  11. }else{
  12. stk.pop();
  13. ++i;
  14. }
  15. }
  16. return stk.empty();
  17. }
  18. };

22. 从上往下打印二叉树

题目描述

从上往下打印出二叉树的每个节点,同层节点从左至右打印。

解题思路

用队列来实现层次遍历,注意异常检测。

代码

  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<int> PrintFromTopToBottom(TreeNode* root) {
  13. if(root==NULL) return {};
  14. vector<int> printArray;
  15. queue<TreeNode* > helperQueue;
  16. helperQueue.push(root);
  17. while(!helperQueue.empty()){
  18. TreeNode* node = helperQueue.front();
  19. helperQueue.pop();
  20. if(node!=NULL){
  21. printArray.push_back(node->val);
  22. helperQueue.push(node->left);
  23. helperQueue.push(node->right);
  24. }
  25. }
  26. return printArray;
  27. }
  28. };

23. 二叉搜索树的后序遍历序列

题目描述

输入一个整数数组,判断该数组是不是某二叉搜索树的后序遍历的结果。如果是则输出Yes,否则输出No。假设输入的数组的任意两个数字都互不相同。

解题思路

二叉搜索树的后序遍历的根节点一定在序列末端。并且存在一个连续序列比根节点小,剩下的连续序列比根节点大。如果不满足条件就返回false,如果满足就分成两个序列递归,一直到序列数量等于1。

代码

  1. class Solution {
  2. public:
  3. bool VerifySquenceOfBST(vector<int> sequence) {
  4. if(sequence.size()==0) return false;
  5. return VerifyCore(sequence, 0, sequence.size()-1);
  6. }
  7. bool VerifyCore(vector<int> sequence, int low, int high){
  8. if(low>=high) return true;
  9. int root=high, left=low, right=high-1;
  10. --high;
  11. while(left<=high && sequence[left]<sequence[root]) ++left;
  12. while(right>=low && sequence[right]>sequence[root]) --right;
  13. if(left!=right+1) return false;
  14. return VerifyCore(sequence, low, right) && VerifyCore(sequence, left, high);
  15. }
  16. };

24. 二叉树中和为某一值的路径

题目描述

输入一颗二叉树和一个整数,打印出二叉树中结点值的和为输入整数的所有路径。路径定义为从树的根结点开始往下一直到叶结点所经过的结点形成一条路径。

解题思路

用前序遍历,每次将期望值减去根节点,如果在叶子节点上期望值为0则证明成功找到一条路径。

代码

  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> > FindPath(TreeNode* root,int expectNumber) {
  13. vector<vector<int> > result;
  14. vector<int> path;
  15. FindPathCore(root, expectNumber, result, path);
  16. return result;
  17. }
  18. void FindPathCore(TreeNode* root, int expectNumber, vector<vector<int> > &result, vector<int> &path){
  19. if(root==NULL) return;
  20. path.push_back(root->val);
  21. expectNumber -= root->val;
  22. if(root->left==NULL && root->right==NULL &&expectNumber==0) result.push_back(path);
  23. FindPathCore(root->left, expectNumber, result, path);
  24. FindPathCore(root->right, expectNumber, result, path);
  25. path.pop_back();
  26. }
  27. };

25. 复杂链表的复制

题目描述

输入一个复杂链表(每个节点中有节点值,以及两个指针,一个指向下一个节点,另一个特殊指针指向任意一个节点),返回结果为复制后复杂链表的head。(注意,输出结果中请不要返回参数中的节点引用,否则判题程序会直接返回空)

解题思路

将复制分为三步,第一步将每个节点复制后插入在原节点后面,方便索引。第二步,将新节点的random指向前一个节点的random的next,不过要记得异常检测。第三步,将新节点和旧节点分割。

代码

  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==NULL) return NULL;
  15. // First
  16. RandomListNode* tmpOld=pHead, *tmpNew;
  17. while(tmpOld!=NULL){
  18. tmpNew=new RandomListNode(tmpOld->label);
  19. tmpNew->next=tmpOld->next;
  20. tmpOld->next=tmpNew;
  21. tmpOld=tmpNew->next;
  22. }
  23. // Second
  24. tmpOld = pHead;
  25. while(tmpOld!=NULL){
  26. tmpNew = tmpOld->next;
  27. if(tmpOld->random!=NULL) tmpNew->random=tmpOld->random->next;
  28. tmpOld=tmpNew->next;
  29. }
  30. // Third
  31. RandomListNode *newHead=pHead->next;
  32. tmpOld = pHead, tmpNew = pHead->next;
  33. while(tmpNew->next!=NULL){
  34. tmpOld->next = tmpOld->next->next;
  35. tmpNew->next = tmpNew->next->next;
  36. tmpNew = tmpNew->next;
  37. tmpOld = tmpOld->next;
  38. }
  39. tmpOld->next=NULL;
  40. return newHead;
  41. }
  42. };

26. 二叉搜索树与双向链表

题目描述

输入一棵二叉搜索树,将该二叉搜索树转换成一个排序的双向链表。要求不能创建任何新的结点,只能调整树中结点指针的指向。

解题思路

要求不能创建新节点,所以用一个指针指链表头,一个指链表尾,中序遍历二叉树,遇到一个节点就插入到链表尾部。

代码

  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. TreeNode* Convert(TreeNode* pRootOfTree)
  13. {
  14. TreeNode *pList=NULL, *pListHead=NULL;
  15. ConvertCore(pRootOfTree, pList, pListHead);
  16. return pListHead;
  17. }
  18. void ConvertCore(TreeNode* root, TreeNode *&pList, TreeNode *&pListHead){
  19. if(root==NULL) return ;
  20. ConvertCore(root->left, pList, pListHead);
  21. if(pList==NULL){
  22. pList=root;
  23. pListHead=root;
  24. }else{
  25. pList->right=root;
  26. root->left=pList;
  27. pList = root;
  28. }
  29. ConvertCore(root->right, pList, pListHead);
  30. }
  31. };

27. 字符串的排列

题目描述

输入一个字符串,按字典序打印出该字符串中字符的所有排列。例如输入字符串abc,则打印出由字符a,b,c所能排列出来的所有字符串abc,acb,bac,bca,cab和cba。
输入描述:
输入一个字符串,长度不超过9(可能有字符重复),字符只包括大小写字母。

解题思路

全排列经典算法,每次都从将子串的第一个数和接下来的每个位置交换。

代码

  1. class Solution {
  2. public:
  3. vector<string> Permutation(string str) {
  4. vector<string> result;
  5. if(str.size()==0) return result;
  6. PermutationCore(str, 0, str.size(), result);
  7. sort(result.begin(), result.end());
  8. return result;
  9. }
  10. void PermutationCore(string str, int k, int length, vector<string> &result){
  11. if(k==length) result.push_back(str);
  12. for(int i=k; i<length; ++i){
  13. if(i!=k&&str[i]==str[k]) continue;
  14. swap(str[i], str[k]);
  15. PermutationCore(str, k+1, length, result);
  16. swap(str[i], str[k]);
  17. }
  18. }
  19. };

28. 数组中出现次数超过一半的数字

题目描述

数组中有一个数字出现的次数超过数组长度的一半,请找出这个数字。例如输入一个长度为9的数组{1,2,3,2,2,2,5,4,2}。由于数字2在数组中出现了5次,超过数组长度的一半,因此输出2。如果不存在则输出0。

解题思路

存储一个计数和记录一个数,不断比较两个数,如果两个数一样就计数加一,如果不一样就减一。如果计数等于0则记录新的数。注意有可能不存在,所以最后还要检测一遍。

代码

  1. class Solution {
  2. public:
  3. int MoreThanHalfNum_Solution(vector<int> numbers) {
  4. if(numbers.size()==0) return 0;
  5. int recordNum = numbers[0];
  6. int count=1;
  7. for(int i=1; i<numbers.size(); ++i){
  8. if(numbers[i]==recordNum) ++count;
  9. else --count;
  10. if(count==0) {
  11. recordNum=numbers[i];
  12. count=1;
  13. }
  14. }
  15. // if this is the result.
  16. count = 0;
  17. for(int i=0; i<numbers.size(); ++i)
  18. if(numbers[i]==recordNum) count++;
  19. return count>(numbers.size()/2)?recordNum:0;
  20. }
  21. };

29. 最小的K个数

题目描述

输入n个整数,找出其中最小的K个数。例如输入4,5,1,6,2,7,3,8这8个数字,则最小的4个数字是1,2,3,4。

解题思路

可以用O(n)的时间复杂度做,用快排的思想。
也可以用堆来做,针对大数据的情况下,O(nlogk)的时间复杂度。

代码

快排:

  1. class Solution {
  2. public:
  3. vector<int> GetLeastNumbers_Solution(vector<int> input, int k) {
  4. if(input.size()==0||k==0||input.size()<k) return {};
  5. int i=0, j=input.size()-1, mid;
  6. while(i<j){
  7. mid = partition(input, i, j);
  8. if(mid+1==k) return vector<int> (input.begin(), input.begin()+k);
  9. else if(mid+1<k) i=mid;
  10. else j=mid-1;
  11. }
  12. return vector<int> (input.begin(), input.begin()+k);
  13. }
  14. int partition(vector<int> &input, int low, int high){
  15. int pivot = input[high];
  16. while(low<high){
  17. while(low<high && input[low]<=pivot) ++low;
  18. swap(input[low], input[high]);
  19. while(low<high && input[high]>pivot) --high;
  20. swap(input[low], input[high]);
  21. }
  22. input[low] = pivot;
  23. return low;
  24. }
  25. };

堆排序:

  1. class Solution {
  2. public:
  3. vector<int> GetLeastNumbers_Solution(vector<int> input, int k) {
  4. if(input.size()==0 || k==0 || input.size()<k) return {};
  5. priority_queue<int> heap;
  6. for(int i=0; i<k; ++i) heap.push(input[i]);
  7. for(int i=k; i<input.size(); ++i){
  8. if(input[i]<heap.top()){
  9. heap.pop();
  10. heap.push(input[i]);
  11. }
  12. }
  13. vector<int> result;
  14. while(!heap.empty()){
  15. result.push_back(heap.top());
  16. heap.pop();
  17. }
  18. return result;
  19. }
  20. };

30. 连续子数组的最大和

题目描述

HZ偶尔会拿些专业问题来忽悠那些非计算机专业的同学。今天测试组开完会后,他又发话了:在古老的一维模式识别中,常常需要计算连续子向量的最大和,当向量全为正数的时候,问题很好解决。但是,如果向量中包含负数,是否应该包含某个负数,并期望旁边的正数会弥补它呢?例如:{6,-3,-2,7,-15,1,2,2},连续子向量的最大和为8(从第0个开始,到第3个为止)。你会不会被他忽悠住?(子向量的长度至少是1)

解题思路

用一个变量存储全局最大,用另一个变量存储当前和,如果当前和比当前数还要小,那就抛弃之前的和。如果当前和比最大和要大,则替换最大和。

代码

  1. class Solution {
  2. public:
  3. int FindGreatestSumOfSubArray(vector<int> array) {
  4. if(array.size()==0) return 0;
  5. int curSum=array[0], curMax=array[0];
  6. for(int i=1; i<array.size(); ++i){
  7. curSum += array[i];
  8. if(curSum<array[i]) curSum=array[i];
  9. if(curSum>curMax) curMax=curSum;
  10. }
  11. return curMax;
  12. }
  13. };

31. 整数中1出现的次数(从1到n整数中1出现的次数)

题目描述

求出1~13的整数中1出现的次数,并算出100~1300的整数中1出现的次数?为此他特别数了一下1~13中包含1的数字有1、10、11、12、13因此共出现6次,但是对于后面问题他就没辙了。ACMer希望你们帮帮他,并把问题更加普遍化,可以很快的求出任意非负整数区间中1出现的次数。

解题思路

跳过

32. 把数组排成最小的数

题目描述

输入一个正整数数组,把数组里所有数字拼接起来排成一个数,打印能拼接出的所有数字中最小的一个。例如输入数组{3,32,321},则打印出这三个数字能排成的最小数字为321323。

解题思路

可以看作是一种排序,而且排序的比较方法是看两个数两种合并方法的大小。

代码

  1. class Solution {
  2. public:
  3. static bool compaire(int a, int b){
  4. string strA = to_string(a), strB = to_string(b);
  5. return stoi(strA+strB)<=stoi(strB+strA);
  6. }
  7. string PrintMinNumber(vector<int> numbers) {
  8. sort(numbers.begin(), numbers.end(), compaire);
  9. string result;
  10. for(int i=0; i<numbers.size(); ++i){
  11. result += to_string(numbers[i]);
  12. }
  13. return result;
  14. }
  15. };

33. 丑数

题目描述

把只包含因子2、3和5的数称作丑数(Ugly Number)。例如6、8都是丑数,但14不是,因为它包含因子7。 习惯上我们把1当做是第一个丑数。求按从小到大的顺序的第N个丑数。

解题思路

将index前的每一个丑数存起来,用三个指针分别存储乘以2、3、5刚好比最后一个丑数要大的那个位置,每次从最后一个丑数乘以2、3、5中三个数的最小值作为最新的丑数,循环到index个。

代码

  1. class Solution {
  2. public:
  3. int GetUglyNumber_Solution(int index) {
  4. if(index<7) return index;
  5. vector<int> uglyResult(index, 1);
  6. int ugly2=0, ugly3=0, ugly5=0;
  7. for(int i=1; i<index; ++i){
  8. uglyResult[i] = min(uglyResult[ugly2]*2, min(uglyResult[ugly3]*3, uglyResult[ugly5]*5));
  9. while(uglyResult[ugly2]*2<=uglyResult[i]) ++ugly2;
  10. while(uglyResult[ugly3]*3<=uglyResult[i]) ++ugly3;
  11. while(uglyResult[ugly5]*5<=uglyResult[i]) ++ugly5;
  12. }
  13. return uglyResult[index-1];
  14. }
  15. };

34. 第一个只出现一次的字符

题目描述

在一个字符串(1<=字符串长度<=10000,全部由字母组成)中找到第一个只出现一次的字符,并返回它的位置

解题思路

用数组hash表存储即可,遍历字符串,找到第一个值为1的字符。

代码

  1. class Solution {
  2. public:
  3. int FirstNotRepeatingChar(string str) {
  4. int hash[256];
  5. memset(hash, 0, sizeof(hash));
  6. for(auto c:str) ++hash[c];
  7. for(int i=0; i<str.size(); ++i) if(hash[str[i]]==1) return i;
  8. return -1;
  9. }
  10. };

35. 数组中的逆序对

题目描述

在数组中的两个数字,如果前面一个数字大于后面的数字,则这两个数字组成一个逆序对。输入一个数组,求出这个数组中的逆序对的总数P。并将P对1000000007取模的结果输出。 即输出P%1000000007
输入描述:
题目保证输入的数组中没有的相同的数字
数据范围:

  1. 对于%50的数据,size<=10^4
  2. 对于%75的数据,size<=10^5
  3. 对于%100的数据,size<=2*10^5

示例1

  1. 输入
  2. 1,2,3,4,5,6,7,0
  3. 输出
  4. 7

解题思路

用归并排序,但是在merge的时候要统计逆序数,从后往前merge,如果第一part的数比第二part要大,则第二part前面的所有数都是逆序对。

代码

  1. class Solution {
  2. public:
  3. int InversePairs(vector<int> data) {
  4. if(data.size()<2) return 0;
  5. vector<int> cache(data.size(), 0);
  6. return InversePairsCore(data, 0, data.size()-1, cache)%1000000007;
  7. }
  8. long long InversePairsCore(vector<int> &data, int low , int high, vector<int> &cache){
  9. if(low>=high) {
  10. return 0 ;
  11. }
  12. int mid = low + (high-low)/2;
  13. long long leftCount = InversePairsCore(data, low, mid, cache);
  14. long long rightCount = InversePairsCore(data, mid+1, high, cache);
  15. long long curCount = 0;
  16. int stop1=mid, stop2=high, begin1=low, begin2=mid+1;
  17. int cacheIdx = high;
  18. while(begin1<=stop1&&begin2<=stop2){
  19. if(data[stop1]<data[stop2]){
  20. cache[cacheIdx--] = data[stop2--];
  21. }else {
  22. curCount += (stop2-begin2+1);
  23. cache[cacheIdx--] = data[stop1--];
  24. }
  25. }
  26. while(begin1<=stop1) cache[cacheIdx--]=data[stop1--];
  27. while(begin2<=stop2) cache[cacheIdx--]=data[stop2--];
  28. for(int i=low; i<=high; ++i)
  29. data[i] = cache[i];
  30. return leftCount+rightCount+curCount;
  31. }
  32. };

36. 两个链表的第一个公共结点

题目描述

输入两个链表,找出它们的第一个公共结点。

解题思路

两个指针一起跑,跑到有一个停止,就马上停止,然后另一个没跑完的再带着那个链表的头指针继续跑完,最后短链表和长链表一起跑,跑到一样的就输出。

代码

  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* FindFirstCommonNode(ListNode* pHead1, ListNode* pHead2) {
  12. if(pHead1==NULL || pHead2==NULL) return NULL;
  13. ListNode *tmp1=pHead1, *tmp2=pHead2;
  14. while(tmp1&&tmp2){
  15. tmp1 = tmp1->next;
  16. tmp2 = tmp2->next;
  17. }
  18. ListNode *longHead, *shortHead;
  19. if(tmp1==NULL){
  20. longHead=pHead2;
  21. shortHead=pHead1;
  22. while(tmp2){
  23. longHead=longHead->next;
  24. tmp2=tmp2->next;
  25. }
  26. }else {
  27. longHead=pHead1;
  28. shortHead=pHead2;
  29. while(tmp1){
  30. longHead=longHead->next;
  31. tmp1=tmp1->next;
  32. }
  33. }
  34. while(longHead!=shortHead){
  35. longHead=longHead->next;
  36. shortHead=shortHead->next;
  37. }
  38. return longHead;
  39. }
  40. };

37. 数字在排序数组中出现的次数

题目描述

统计一个数字在排序数组中出现的次数。

解题思路

定义两个函数,一个找左起点,一个找右节点,用二分查找。

代码

  1. class Solution {
  2. public:
  3. int GetNumberOfK(vector<int> data ,int k) {
  4. if(data.size()==0) return 0;
  5. return getRightIdx(data, k)-getLeftIdx(data, k)+1;
  6. }
  7. int getLeftIdx(vector<int> data, int k){
  8. int mid, i=0, j=data.size()-1;
  9. while(i<=j){
  10. mid = (i+j)/2;
  11. if(data[mid]>k){
  12. j=mid-1;
  13. }else if(data[mid]<k){
  14. i=mid+1;
  15. }else{
  16. if(mid==0||data[mid-1]!=k) return mid;
  17. j=mid-1;
  18. }
  19. }
  20. return -1;
  21. }
  22. int getRightIdx(vector<int> data, int k){
  23. int mid, i=0, j=data.size()-1;
  24. while(i<=j){
  25. mid = (i+j)/2;
  26. if(data[mid]>k){
  27. j=mid-1;
  28. }else if(data[mid]<k){
  29. i=mid+1;
  30. }else{
  31. if(mid==data.size()-1||data[mid+1]!=k) return mid;
  32. i=mid+1;
  33. }
  34. }
  35. return -2;
  36. }
  37. };

38. 二叉树的深度

题目描述

输入一棵二叉树,求该树的深度。从根结点到叶结点依次经过的结点(含根、叶结点)形成树的一条路径,最长路径的长度为树的深度。

解题思路

后序遍历,返回左右子树的最大深度+1。

代码

  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. class Solution {
  9. public:
  10. int TreeDepth(TreeNode* pRoot)
  11. {
  12. if(pRoot==NULL) return 0;
  13. return max(TreeDepth(pRoot->left), TreeDepth(pRoot->right))+1;
  14. }
  15. };

39. 平衡二叉树

题目描述

输入一棵二叉树,判断该二叉树是否是平衡二叉树。

解题思路

判断左右子树是否是平衡二叉树,并且根据左右子树的高的来判断根节点是否是一颗平衡二叉树。

代码

  1. class Solution {
  2. public:
  3. bool IsBalanced_Solution(TreeNode* pRoot) {
  4. int height;
  5. return IsBalancedCore(pRoot, height);
  6. }
  7. bool IsBalancedCore(TreeNode* pRoot, int &height){
  8. if(pRoot==NULL){
  9. height=0;
  10. return true;
  11. }
  12. int leftHeight, rightHeight;
  13. bool isBalanced = IsBalancedCore(pRoot->left, leftHeight) && IsBalancedCore(pRoot->right, rightHeight);
  14. height = max(leftHeight, rightHeight) + 1;
  15. return isBalanced && abs(leftHeight-rightHeight)<=1;
  16. }
  17. };

40. 数组中只出现一次的数字

题目描述

一个整型数组里除了两个数字之外,其他的数字都出现了两次。请写程序找出这两个只出现一次的数字。

解题思路

用异或做,并且将异或结果分组,得到两组,分别做抑异或。

代码

  1. class Solution {
  2. public:
  3. void FindNumsAppearOnce(vector<int> data,int* num1,int *num2) {
  4. int xorSum = 0;
  5. for(int a:data) xorSum ^= a;
  6. int splitBit = 0;
  7. while(((xorSum>>splitBit)&1)==0){
  8. ++splitBit;
  9. }
  10. *num1=0, *num2=0;
  11. for(int a:data){
  12. if((a>>splitBit)&1){
  13. *num1 ^=a;
  14. }else {
  15. *num2 ^=a;
  16. }
  17. }
  18. }
  19. };

41. 和为S的连续正数序列

题目描述

小明很喜欢数学,有一天他在做数学作业时,要求计算出9~16的和,他马上就写出了正确答案是100。但是他并不满足于此,他在想究竟有多少种连续的正数序列的和为100(至少包括两个数)。没多久,他就得到另一组连续正数和为100的序列:18,19,20,21,22。现在把问题交给你,你能不能也很快的找出所有和为S的连续正数序列? Good Luck!
输出描述:
输出所有和为S的连续正数序列。序列内按照从小至大的顺序,序列间按照开始数字从小到大的顺序

解题思路

用两个指针表示一个连续序列,sum=(begin+end)*(end-begin+1)/2, begin代表开始的数,end代表结束的数,每次得到的sum与目标一样就加入结果,不一样就调整指针。

代码

  1. class Solution {
  2. public:
  3. vector<vector<int> > FindContinuousSequence(int sum) {
  4. int curSum=0, length=(sum+1)/2;
  5. int begin=1, end=2;
  6. vector<vector<int> > result;
  7. while(end<=length){
  8. curSum = (begin+end)*(end-begin+1)/2;
  9. if(curSum==sum){
  10. vector<int> seq;
  11. for(int j=begin; j<=end; ++j){
  12. seq.push_back(j);
  13. }
  14. result.push_back(seq);
  15. ++begin;
  16. }else if(curSum<sum){
  17. ++end;
  18. }else {
  19. ++begin;
  20. }
  21. }
  22. return result;
  23. }
  24. };

42. 和为S的两个数字

题目描述

输入一个递增排序的数组和一个数字S,在数组中查找两个数,是的他们的和正好是S,如果有多对数字的和等于S,输出两个数的乘积最小的。
输出描述:
对应每个测试案例,输出两个数,小的先输出。

解题思路

因为排序了,所以直接用左右指针往中间靠拢就可以了。sum比目标大就减右指针,比目标小就加左指针。

代码

  1. class Solution {
  2. public:
  3. vector<int> FindNumbersWithSum(vector<int> array,int sum) {
  4. if(array.size()<=1) return {};
  5. int low=0, high=array.size()-1;
  6. while(low<high){
  7. int curSum = array[low]+array[high];
  8. if(curSum==sum){
  9. return {array[low], array[high]};
  10. }else if(curSum<sum){
  11. ++low;
  12. }else{
  13. --high;
  14. }
  15. }
  16. return {};
  17. }
  18. };

43. 左旋转字符串

题目描述

汇编语言中有一种移位指令叫做循环左移(ROL),现在有个简单的任务,就是用字符串模拟这个指令的运算结果。对于一个给定的字符序列S,请你把其循环左移K位后的序列输出。例如,字符序列S=”abcXYZdef”,要求输出循环左移3位后的结果,即“XYZdefabc”。是不是很简单?OK,搞定它!

解题思路

先将旋转部分和剩下部分各自翻转,最后整个字符串翻转。

代码

  1. class Solution {
  2. public:
  3. string LeftRotateString(string str, int n) {
  4. if(str.size()==0 || n==0) return str;
  5. rotate(str, 0, n-1);
  6. rotate(str, n, str.size()-1);
  7. rotate(str, 0, str.size()-1);
  8. return str;
  9. }
  10. void rotate(string &str, int left, int right){
  11. while(left<right){
  12. swap(str[left++], str[right--]);
  13. }
  14. }
  15. };

44. 翻转单词顺序列

题目描述

牛客最近来了一个新员工Fish,每天早晨总是会拿着一本英文杂志,写些句子在本子上。同事Cat对Fish写的内容颇感兴趣,有一天他向Fish借来翻看,但却读不懂它的意思。例如,“student. a am I”。后来才意识到,这家伙原来把句子单词的顺序翻转了,正确的句子应该是“I am a student.”。Cat对一一的翻转这些单词顺序可不在行,你能帮助他么?

解题思路

单词各自翻转,最后整个字符串翻转。

代码

  1. class Solution {
  2. public:
  3. string ReverseSentence(string str) {
  4. if(str.size()<=1) return str;
  5. for(int i=0, j=0; i<str.size(); ++i){
  6. if(str[i]==' ') {
  7. rotate(str, j, i-1);
  8. j=i+1;
  9. }else if(i==str.size()-1){
  10. rotate(str, j, i);
  11. }
  12. }
  13. rotate(str, 0, str.size()-1);
  14. return str;
  15. }
  16. void rotate(string &str, int low, int high){
  17. while(low<high)
  18. swap(str[low++], str[high--]);
  19. }
  20. };

45. 扑克牌顺子

题目描述

LL今天心情特别好,因为他去买了一副扑克牌,发现里面居然有2个大王,2个小王(一副牌原本是54张^_^)...他随机从中抽出了5张牌,想测测自己的手气,看看能不能抽到顺子,如果抽到的话,他决定去买体育彩票,嘿嘿!!“红心A,黑桃3,小王,大王,方片5”,“Oh My God!”不是顺子.....LL不高兴了,他想了想,决定大\小 王可以看成任何数字,并且A看作1,J为11,Q为12,K为13。上面的5张牌就可以变成“1,2,3,4,5”(大小王分别看作2和4),“So Lucky!”。LL决定去买体育彩票啦。 现在,要求你使用这幅牌模拟上面的过程,然后告诉我们LL的运气如何。为了方便起见,你可以认为大小王是0。

解题思路

先排序,算出有几个大小王,然后计算相邻数的总间隙不超过大小王个数,如果有两个点数相同的不能算顺子。

代码

  1. class Solution {
  2. public:
  3. bool IsContinuous( vector<int> numbers ) {
  4. if(numbers.size()==0) return false;
  5. sort(numbers.begin(), numbers.end());
  6. int i=0, count=0;
  7. for(;numbers[i]==0; ++i, ++count);
  8. for(++i;i<numbers.size(); ++i){
  9. if(numbers[i]==numbers[i-1]) return false;
  10. count -= numbers[i]-numbers[i-1]-1;
  11. if(count<0) return false;
  12. }
  13. return true;
  14. }
  15. };

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

题目描述

每年六一儿童节,牛客都会准备一些小礼物去看望孤儿院的小朋友,今年亦是如此。HF作为牛客的资深元老,自然也准备了一些小游戏。其中,有个游戏是这样的:首先,让小朋友们围成一个大圈。然后,他随机指定一个数m,让编号为0的小朋友开始报数。每次喊到m-1的那个小朋友要出列唱首歌,然后可以在礼品箱中任意的挑选礼物,并且不再回到圈中,从他的下一个小朋友开始,继续0...m-1报数....这样下去....直到剩下最后一个小朋友,可以不用表演,并且拿到牛客名贵的“名侦探柯南”典藏版(名额有限哦!!^_^)。请你试着想下,哪个小朋友会得到这份礼品呢?(注:小朋友的编号是从0到n-1)

解题思路

每次都少了第m-1个人,假设原来的序号为0到n-1,下次就从m开始。将其重新编号。

m       0
m+1     1
.       .
.       .
n-1   n-m-1
0      n-m
.       .
m-2    n-2

所以假设下一次编号为x,这次为f(x),则f(x)= (x+m)%n。当剩下一个人的时候,编号为0,所以从0递推起。

代码

  1. class Solution {
  2. public:
  3. int LastRemaining_Solution(int n, int m)
  4. {
  5. if(n<=1) return n-1;
  6. int x=0;
  7. for(int i=2; i<=n; ++i){
  8. x= (x+m)%i;
  9. }
  10. return x;
  11. }
  12. };

47. 求1+2+3+...+n

题目描述

求1+2+3+...+n,要求不能使用乘除法、for、while、if、else、switch、case等关键字及条件判断语句(A?B:C)。

解题思路

用递归,利用短路特性。

  1. class Solution {
  2. public:
  3. int Sum_Solution(int n) {
  4. int result=1;
  5. n-1==0 || (result=Sum_Solution(n-1)+n);
  6. return result;
  7. }
  8. };

48. 不用加减乘除做加法

题目描述

写一个函数,求两个整数之和,要求在函数体内不得使用+、-、*、/四则运算符号。

解题思路

用与和异或,异或相当于位相加,不进位。与再往左移一位相当于进位。

代码

  1. class Solution {
  2. public:
  3. int Add(int num1, int num2)
  4. {
  5. while(num1){
  6. int tmp = num1^num2;
  7. num1 = (num1&num2)<<1;
  8. num2 = tmp;
  9. }
  10. return num2;
  11. }
  12. };

49. 把字符串转换成整数

题目描述

将一个字符串转换成一个整数,要求不能使用字符串转换整数的库函数。 数值为0或者字符串不是一个合法的数值则返回0
输入描述:
输入一个字符串,包括数字字母符号,可以为空
输出描述:
如果是合法的数值表达则返回该数字,否则返回0
示例1
输入

+2147483647
    1a33

输出

2147483647
    0

解题思路

异常检测非常重要,有左右两边有空格,还有正负号。

代码

  1. class Solution {
  2. public:
  3. int StrToInt(string str) {
  4. if(str.size()==0) return 0;
  5. int i=0, j=str.size()-1;
  6. while(i<=j&&str[i]==' ') ++i;
  7. while(str[i]==' ') --j;
  8. if(i>j) return 0;
  9. bool isNeg=false;
  10. if(str[i]=='-') isNeg=true, ++i;
  11. else if(str[i]=='+') ++i;
  12. int result=0;
  13. while(i<=j){
  14. if(str[i]<'0'||str[i]>'9') return 0;
  15. result = 10*result + (str[i++]-'0');
  16. }
  17. return isNeg?-result:result;
  18. }
  19. };

50.数组中重复的数字

题目描述

在一个长度为n的数组里的所有数字都在0到n-1的范围内。 数组中某些数字是重复的,但不知道有几个数字是重复的。也不知道每个数字重复几次。请找出数组中任意一个重复的数字。 例如,如果输入长度为7的数组{2,3,1,0,2,5,3},那么对应的输出是第一个重复的数字2。

解题思路

关键点在于0到n-1。所以可以将数字作为索引,索引到某个位置上,将这个位置的数做一些操作,可以标志这个作为索引的那个数字是否重复,并且这个操作是可以恢复的。

代码

  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: true if the input is valid, and there are some duplications in the array number
  8. // otherwise false
  9. bool duplicate(int numbers[], int length, int* duplication) {
  10. for(int i=0; i<length; ++i){
  11. int num = (numbers[i]+length)%length;
  12. if(numbers[num]<0){
  13. *duplication=num;
  14. return true;
  15. }else
  16. numbers[num] -= length;
  17. }
  18. return false;
  19. }
  20. };

51. 构建乘积数组

题目描述

给定一个数组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]。不能使用除法。

解题思路

将乘积分成左右两部分,用两个循环来乘,最后再乘起来。

代码

  1. class Solution{
  2. public:
  3. vector<int> multiply(const vector<int>& A) {
  4. if(A.size()==0) return A;
  5. vector<int> B(A.size());
  6. B[0]=1;
  7. for(int i=1; i<A.size(); ++i){
  8. B[i] = B[i-1]*A[i-1];
  9. }
  10. vector<int> C(A.size());
  11. C[A.size()-1]=1;
  12. for(int i=A.size()-2; i>=0; --i){
  13. C[i] = C[i+1]*A[i+1];
  14. }
  15. for(int i=0; i<A.size(); ++i){
  16. B[i] *= C[i];
  17. }
  18. return B;
  19. }
  20. };

52. 正则表达式匹配

题目描述

请实现一个函数用来匹配包括'.'和''的正则表达式。模式中的字符'.'表示任意一个字符,而''表示它前面的字符可以出现任意次(包含0次)。 在本题中,匹配是指字符串的所有字符匹配整个模式。例如,字符串"aaa"与模式"a.a"和"ab*ac*a"匹配,但是与"aa.a"和"ab*a"均不匹配

解题思路

关键是分析情况,对于*这种情况做好分析。
有三种情况:
1. 假设模式当前字母是'\0',而字符串不是,则返回false,否则返回true。
2. 假设模式第二个字符不是'*'
* 直接匹配当前字母,如果一样就字符串和模式都往后移一位。
* 如果不匹配则直接返回false。
3. 假设模式第二个字符是'*'
* 如果当前字符不匹配,则模式往后调两个。
* 如果当前字符匹配了
* 字符串不变,模式往后调两个
* 或者字符串后移一位,模式后移两位
* 或者字符串后移一位,模式不变。

代码

  1. class Solution {
  2. public:
  3. bool match(char* str, char* pattern)
  4. {
  5. if(str==NULL || pattern==NULL) return false;
  6. return matchCore(str, pattern);
  7. }
  8. bool matchCore(char* str, char* pattern){
  9. if(*pattern=='\0') return *str=='\0';
  10. if(*(pattern+1)!='*'){
  11. if(*pattern== *str ||(*pattern=='.'&&*str!='\0'))
  12. return matchCore(str+1, pattern+1);
  13. else
  14. return false;
  15. }else {
  16. if(*pattern== *str ||(*pattern=='.'&&*str!='\0')){
  17. return matchCore(str, pattern+2) || matchCore(str+1, pattern+2) || matchCore(str+1, pattern);
  18. }else {
  19. return matchCore(str, pattern+2);
  20. }
  21. }
  22. }
  23. };

53. 表示数值的字符串

题目描述

请实现一个函数用来判断字符串是否表示数值(包括整数和小数)。例如,字符串"+100","5e2","-123","3.1416"和"-1E-16"都表示数值。 但是"12e","1a3.14","1.2.3","+-5"和"12e+4.3"都不是。

54. 字符流中第一个不重复的字符

题目描述

请实现一个函数用来找出字符流中第一个只出现一次的字符。例如,当从字符流中只读出前两个字符"go"时,第一个只出现一次的字符是"g"。当从该字符流中读出前六个字符“google"时,第一个只出现一次的字符是"l"。
输出描述:
如果当前字符流没有存在出现一次的字符,返回#字符。

解题思路

用一个hash表存好每个字符出现的位置,如果出现两次置为特殊值。输出的时候遍历hash表,找到最小的位置。

代码

  1. class Solution
  2. {
  3. public:
  4. int hash[256];
  5. int index;
  6. Solution():index(0)
  7. {
  8. memset(hash, -1, sizeof(hash));
  9. }
  10. //Insert one char from stringstream
  11. void Insert(char ch)
  12. {
  13. if(hash[ch]==-1)
  14. hash[ch]=index;
  15. else if(hash[ch]>=0)
  16. hash[ch]=-2;
  17. ++index;
  18. }
  19. //return the first appearence once char in current stringstream
  20. char FirstAppearingOnce()
  21. {
  22. char result='#';
  23. int minIdx = INT_MAX;
  24. for(int i=0; i<256; ++i){
  25. if(hash[i]>=0&&hash[i]<minIdx){
  26. minIdx=hash[i];
  27. result = i;
  28. }
  29. }
  30. return result;
  31. }
  32. };

55. 链表中环的入口结点

题目描述

一个链表中包含环,请找出该链表的环的入口结点。

解题思路

先用快慢指针跑,如果没环快指针就会遇到NULL,如果有环,快慢指针一定会相遇。然后快指针指向头节点,与慢支针一起走,指导相遇。

代码

  1. /*
  2. struct ListNode {
  3. int val;
  4. struct ListNode *next;
  5. ListNode(int x) :
  6. val(x), next(NULL) {
  7. }
  8. };
  9. */
  10. class Solution {
  11. public:
  12. ListNode* EntryNodeOfLoop(ListNode* pHead)
  13. {
  14. if(pHead==NULL||pHead->next==NULL) return NULL;
  15. ListNode *fast=pHead, *slow=pHead;
  16. while(fast->next){
  17. fast = fast->next->next;
  18. slow = slow->next;
  19. if(fast==NULL) return NULL;
  20. if(fast==slow)
  21. break;
  22. }
  23. fast=pHead;
  24. while(fast!=slow){
  25. fast = fast->next;
  26. slow = slow->next;
  27. }
  28. return slow;
  29. }
  30. };

56. 删除链表中重复的结点

题目描述

在一个排序的链表中,存在重复的结点,请删除该链表中重复的结点,重复的结点不保留,返回链表头指针。 例如,链表1->2->3->3->4->4->5 处理后为 1->2->5

解题思路

用一个辅助Node指向头节点,用一个指针指向当前节点,判断当前节点的next是否相等,如果相等则往前移动。注意处理当前节点的next为空的情况。

代码

  1. /*
  2. struct ListNode {
  3. int val;
  4. struct ListNode *next;
  5. ListNode(int x) :
  6. val(x), next(NULL) {
  7. }
  8. };
  9. */
  10. class Solution {
  11. public:
  12. ListNode* deleteDuplication(ListNode* pHead)
  13. {
  14. ListNode* helperNode = new ListNode(0), *curNode=pHead;
  15. helperNode->next = pHead;
  16. ListNode* preNode = helperNode;
  17. while(curNode){
  18. if(curNode->next!=NULL&&curNode->val==curNode->next->val){
  19. while(curNode->next!=NULL&&curNode->val==curNode->next->val){
  20. curNode=curNode->next;
  21. }
  22. curNode = curNode->next;
  23. preNode->next=curNode;
  24. }else {
  25. curNode = curNode->next;
  26. preNode = preNode->next;
  27. }
  28. }
  29. return helperNode->next;
  30. }
  31. };

57. 二叉树的下一个结点

题目描述

给定一个二叉树和其中的一个结点,请找出中序遍历顺序的下一个结点并且返回。注意,树中的结点不仅包含左右子结点,同时包含指向父结点的指针。

解题思路

分两种情况:
1. 如果右孩子存在,则输出右子树的最左节点。
2. 如果右孩子不存在,则从根节点里找,直到是根节点的左子树或者无根节点的时候停止。

代码

  1. class Solution {
  2. public:
  3. TreeLinkNode* GetNext(TreeLinkNode* pNode)
  4. {
  5. if(pNode->right){
  6. pNode = pNode->right;
  7. while(pNode->left) pNode=pNode->left;
  8. return pNode;
  9. }else {
  10. while(pNode->next&&pNode->next->right==pNode) pNode = pNode->next;
  11. return pNode->next;
  12. }
  13. }
  14. };

58. 对称的二叉树

题目描述

请实现一个函数,用来判断一颗二叉树是不是对称的。注意,如果一个二叉树同此二叉树的镜像是同样的,定义其为对称的。

解题思路

将左右子树前序遍历,左子树使用 中->左->右的顺序,右子树使用 中->右->左的顺序。

代码

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

59. 按之字形顺序打印二叉树

60. 把二叉树打印成多行

题目描述

从上到下按层打印二叉树,同一层结点从左至右输出。每一层输出一行。

解题思路

用一个指针一直指着队列中的末尾,遇到换行就切换。

代码

  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==NULL) return result;
  16. vector<int> oneLine;
  17. queue<TreeNode*> helper;
  18. helper.push(pRoot);
  19. TreeNode* rightNode = pRoot;
  20. while(!helper.empty()){
  21. TreeNode* node = helper.front();
  22. helper.pop();
  23. oneLine.push_back(node->val);
  24. if(node->left) helper.push(node->left);
  25. if(node->right) helper.push(node->right);
  26. if(node==rightNode){
  27. rightNode = helper.back();
  28. result.push_back(oneLine);
  29. oneLine.clear();
  30. }
  31. }
  32. return result;
  33. }
  34. };

61. 序列化二叉树

题目描述

请实现两个函数,分别用来序列化和反序列化二叉树

解题思路

62. 二叉搜索树的第k个结点

题目描述

给定一颗二叉搜索树,请找出其中的第k小的结点。例如,

     5 
    / \ 
    3 7 
   /\ /\ 
  2 4 6 8 

中,按结点数值大小顺序第三个结点的值为4。

解题思路

中序遍历,按 左->中->右的顺序,找到k=0则停止。

代码

  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(k<=0) return NULL;
  16. TreeNode* result=NULL;
  17. KthNodeCore(pRoot, k, result);
  18. return result;
  19. }
  20. void KthNodeCore(TreeNode* pRoot, int& k, TreeNode* &result)
  21. {
  22. if(pRoot==NULL) return;
  23. KthNodeCore(pRoot->left, k, result);
  24. if(k==1){
  25. if(result==NULL)
  26. result=pRoot;
  27. return;
  28. }
  29. --k;
  30. KthNodeCore(pRoot->right, k, result);
  31. }
  32. };

63.数据流中的中位数

题目描述

如何得到一个数据流中的中位数?如果从数据流中读出奇数个数值,那么中位数就是所有数值排序之后位于中间的数值。如果从数据流中读出偶数个数值,那么中位数就是所有数值排序之后中间两个数的平均值。

解题思路

用两个堆,一个放左边的最大值,一个放右边的最小值。

代码

  1. class Solution {
  2. private:
  3. priority_queue<int> leftHeap;
  4. priority_queue<int, vector<int>, greater<int> > rightHeap;
  5. public:
  6. void Insert(int num)
  7. {
  8. if(leftHeap.empty()) leftHeap.push(num);
  9. else if(num>leftHeap.top()){
  10. rightHeap.push(num);
  11. if(rightHeap.size()>leftHeap.size()){
  12. leftHeap.push(rightHeap.top());
  13. rightHeap.pop();
  14. }
  15. }
  16. else {
  17. leftHeap.push(num);
  18. if(leftHeap.size()-1>rightHeap.size()){
  19. rightHeap.push(leftHeap.top());
  20. leftHeap.pop();
  21. }
  22. }
  23. }
  24. double GetMedian()
  25. {
  26. if(leftHeap.size()==0)
  27. return 0;
  28. if(leftHeap.size()==rightHeap.size())
  29. return (leftHeap.top()+rightHeap.top())/2.0;
  30. return leftHeap.top();
  31. }
  32. };

64. 滑动窗口的最大值

题目描述

给定一个数组和滑动窗口的大小,找出所有滑动窗口里数值的最大值。例如,如果输入数组{2,3,4,2,6,2,5,1}及滑动窗口的大小3,那么一共存在6个滑动窗口,他们的最大值分别为{4,4,6,6,6,5}; 针对数组{2,3,4,2,6,2,5,1}的滑动窗口有以下6个: {[2,3,4],2,6,2,5,1}, {2,[3,4,2],6,2,5,1}, {2,3,[4,2,6],2,5,1}, {2,3,4,[2,6,2],5,1}, {2,3,4,2,[6,2,5],1}, {2,3,4,2,6,[2,5,1]}。

65. 矩阵中的路径

题目描述

请设计一个函数,用来判断在一个矩阵中是否存在一条包含某字符串所有字符的路径。路径可以从矩阵中的任意一个格子开始,每一步可以在矩阵中向左,向右,向上,向下移动一个格子。如果一条路径经过了矩阵中的某一个格子,则该路径不能再进入该格子。 例如
a b c e
s f c s
a d e e
矩阵中包含一条字符串"bcced"的路径,但是矩阵中不包含"abcb"路径,因为字符串的第一个字符b占据了矩阵中的第一行第二个格子之后,路径不能再次进入该格子。

添加新批注
在作者公开此批注前,只有你和作者可见。
回复批注