日练三题,冰冻三尺非一日之寒。
今日题目:1、顶端迭代器;2、完美平方数;3、根节点到叶节点数字和。
今日摘录:
人生是一场旅程。我们经历了几次轮回,才换来这个旅程。而这个旅程很短,因此不妨大胆一些,不妨大胆一些去爱一个人,去攀一座山,去追一个梦……有很多事我都不明白。但我相信一件事。上天让我们来到这个世上,就是为了让我们创造奇迹 ——-《大鱼海棠》
284. Peeking Iterator | Difficulty: Medium
Given an Iterator class interface with methods: next() and hasNext(), design and implement a PeekingIterator that support the peek()operation – it essentially peek() at the element that will be returned by the next call to next().
Here is an example. Assume that the iterator is initialized to the beginning of the list: [1, 2, 3].
Call next() gets you 1, the first element in the list.
Now you call peek() and it returns 2, the next element. Calling next() after that still return 2.
You call next() the final time and it returns 3, the last element. Calling hasNext() after that should return false.
题意:实现一个类
思路:
1、利用复制构造函数
// Below is the interface for Iterator, which is already defined for you.
// **DO NOT** modify the interface for Iterator.
class Iterator {
struct Data;
Data* data;
public:
Iterator(const vector<int>& nums);
Iterator(const Iterator& iter);
virtual ~Iterator();
// Returns the next element in the iteration.
int next();
// Returns true if the iteration has more elements.
bool hasNext() const;
};
class PeekingIterator : public Iterator {
public:
PeekingIterator(const vector<int>& nums) : Iterator(nums) {
// Initialize any member here.
// **DO NOT** save a copy of nums and manipulate it directly.
// You should only use the Iterator interface methods.
}
// Returns the next element in the iteration without advancing the iterator.
int peek() {
return Iterator(*this).next();
}
// hasNext() and next() should behave the same as in the Iterator interface.
// Override them if needed.
int next() {
return Iterator::next();
}
bool hasNext() const {
return Iterator::hasNext();
}
};
结果:4ms
2、
// Below is the interface for Iterator, which is already defined for you.
// **DO NOT** modify the interface for Iterator.
class Iterator {
struct Data;
Data* data;
public:
Iterator(const vector<int>& nums);
Iterator(const Iterator& iter);
virtual ~Iterator();
// Returns the next element in the iteration.
int next();
// Returns true if the iteration has more elements.
bool hasNext() const;
};
class PeekingIterator : public Iterator {
private:
int m_next;
bool m_hasnext;
public:
PeekingIterator(const vector<int>& nums) : Iterator(nums) {
// Initialize any member here.
// **DO NOT** save a copy of nums and manipulate it directly.
// You should only use the Iterator interface methods.
m_hasnext = Iterator::hasNext();
if (m_hasnext) m_next = Iterator::next();
}
// Returns the next element in the iteration without advancing the iterator.
int peek() {
return m_next;
}
// hasNext() and next() should behave the same as in the Iterator interface.
// Override them if needed.
int next() {
int res = m_next;
m_hasnext = Iterator::hasNext();
if (m_hasnext) m_next = Iterator::next();
else m_next=NULL;
return res;
}
bool hasNext() const {
return m_hasnext;
}
};
结果:4ms
279. Perfect Squares | Difficulty: Medium
Given a positive integer n, find the least number of perfect square numbers (for example, 1, 4, 9, 16, …) which sum to n.
For example, given n = 12, return 3 because 12 = 4 + 4 + 4; given n = 13, return 2 because 13 = 4 + 9.
题意:完美平方数,要使用最少的平方数来组成我们的完美平方数,返回这个最小的个数。
思路:
1、这道题很容易看出能用DP解,用DP主要是找到状态之间的关系,先尝试从最低位开始计算。
dp[0]=0,dp[1] = 1,计算dp[2],我们需要考虑哪些情况?2=1*1+1*1,3=1*1+1*1+1*1,4=1*1+1*1+1*1+1*1=2*2
看一个大一点的数字,100=1*1+dp[99]=2*2+dp[96]=3*3+dp[91]+……=10*10+dp[0]。而1*1=2*2=…=10*10=1
这样看来规律就比较明显了,dp[n] = 1+dp[n-i*i],i={1,2,3……floor(sqrt(n))}
照此思路写出代码
class Solution {
public:
int numSquares(int n) {
if(n<=0) return 0;
vector<int> dp(n+1,INT_MAX);
dp[0] = 0;
for(int i=1;i<=n;i++)
{
for(int j=1;j*j<=i;j++)
dp[i] = min(dp[i],dp[i-j*j]+1);
}
return dp[n];
}
};
结果:440ms
2、显然还能提高,但是我不知道怎么去做了,以下三种方法参考自:
静态DP,和DP思想一样,只不过初始化的时候只初始化一个dp[0],然后后面逐个向里面加。
class Solution {
public:
int numSquares(int n) {
if(n<=0) return 0;
static vector<int> dp{0};
while(dp.size()<=n)
{
int num = INT_MAX;
for(int i=1;i*i<=dp.size();i++)
num = min(num,dp[dp.size()-i*i]+1);
dp.push_back(num);
}
return dp[n];
}
};
结果:16ms
3、数学思路
首先我们尝试枚举一些数字
1 1
10 2
11 3
100 1
101 2
110 3
111 4
1000 2
1001 1
1010 2
1011 3
1100 3
1101 2
1110 3
1111 4
10000 1
10001 2
10010 2
10011 3
10100 2
……
先就这些数字,观察下规律,貌似结果永远是1-4?这个猜想暂时还不能确定。那么先就这么假设。
首先如果一个数是4的倍数,就一直右移两位,直到这个数的低4位至少有一个1存在。现在我们现在看看
1的情况有哪些,完全平方数,
2的情况是偶数位有一个1或者奇数位有两个1
3的情况是奇数位1个1,偶数位1个1
4的情况呢,最低三位都是1
开始想从位运算上面去找规律,但是找了好久规律,还是没找到。
1和4还是很好写出来,1就是完全平方,4就是如果一个数是4的倍数,就除以4,然后剩下的数中低三位都是1.
2呢?也很好理解,从1到sqrt(n)中,只要能找到一个数X,将n-x之后的数是完全平方数即可。
剩下就是3.
class Solution {
public:
bool is_square(int n)
{
int square_n = (int)sqrt(n);
return(square_n*square_n==n);
}
int numSquares(int n) {
if(n<=0) return 0;
if(is_square(n)) return 1;
while((n&3)==0) n >>=2;
if((n &7)==7) return 4;
int sqrt_n = (int)(sqrt(n));
for(int i = 1; i <= sqrt_n; i++)
{
if (is_square(n - i*i))
{
return 2;
}
}
return 3;
}
};
结果:4ms
4、BFS的思想,首先标记所有的完全平方数,然后标记所有两个完全平方数能够组成的数,然后三个,最后4个,直到标记完所有数为止,本题最多只到4个完全平方数。
class Solution {
public:
int numSquares(int n) {
if(n<=0) return 0;
vector<int>cntPerfectNum(n,0);
vector<int>perfectNums;
for(int i=1;i*i<=n;i++)
{
cntPerfectNum[i*i-1] = 1;
perfectNums.push_back(i*i);
}
if(perfectNums.back()==n) return 1;
queue<int> search;
for(auto num:perfectNums)
{
search.push(num);
}
int Cnt = 1;
while(!search.empty())
{
Cnt++;
int size = search.size();
for(int i=0;i<size;i++)
{
int tmp = search.front();
for(auto num : perfectNums)
{
if(tmp+num==n) return Cnt;
else if(tmp+num<n && cntPerfectNum[tmp+num-1]==0)
{
cntPerfectNum[tmp+num-1] = Cnt;
search.push(tmp+num);
}
else if(tmp+num>n) break;
}
search.pop();
}
}
return 0;
}
};
结果:85ms
- Sum Root to Leaf Numbers | Difficulty: Medium
Given a binary tree containing digits from 0-9 only, each root-to-leaf path could represent a number.
An example is the root-to-leaf path 1->2->3 which represents the number 123.
Find the total sum of all root-to-leaf numbers.
For example,
1
/ \
2 3
The root-to-leaf path 1->2 represents the number 12.
The root-to-leaf path 1->3 represents the number 13.
Return the sum = 12 + 13 = 25.
题意:找到二叉树所有根节点到叶节点的路径和。
相关题目:path sum
思路:
1、DFS思想,每次都到最深,和path sum一题非常类似。
/**
* Definition for a binary tree node.
* struct TreeNode {
* int val;
* TreeNode *left;
* TreeNode *right;
* TreeNode(int x) : val(x), left(NULL), right(NULL) {}
* };
*/
class Solution {
public:
int sumNumbers(TreeNode* root) {
if(!root) return 0;
stack<TreeNode*> nodes;
TreeNode *pre=NULL,*cur=root;
int sum=0;
int res = 0;
while(cur||!nodes.empty())
{
while(cur)
{
nodes.push(cur);
sum=sum*10+cur->val;
cur = cur->left;
}
cur=nodes.top();
if(cur->right==NULL && cur->left==NULL) res+=sum;
if(cur->right&&pre!=cur->right)
{
cur = cur->right;
}
else
{
pre=cur;
sum/=10;
nodes.pop();
cur=NULL;
}
}
return res;
}
};
结果:5ms
2、该写成递归版本
递归基:叶子节点,返回路径和sum
递归调用:每个节点不是递归基的情况下路径和等于左边路径和加上右边路径和
/**
* Definition for a binary tree node.
* struct TreeNode {
* int val;
* TreeNode *left;
* TreeNode *right;
* TreeNode(int x) : val(x), left(NULL), right(NULL) {}
* };
*/
class Solution {
public:
int sumNumbers(TreeNode* root) {
if(!root) return 0;
return recursion(root,0);
}
int recursion(TreeNode*root,int val)
{
if(root->left==NULL && root->right==NULL) return val*10+root->val;
int newVal=0;
if(root->left!=NULL) newVal+=recursion(root->left,val*10+root->val);
if(root->right!=NULL) newVal+=recursion(root->right,val*10+root->val);
return newVal;
}
};
结果:4ms