牛客网剑指offer-66题

第一题有序查找, 看到题目就想是不是二分,

题解

我们观察二维矩阵的性质, 对于任意一个点, 将矩阵分为四个区域, 左上是小于当前点, 右下大于当前点, 其他位置不确定, 然后我们要设计一个方法, 去缩小查找的范围。因为数据分为四个区域, 所以为了实现单调, 所以我们选择右上角的点。
对于右上角的点
如果array[row][col] > target 那么右下方区域(在这里是该列)应该被去除.
如果array[row][col] < target 那么左上方区域(也就是该行)被去除.
否则就直接返回true。
复杂度O(n+m)

第一题其实还可以用更快的做法,就是
第一行二分, 删去右侧区域, 然后对最后一列二分, 删去上方区域,然后对最后一行二分删去左侧区域, 然后对第一列二分, 删去右侧区域, 这样循环往复
复杂度是O(logn + logm)级别的

class Solution {
public:
    bool Find(int target, vector<vector<int> > array) {
        int maxn = (int)array.size()-1, maxm = (int)array[0].size()-1;
        int p1 = 0, p2 = maxm;
        while(p1 <= maxn && p2 >= 0)
        {
            if (array[p1][p2] == target)
                return true;
            else if (array[p1][p2] > target)
                p2--;
            else
                p1++;
        }
        return false;
    }
};

下面复习一下最基本对int数组的二分查找,

首先知道lower_bound, 和upper_bound的实现
lower_bound 是查找最小的pos, 使得array[pos] >= key,
upper_bound 则是查找最小的pos, 使得array[pos] > key,

现在考虑一个上升序列 (1,3,5,5,7)
我们查找5
我们将一段区间(p, p+len)分成两个区间
(p,p+len/2), (p+len/2+1,p+len)
然后我们取mid = p+len/2

  1. 如果key>A[mid] 也就是key肯定在右区间, 所以我们令p = mid+len/2+1
  2. 如果key<=A[mid] 也就是key在左区间, 所以我们令len = len/2

这样我们知道了递归的写法(实际上使用while写的)
那么递归的终止条件应该是当区间长度len=0的时候。
然后我们再举例几个递归终点
3. len = 1, 只有一个数,若key <= A[mid], first = mid 就是该问题的解, 否则解是mid+1,
4. len = 2, 有两个数, 左区间(p, p+1), 右区间是空区间, 如果key <= A[mid] , 那么令len=1, 回到len=1的情况, 否则, 解是mid+1
5. len = 3
6. len = 4

#include<bits/stdc++.h>

using namespace std;

int my_lower_bound(int *array, int len, int key)
{
    int first = 0, half, mid;
    while(len>0)
    {
        half = len>>1;
        mid = first + half;
        if (array[mid] < key)
        {
            first = mid+1;
            len = len-half-1;
        }
        else
            len = half;
    }
    return first;
}

int my_upper_bound(int *array, int len, int key)
{
    int first = 0, half, mid;
    while(len>0)
    {
        half = len>>1;
        mid = first + half;
        if (array[mid] <= key)
        {
            first = mid+1;
            len = len-half-1;
        }
        else
            len = half;
    }
    return first;
}
int main()
{
    int A[] = {
        1, 3, 5, 7, 7, 9, 9
    };
    cout << my_lower_bound(A,6,9) << endl;
    cout << my_upper_bound(A,6,9) << endl;
    return 0;
}

第二题 替换空格

class Solution {
public:
	void replaceSpace(char *str,int length) {
        int len = 0;
        vector<char> temp;
        for (int i=0;i<=length;i++)
        {
            if (str[i] == ' ')
            {
                temp.push_back('%');
                temp.push_back('2');
                temp.push_back('0');
            }
            else
                temp.push_back(str[i]);
        }
        len = 0;
        for (auto &p: temp)
        {
            str[len++] = p;
        }
	}
};

第三题 从尾到头打印链表

/**
*  struct ListNode {
*        int val;
*        struct ListNode *next;
*        ListNode(int x) :
*              val(x), next(NULL) {
*        }
*  };
*/
class Solution {
public:
    vector<int> printListFromTailToHead(ListNode* head) {
        stack<int> s;
        vector<int> ret;
        for(ListNode *p = head; p != NULL; p = p->next)
        {
            ret.push_back(p->val);
        }
        int n = ret.size();
        for (int i = 0; i < n/2; i++)
        {
            swap(ret[i], ret[n-1-i]);
        }
        return ret;
    }
};

第四题 重建二叉树

以前写过类似的题, 重点是找根, 然后递归地去重建二叉树, 最后输出后序遍历的序列

/*
先序遍历的第一个是子树的跟,中序遍历的左边是左子树, 右边是右子树,然后递归重建树即可。
*/
class Solution {
public:
    TreeNode* reConstructBinaryTree(vector<int> pre,vector<int> vin) {
//        for (auto &p: pre)
//            cout << p << ' ';
//        cout << endl;
//        for (auto &p: vin)
//            cout << p << ' ';
//        cout << endl;
        if ((int)pre.size() == 0) return NULL;
        int root_value = pre[0], loc=-1;
        for (int i=0;i<(int)vin.size();i++)
            if (vin[i] == root_value)
                loc = i;
        TreeNode *root = new TreeNode(root_value);
        TreeNode *lson = NULL, *rson = NULL;
        if (loc > 0)
            lson = reConstructBinaryTree(vector<int> (pre.begin()+1,pre.begin()+loc+1), vector<int> (vin.begin(), vin.begin()+loc));
        if (loc < (int)vin.size()-1)
            rson = reConstructBinaryTree(vector<int> (pre.begin()+loc+1,pre.end()), vector<int> (vin.begin()+loc+1, vin.end()));
        root->left = lson;
        root->right = rson;
        return root;
    }
};

第五题 用两个栈实现队列

直接模拟队列操作, 每次操作复杂度是o(n)

class Solution
{
public:
    void push(int node) {
        while(!stack2.empty())
        {
            stack1.push(stack2.top());
            stack2.pop();
        }
        stack1.push(node);
        while(!stack1.empty())
        {
            stack2.push(stack1.top());
            stack1.pop();
        }
    }

    int pop() {
        int ret = stack2.top();
        stack2.pop();
        return ret;
    }

private:
    stack<int> stack1;
    stack<int> stack2;
};

第六题 旋转数组的最小数字

考虑非递减性质, 可以用二分来O(logn)完成查找。
二分条件根据中点和切分点的值(A[0]) , 来判断目标点,在左区间, 还是右。
本质上是二分查找第一个 <= A[0]的数。

class Solution {
public:
    int minNumberInRotateArray(vector<int> rotateArray) {
        vector<int> &A = rotateArray;
        int l = 0, r = A.size()-1, m;
        while(l < r)
        {
            m = (l+r)/2;
            if (A[m] >= A[0])
            {
                l = m+1;
            }
            else
            {
                r = m;
            }
        }
        return A[l];
    }
};

第7题 斐波那契数列

class Solution {
public:
    int Fibonacci(int n) {
        int A[2] = {0,1};
        for(int i=0;i<n/2;i++)
        {
            A[0] += A[1];
            A[1] += A[0];
        }
        if (n%2==0)
            return A[0];
        else
            return A[1];
    }
};

第8题 跳台阶

转移方程就是dp[i] = dp[i-1] + dp[i-2];
所以dp[i] = Fib(n+1);

//第7题加一行
int n = number+1;

第9题 变态跳台阶

d p [ i ] = ∑ 0 i − 1 d p [ j ] = d p [ i − 1 ] + ∑ 0 i − 2 d p [ j ] = 2 ∗ d p [ i − 1 ] dp[i] = \sum_{0}^{i-1}dp[j]=dp[i-1]+\sum_{0}^{i-2}dp[j]=2*dp[i-1] dp[i]=0i1dp[j]=dp[i1]+0i2dp[j]=2dp[i1]
并 且 d p [ 0 ] = 1 并且dp[0]=1 dp[0]=1
所以dp[i] = 2^i = (1<<(i-1))

class Solution {
public:
    int jumpFloorII(int number) {
        return 1<<(number-1);
    }
};
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值