剑指offer 66题 part6(31~36题)

本文精选了多个经典编程题目,包括计算1在整数中的出现次数、构造最小数值、寻找丑数、查找首个唯一字符及逆序对等,并提供了详细的算法思路与实现代码。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

第三十一题:求1~n中所有整数里面1出现次数和

求出1~13的整数中1出现的次数,并算出100~1300的整数中1出现的次数?为此他特别数了一下1~13中包含1的数字有1、10、11、12、13因此共出现6次,但是对于后面问题他就没辙了。ACMer希望你们帮帮他,并把问题更加普遍化,可以很快的求出任意非负整数区间中1出现的次数

class Solution {
/*
本题解法我们可以用两个数字来模拟即可懂
43147
43247
对于百位来说,是不同的,在下列程序运行的结果也不一样
因为我们找规律可以发现,百位出现1的次数,由前后两部分影响
并且如果它还是1的话,后面一部分影响的值有变换
+8的意思是:如果当前位大于1使得其进一位,方便计算
*/
public:
    int NumberOf1Between1AndN_Solution(int n)
    {
        int a,b,cnt=0;
        for(int m=1;m<=n;m*=10){
            a=n/m;
            b=n%m;
            cnt+=(a+8)/10*m+(a%10==1?1:0)*(b+1);
        }
        return cnt;
    }
};

第三十二题:把数组排成最小的数

输入一个正整数数组,把数组里所有数字拼接起来排成一个数,打印能拼接出的所有数字中最小的一个。例如输入数组{3,32,321},则打印出这三个数字能排成的最小数字为321323

/*
sort中的比较函数compare要声明为静态成员函数或全局函数
因为:非静态成员函数是依赖于具体对象的,而std::sort这类函数是全局的
静态成员函数或者全局函数是不依赖于具体对象的, 可以独立访问,无须创建任何对象实例就可以访问。
同时静态成员函数不可以调用类的非静态成员
*/
class Solution {
private:
    static bool cmp(int a,int b)
    {
        string A=to_string(a)+to_string(b);
        string B=to_string(b)+to_string(a);
        return A<B;
    }
public:
    string PrintMinNumber(vector<int> numbers) {
        string ans="";
        sort(numbers.begin(),numbers.end(),cmp);
        for(int i=0;i<numbers.size();i++)
            ans+=to_string(numbers[i]);
        return ans;
    }
};

第三十三题:丑数

把只包含因子2、3和5的数称作丑数(Ugly Number)。例如6、8都是丑数,但14不是,因为它包含因子7。 习惯上我们把1当做是第一个丑数。求按从小到大的顺序的第N个丑数

题解:

设定三个指针,分别从同一个位置开始,当指针指向的下一个数字是最小的时候,对应的指针指向移动一个

class Solution {
public:
    int GetUglyNumber_Solution(int index) {
        if(index==0) return 0;
        vector<int>ans(index);
        int t2=0,t3=0,t5=0;
        ans[0]=1;
        for(int i=1;i<index;i++){
            ans[i]=min(2*ans[t2],min(3*ans[t3],5*ans[t5]));
            if(ans[i]==ans[t2]*2) t2++;
            if(ans[i]==ans[t3]*3) t3++;
            if(ans[i]==ans[t5]*5) t5++;
        }
        return ans[index-1];
    }
};

第三十四题:第一个只出现一次的字符

在一个字符串(1<=字符串长度<=10000,全部由字母组成)中找到第一个只出现一次的字符,并返回它的位置

题解:

hash,map(红黑树)等等

class Solution {
public:
    int FirstNotRepeatingChar(string str) {
        map<char,int>mp;
        for(int i=0;i<str.size();i++)
            mp[str[i]]++;
        for(int i=0;i<str.size();i++)
            if(mp[str[i]]==1) return i;
        return -1;
    }
};

第三十五题:数组的逆序数对

在数组中的两个数字,如果前面一个数字大于后面的数字,则这两个数字组成一个逆序对。输入一个数组,求出这个数组中的逆序对的总数P。并将P对1000000007取模的结果输出。 即输出P%1000000007

数据范围有点大,不能暴力

搞过acm的都知道可以用,线段树,树状数组,归并思想

下面代码使用归并思想:

递归到叶子节点后再返回上一级合并,这里使用一个辅助数组,但是利用了辅助数组却没有把辅助数组的内容复制回去到原数组,见代码注释即可理解

递归到叶子节点很简单,合并的时候见下图


class Solution {
private:
    long long deal(vector<int> &data,vector<int> &cp,int start,int end)
    {
        if(start==end){
            cp[start]=data[start];
            return 0;
        }
        int len=(end-start)/2;
        long long left=deal(cp,data,start,start+len);//这里直接交换了数组,就不需要再把辅助数组复制回去
        long long right=deal(cp,data,start+len+1,end);
        
        int i=start+len,j=end;
        int index=end;
        long long cnt=0;
        while(i>=start&&j>=start+len+1){
            if(data[i]>data[j]){
                cp[index--]=data[i--];
                cnt+=j-start-len;
            }
            else cp[index--]=data[j--];
        }
        while(i>=start) cp[index--]=data[i--];
        while(j>=start+len+1) cp[index--]=data[j--];
        return (left+right+cnt)%1000000007;
    }
public:
    int InversePairs(vector<int> data) {
        int size=data.size();
        if(size<=0) return 0;
        vector<int> cp=data; //这里已经保证了两个数组一致
        long long cnt=deal(data,cp,0,size-1);
        return cnt%1000000007;
    }
};


第三十六题:两个链表的第一个公共节点

输入两个链表,找出它们的第一个公共结点

题解:根据结构体可以发现,一旦有公共节点,那么两个链表的尾部就一定是相同的

所以,我们先跑一边得到链表的长度,然后再把最长的先跑相应节点使得两条链表尾部长度一样,最后再一起跑,如果相同肯定会一起到达尾部,在此过程中就可以碰到第一个相同的节点返回即可

/*
struct ListNode {
	int val;
	struct ListNode *next;
	ListNode(int x) :
			val(x), next(NULL) {
	}
};*/
class Solution {
public:
    ListNode* FindFirstCommonNode( ListNode* pHead1, ListNode* pHead2) {
        int a=0,b=0,t;
        ListNode *p1=pHead1,*p2=pHead2;
        while(p1) p1=p1->next,a++;
        while(p2) p2=p2->next,b++;
        if(a>b){
            t=a-b;
            while(t--) pHead1=pHead1->next;
            t=b;
        }
        else if(b>a){
            t=b-a;
            while(t--) pHead2=pHead2->next;
            t=a;
        }
        else t=a;
        
        while(t&&(pHead1->val)!=(pHead2->val)){
            pHead1=pHead1->next;
            pHead2=pHead2->next;
            t--;
        }
        return pHead1;
    }
};


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值