【剑指offer系列】系列四

系列四:30~39题

【系列三】:传送门             【剑指offer系列】:所有系列传送门                【系列五】:传送门

目录

系列四:30~39题

1、连续子数组的最大和【数组】

题目描述

1.1、分析

1.2、代码

 2、数组中1出现的次数(从1到n整数中出现的次数)【查找】【数学】

题目描述

2.1、分析

2.2、代码

 3、把数组排成最小的数【数组】

题目描述

3.1、分析

3.2、代码

4、 丑数【穷举】

题目描述

4.1、分析

4.2、代码

5、第一个只出现一次的字符

题目描述

5.1、分析

5.2、代码

6、数组中的逆序对【数组】

题目描述

6.1、分析

6.2、代码

7、两个链表的第一个公共节点

题目描述

7.1、分析

7.2、代码

8、数字在排序数组中出现的次数

题目描述

8.1、分析

8.2、代码

9、二叉树深度

题目描述

9.1、分析

9.2、代码

10、平衡二叉树

题目描述

10.1、分析

10.2、代码

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);
    }
};

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

烊萌

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值