第一题有序查找, 看到题目就想是不是二分,
题解
我们观察二维矩阵的性质, 对于任意一个点, 将矩阵分为四个区域, 左上是小于当前点, 右下大于当前点, 其他位置不确定, 然后我们要设计一个方法, 去缩小查找的范围。因为数据分为四个区域, 所以为了实现单调, 所以我们选择右上角的点。
对于右上角的点
如果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
- 如果key>A[mid] 也就是key肯定在右区间, 所以我们令p = mid+len/2+1
- 如果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]=∑0i−1dp[j]=dp[i−1]+∑0i−2dp[j]=2∗dp[i−1]
并
且
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);
}
};