系列四:30~39题
【系列三】:传送门 【剑指offer系列】:所有系列传送门 【系列五】:传送门
目录
2、数组中1出现的次数(从1到n整数中出现的次数)【查找】【数学】
1、连续子数组的最大和【数组】
题目:原题链接
题目描述
HZ偶尔会拿些专业问题来忽悠那些非计算机专业的同学。今天测试组开完会后,他又发话了:在古老的一维模式识别中,常常需要计算连续子向量的最大和,当向量全为正数的时候,问题很好解决。但是,如果向量中包含负数,是否应该包含某个负数,并期望旁边的正数会弥补它呢?例如:{6,-3,-2,7,-15,1,2,2},连续子向量的最大和为8(从第0个开始,到第3个为止)。给一个数组,返回它的最大连续子序列的和,你会不会被他忽悠住?(子向量的长度至少是1)
1.1、分析
我们在这里用动态规划来做这道题,根据最后一步,即以array[i]结尾的子串和最大,即f[i]表示以i结尾的子串最大和,那么状态计算则是:f[i]=max(f[i-1],0)+array[i],因为以i-1结尾的子串最大子串和可能是负数,那么我们宁愿不要前面的,直接以array[i]为一个子序列,最后我们比较下以每一个字符结尾时子串最大和为结果。
1.2、代码
class Solution {
public:
int FindGreatestSumOfSubArray(vector<int> array) {
int n=array.size();
vector<int> f(n);
for(int i=0;i<n;i++){
f[i]=INT_MIN; //一定要初始化为INT_MIN,因为和可能为负数
if(i==0){
f[i]=array[i];
continue;
}
f[i]=max(f[i-1],0)+array[i];
}
int res=INT_MIN;
for(int i=0;i<n;i++) res=max(res,f[i]);
return res;
}
};
2、数组中1出现的次数(从1到n整数中出现的次数)【查找】【数学】
题目:原题链接
题目描述
求出1~13的整数中1出现的次数,并算出100~1300的整数中1出现的次数?为此他特别数了一下1~13中包含1的数字有1、10、11、12、13因此共出现6次,但是对于后面问题他就没辙了。ACMer希望你们帮帮他,并把问题更加普遍化,可以很快的求出任意非负整数区间中1出现的次数(从1 到 n 中1出现的次数)。
2.1、分析
2.2、代码
class Solution {
public:
int NumberOf1Between1AndN_Solution(int n)
{
if(!n) return 0;
vector<int> f;
while(n){ //分离出每一位
f.push_back(n%10);
n/=10;
}
int res=0;
for(int i=f.size()-1;i>=0;i--){
int left=0,right=0,t=1; //分别计算左边和右边的数值大小
for(int j=f.size()-1;j>i;j--) left=left*10+f[j];
for(int j=i-1;j>=0;j--) right=right*10+f[j],t*=10;
res+=left*t;
if(f[i]==1) res+=right+1;
else if(f[i]>1) res+=t;
}
return res;
}
};
3、把数组排成最小的数【数组】
题目:原题链接
题目描述
输入一个正整数数组,把数组里所有数字拼接起来排成一个数,打印能拼接出的所有数字中最小的一个。例如输入数组{3,32,321},则打印出这三个数字能排成的最小数字为321323。
3.1、分析
我们重新定义一个新的排序方式,按照他们组合的字符串大小排,当ab<ba时就为真。这样最后只需要把所有数串起来即可。
3.2、代码
class Solution {
public:
static bool cmp(int a,int b){
string s1=to_string(a),s2=to_string(b);
return s1+s2<s2+s1;
}
string PrintMinNumber(vector<int> numbers) {
sort(numbers.begin(),numbers.end(),cmp);
string res;
for(auto x:numbers){
res+=to_string(x);
}
return res;
}
};
4、 丑数【穷举】
题目:原题链接
题目描述
把只包含质因子2、3和5的数称作丑数(Ugly Number)。例如6、8都是丑数,但14不是,因为它包含质因子7。 习惯上我们把1当做是第一个丑数。求按从小到大的顺序的第N个丑数。
4.1、分析
我们可以把质数包括2,3,5的所有数都枚举出来,然后对此进行归并。
4.2、代码
class Solution {
public:
int GetUglyNumber_Solution(int index) {
if(index<1) return 0;
vector<int> q(1,1);
int i=0,j=0,k=0;
while(--index){
//这和归并操作类似
int t=min(q[i]*2,min(q[j]*3,q[k]*5));
if(t==q[i]*2) i++;
if(t==q[j]*3) j++;
if(t==q[k]*5) k++;
q.push_back(t);
}
return q.back();
}
};
5、第一个只出现一次的字符
题目:原题链接
题目描述
在一个字符串(0<=字符串长度<=10000,全部由字母组成)中找到第一个只出现一次的字符,并返回它的位置, 如果没有则返回 -1(需要区分大小写).
5.1、分析
我们先遍历一遍字符串,然后用哈希表记录每个字符出现的次数,然后遍历哈希表,当出现第一个次数等于1的字符时,我们就输出它对应的下标。
5.2、代码
class Solution {
public:
int FirstNotRepeatingChar(string str) {
if(str.empty()) return -1;
unordered_map<char,int> hash;
for(int i=0;i<str.size();i++){
hash[str[i]]++;
}
for(int i=0;i<str.size();i++){
if(hash[str[i]]==1) return i;
}
return -1;
}
};
6、数组中的逆序对【数组】
题目:原题链接
题目描述
在数组中的两个数字,如果前面一个数字大于后面的数字,则这两个数字组成一个逆序对。输入一个数组,求出这个数组中的逆序对的总数P。并将P对1000000007取模的结果输出。 即输出P%1000000007
输入描述:
题目保证输入的数组中没有的相同的数字
数据范围:
对于%50的数据,size<=10^4
对于%75的数据,size<=10^5
对于%100的数据,size<=2*10^5
6.1、分析
利用归并排序,因为每次进行归并排序时,两边的序列都是有序的,所以比较好计算两者之间的逆序对数量。
6.2、代码
class Solution {
public:
const int mod=1e9+7;
int InversePairs(vector<int> data) {
if(data.empty()) return 0;
return merge_sort(data,0,data.size()-1);
}
int merge_sort(vector<int> &data,int l,int r){
if(l>=r) return 0;
int res=0;
int mid=l+r>>1;
res+=merge_sort(data,l,mid); //计算左边区间的逆序对
res+=merge_sort(data,mid+1,r); //计算右边区间的逆序对
static vector<int> w;
w.clear();
//进行归并排序
int i=l,j=mid+1;
while(i<=mid&&j<=r){
if(data[i]<=data[j]) w.push_back(data[i++]);
else{
res+=mid-i+1; //计算两个区间的逆序对
res%=mod;
w.push_back(data[j++]);
}
}
while(i<=mid) w.push_back(data[i++]);
while(j<=r) w.push_back(data[j++]);
for(int i=l,j=0;j<w.size();j++,i++) data[i]=w[j];
return res;
}
};
7、两个链表的第一个公共节点
题目:原题链接
题目描述
输入两个链表,找出它们的第一个公共结点。
7.1、分析
让两个指针pA、pB分别从A和B同时出发,当走到链表尽头时,再从另一个链表的头开始重新往后走,则一定会在相交点相遇。
7.2、代码
/*
struct ListNode {
int val;
struct ListNode *next;
ListNode(int x) :
val(x), next(NULL) {
}
};*/
class Solution {
public:
ListNode* FindFirstCommonNode( ListNode* pHead1, ListNode* pHead2) {
auto p=pHead1,q=pHead2;
while(p!=q){
if(!p) p=pHead2;//当走到头了,就从另一个链表头开始走
else p=p->next;
if(!q) q=pHead1;
else q=q->next;
}
return p;
}
};
8、数字在排序数组中出现的次数
题目:原题链接
题目描述
统计一个数字在排序数组中出现的次数。
8.1、分析
由于是排序数组,所有相同的数字是相连的,如果已经找到了与目标值相等的元素,那么往后遍历过程中,如果遇到了和目标值不相等的元素,那就可以直接返回了。
8.2、代码
class Solution {
public:
int GetNumberOfK(vector<int> data ,int k) {
if(data.empty()) return 0;
int res=0;
for(int i=0;i<data.size();i++){
if(data[i]==k) res++;
if(res&&data[i]!=k) return res;
}
return res;
}
};
9、二叉树深度
题目:原题链接
题目描述
输入一棵二叉树,求该树的深度。从根结点到叶结点依次经过的结点(含根、叶结点)形成树的一条路径,最长路径的长度为树的深度。
9.1、分析
用深搜来解决这个问题,对于结果肯定是左右最大深度加上根节点的深度1,而每次向上返回每一次的深度。
9.2、代码
/*
struct TreeNode {
int val;
struct TreeNode *left;
struct TreeNode *right;
TreeNode(int x) :
val(x), left(NULL), right(NULL) {
}
};*/
class Solution {
public:
int res=0;
int TreeDepth(TreeNode* pRoot)
{
dfs(pRoot);
return res;
}
int dfs(TreeNode* root){
if(!root) return 0;
res=max(dfs(root->left),dfs(root->right))+1;
return res;
}
};
10、平衡二叉树
题目:原题链接
题目描述
输入一棵二叉树,判断该二叉树是否是平衡二叉树。
10.1、分析
我们这题还是用深搜,每次递归返回这一层的深度,当左右深度相隔大于1时,我们就用一个bool变量记录下来这不是一个平衡树。
10.2、代码
class Solution {
public:
bool res=true;
bool IsBalanced_Solution(TreeNode* pRoot) {
dfs(pRoot);
return res;
}
int dfs(TreeNode* root){
if(!root) return 0;
int left=dfs(root->left)+1;
int right=dfs(root->right)+1;
if(abs(left-right)>1) res=false;
return max(left,right);
}
};