小奥的学习笔记

  • Home
  • Learning & Working
    • Speech Enhancement Notes
    • Programming language
    • Computer & DL
    • MOOC
  • Life
    • Life Time
    • Thinking & Comprehension
    • Volunteer
    • Plan
    • Travel
  • Footprints
  • GuestBook
  • About
    • About Me
    • 个人履历
    • 隐私策略
  1. 首页
  2. Study-notes
  3. Programming language
  4. Algorithm
  5. 剑指offer
  6. 正文

《剑指Offer》题目解析(8)

2019年3月7日 1515点热度 0人点赞 0条评论

题目1 数组中出现次数超过一半的数字

题目描述

数组中有一个数字出现的次数超过数组长度的一半,请找出这个数字。例如输入一个长度为9的数组{1,2,3,2,2,2,5,4,2}。由于数字2在数组中出现了5次,超过数组长度的一半,因此输出2。如果不存在则输出0。

题目解析

最简单的想法就是我们这个数组进行排序,由于数字超过了一半,那么中间的数字(中位数)肯定是我们所需要找的数字。代码实现如代码1所示,但是排序使用快速排序,时间复杂度为O(nlogn),并没有达到最好的速度。因此参考《剑指offer》一书,我们还可以有两种时间复杂度为O(n)的算法。
基于Partition函数的方法
这种算法受快速排序算法启发。在快速排序中,我们现在数组中随机选择一个数字,然后调整数组中数字的顺序,使得比选中的数字小的数字都排在它的左边,大的都在有边。如果这个选中的数字的下表刚好是n/2,那么这个数字就是数组的中位数;如果它的下表大于n/2,那么中位数应该位于它左边,我们可以直接在它左边部分的数组中查找;如果它的下表小于n/2,那么中位数应该位于它的有边,我们可以在它右边部分的数组查找。
此外,我们还要考虑一些无效输入。如果函数的输入参数是一个指针(数组在参数传递的时候退化为指针),就要考虑这个指针可能为nullptr。CheckInvalidArray()函数可以判断输入的数组是不是无效的。

如果输入的数组中出现频率最高的数字没有超过一半怎么办?定义一个CheckMoreThanHalf()函数来进行处理。

根据数组特点找出时间复杂度为O(n)的算法
数组中有一个数字出现次数超过长度一半,也就是说它出现的次数比其他所有数字出现的次数的和还要多。因此我们可以考虑在遍历数组的时候保存两个值:一个是数组中的一个数字;另一个是次数。当我们遍历到下一个数字的时候,如果下一个数字和我们之前保存的数字相同,则次数加1;如果下一个数字和我们之前保存的数字不同,则次数减1.如果次数为0,那么我们需要保存下一个数字,并把次数设为1.由于我们要找的数字出现次数比其他所有数字出现的次数之和还要多,那么要找的数字肯定是最后一次把次数设置为1时对应的数字。见代码3。

代码1

int MoreThanHalfNum_Solution(vector<int> numbers) {
    int len = numbers.size();
    int count=0;
    if(len==0)
        return 0;
    if(len==1)
        return numbers[0];
    sort(numbers.begin(), numbers.end());
   int mid = numbers[len/2];
    for(int i=0;i<len;i++)
    {
        if(numbers[i]==mid)
            ++count;
    }
    return (count>(len/2))?mid:0;
}

代码2

bool g_bInputInvaild = false;
bool CheckInvalidArray(int* numbers, int length)
{
    g_bInputInvalid = false;
    if (numbers == nullptr && length <= 0)
        g_bInputInvalid = true;
    return g_bInputInvalid;
}
bool CheckMoreThanHalf(int* numbers, int length, int number)
{
    int times = 0;
    for (int i = 0; i < length; ++i)
    {
        if (numbers[i] == number)
            times++;
    }
    bool isMoreThanHalf = true;
    if (times * 2 <= length)
    {
        g_bInputInvalid = true;
        isMoreThanHalf = false;
    }
    return isMoreThanHalf;
}
int Partition(int data[],int length,int start,int end)
{
    if(data==NULL || length<=0||start<0 || end>=length)
    {
        cout<<"error1!"<<endl;
        exit(0);
    }
    int index=RandomInRange(start,end);

    swap_element(&data[index],&data[end]);
    int small=start-1;
    for(index=start;index<end;index++)
    {
       if(data[index]<data[end])
        {
                 ++small;
            if(small != index)
            {
                swap_element(&data[index],&data[small]);
            }
        }
    }
    ++small;
    swap_element(&data[small],&data[end]);
    return small;
}
  int MoreThanHalfNum_Solution(vector<int> numbers) {
        if(CheckInvalidArray(numbers,length)
            return 0;
        int middle=length>>1;
        int start=0;
        int end=length-1;
        int index=Partition(numbers,length,start,end);
        while(index!=middle)
        {
            if(index>middle)
            {
                end=index-1;
                index=Partition(numbers,length,start,end);
            }
            else{
                start=index+1;
                index=Partition(numbers,length,start,end);
            }
        }

    int result=numbers[middle];
    if(!CheckMoreThanHalf(numbers,length,result))
        result=0;
    return result;
}

代码3

int MoreThanHalfNum_Solution2(int* numbers, int length)
    {
        if (CheckInvalidArray(numbers, length))
            return 0;
        int result = numbers[0];
        int times = 1;
        for (int i = 1; i < length; ++i)
        {
            if (times == 0)
            {
                result = numbers[i];
                times = 1;
            }
            else if (numbers[i] == result)
                times++;
            else
                times--;
        }
        if (!CheckMoreThanHalf(numbers, length, result))
            result = 0;
        return result;
    }

题目2 最小的k个数

题目描述

输入n个整数,找出其中最小的K个数。例如输入4,5,1,6,2,7,3,8这8个数字,则最小的4个数字是1,2,3,4,。

题目解析

最简单的方法莫过于对这个数组进行排序,然后输出前k个数,但是这样复杂度是O(nlogn),当数字很大的时候,还是复杂度高了些。

但是我们可以考虑借鉴题目1中的方法,也就是借鉴快速排序进行处理,复杂度有O(n),当然这种方法只有当我们可以修改输入的数组的时候才可以用。如果基于数组的第k个数字来调整,那么比第k个数字小的所有数字位于数组的左边,比第k个数字大的数字都位于数字有边。这样调整以后,位于数组中左边的k个数字就是最小的k个数字(这k个数字不一定有序)。代码如代码1所示。

代码

    void GetLeastNumbers(int* input, int n, int* output, int k)
    {
        if(input==nullptr||output==nullptr||k>n||n<=0||k<=0)
            return;
        int start=0;
        int end=n-1;
        int index=Partition(intput,n,start,end);
        while(index!=k-1)
        {
            if(index>k-1)
            {
                end=index-1;
                index=Partition(input,n,start,end);
            }
            else
            {
                start=index+1;
                index=Partition(intput,n,start,end);
            }
        }
        for(int i=0;i<k;++i)
            output[i]=input[i];
    }

题目3 连续子数组的最大和

题目描述

HZ偶尔会拿些专业问题来忽悠那些非计算机专业的同学。今天测试组开完会后,他又发话了:在古老的一维模式识别中,常常需要计算连续子向量的最大和,当向量全为正数的时候,问题很好解决。但是,如果向量中包含负数,是否应该包含某个负数,并期望旁边的正数会弥补它呢?例如:{6,-3,-2,7,-15,1,2,2},连续子向量的最大和为8(从第0个开始,到第3个为止)。给一个数组,返回它的最大连续子序列的和,你会不会被他忽悠住?(子向量的长度至少是1)

题目解析

使用在线规划,复杂度为O(n)。在线规划的思想就是定义一个当前和(ThisSum)和一个最大和(MaxSum)。就是从第一个数开始加,赋值给ThisSum,然后如果ThisSum比MaxSum大的话,那就把ThisSum给MaxSum,否则的话不给。如果ThisSum加着加着都小于0了,那么这几个数字的和就没必要保留,直接舍弃就好了。由于题目说了长度至少是1,所以无需判断长度为0的情况,但是脑子里应该要想着这件事。

代码

int FindGreatestSumOfSubArray(vector<int> array) {
    int ThisSum =0;
    int MaxSum = array[0];
    int N = array.size();
    int i;
    for(i=0;i<N;i++)
    {
        ThisSum += array[i];
        if(ThisSum>MaxSum)
            MaxSum=ThisSum;
        else if(ThisSum<0)
            ThisSum=0;
    }
    return MaxSum;

}

题目4 整数中出现1的次数

题目描述

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

题目解析

可以直接判断,用数字%10的出来的余数看是不是等于1即可。复杂度为O(n)。

代码

int NumberOf1Between1AndN_Solution(int n)
{
    int count = 0;
    if(n==0)
        return count;
    for(int i=1;i<=n;i++)
    {
        int tmp=i;
        while(tmp)
        {
            if(tmp%10==1)
                ++count;
            tmp /=10;
        }
    }
return count;
}

题目5 把数组排成最小的数字

题目描述

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

题目解析

我们应该考虑怎么去拼接数字。要注意,m和n在int型能表达范围之内,但是mn或者nm可就不一定了。所以我们可以把数字转换成字符串进行处理。
根据题目要求,若mn<nm,则输出mn;若nm<mn,则输出nm。然后我们可以通过证明得到以下结论(证明省略):

若ab > ba 则 a > b,即排序为ba;
若ab < ba 则 a < b,即排序为ab;
若ab = ba 则 a = b,即排序为ab。

代码

    static bool cmp(int a, int b)
{
//意思是若ab<ba,则返回(a,b);若ab>ba,则返回(b,a)。
    string A = to_string(a) + to_string(b);
    string B = to_string(b) + to_string(a);
    return A < B;
}
    string PrintMinNumber(vector<int> numbers) {
        int len =numbers.size();
        if(len==0)
            return "";
        string result;
        sort(numbers.begin(),numbers.end(),cmp);
        for(int i=0;i<len;i++)
            result += to_string(numbers[i]);
        return result;

    }
本作品采用 知识共享署名 4.0 国际许可协议 进行许可
标签: 剑指offer
最后更新:2019年3月7日

davidcheung

这个人很懒,什么都没留下

打赏 点赞
< 上一篇
下一篇 >

文章评论

razz evil exclaim smile redface biggrin eek confused idea lol mad twisted rolleyes wink cool arrow neutral cry mrgreen drooling persevering
取消回复

搜索
欢迎关注我的个人公众号
最新 热点 随机
最新 热点 随机
DEEPFILTERNET:一种基于深度滤波的全频带音频低复杂度语音增强框架 奥地利匈牙利九日游旅程 论文阅读之Study of the General Kalman Filter for Echo Cancellation 小奥看房之鸿荣源珈誉府 杭州往返旅途及西溪喜来登和万怡的体验报告 2022年的第一篇碎碎念
奥地利匈牙利九日游旅程论文阅读之Study of the General Kalman Filter for Echo CancellationDEEPFILTERNET:一种基于深度滤波的全频带音频低复杂度语音增强框架
“五一”假期作业 《鸟哥的Linux私房菜》(基础篇)笔记整理(第11章) 网络技术与应用附录1:网络分类 Java语言程序设计(进阶)(第八章)整理 day7练习2:使用管道实现兄弟进程间通信 关于更新github、微信公众号和博客周期的说明
标签聚合
鸟哥的linux私房菜 学习 linux python学习 Java 生活 Python 算法 leetcode 高中
最近评论
davidcheung 发布于 5 个月前(02月09日) The problem has been fixed. May I ask if you can s...
tk88 发布于 5 个月前(02月07日) Hmm is anyone else having problems with the pictur...
cuicui 发布于 9 个月前(10月20日) :wink:
niming 发布于 10 个月前(09月19日) 同级校友,能刷到太巧了
davidcheung 发布于 2 年前(08月16日) 我得找一下我之前整理的word文档看一下,如果找到了我就更新一下这篇文章。
Nolan 发布于 2 年前(07月25日) 您的笔记非常有帮助。贴图不显示了,可以更新一下吗?
davidcheung 发布于 3 年前(06月19日) 到没有看webrtc的代码。现在主要在看我们公司的代码了。。。只是偶尔看一看webrtc的东西。。。
aobai 发布于 3 年前(03月13日) gain_change_hangover_ 应该是每三个block 只能够调整一次,这样保证每帧...
匿名 发布于 5 年前(12月30日) 烫
小奥 发布于 5 年前(12月12日) webRTC里面的NS本身我记得就是在C++里面呀

COPYRIGHT © 2025 小奥的学习笔记. ALL RIGHTS RESERVED.

Theme Kratos Made By Seaton Jiang

陕ICP备19003234号-1

鲁公网安备37120202000100号