面试题41:数据流的中位数

#include<iostream>
#include<vector>
#include<queue>
#include<functional>
using namespace std;//题目:数据流的中位数
/* 如何得到一个数据流中的中位数?如果从数据流中读出奇数个数值,那么中位数就是所有数值排序之后位于中间的数值。如果从数据流中读出偶数个数值,* 那么中位数就是所有数值排序之后中间两个数的平均值。我们使用Insert()方法读取数据流,使用GetMedian()方法获取当前读取数据的中位数。
*//*思路:* 获得一个stream的中位数,要求每次的响应速度要快,而不能每次都去重新找,这是很影响效率的* 借助一个最大堆和一个最小堆来实现是比较好的选择* 维护一个最大堆和最小堆,最小堆中任意一个数都大于最大堆中最大的数* 假设总数为偶数时,新数据会被插入到最小堆中,总数变为奇数,那么此时中位数就是最小堆的头* 总数为奇数时,新数据插入到最大堆,总数变为偶数,中位数就是最大堆和最小堆的头元素的平均数* 根据当前两堆的总元素和,来确定中位数是一个数还是两个数的均值* 情况1:总数为偶* 按理说此时应该将元素插入到最小堆,但是如果此时该元素比最大堆的头元素小,就需要把这个元素插入到最大堆的合适位置来保证顺序* 然后把最大堆头元素拿出来放到插入到最小堆去,这样才能保证最小堆的任意一个数都大于最大堆头元素,保证当前最小堆的头元素就是中位数** 情况2:总数为奇* 当总元素个数为奇数时,需要将新元素插入到最大堆,但新元素可能比最小堆的堆顶大,如果发生这种情况,需要将最小堆的头元素拿出来,把新数据插入到最小堆的对应位置* 再把最小堆的头元素插入最大堆的适当位置* *///leetcode:https://leetcode-cn.com/problems/find-median-from-data-stream/submissions/
//          https://leetcode-cn.com/problems/find-median-from-data-stream/
//牛客:https://www.nowcoder.com/practice/9be0172896bd43948f8a32fb954e1be1?tpId=13&&tqId=11216&rp=1&ru=/ta/coding-interviews&qru=/ta/coding-interviews/question-ranking
class MedianFinder {priority_queue<int> big_heap;priority_queue<int, vector<int>, greater<int>> small_heap;
public:/** initialize your data structure here. */MedianFinder() {}void addNum(int num) {if (big_heap.size() == small_heap.size()){if (big_heap.size()>0 && num<big_heap.top()){big_heap.push(num);//保存最大堆的堆顶元素num=big_heap.top();//将最大堆堆顶元素弹出big_heap.pop();}//此时元素总数变为奇数,中位数即为最小堆堆顶元素small_heap.push(num);}else{if (small_heap.size()>0 && small_heap.top()<num){small_heap.push(num);//保存最小堆的堆顶元素num = small_heap.top();//将最小堆中的堆顶元素弹出small_heap.pop();}//将num插入最大堆并重新排序,此时中位数就是(big_heap[0]+small_heap[0])/2big_heap.push(num);}}double findMedian() {if (big_heap.size() == small_heap.size())return (double)(big_heap.top() + small_heap.top())*1.0 / 2;elsereturn (double)(small_heap.top());}
};int main()
{MedianFinder *m=new MedianFinder;m->addNum(1);cout<<m->findMedian()<<endl;m->addNum(4);cout<<m->findMedian()<<endl;m->addNum(2);cout<<m->findMedian()<<endl;m->addNum(3);cout<<m->findMedian()<<endl;m->addNum(5);cout<<m->findMedian()<<endl;return 0;
}

面试题42:连续子数组的最大数

#include<iostream>
#include<vector>
using namespace std;/*题目:连续子数组的最大和* 输入一个整型数组,数组里有正数也有负数。数组中的一个或连续多个整数组成一个子数组。求所有子数组的和的最大值。* 要求时间复杂度为O(n)。
*//*思路:* 这道题比较简单,这里只做简单的讲解* 可以这样去想,先设置一个初始maxSum为int的最小取值,而curSum即当前最大和设置为0,当前即所有元素都不选,子数组为空时的元素和* 随后开始更新,只要当前的和不小于0,就说明比初始的maxSum大,就有保留并继续去加的必要,* 一旦当前和小于0,就需要抛弃掉当前下标以前的数组,因为这部分相加之后得到的是负数,不如不要* 每次遍历都需要更新maxSum值** 其实,这个实质还是在用双指针来实现,只不过在这里有一个衡量指标就是0,比0小就直接抛弃掉* 每次进入curSum<0的部分其实就是在更新子数组的左指针,而else里则是在更新子数组的右指针* 然后每次更新后要更新maxSum* *///leetcode:https://leetcode-cn.com/problems/lian-xu-zi-shu-zu-de-zui-da-he-lcof/
//          https://leetcode-cn.com/problems/maximum-subarray/
//牛客:https://www.nowcoder.com/practice/459bd355da1549fa8a49e350bf3df484?tpId=13&rp=1&ru=%2Fta%2Fcoding-interviews&qru=%2Fta%2Fcoding-interviews%2Fquestion-ranking
int FindGreatestSumOfSubArray(vector<int> array) {if(array.size()==0)return 0;int curSum=0;int maxSum=0x80000000;for(int i=0;i<array.size();i++){if(curSum<0){curSum=array[i];}elsecurSum+=array[i];if(curSum>maxSum)maxSum=curSum;}return maxSum;
}int main()
{int a[]={-2,1,-3,4,-1,2,1,-5,4};vector<int> v(a,a+9);int res=FindGreatestSumOfSubArray(v);cout<<res<<endl;return 0;
}

面试题43:1~n中整数出现的次数

#include<iostream>
using namespace std;//题目:1~n中整数出现的次数
//输入一个整数 n ,求1~n这n个整数的十进制表示中1出现的次数。
//例如,输入12,1~12这些整数中包含1 的数字有1、10、11和12,1一共出现了5次。//leetcode:https://leetcode-cn.com/problems/1nzheng-shu-zhong-1chu-xian-de-ci-shu-lcof
//          https://leetcode-cn.com/problems/number-of-digit-one/
//牛客:https://www.nowcoder.com/practice/bd7f978302044eee894445e244c7eee6?tpId=13&&tqId=11184&rp=1&ru=/ta/coding-interviews&qru=/ta/coding-interviews/question-ranking/*思路1:
*
* 对于1出现的次数进行统计,首先要保证条理,按位来统计是一个不错的选择,分段来统计也可以
* 这里介绍剑指offer和leetcode上的两种思路
* 剑指offer上的方法,基于递归来进行分段统计,但实质还是对每一个分段上的数字进行按位统计,只不过这里只分了最高位和非最高位
* 每次递归时都会砍掉当前的最高位,比如初始n为12345,则进入下次递归时n为2345
* 当n为个位数时,直接返回1,否则
* temp表示当前位的基数,即个十百千万...* 首先获取最高位数字first,计算在当前分段(n%temp~n )中1出现在最高位的次数
* 若该值为1,比如12345其首位为1,则2345~12345之间最高位出现1的次数firstNumOf1为n%temp+1,这个1对应的数字为10000
* 若该值大于1,比如22345,其首位为2,所有最高位为1的5位数都被2345~22345所包括,此时最高位出现1的次数为temp* 随后再计算当前分段(n%temp~n)出现在非最高位的1的次数,为first * (len - 1)*(temp / 10)
* 这里再举刚才的例子,12345,otherNumOf1是2345-12345之间除最高位之外出现1的次数
* 为什么是first*(len-1)*(temp/10)?
* first决定了将这些数分成多少块,如果数是12345,这部分求的就是2345-12345中1的个数
* 如果数是22345,这部分求的就是2345-12345和12345-22345两部分,且这两部分中1出现在非最高位的次数是完全一样的
* 因为每一位都可能出现1,所以共有len-1种可能,而每一种可能下其余位置都可以取0-9这10个数
* 所以总数如公式表达,这有点类似一个简单的带约束的排列组合* 最后,调用函数进入递归,countDigitOne1(n%temp)表示的是1-2345上1的次数
* 三部分相加即是最终的结果,函数会一直递归,让每一位都“做一次最高位”
*/int getLength(int n)
{int len = 0;while (n != 0){len++;n /= 10;}return len;
}
int countDigitOne1(int n) {if (n<1)return 0;int len = getLength(n);if (len == 1)return 1;int temp = pow(10, len - 1);int first = n / temp;//获取最高位数字//当前最高位的值不同,最高位出现1的次数也不同//最高位为1,则最高位出现1的个数为n除temp的余数+1,以12345为例//最高位出现1的个数为12345%10000+1=2346//如果最高位大于1,则最高位出现1的个数为tempint firstNumOf1 = first == 1 ? n % temp + 1 : temp;// 在介于n%tmp到n之间的数字中,除了最高位为1,其余各个数字分别为1的总数和//举个例子,一个数12345,则firstNumOf1就是2345-12345之间最高位出现1的次数//otherNumOf1是2345-12345之间除最高位之外出现1的次数,countDigitOne1表示的是1-2345上1的次数//为什么是first*(len-1)*(temp/10)?//first决定了将这些数分成多少块,如果数是12345,这部分求的就是2345-12345中1的个数//如果数是22345,这部分求的就是2345-12345和12345-22345两部分//因为每一位都可能为1,所以有len-1种可能,而每一种可能下其余为止都可以取0-9这10个数//所以总数如公式表达,这里求的是1的个数,不是数字的个数,要注意int otherNumOf1 = first * (len - 1)*(temp / 10);return firstNumOf1 + otherNumOf1 + countDigitOne1(n%temp);
}/*思路2:
* 这里再来说说第二种思路,其实实质上和上面的是一样的,只是这次变成了从最低位开始统计,而且无需递归,逻辑也更加清晰
* 举个例子,计算从1到12340中1的次数
* 首先从最低位开始,每次计算要统计当前位的值、高一位的值以及低一位的值
* 计算规则为:current = (n / i) % 10; //当前数字before = n / (i * 10); //高位数字after = n - (n / i)*i; //低位数字
* 对于12340,最低位为0,此时若更高位置的数值不变,即该数字最高三位始终保持为123,则最低位出现1的次数为before*i,这里的i和上面的temp意义一样
* 对于该数,计算得此值为4,分别为12301 12311 12321 12331这四个数
* 下一步,更新i值,进入下一次循环,此时current值为4,after为0,before为3
* 若更高位数值不变,即最高两位数字始终为12,则当前位置(十位)出现1的次数为(before + 1) * i,计算得次数为40
* 为什么current的值不同,会影响计数?举个例子,对于数字101,十位取1的次数为10,有10 11 12 13 14 15 16 17 18 19这10种情况;
* 对于数字111,十位取1的次数为12,包括10 11 12 13 14 15 16 17 18 19 以及110 111
* 而对于121,十位取1的次数则为20,包括10~19以及110~119
* 这里一定要注意,其实说白了还是受分段的影响
* 回到刚才的问题,继续计算直到循环退出,最终得到数值
* 思路1和思路2既有相同的地方,如按位统计的思想,也有不同的地方,在第一种思路中,分段的意图比较明显,而思路二中则在没有强调(实际上蕴含了)
* 分段的情况下更好地实现了计数
* 总体来说,思路2是更先进的,但思路1对于理解问题也是有帮助的
*/int countDigitOne2(int n)
{int count = 0;//1的个数int i = 1;//当前位int current = 0, after = 0, before = 0;while ((n / i) != 0) {current = (n / i) % 10; //当前数字before = n / (i * 10); //高位数字after = n - (n / i)*i; //低位数字//如果为0,出现1的次数由高位决定,等于高位数字 * 当前位数if (current == 0)count += before * i;//如果为1,出现1的次数由高位和低位决定,高位*当前位+低位+1else if (current == 1)count += before * i + after + 1;//如果大于1,出现1的次数由高位决定,//(高位数字+1)* 当前位数elsecount += (before + 1) * i;//前移一位i = i * 10;}return count;
}

面试题44:数字序列中某一位的数值

#include<iostream>
#include<string>
using namespace std;/*题目:数字序列中某一位的数值
* 数字以0123456789101112131415…的格式序列化到一个字符序列中。在这个序列中,第5位(从下标0开始计数)是5,第13位是1,第19位是4,等等。
* 请写一个函数,求任意第n位对应的数字。
*///leetcode:https://leetcode-cn.com/problems/shu-zi-xu-lie-zhong-mou-yi-wei-de-shu-zi-lcof
//          https://leetcode-cn.com/problems/nth-digit/
//牛客:无/*思路:
* 首先对这个序列进行分析01234567891011121314151617181912021...90919293949596979899100101102...9989991000...
* 可以看到1位数0-9共10个,占据了1-10位
* 两位数10-99共90个,占据了11-190位
* 三位数100-999共900个,占据了191-2890位
* 以此类推
* 简单介绍变量len和nextSize,len类似于一个计数器最终用于记录第n位是几位数,初始时为1
* nextSize用于表示(当前位数+1)的数字共占据多少位(多少下标),由len计算而来* 我们首先得知道第n位到达是个几位数
* 这可以通过不断得更新和减掉X位数占据的位数来最终确定,举个例子,求第200位
* 首先减掉10,更新nextSize值为180
* 再减掉180,n变为10,此时更新nextSize为1800,对应的三位数,而n<nextSize,说明第200位是一个三位数* 随后再进一步确定n在len位数的第几个数字的下标中包括,并拿出这个数字,这里为了方便最终取值,把这个数字转换成了字符串
* 这个数字值为pow(10, len-1) + (n - 1) / len,根据位数计算而来,pow(10, len-1)为基数,即len位数的第一个数
* 每个len位数占据len位下标,而n从1开始,下标从0开始,所以需要减去1,得到这个数字是len位数的第几个(非最高位的值)
* 加上基数即可知道这个数具体为多少,将该数转化为字符串
* 回顾前面的例子,n=200,我们已经知道了这个数是三位数,而n此时只剩下10,代入上式可知,下标n所在的数为103* 最后,再计算具体属于这个数的第几位,由n - (n - 1) / len * len - 1来计算,将字符转回数字
*/int findNthDigit(int n) {if (n < 10) {return n;}int len = 1;//单个数字的长度long long nextSize = pow(10, len-1) * len * 9;//长度为len + 1的数字所构成的字符串的长度//第一步:确定n所处的数字的位数while (n > nextSize) {n -= nextSize;len += 1;nextSize = pow(10, len-1) * len * 9;//长度为len + 1的数字所构成的字符串的长度}//第二步:确定n是位数为len + 1的数字中的第几个数字,并且转换为字符串string resStr = to_string((long long)pow(10, len-1) + (n - 1) / len);//返回n在这个数字中对应的位,(比如当n == 15,求得len = 1,n = 6, resStr = “12”,然后取出“12”字符串的第二位)//注意返回的int数字,并不是返回字符'k',需要减去'0'return resStr[n - (n - 1) / len * len - 1] - '0';
}

面试题45:把数组排成最小的数

#include<iostream>
#include<string>
#include<vector>
#include<algorithm>
using namespace std;/*题目:把数组排成最小的数
* 输入一个非负整数数组,把数组里所有数字拼接起来排成一个数,打印能拼接出的所有数字中最小的一个。
*///leetcode:https://leetcode-cn.com/problems/ba-shu-zu-pai-cheng-zui-xiao-de-shu-lcof/
//牛客:https://www.nowcoder.com/practice/8fecd3f8ba334add803bf2a06af1b993?tpId=13&&tqId=11185&rp=1&ru=/ta/coding-interviews&qru=/ta/coding-interviews/question-ranking/*思路:
* 这道题难度不大,其重点就在于通过何种方式来确定各数字的位置,来组成最小数字
* 这里通过转换成字符串,借助大于小于号的重载来实现,因为直接比较数字会有很多情况处理,且过程复杂
* 而转换成字符串比较时只需要做两次拼接,来比较即可
* 借助sort函数,根据比较的结果完成排序
* 比较过程由compare函数来实现,其实质是:如果两个字符串s1和s2,s1+s2小于s2+s1,就把s1放在s2前面
* 最终将排好序的字符串数组拼接,转换回int即可
*/static bool compare(string s1, string s2)
{string str1 = s1 + s2;string str2 = s2 + s1;return str1<str2;
}string PrintMinNumber(vector<int> numbers) {if (numbers.size() == 0)return "";vector<string> strs;for (int i = 0; i<numbers.size(); i++){string tmp = to_string(numbers[i]);strs.push_back(tmp);}sort(strs.begin(), strs.end(), compare);string res;for (int i = 0; i<strs.size(); i++)res += strs[i];return res;
}

《剑指offer》41~45相关推荐

  1. 【LeetCode】剑指 Offer 41. 数据流中的中位数

    [LeetCode]剑指 Offer 41. 数据流中的中位数 文章目录 [LeetCode]剑指 Offer 41. 数据流中的中位数 package offer;import java.util. ...

  2. 【LeetCode笔记】剑指Offer 41. 数据流中的中位数(Java、堆、优先队列、知识点)

    文章目录 题目描述 知识点 1. 优先队列 2. Java 中 queue 的 offer.poll 等区别 思路 && 代码 二刷 打卡第十一天- 题目描述 虽然但是,这是一道很ni ...

  3. 《剑指 Offer I》刷题笔记 41 ~ 50 题

    <剑指 Offer I>刷题笔记 41_50 排序(中等) 41. 最小的k个数# _解法1:排序 API + 数组复制 API 42. 数据流中的中位数 _解法1:暴力 搜索和回溯算法( ...

  4. 剑指offer第二版答案详细版(带详细解题思路)

    1.滑动窗口的最大值(剑指offer原59题) 解题思路:其实是一个队列的问题,用一个队列去维护当前窗口中的所有元素:首先将超出窗口中的队头元素先删掉,然后将新的元素插入当前窗口中,插入时要判断新插入 ...

  5. leetcode 打印_剑指 Offer 总结 - leetcode 剑指offer系列

    剑指 Offer 系列完结撒花!! 本篇文章是对整个系列的精华总结, 对系列的每篇文章进行了分类, 并用一句话概括每道题的思路, 方便大家理解和记忆, 当然也包含原文完整链接供大家参考 总的来说, 写 ...

  6. C#LeetCode刷题-剑指Offer

    本文由 比特飞 原创发布,欢迎大家踊跃转载. 转载请注明本文地址:C#LeetCode刷题-剑指Offer | .Net中文网. C#LEETCODE刷题概述 概述 所有LeetCode剑指Offer ...

  7. 【LeetCode】《剑指Offer》第Ⅴ篇⊰⊰⊰ 39 - 47题

    [LeetCode]<剑指Offer>第Ⅴ篇⊰⊰⊰ 39 - 47题 文章目录 [LeetCode]<剑指Offer>第Ⅴ篇⊰⊰⊰ 39 - 47题 39. 数组中出现次数超过 ...

  8. 剑指offer刷题(2)--面向华为

    目录 11. 双指针(√) 10.1 剑指 Offer 18. 删除链表的节点 10.2 剑指 Offer 22. 链表中倒数第k个节点 12. 双指针(简单)(√) 12.1 剑指 Offer 25 ...

  9. 《剑指 Offer》题目汇总

    文章目录 1. 数组 2. 链表 3. 栈和队列 4. 哈希表 5. 字符串 6. 树 7. 堆 8. 回溯和深度优先搜索 9. 递归和循环 10. 双指针 11. 动态规划 12. 贪心算法 13. ...

  10. 【剑指offer刷题】排序算法

    记录在Leetcode刷<剑指offer>的笔记,希望提高自己的算法基础和编程水平.这一篇文章刷的是排序算法的题目集合,在CSDN做一下记录,随时更新,一起学习吧. 刷题链接:https: ...

最新文章

  1. C#之windows桌面软件第七课:(下集)串口工具实现数据校验、用灯反应设备状态
  2. 【Win32汇编】五种寻址方式
  3. 电商的本质是“商”还是“用户”?
  4. 易语言关闭指定窗口_易语言取外部程序指定窗口位置源码 _易语言源码网_易语言资源网...
  5. 机器学习实战-决策树-22
  6. Oracle sys或者system的默认密码
  7. Unity3D For Android 开发教程【转http://game.ceeger.com/Unity/Doc/2011/Unity3D_For_Android.html】...
  8. 【每日SQL打卡】​​​​​​​​​​​​​​​DAY 18丨即时食物配送 I【难度简单】​
  9. mac开发配置手册(全)
  10. 从零开始学前端:CSS背景颜色 --- 今天你学习了吗?(CSS:Day09)
  11. 外观模式和代理模式的联系和区别_设计模式之代理模式
  12. vSphere 故障排错思路总结
  13. L1-078 吉老师的回归 (15 分)-PAT 团体程序设计天梯赛 GPLT
  14. 怎么分行显示java程序,月光软件站 - 编程文档 - Java - 如何在J2ME的低级界面中轻松实现各种文字的自然分行显示...
  15. 直接下载Google Play上APP的安装包
  16. 快捷键,总结一些实用高效的快捷键
  17. 揭秘慕思“智商税”:狂砸40亿搞营销,发明专利仅7项
  18. php 图片外链限制,如何解决新浪微博图床限制图片外链的问题 | 骤雨打新荷
  19. AMD:无限你我的无限
  20. java 如何转化成机器指令_Java代码到底是如何编译成机器指令的

热门文章

  1. MobileNet V3(2019)
  2. java实现红黑树 新增节点 左旋右旋 hash取模
  3. Zookeeper之Linux分布式集群搭建及客户端shell命令操作
  4. linux 中samba账号登录密码,samba账号密码无法登录问题
  5. 基于ASP.NET的旅行社信息管理系统设计与实现
  6. 基于Java毕业论文管理系统设计实现(源码+lw+部署文档+讲解等)
  7. 广播通信设计——WinSock编程(QT界面)
  8. 如何把百度网盘下载速度提高 100 倍,我推荐这个下载工具
  9. 计算机工程学院学生会口号,计算机工程学院学生会活动策划书模板
  10. Leetcode377(力扣377):组合总和