剑指Offer(33 把数组排成最小数 ,34 丑数,40 数组中只出现一次的数字)

33.把数组排成最小数

问题描述:

输入一个正整数数组,将它们连接起来排成一个数,输出能排出的所有数字中最小的一个。例如输入数组{32,  321},则输出这两个能排成的最小数字32132。请给出解决问题的算法,并证明该算法。

 思路:

先将整数数组转为字符串数组,然后字符串数组进行排序,最后依次输出字符串数组即可。这里注意的是字符串的比较函数需要重新定义,不是比较a和b,而是比较ab与 ba。如果ab < ba,则a < b;如果ab > ba,则a > b;如果ab = ba,则a = b。比较函数的定义是本解决方案的关键。这道题其实就是希望我们能找到一个排序规则,根据这个规则排出来的数组能排成一个最小的数字。

#include <iostream>
#include <string>
#include <sstream>
#include <algorithm>
#include <vector>
using namespace std;

bool compare(const string& str1, const string &str2)
{
    string s1=str1+str2;
    string s2=str2+str1;
    return s1<s2;       //如果是求最大,则把小于改成大于即可
}

//void ComArrayMin(int *pArray, int num)
//{
//    int i;
//    string *pStrArray=new string[num];

//    for(i=0; i<num; i++)
//    {
//        stringstream stream;
//        stream<<pArray[i];
//        stream>>pStrArray[i];
//    }

//    sort(pStrArray, pStrArray+num, compare);

//    for(i=0; i<num; i++)
//        cout<<pStrArray[i];

//    cout<<endl;

//    delete[] pStrArray;

//}
void ComArrayMin(int *pArray, int num)
{
    int i;
    vector<string> vec;

    for(i=0; i<num; i++)
    {
       vec.push_back(to_string(pArray[i]));
    }

    sort(vec.begin(),vec.end(), compare);

    for(i=0; i<num; i++)
        cout<<vec[i];

    cout<<endl;


}

int main()
{
    int Num;
    cin>>Num;
    int *pArray=new int[Num];

    for(int i=0; i<Num; i++)
        cin>>pArray[i];

    ComArrayMin(pArray, Num);
    return 0;

}

参考链接:https://blog.csdn.net/cxllyg/article/details/7659525

34.丑数

题目描述:

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

输入:

输入包括一个整数N(1<=N<=1500)。

输出:

可能有多组测试数据,对于每组数据,

输出第N个丑数。

思路: 将查找的丑数按从小到大的顺序排好序,注意每个丑数毕为前面的丑数乘以2、3或5得到的;把现有最大丑数记为M,则把第一个乘以2后大于M的结果记为M2,同理,把每个丑数乘以3和5 ,得到的第一个大于M的结果为M3和M5,那么下一个丑数必为M2/M3/M5这三个数的最小者。

//丑数
#include<iostream>
#include<algorithm>
#include<vector>
using namespace std;

int GetUglyNumber_Solution(int index) {
    if(index <=0)
        return 0;
    vector<int>num(index);
    num[0] =1;
    int t2= 0, t3 = 0, t5 = 0, i;

    for (i= 1; i < index; ++i)
    {
        num[i] =min(num[t2] * 2, min(num[t3] * 3, num[t5] * 5));    //M2/M3/M5这三个数的最小者

        if(num[i] == num[t2] * 2)t2++;  //如果最小的这个数是M2,则将当前数组下标前移,继续比较,下面同理

        if(num[i] == num[t3] * 3)t3++;

        if(num[i] == num[t5] * 5)t5++;
    }
    return num[index - 1];
}

int main()
{
    int x;
    cout <<"输入一个数:";
    while (cin >> x)
    {
        cout <<"第"<< x <<"个丑数为"<<GetUglyNumber_Solution(x)<<endl;
        cout <<"输入一个数:";
    }
    return 0;

}

参考链接:https://blog.csdn.net/liqing1310200526/article/details/79659726

                 https://www.cnblogs.com/xing901022/p/3796777.html

40.数组中只出现一次的数字

题目描述:

一个整型数组里除了两个数字之外,其他的数字都出现了两次。请写程序找出这两个只出现一次的数字。
输入:
每个测试案例包括两行:
第一行包含一个整数n,表示数组大小。2<=n <= 10^6。
第二行包含n个整数,表示数组元素,元素均为int。
输出:
对应每个测试案例,输出数组中只出现一次的两个数。输出的数字从小到大的顺序。
样例输入:
8
2 4 3 6 3 2 5 5
样例输出:
4 6

思路:利用异或去重的原理但这里是有两个只出现一次的数字,我们便要想办法把他分为两个子数组,每个子数组中包含一个只出现一次的数字,其他的数字都出现了两次。剑指offer上的思路很巧妙,依然从头到尾异或所有的数字,这样得到的结果实际上就是两个只出现了一次的数字异或的结果,我们在异或后的结果中找出其二进制中最右边为1的位,该位既然为1,说明异或的两个数字对应的该位肯定不同,必定一个为1,一个为0,因此我们可以考虑根据此位是否为1来划分这两个子数组,这样两个只出现一次的数字就分开了,但我们还要保证出现两次的数字都分到同一个子数组中,肯定不能两个重复的数字分在两个不同的子数组中,这样得到的结果是不对的,很明显,相同的数字相同的位上的值是相同的,要么都为1,要么都为0,因此我们同样可以通过判断该位是否为1来将这些出现两次的数字划分到同一个子数组中,该位如果为1,就分到一个子数组中,如果为0,就分到另一个子数组中。这样就能保证每个子数组中只有一个出现一次的数字,其他的数字都出现两次,分别全部异或即可得到这两个只出现一次的数字。时间复杂度为O(n)。

/*数字中第一次出现1的位置,从右到左,由低位到高位*/
unsigned int FindFirstBitIs1(int num)
{
    int index = 0;
    while((num & 1) == 0 && (index < 8 * sizeof(int)))
    {
        num = num >> 1;
        index++;
    }
    return index;
}

/*数字在index位置上的值是否为1*/
bool IsBit1(int num, int index)
{
    num = num >> index;
    return(num & 1);
}

/*查找数组中只出现一次的数字*/
void FindNumsAppearOnce(vector<int> data,int* num1,int *num2) 
{
    if(data.empty() || data.size() < 2 || num1 == NULL || num2 == NULL)
        return;
    int result = 0;
    //将所有数字相互异或
    for(int i = 0; i < data.size(); i++)
        result ^= data[i];
    //找到结果中1第一次出现的位置
    unsigned int indexOf1 = FindFirstBitIs1(result);
    *num1 = *num2 = 0;
    for(int j = 0; j < data.size(); j++)
    {
        //根据index位置上的值是否为1将数组分为两部分
        //并将两部分数字分别互相异或
        //两部分数字异或最终的结果分别为数组中只出现一次的两个数字
        if(IsBit1(data[j], indexOf1))
            *num1 ^= data[j];
        else
            *num2 ^= data[j];
    }
}

参考链接:https://blog.csdn.net/ns_code/article/details/27649027

                 https://blog.csdn.net/zengzhen_CSDN/article/details/50571083


猜你喜欢

转载自blog.csdn.net/u012864854/article/details/80053069