StackOverflow人工智能深度学习问题Top100【面试备用速查】
函数逼近:瓦片编码与高度离散的状态空间有何不同?生成数值概念层次的算法面向目标 C 的 AI换位表会不会导致搜索不稳定Python 中的六角自组织映射“uniform-cost search”算法中的路径如何获取?为什么张量流的 MNIST 教程中 x 变量张量被重塑为 -1?吃豆子幽灵 AI与人工智能中的最佳优先搜索相关的问题是什么?对于神经网络,学习率是否应该以某种方式与隐藏层大小成正比?他们应该互相影响吗?ValueError:未知激活函数:my_custom_activation_function衡量分类算法的性能如何在 eclipse java 中重用从资源管理器(在 weka 中)创建的已保存分类器基于收听习惯的自动音乐评级即使使用显式 cuda() 调用,pytorch 实例张量也不会移动到 gpunltk.TweetTokenizer 与 nltk.word_tokenize 有何不同?神经网络 (ANN) 入门?在强化学习中设置 gamma 和 lambdaAI 什么时候变得不仅仅是复杂的预定义逻辑?最佳优先对比 广度优先在 MATLAB 中实现和绘制感知器分类或关键字匹配自然语言字符串或短语如何修复简单 GA(Python)中的过早收敛?使用神经网络和/或强化学习提升我的遗传算法在二维数组中实现 A* 寻路开发一个人工智能系统来挑选梦幻足球队使用 JavaScript 过滤垃圾邮件的最佳方式是什么?具有离散和连续属性的聚类算法?线路检测 | 用Java进行角度检测绘图和绘画机器人的算法 - 有什么技巧吗?If(), else if() c++中的替代方案(这是AI吗?)带方向的粒子布朗运动井字游戏的 Q 学习算法蒙特卡洛树搜索在实践中是如何实现的为什么我们应该使用 RNN 而不是马尔可夫模型?将高度自治的参与者视为代理人是否合理?为什么 A* 寻路有时走直线有时走对角线?(爪哇)约束满足问题在非常大的神经网络中是否需要偏置节点?Minimax 与 Alpha Beta 剪枝算法模拟退火算法中的邻居选择数据挖掘中的“相似性”Google Deep Dream 艺术:如何在神经网络中选择一个层并对其进行增强天真和半天真的评估有什么区别?模块 jdk.compiler 不会“打开 com.sun.tools.javac.processing”到未命名的模块 @4bae33a6AI:查找路径是否存在的最快算法?感知器可以用来检测手写数字吗?一组简单神经网络的训练图像评定数组单调性的算法(即判断数组的“有序性”)帮助 Neuroph 神经网络蒙特卡洛和马尔可夫链技术之间有什么区别?Resolution 算法如何用于命题逻辑?曼哈顿距离如何成为一种可接受的启发式算法?图灵测试的替代方案当速度是主要关注点时,表示跳棋盘的最佳数据结构是什么?在 C# 中创建 AI 行为树 - 如何?进化算法:最优种群分解感知器中的阈值有什么意义?神经网络“繁殖”编程技术:如何创建一个简单的纸牌游戏神经网络可以用来找到函数最小值(a)吗?Android 机器学习库生成段落模式的AI程序有没有办法在递归调用之前检查可用的堆栈大小?(C#)如何传播/激发递归神经网络(RNN)?Java 或 Lisp 或 C# 中的多线程 A* 搜索在神经网络反向传播算法中循环训练数据游戏中的 Q-learning 未按预期工作Dominion AI 播放器的正确 OOP 结构井字棋策略还原F# 和模糊逻辑理解小波理论的先决条件如何将多线程应用于反向传播神经网络训练?为什么蒙特卡洛树搜索会重置树根据需要组装一个函数并快速计算神经网络架构设计任何允许在线学习的 python 支持向量机库?了解 mfcc 的输出如何从点列表中找到模式(线、圆……)?人工智能中的效用理论是什么?使用 P. Viola, M. Jones Framework 计算最佳阈值的最佳方法U-net vs FCN 语义分割背后的直觉波束搜索算法中的波束大小代表什么?计算粗麻布如何用于神经网络学习什么是决策树中的多元分类?如何确定主语、宾语等词?TicTacToe AI 做出错误的决定双向图搜索的实现Unity3D 中的随机数?如何检查一个游戏对象是否可以看到另一个游戏对象?如何检查/查找项目是否在 DEQUE 中进化计算中的适应度共享和生态位计数是什么?matlab中的支持向量机在游戏编程中,如何测试使用的启发式算法是否一致?国际象棋:Alpha-Beta 中的错误易于使用的用于神经网络的 Perl 模块为什么减少 K 近邻中的 K 会增加复杂性?更新反向传播算法中的权重如何将产品标题(非结构化)解析为结构化数据?将一段 Lisp 代码移植到 Clojure (PAIP)使用 minimax 搜索具有不完全信息的纸牌游戏您如何得出 alpha-beta 修剪的时间复杂度?为什么神经网络如此有效?结构化、因式分解和原子表示?将递归神经网络与强化学习一起使用时有哪些用途?反向传播训练算法如何工作?匹配“嘈杂”名称的算法基于他们的 Facebook 个人资料的用户分析?Java 中的梯度下降基于 Gomoku 数组的 AI 算法?AI:我将如何在多台机器上训练神经网络?人工智能和机器学习中的随机性启发式识别一系列 4 字节数据块是整数还是浮点数神经网络反向传播算法卡在异或训练模式上train_on_batch() 在 keras 模型中做了什么?用于对数码照片进行分类的人脸识别?什么是 NEAT(增强拓扑的神经进化)?神经网络可以提供帮助的常见 Web 问题什么是“因子图”,它们有什么用?支持向量机——分离超平面问题教会编程语言的应用如何在 Java 中实现遗传算法的高斯变异算子Javascript 中的神经网络无法正常学习如何以这样的方式对齐两张不同的图片,使它们尽可能接近?启发式总是低估时 A* 算法最优性的证明将自然语言表示为 RDF验证数据可以成为 tensorflow.keras 2.0 中的生成器吗?你听说过的最酷的 AI 项目是什么?信息增益和熵帮助--LibSVM 100% 准确率?多输出神经网络为什么 DFS 不是最优的而 BFs 是最优的Minimax 的 Alpha-beta 剪枝NLTK。检测一个句子是否是疑问句?进化计算可以成为强化学习的一种方法吗?如何知道何时使用特定类型的相似性索引?欧氏距离与皮尔逊相关性keras 预测错误contextual embedding 和 word embedding 有什么区别实现 TD-Gammon 算法如何设计格斗游戏(街头霸王或魂剑)的人工智能?Wolfram Alpha 是如何工作的?A*算法的正确表述如何将转置表与 MTD(f) 一起使用将输入数据缩放到神经网络张量流中使用的钩子是什么意思井字棋 AI:如何制作树?神经网络的正确性TSP遗传算法中的交叉操作统一成本搜索的时间复杂度周围有任何自我改进的编译器吗?使用 C# 的 Tic-Tac-Toe 中的人工智能编写国际象棋 AIReLU 可以处理负输入吗?SOM(自组织映射)和 K-Means 有什么区别?知情搜索和不知情搜索有什么区别?将交叉和变异应用于图形(遗传算法)从句子中寻找有意义的子句ValueError:形状为 (3,1) 的不可广播输出操作数与广播形状 (3,4) 不匹配给神经网络“痛苦”一级与二级目标检测使用什么语言来创建 Google 自动驾驶汽车软件?为多个对手扩展 minimax 算法弱分类器算法:所有点之间的最短路径结果中的均值和高斯滤波器之间的差异使用 PHP 进行游戏开发和人工智能在redis中存储numpy数组的最快方法什么是玩五子棋的好 AI 策略?使用 PHP 创建 A* 搜索卷积神经网络 - 如何获得特征图?用 lisp 进行机器人编程?PyTorch 二进制分类 - 相同的网络结构,“更简单”的数据,但性能更差?神经网络与进化算法的区别难以理解神经网络中的反向传播算法将用户反馈纳入 ML 模型聚类树结构数据井字棋的遗传算法桌游《围棋》NP完成了吗?YOLO 中的锚框:它们是如何决定的scikit-learn 虚拟分类器的理论基础是什么?Python:证明 NumPy 数组的合理性Java Minimax Alpha-Beta 修剪递归返回蒙特卡洛树搜索:井字游戏的实现设计二十个问题的算法Alpha-beta 移动顺序有人可以给我一个不一致的可接受启发式的例子吗?换位表?如何构建知识图谱?如何衡量用 Java 编写的代码的速度?(人工智能算法)统一成本搜索和最佳优先搜索方法有什么区别?线性问题和非线性问题的区别?点积和内核技巧的本质解决游戏“Globs”/flood fill/“FloodIt”的算法和数据结构带转置表的 Alpha-beta 剪枝,迭代加深如何使用对偶图变换将 n 元 CSP 转换为二进制 CSP我正在学习 AI,我可以实现什么游戏来实践它?识别数据模式的最佳方法是什么?了解有关该主题的更多信息的最佳方法是什么?GridSearchCV 是否存储所有参数组合的所有分数?最佳优先搜索和 A* 搜索有什么区别?为什么A*的复杂度在内存中呈指数级增长?有没有使用马尔可夫链的商业例子?如何使用 pybrain 黑盒优化训练神经网络以监督数据集?通过神经网络进行时间序列预测蒙特卡洛树搜索 UCT 实现使用马尔可夫链(或类似的东西)来制作 IRC 机器人机器学习挑战:java/groovy 中的诊断程序(数据挖掘、机器学习)伪代码解释器?回溯和暴力搜索之间的区别逻辑回归中的微调参数FIND-S算法-简单题将机器学习应用于猜谜游戏?单调性和启发式的可接受性之间有什么区别?人工智能编译器如何为一款游戏打造良好的评价功能?如何创建现实生活中的机器人?将方向存储到数组 - 并进行比较深度优先搜索的完备性带有传送器的网格上的 A* 可接受的启发式算法?实时战略兵棋人工智能算法模拟退火和遗传算法之间有什么区别?曼哈顿距离高估了,让我发疯每一层应该有一个偏置还是每个节点有一个偏置?Tensorflow TypeError:获取参数 None 的类型 <type 'NoneType'> 无效?Wolfram Alpha 或 Mathematica 等系统如何求解方程?贪心最佳优先搜索算法与最佳优先搜索算法有区别吗?激活函数和传递函数之间有什么区别吗?A* 启发式,高估/低估?如何利用赫布学习?强化学习的良好实施?图像处理中的“能量”是什么?什么是差异进化,它与遗传算法相比如何?如何在 Pytorch 的 `nn.Sequential` 中压平输入Pong:球拍如何知道球会击中哪里?构建 NetHack 机器人:贝叶斯分析是一个好的策略吗?检测在线扑克作弊我自己的 Python OCR 程序sigmoid 的导数贪婪搜索和统一成本搜索有什么区别?为什么需要目标网络?如何使用 Stanford 解析器将文本拆分成句子?面向对象的贝叶斯垃圾邮件过滤?如何使用 BERT 对相似句子进行聚类如何为国际象棋编写神经网络?卷积神经网络 - 多通道在图像中定位文本Rasa 核心和 Rasa nlu 之间的区别是否有具有推理能力的开源专家系统?一阶逻辑中的谓词与函数8 拼图有多少种可能的状态?地图应用的聚类算法内容感知填充如何工作?如何识别此图像中的矩形?尖峰时刻 - 解决游戏我如何编写一个简单的聊天机器人 AI?人工智能的游乐场?宇宙飞船推进的 AI:在 position=0 和 angle=0 处降落一艘 3D 飞船阅读神经网络书籍所需的先决条件(并理解它们)神经网络中的连续输出批量归一化而不是输入归一化四边形找形算法SVM 和神经网络如何将人工神经网络的输出转化为概率?在 scikit-learn 流水线中插入或删除一个步骤Q 学习和价值迭代有什么区别?“边缘检测”和“图像轮廓”的区别什么是模糊逻辑?为什么权重向量与神经网络中的决策平面正交人工智能领域有哪些令人印象深刻的算法或软件?一致且可接受的启发式前向链接与后向链接在深度优先搜索中跟踪和返回路径迭代加深与深度优先搜索SGD 和反向传播有什么区别?神经网络训练中一个时代的意义当输入数量可变时,如何使用神经网络?高阶统一马尔可夫链聊天机器人如何工作?“类型的同义词已弃用;在 numpy 的未来版本中,它将被理解为 (type, (1,)) / '(1,)type'。” TensorFlow 中的问题luis.ai 与 api.ai 与 wit.ai 之间的比较?聚类数量未知的无监督聚类如何在 OpenAI 中创建一个新的健身房环境?如何获取 Tensorflow 张量尺寸(形状)作为 int 值?20 题 AI 算法是如何工作的?计算机是否有可能通过用户提供的示例“学习”正则表达式?如何使用 scikit 学习计算多类案例的精度、召回率、准确性和 f1 分数?Q-learning 和 SARSA 有什么区别?神经网络中的训练集、验证集和测试集有什么区别?Dijkstra 算法与 A-Star 相比如何?吃豆子:眼睛是怎么回到怪物洞里的?2048 游戏的最佳算法是什么?
Java的学习笔记(02)基础
Java基础01:注释(书写注释是一个非常好的习惯)单行注释 ////输出一个Hello world多行注释 / //*
多行注释多行所行
*/文档注释/**
*java doc
*java doc
*java doc
*/Java基础02:标识型和关键字重要Java关键字列表abstract:它利用计算机来完成抽象。它是与类和方法相关的非访问修饰符。enum:它利用Java描述enum。instanceof:它用于知道对象是否是预定义写入(类、子类或接口)的情况。private:它是一个访问修饰符。任何被宣布为私有的东西都不能在其类外看到。protected:如果您需要使组件能够在当前类之外查看,那么,只需对特定于您的类的子类进行查看,此时将宣布该组件。public: 任何人从任何地方都可以到达的地方。访问修改函数static:它用于使成员(块、方法、变量或嵌套类)可以独立于任何其他成员使用,而无需参考特定情况。strictfp:用于限制、浮点、浮点运算和计算,并在浮点变量中执行任务时保证每个阶段的结果相同。synchronized:它用于在Java中获得适用于块和方法的同步。transient:作为序列化的一部分使用的因子修饰符。在序列化的季节,如果我们不想对记录中的特定变量进行多余的估计,那么此时,我们使用transient关键字。volatile:如果修饰符告诉编译器,由不稳定变量调整的变量通过程序的不同部分发生惊人的变化,则使用volatile。Java关键字的类型EnumEnum有效地表示编程语言中命名常量的集合。例如,一副扑克牌中的四套西服可能有四个名为Club、Diamond、Heart和Spade的统计员,其中一个位置有一个名为西服的已识别类别。不同的插图结合了正常识别的构图(如行星、一周中的几天、色调、方位等)。当我们在编译时知道每一个可能的值时,JavaEnum就可以利用它。例如,这可能包括菜单上的决策、调整模式、召唤线横幅等。枚举写入中常量的排列永远保持不变并不重要。Strictfp关键字strictfp是Java中的一个关键字,它利用限制浮点值,并在执行滑行点变量中的任务时保证每个阶段的结果相同。浮点计算从属于平台,即当类文档在各种平台(16/32/64位处理器)上运行时,不同的输出(浮点变量)完成。为了解决这类问题,根据IEEE754滑行点计数指南,以JDK1.2的形式提出了strictfp口号。Strictfp的重要关注点:Strictfp修饰符按原样使用类、接口和技术。Strictfp无法使用独特的策略。尽管如此,它还是利用了理论类/接口。由于接口的技术当然是动态的,strictfp不能与接口内的任何方法一起使用。标识符注意点所有标识符都应该以字母,或者美元符号($)或者下划线(_)开始不能使用关键字作为变量名或者方法名大小写非常敏感不能以数字或者其他符号开头可以使用中文名和拼音,但是不建议使用,不方便后期维护Java基础03:数据型和关键字基本数据类型整数类型byte 占1个字节(8位)short 占2个字节(16位)int 占4个字节(32位)long 占8个字节(64位)浮点数类型float 4字节double 8字节字符类型char 2字节Boolean类型1位:true falsepublic class demo02 {
public static void main(String[] args) {
int num1 = 10;
int num2 = 20;
int num3 = 40;
long num4 = 100L; //Long 类型要在数字后加个L
//小数:浮点数
float num5 = 50.1F; //float 类型要在数字后加个F
//字符
char name = 'A';
char name3 = '张';
//字符串
String name2 = "张三";
//布尔类型
boolean flag = true;
}
}Java基础04 数据类型扩展及面试题最好完全避免使用浮点数进行比较!!!!!!!!public class demo03 {
public static void main(String[] args) {
//整数拓展 进制 二进制0b 十进制 八进制0 十六进制0x
int i = 10;
int i2 = 010;
int i3 = 0x10;
System.out.println(i);
System.out.println(i2);
System.out.println(i3);
//===================================================================
//浮点数拓展
//===================================================================
//float: 有限 离散 舍入误差 大约 接近但不等于
//double
//最好完全避免使用浮点数进行比较
float f = 0.1f;
double d = 0.1;
System.out.println(f==d); //判断是否相等 :false
System.out.println(f);
System.out.println(d);
//字符
char c1 = 'a';
char c2 = '中';
System.out.println(c1);
System.out.println((int) c1); //强制转换,将a转化为数字
System.out.println(c2);
System.out.println((int) c2); //强制转换
// 所有字符本本质还是数字
//转义字符
// \t 制表符
// \n 换行
}
}Java基础05 类型转换运算中,不同类型的数据先转换为同一类型,然后进行运算。从低到高位数据类型:byte 、short、char—>int—>long—>float—>doublepublic class demo05 {
public static void main(String[] args) {
int i = 128;
byte b = (byte) i; //强制转换
System.out.println(i);
System.out.println(b); //溢出
}
}强制转换 (类型)变量名 高—>低位自动转化 低—>高位注意点:不能对布尔值进行转换不能把对象类型转换为不想干的类型在把高容量转换到低容量的时候,强制转换转换的是时候可能存在内存溢出,或者精度问题!!!!!!!!public class demo05 {
public static void main(String[] args) {
int i = 128;
byte b = (byte) i; //强制转换
System.out.println(i);
System.out.println(b); //溢出
System.out.println((int)23.7); //double类型 23
System.out.println((int)-45.89f); //浮点数类型 -45
char c = 'a';
int d = c+1;
System.out.println(d); //98
System.out.println((char) d); //b
}
}public class demo06 {
public static void main(String[] args) {
// 操作比较大的数字的时候,注意溢出的问题
// JDK7 新特性,数字之间可以用下划线分割
int money = 10_0000_0000L; //Long 类型要在数字后加个L
int years = 20;
int total = money*years; //-1474836480 ,计算的时候已经溢出了
long total2 =money*years; //默认是int类型,转换之前就已经存在问题了
long total3 = money*((long)years); //需要把years转换为long类型
System.out.println(total3);
}
}Java基础06:变量、常量、作用域类变量实例变量局部变量public class demo08 {
// 实例变量,从属于对象;如果不进行初始化,这个类型的默认值 0 0.0
// 布尔值默认为false
//除了基本类型,其余的类型默认值为null
String name;
int age;
//类变量
static double salary = 2500;
//main方法
public static void main(String[] args) {
//局部变量;使用前必须声明和初始化值
int i = 10;
System.out.println(i);
//变量类型 变量名字= new demo08();
demo08 demo08 = new demo08();
System.out.println(demo08.age); // 0
System.out.println(demo08.name); // null
//类变量
System.out.println(salary);
}
//其他方法
public void add(){
}
}常量:设置之后不可再更改常量修饰符: finalpublic class demo09 {
//常量修饰符: final
//修饰符,不存在先后顺序
static final double PI = 3.1415926;
public static void main(String[] args) {
System.out.println(PI);
}
}命名规范见名知意类成员变量: 首字母小写和驼峰原则:monthSalary 除了第一个单词意外后面单词首字母大写。局部变量:首字母小写和驼峰原则常量:大写字母和下划线 MAX_VALUE类名:首字母大写和驼峰原则:Man,GoogMan方法名:首字母大写和驼峰原则:run(),runRun()Java基础07运算符算术运算符 +、-、*、/、%、++、--赋值运算符 =关系运算符 >、<、>=、<=、==、!=、instanceof逻辑运算符 && || ! (与或非)位运算符条件运算符拓展运算符package operator;
public class demo1 {
public static void main(String[] args) {
//2元运算符号
//CTRL + D 复制当前行到下一行
int a = 10;
int b = 20;
int c = 30;
int d = 40;
System.out.println(a+b);
System.out.println(a-b);
System.out.println(a*b);
System.out.println(a/(double)b); //可能产生小数,强转成double
}
}package operator;
public class Demo02 {
public static void main(String[] args) {
long a = 12345678946545L;
int b = 123;
short c = 10;
byte d = 8;
System.out.println(a+b+c+d); //long
System.out.println(b+c+d); //int
System.out.println(c+d); //int 没有long类型,数据类型自动转为int类型,除非强转成double
}
}package operator;
public class Demo03 {
public static void main(String[] args) {
//关系运算符号
int a = 10;
int b = 20;
int c = 21;
System.out.println(a>b);
System.out.println(a<b);
System.out.println(a==b);
System.out.println(a!=b);
System.out.println(c%a); //取余运算,模运算
}
}Java基础08 自增自减运算符、初识math类package operator;
public class Demo04 {
public static void main(String[] args) {
// ++ -- 自增、自减运算符
int a = 3;
int b = a++; // a = a +1 先赋值,再自增
System.out.println(a); // a = 4;
int c = ++a; // ++a a = a + 1; 先自增,再赋值
System.out.println(a);
System.out.println(a);
System.out.println(b);
System.out.println(b);
System.out.println(c);
System.out.println(c);
//幂运算 2^3 2*2*2 = 8
double pow = Math.pow(2,3);
System.out.println(pow);
}
}Java基础09 逻辑运算符、位运算符package operator;
public class Demo05 {
public static void main(String[] args) {
// 逻辑运算符 && || !
boolean a = true;
boolean b = false;
System.out.println("a && b " +(a && b)); //两个都是真,结果为真
System.out.println("a || b " + (a || b)); //两个其中有一个是真,结果为真
System.out.println("!(a && b) " + !(a && b)); // 如果为真,则为假;如果为假,则为真。
//短路运算
int c = 5;
boolean d = (c<4)&&(c++<4); // c<4 为false ,不运行c++ ,结果最后还是c = 5;
System.out.println(c);
System.out.println(d);
}
}package operator;
public class Demo06 {
public static void main(String[] args) {
/*
* 位运算
* A = 0011 1100
* B = 0000 1101
*
* A&B = 0000 1100
* A|B = 0011 1101
* A^B = 0011 0001 //异或 相同取0,不同取1
* ~B = 1111 0010 //取反
*
* 2*8 = 16 2*2*2*2
* << *2 // 左移: 2倍
* >> /2 // 右移除: 除以2
*
*
* 0000 0000 0
* 0000 0001 1
* 0000 0010 2
* 0000 0011 3
* 0000 0100 4
* 0000 1000 8
* 0001 0000 16
*
*
* */
System.out.println(2<<3);
}
}Java基础10 三元运算符及小结package operator;
public class Domo07 {
public static void main(String[] args) {
int a = 10;
int b = 20;
a+=b; // a=a+b
a-=b; // a=a-b
System.out.println(a);
//字符串连接符 + ,string
System.out.println(""+a+b); // 1020 其中的“+”是字符串连接符
System.out.println(a+b+""); // 30 其中a与b之间的“+”是运算符,b后面的的“+”是字符串连接符
}
}package operator;
public class Demo08 {
public static void main(String[] args) {
//三元运算符
// x ? y :z
//如果 x==true ,则结果为y,否者为z
int score = 80;
String Type2 = score < 60 ? "不及格":"及格";
System.out.println(Type2);
}
}Java基础11 包机制包的本质就是一个文件夹()一般利用公司域名倒置作为包名:com.luomoe.basepackage com.baidu.base; //包
import com.baidu.base.demo03; //引入
import com.baidu.base.*; //引入 包内所有的类Java基础12 JavaDoc生成文档javadoc 命令是用来生成自己的API文档 /** 然后回车/**
* @author zhangshan
* @version 1.0
* @since 1.8
*
*
*/cmd命令(生成web文档)javadoc -encoding UTF-8 -charset UTF-8 Doc.java使用IEDA生成javadoc文档(1)打开 idea,点击 Tools–> Generate JavaDoc,会弹出生成JavaDoc文档的配置页面。(2)进行参数配置(3)在输出目录里生成JavaDoc。
剑指 Offer(第 2 版)完整题解笔记 & C++代码实现(LeetCode版)
@[toc]原书目录剑指 Offer 03. 数组中重复的数字剑指 Offer 04. 二维数组中的查找剑指 Offer 05. 替换空格剑指 Offer 06. 从尾到头打印链表剑指 Offer 07. 重建二叉树剑指 Offer 09. 用两个栈实现队列剑指 Offer 10- I. 斐波那契数列剑指 Offer 10- II. 青蛙跳台阶问题剑指 Offer 11. 旋转数组的最小数字剑指 Offer 12. 矩阵中的路径剑指 Offer 13. 机器人的运动范围剑指 Offer 14- I. 剪绳子剑指 Offer 14- II. 剪绳子 II剑指 Offer 15. 二进制中1的个数剑指 Offer 16. 数值的整数次方剑指 Offer 17. 打印从1到最大的n位数剑指 Offer 18. 删除链表的节点剑指 Offer 19. 正则表达式匹配剑指 Offer 20. 表示数值的字符串剑指 Offer 21. 调整数组顺序使奇数位于偶数前面剑指 Offer 22. 链表中倒数第k个节点剑指 Offer 24. 反转链表剑指 Offer 25. 合并两个排序的链表剑指 Offer 26. 树的子结构剑指 Offer 27. 二叉树的镜像剑指 Offer 28. 对称的二叉树剑指 Offer 29. 顺时针打印矩阵剑指 Offer 30. 包含min函数的栈剑指 Offer 31. 栈的压入、弹出序列剑指 Offer 32 - I. 从上到下打印二叉树剑指 Offer 32 - II. 从上到下打印二叉树 II剑指 Offer 32 - III. 从上到下打印二叉树 III剑指 Offer 33. 二叉搜索树的后序遍历序列剑指 Offer 34. 二叉树中和为某一值的路径剑指 Offer 35. 复杂链表的复制剑指 Offer 36. 二叉搜索树与双向链表剑指 Offer 37. 序列化二叉树剑指 Offer 38. 字符串的排列剑指 Offer 39. 数组中出现次数超过一半的数字剑指 Offer 40. 最小的k个数剑指 Offer 41. 数据流中的中位数剑指 Offer 42. 连续子数组的最大和剑指 Offer 43. 1~n 整数中 1 出现的次数剑指 Offer 44. 数字序列中某一位的数字剑指 Offer 45. 把数组排成最小的数剑指 Offer 46. 把数字翻译成字符串剑指 Offer 47. 礼物的最大价值剑指 Offer 48. 最长不含重复字符的子字符串剑指 Offer 49. 丑数剑指 Offer 50. 第一个只出现一次的字符剑指 Offer 51. 数组中的逆序对剑指 Offer 52. 两个链表的第一个公共节点剑指 Offer 53 - I. 在排序数组中查找数字 I剑指 Offer 53 - II. 0~n-1中缺失的数字剑指 Offer 54. 二叉搜索树的第k大节点剑指 Offer 55 - I. 二叉树的深度剑指 Offer 55 - II. 平衡二叉树剑指 Offer 56 - I. 数组中数字出现的次数剑指 Offer 56 - II. 数组中数字出现的次数 II剑指 Offer 57 - II. 和为s的连续正数序列剑指 Offer 57. 和为s的两个数字剑指 Offer 58 - I. 翻转单词顺序剑指 Offer 58 - II. 左旋转字符串剑指 Offer 59 - I. 滑动窗口的最大值剑指 Offer 59 - II. 队列的最大值剑指 Offer 60. n个骰子的点数剑指 Offer 61. 扑克牌中的顺子剑指 Offer 62. 圆圈中最后剩下的数字剑指 Offer 63. 股票的最大利润剑指 Offer 64. 求1+2+…+n剑指 Offer 65. 不用加减乘除做加法剑指 Offer 66. 构建乘积数组剑指 Offer 67. 把字符串转换成整数剑指 Offer 68 - I. 二叉搜索树的最近公共祖先剑指 Offer 68 - II. 二叉树的最近公共祖先剑指 Offer 03. 数组中重复的数字找出数组中重复的数字。在一个长度为 n 的数组 nums 里的所有数字都在 0~n-1 的范围内。数组中某些数字是重复的,但不知道有几个数字重复了,也不知道每个数字重复了几次。请找出数组中任意一个重复的数字。示例 1:输入:[2, 3, 1, 0, 2, 5, 3]输出:2 或 3 限制:2 <= n <= 100000class Solution {
public:
int findRepeatNumber(vector<int>& nums) {
set<int>se;
int ok = nums[0];
for(int x : nums){
if(se.count(x)){
ok = x;
break;
}
se.insert(x);
}
return ok;
}
};剑指 Offer 04. 二维数组中的查找在一个 n * m 的二维数组中,每一行都按照从左到右递增的顺序排序,每一列都按照从上到下递增的顺序排序。请完成一个高效的函数,输入这样的一个二维数组和一个整数,判断数组中是否含有该整数。示例:现有矩阵 matrix 如下:[ [1, 4, 7, 11, 15], [2, 5, 8, 12, 19], [3, 6, 9, 16, 22], [10, 13, 14, 17, 24], [18, 21, 23, 26, 30]]给定 target = 5,返回 true。给定 target = 20,返回 false。限制:0 <= n <= 10000 <= m <= 1000class Solution {
public:
bool findNumberIn2DArray(vector<vector<int>>& matrix, int target) {
for(const auto& row : matrix){
auto it = lower_bound(row.begin(),row.end(),target);
if(it!=row.end() && *it==target){
return true;
}
}
return false;
}
};剑指 Offer 05. 替换空格请实现一个函数,把字符串 s 中的每个空格替换成"%20"。示例 1:输入:s = "We are happy."输出:"We%20are%20happy."限制:0 <= s 的长度 <= 10000class Solution {
public:
string replaceSpace(string s) {
string t = "";
for(char ch : s){
if(ch!=' ')t += ch;
else t += "%20";
}
return t;
}
};剑指 Offer 06. 从尾到头打印链表输入一个链表的头节点,从尾到头反过来返回每个节点的值(用数组返回)。示例 1:输入:head = [1,3,2]输出:[2,3,1]限制:0 <= 链表长度 <= 10000/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* ListNode *next;
* ListNode(int x) : val(x), next(NULL) {}
* };
*/
class Solution {
public:
vector<int> reversePrint(ListNode* head) {
vector<int>res;
ListNode* cur = head;
while(cur){
res.push_back(cur->val);
cur = cur->next;
}
reverse(res.begin(), res.end());
return res;
}
};剑指 Offer 07. 重建二叉树输入某二叉树的前序遍历和中序遍历的结果,请构建该二叉树并返回其根节点。假设输入的前序遍历和中序遍历的结果中都不含重复的数字。示例 1:Input: preorder = [3,9,20,15,7], inorder = [9,3,15,20,7]Output: [3,9,20,null,null,15,7]示例 2:Input: preorder = [-1], inorder = [-1]Output: [-1]限制:0 <= 节点个数 <= 5000/**
* Definition for a binary tree node.
* struct TreeNode {
* int val;
* TreeNode *left;
* TreeNode *right;
* TreeNode() : val(0), left(nullptr), right(nullptr) {}
* TreeNode(int x) : val(x), left(nullptr), right(nullptr) {}
* TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {}
* };
*/
class Solution {
public:
unordered_map<int, int> index;
TreeNode * build(vector<int>& preorder, vector<int>& inorder, int pl, int pr, int il, int ir){
if(pl > pr)return nullptr;
//根节点
int prt = pl;
int irt = index[preorder[prt]];
//
TreeNode* root = new TreeNode(preorder[prt]);
int lsz = irt-il;
root->left = build(preorder, inorder, pl+1,pl+lsz, il, irt-1);
root->right = build(preorder, inorder, pl+lsz+1,pr, irt+1,ir);
return root;
}
TreeNode* buildTree(vector<int>& preorder, vector<int>& inorder) {
int n = preorder.size();
for(int i = 0; i < n; i++){
index[inorder[i]] = i;
}
return build(preorder, inorder, 0,n-1, 0, n-1);
}
};剑指 Offer 09. 用两个栈实现队列用两个栈实现一个队列。队列的声明如下,请实现它的两个函数 appendTail 和 deleteHead ,分别完成在队列尾部插入整数和在队列头部删除整数的功能。(若队列中没有元素,deleteHead 操作返回 -1 )示例 1:输入:["CQueue","appendTail","deleteHead","deleteHead"][[],[3],[],[]]输出:[null,null,3,-1]示例 2:输入:["CQueue","deleteHead","appendTail","appendTail","deleteHead","deleteHead"][[],[],[5],[2],[],[]]输出:[null,-1,null,null,5,2]提示:1 <= values <= 10000最多会对 appendTail、deleteHead 进行 10000 次调用class CQueue {
public:
stack<int>in, out;
CQueue() {}
void appendTail(int value) {
in.push(value);
}
int deleteHead() {
if(out.empty()){
if(in.empty())return -1;
else{
while(!in.empty()){
out.push(in.top()); in.pop();
}
}
}
int x = out.top();
out.pop();
return x;
}
};
/**
* Your CQueue object will be instantiated and called as such:
* CQueue* obj = new CQueue();
* obj->appendTail(value);
* int param_2 = obj->deleteHead();
*/剑指 Offer 10- I. 斐波那契数列写一个函数,输入 n ,求斐波那契(Fibonacci)数列的第 n 项(即 F(N))。斐波那契数列的定义如下:F(0) = 0, F(1) = 1F(N) = F(N - 1) + F(N - 2), 其中 N > 1.斐波那契数列由 0 和 1 开始,之后的斐波那契数就是由之前的两数相加而得出。答案需要取模 1e9+7(1000000007),如计算初始结果为:1000000008,请返回 1。示例 1:输入:n = 2输出:1示例 2:输入:n = 5输出:5提示:0 <= n <= 100class Solution {
public:
int fib(int n) {
if(n==0)return 0;
if(n==1)return 1;
int a = 0, b = 1, mod = 1e9+7;
for(int i = 2; i <= n; i++){
int c = b;
b = (a+b)%mod;
a = c;
}
return b;
}
};剑指 Offer 10- II. 青蛙跳台阶问题一只青蛙一次可以跳上1级台阶,也可以跳上2级台阶。求该青蛙跳上一个 n 级的台阶总共有多少种跳法。答案需要取模 1e9+7(1000000007),如计算初始结果为:1000000008,请返回 1。示例 1:输入:n = 2输出:2示例 2:输入:n = 7输出:21示例 3:输入:n = 0输出:1提示:0 <= n <= 100class Solution {
public:
int mod = 1e9+7;
int numWays(int n) {
if(n<=1)return 1;
vector<int>f(n+1,0);
f[0] = 1; f[1] = 1;
for(int i = 2; i <= n; i++){
f[i] = (f[i-1]+f[i-2])%mod;
}
return f[n];
}
};剑指 Offer 11. 旋转数组的最小数字把一个数组最开始的若干个元素搬到数组的末尾,我们称之为数组的旋转。给你一个可能存在 重复 元素值的数组 numbers ,它原来是一个升序排列的数组,并按上述情形进行了一次旋转。请返回旋转数组的最小元素。例如,数组 [3,4,5,1,2] 为 [1,2,3,4,5] 的一次旋转,该数组的最小值为 1。 注意,数组 [a[0], a[1], a[2], ..., a[n-1]] 旋转一次 的结果为数组 [a[n-1], a[0], a[1], a[2], ..., a[n-2]] 。示例 1:输入:numbers = [3,4,5,1,2]输出:1示例 2:输入:numbers = [2,2,2,0,1]输出:0提示:n == numbers.length1 <= n <= 5000-5000 <= numbers[i] <= 5000numbers 原来是一个升序排序的数组,并进行了 1 至 n 次旋转class Solution {
public:
int minArray(vector<int>& numbers) {
int l = 0, r = numbers.size()-1;
while(l < r){
int mid = l+r>>1;
if(numbers[r]>numbers[mid]){
r = mid;
}else if(numbers[r]<numbers[mid]){
l = mid+1;
}else r--;
}
return numbers[l];
}
};剑指 Offer 12. 矩阵中的路径给定一个 m x n 二维字符网格 board 和一个字符串单词 word 。如果 word 存在于网格中,返回 true ;否则,返回 false 。单词必须按照字母顺序,通过相邻的单元格内的字母构成,其中“相邻”单元格是那些水平相邻或垂直相邻的单元格。同一个单元格内的字母不允许被重复使用。例如,在下面的 3×4 的矩阵中包含单词 "ABCCED"(单词中的字母已标出)。示例 1:输入:board = [["A","B","C","E"],["S","F","C","S"],["A","D","E","E"]], word = "ABCCED"输出:true示例 2:输入:board = [["a","b"],["c","d"]], word = "abcd"输出:false提示:m == board.lengthn = board[i].length1 <= m, n <= 61 <= word.length <= 15board 和 word 仅由大小写英文字母组成class Solution {
public:
//check(i,j,k)表示从(i,j)出发,能否搜索到单词word[k..n]
bool check(vector<vector<char>>&board, string& word, vector<vector<int>>&vis, int i, int j, int k){
int n = board.size(), m = board[0].size();
if(board[i][j] != word[k])return false;
else if(k==word.size()-1)return true;
vis[i][j] = true;
vector<pair<int,int> >d{{0,1},{0,-1},{1,0},{-1,0}};
bool ok = false;
for(auto dd : d){
int ni = i+dd.first, nj = j+dd.second;
if(ni>=0&&ni<n&&nj>=0&&nj<m&&!vis[ni][nj]){
if(check(board,word,vis,ni,nj,k+1)){
ok = true;
break;
}
}
}
vis[i][j] = false;
return ok;
}
bool exist(vector<vector<char>>& board, string word) {
int n = board.size(), m = board[0].size();
vector<vector<int>> vis(n, vector<int>(m));
for(int i = 0; i < n; i++){
for(int j = 0; j < m; j++){
if(check(board,word,vis,i,j,0)){
return true;
}
}
}
return false;
}
};剑指 Offer 13. 机器人的运动范围地上有一个m行n列的方格,从坐标 [0,0] 到坐标 [m-1,n-1] 。一个机器人从坐标 [0, 0] 的格子开始移动,它每次可以向左、右、上、下移动一格(不能移动到方格外),也不能进入行坐标和列坐标的数位之和大于k的格子。例如,当k为18时,机器人能够进入方格 [35, 37] ,因为3+5+3+7=18。但它不能进入方格 [35, 38],因为3+5+3+8=19。请问该机器人能够到达多少个格子?示例 1:输入:m = 2, n = 3, k = 1输出:3示例 2:输入:m = 3, n = 1, k = 0输出:1提示:1 <= n,m <= 1000 <= k <= 20class Solution {
public:
int get(int x){
int res = 0;
while(x){ res += x%10; x /= 10; }
return res;
}
int movingCount(int m, int n, int k) {
if(!k)return 1;
vector<vector<int>>vis(m,vector<int>(n,0));
vis[0][0] = 1;
int ans = 1;
for(int i = 0; i < m; i++){
for(int j = 0; j < n; j++){
if(i==0&&j==0)continue;
if(get(i)+get(j)>k)continue;
if(i-1>=0)vis[i][j] |= vis[i-1][j];
if(j-1>=0)vis[i][j] |= vis[i][j-1];
ans += vis[i][j];
}
}
return ans;
}
};剑指 Offer 14- I. 剪绳子给你一根长度为 n 的绳子,请把绳子剪成整数长度的 m 段(m、n都是整数,n>1并且m>1),每段绳子的长度记为 k[0],k[1]...k[m-1] 。请问 k[0]k[1]...*k[m-1] 可能的最大乘积是多少?例如,当绳子的长度是8时,我们把它剪成长度分别为2、3、3的三段,此时得到的最大乘积是18。示例 1:输入: 2输出: 1解释: 2 = 1 + 1, 1 × 1 = 1示例 2:输入: 10输出: 36解释: 10 = 3 + 3 + 4, 3 × 3 × 4 = 36提示:2 <= n <= 58class Solution {
public:
int mod = 1e9+7;
long long pows(long long a, long long b){long long res = 1; while(b){if(b&1)res=res*a%mod;a=a*a%mod; b>>=1; } return res; }
//结论题
int cuttingRope(int n) {
if(n<=3)return n-1;
int x = n/3, y = n%3;
if(y==0)return (int)pows(3,x);
if(y==1)return (int)(pows(3,x-1)*4%mod);
return (int)(pows(3,x)*2%mod);
}
};剑指 Offer 14- II. 剪绳子 II给你一根长度为 n 的绳子,请把绳子剪成整数长度的 m 段(m、n都是整数,n>1并且m>1),每段绳子的长度记为 k[0],k[1]...k[m - 1] 。请问 k[0]k[1]...*k[m - 1] 可能的最大乘积是多少?例如,当绳子的长度是8时,我们把它剪成长度分别为2、3、3的三段,此时得到的最大乘积是18。答案需要取模 1e9+7(1000000007),如计算初始结果为:1000000008,请返回 1。示例 1:输入: 2输出: 1解释: 2 = 1 + 1, 1 × 1 = 1示例 2:输入: 10输出: 36解释: 10 = 3 + 3 + 4, 3 × 3 × 4 = 36提示:2 <= n <= 1000class Solution {
public:
int mod = 1e9+7;
long long pows(long long a, long long b){long long res = 1; while(b){if(b&1)res=res*a%mod;a=a*a%mod; b>>=1; } return res; }
//结论题
int cuttingRope(int n) {
if(n<=3)return n-1;
int x = n/3, y = n%3;
if(y==0)return (int)pows(3,x);
if(y==1)return (int)(pows(3,x-1)*4%mod);
return (int)(pows(3,x)*2%mod);
}
};剑指 Offer 15. 二进制中1的个数编写一个函数,输入是一个无符号整数(以二进制串的形式),返回其二进制表达式中数字位数为 '1' 的个数(也被称为 汉明重量).)。提示:请注意,在某些语言(如 Java)中,没有无符号整数类型。在这种情况下,输入和输出都将被指定为有符号整数类型,并且不应影响您的实现,因为无论整数是有符号的还是无符号的,其内部的二进制表示形式都是相同的。在 Java 中,编译器使用 二进制补码 记法来表示有符号整数。因此,在上面的 示例 3 中,输入表示有符号整数 -3。示例 1:输入:n = 11 (控制台输入 00000000000000000000000000001011)输出:3解释:输入的二进制串 00000000000000000000000000001011 中,共有三位为 '1'。示例 2:输入:n = 128 (控制台输入 00000000000000000000000010000000)输出:1解释:输入的二进制串 00000000000000000000000010000000 中,共有一位为 '1'。示例 3:输入:n = 4294967293 (控制台输入 11111111111111111111111111111101,部分语言中 n = -3)输出:31解释:输入的二进制串 11111111111111111111111111111101 中,共有 31 位为 '1'。提示:输入必须是长度为 32 的 二进制串 。class Solution {
public:
int hammingWeight(uint32_t n) {
int res = 0;
while(n){
if(n&1)res++;
n>>=1;
}
return res;
}
};剑指 Offer 16. 数值的整数次方实现 pow(x, n) ,即计算 x 的 n 次幂函数(即,xn)。不得使用库函数,同时不需要考虑大数问题。示例 1:输入:x = 2.00000, n = 10输出:1024.00000示例 2:输入:x = 2.10000, n = 3输出:9.26100示例 3:输入:x = 2.00000, n = -2输出:0.25000解释:2-2 = 1/22 = 1/4 = 0.25提示:-100.0 < x < 100.0-231 <= n <= 231-1-104 <= xn <= 104class Solution {
public:
double myPow(double x, int n) {
if(n==0)return 1;
if(n==1)return x;
if(n==-1)return 1/x;
double y = myPow(x,n/2);
double yy = myPow(x,n%2);
return y*y*yy;
}
};剑指 Offer 17. 打印从1到最大的n位数输入数字 n,按顺序打印出从 1 到最大的 n 位十进制数。比如输入 3,则打印出 1、2、3 一直到最大的 3 位数 999。示例 1:输入: n = 1输出: [1,2,3,4,5,6,7,8,9]说明:用返回一个整数列表来代替打印n 为正整数class Solution {
public:
vector<int> printNumbers(int n) {
vector<int>res;
int ed = stoi(string(n,'9'));
for(int i = 1; i <= ed; i++)res.push_back(i);
return res;
}
};剑指 Offer 18. 删除链表的节点给定单向链表的头指针和一个要删除的节点的值,定义一个函数删除该节点。返回删除后的链表的头节点。注意:此题对比原题有改动示例 1:输入: head = [4,5,1,9], val = 5输出: [4,1,9]解释: 给定你链表中值为 5 的第二个节点,那么在调用了你的函数之后,该链表应变为 4 -> 1 -> 9.示例 2:输入: head = [4,5,1,9], val = 1输出: [4,5,9]解释: 给定你链表中值为 1 的第三个节点,那么在调用了你的函数之后,该链表应变为 4 -> 5 -> 9.说明:题目保证链表中节点的值互不相同若使用 C 或 C++ 语言,你不需要 free 或 delete 被删除的节点/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* ListNode *next;
* ListNode(int x) : val(x), next(NULL) {}
* };
*/
class Solution {
public:
ListNode* deleteNode(ListNode* head, int val) {
ListNode* cur = head;
ListNode* pre = NULL;
while(cur){
if(cur->val == val){
if(pre!=NULL)pre->next = cur->next;
else return head->next;
}else{
pre = cur;
}
cur = cur->next;
}
return head;
}
};剑指 Offer 19. 正则表达式匹配请实现一个函数用来匹配包含'. '和''的正则表达式。模式中的字符'.'表示任意一个字符,而''表示它前面的字符可以出现任意次(含0次)。在本题中,匹配是指字符串的所有字符匹配整个模式。例如,字符串"aaa"与模式"a.a"和"abaca"匹配,但与"aa.a"和"ab*a"均不匹配。示例 1:输入:s = "aa"p = "a"输出: false解释: "a" 无法匹配 "aa" 整个字符串。示例 2:输入:s = "aa"p = "a*"输出: true解释: 因为 '*' 代表可以匹配零个或多个前面的那一个元素, 在这里前面的元素就是 'a'。因此,字符串 "aa" 可被视为 'a' 重复了一次。示例 3:输入:s = "ab"p = ".*"输出: true解释: "." 表示可匹配零个或多个('')任意字符('.')。示例 4:输入:s = "aab"p = "cab"输出: true解释: 因为 '*' 表示零个或多个,这里 'c' 为 0 个, 'a' 被重复一次。因此可以匹配字符串 "aab"。示例 5:输入:s = "mississippi"p = "misisp*."输出: falses 可能为空,且只包含从 a-z 的小写字母。p 可能为空,且只包含从 a-z 的小写字母以及字符 . 和 ,无连续的 ''。//f[i][j]表示s的前i个能否被p的前j个匹配。
//转移若s[i]==p[j]则f[i][j]=f[i-1][j-1]. 否则若p[j]=.也可以匹配,若=*则再判断p[j-1]与s[i]的关系
class Solution {
public:
bool isMatch(string s, string p) {
s = " "+s; p = " "+p; //s,p可能为空
int n = s.size(), m = p.size();
bool f[n+1][m+1];
memset(f,false,(n+1)*(m+1));
f[0][0] = true;
for(int i = 1; i <= n; i++){
for(int j = 1; j <= m; j++){
if(s[i-1]==p[j-1] || p[j-1]=='.'){
f[i][j] = f[i-1][j-1];
}
else if(p[j-1]=='*'){
if(p[j-2]!=s[i-1] && p[j-2]!='.'){//*找前一个,前一个匹配不上,再往前找
f[i][j] = f[i][j-2];
}else{ //匹配上了
f[i][j] = (f[i-1][j] || f[i][j-1] || f[i][j-2]);//多个字符,单个字符,没有匹配
}
}
}
}
return f[n][m];
}
};剑指 Offer 20. 表示数值的字符串请实现一个函数用来判断字符串是否表示数值(包括整数和小数)。数值(按顺序)可以分成以下几个部分:若干空格一个 小数 或者 整数(可选)一个 'e' 或 'E' ,后面跟着一个 整数若干空格小数(按顺序)可以分成以下几个部分:(可选)一个符号字符('+' 或 '-')下述格式之一:至少一位数字,后面跟着一个点 '.'至少一位数字,后面跟着一个点 '.' ,后面再跟着至少一位数字一个点 '.' ,后面跟着至少一位数字整数(按顺序)可以分成以下几个部分:(可选)一个符号字符('+' 或 '-')至少一位数字部分数值列举如下:["+100", "5e2", "-123", "3.1416", "-1E-16", "0123"]部分非数值列举如下:["12e", "1a3.14", "1.2.3", "+-5", "12e+5.4"]示例 1:输入:s = "0"输出:true示例 2:输入:s = "e"输出:false示例 3:输入:s = "."输出:false示例 4:输入:s = " .1 "输出:true提示:1 <= s.length <= 20s 仅含英文字母(大写和小写),数字(0-9),加号 '+' ,减号 '-' ,空格 ' ' 或者点 '.' 。class Solution {
public:
bool isNumber(string s) {
//是否存在E, 小数点, 数字,符号,结束。
int have_e = 0, have_p = 0, have_n = 0, have_s = 0, ed = 0;
for(int i = 0; i < s.size(); i++){
int isnull = have_e||have_p||have_n||have_s;
if(ed && s[i]!=' ')return false;
if(have_n && (s[i]=='+' || s[i]=='-'))return false;
if(!have_n && (s[i]=='E' || s[i]=='e'))return false;
if(have_p && !have_e && (s[i]=='+' || s[i]=='-'))return false;
if((s[i]=='+' || s[i]=='-') && have_s)return false;
else if((s[i]=='+' || s[i]=='-') && !have_s)have_s = 1;
else if(s[i] == '.' && have_p)return false;
else if(s[i] == '.' && !have_p)have_p = 1;
else if((s[i]== 'E' || s[i]=='e') && have_e)return false;
else if((s[i]== 'E' || s[i]=='e') && !have_e){
have_e = 1; have_s = 0; have_p = 1; have_n = 0;
if(i+1>=s.size())return false;
}else if(s[i]-'0'>=0 && s[i]-'0'<=9)have_n = 1;
else if(s[i] == ' ' && !isnull)continue;
else if(s[i] == ' ' && isnull)ed = 1;
else return false;
}
if(!have_n)return false;
return true;
}
};剑指 Offer 21. 调整数组顺序使奇数位于偶数前面输入一个整数数组,实现一个函数来调整该数组中数字的顺序,使得所有奇数在数组的前半部分,所有偶数在数组的后半部分。示例:输入:nums = [1,2,3,4]输出:[1,3,2,4] 注:[3,1,2,4] 也是正确的答案之一。提示:0 <= nums.length <= 500000 <= nums[i] <= 10000class Solution {
public:
vector<int> exchange(vector<int>& nums) {
vector<int>res(nums.size());
int l = 0, r = nums.size()-1;
for(int x : nums){
if(x%2==1)res[l] = x, l++;
else res[r] = x, r--;
}
return res;
}
};剑指 Offer 22. 链表中倒数第k个节点输入一个链表,输出该链表中倒数第k个节点。为了符合大多数人的习惯,本题从1开始计数,即链表的尾节点是倒数第1个节点。例如,一个链表有 6 个节点,从头节点开始,它们的值依次是 1、2、3、4、5、6。这个链表的倒数第 3 个节点是值为 4 的节点。示例:给定一个链表: 1->2->3->4->5, 和 k = 2.返回链表 4->5./**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* ListNode *next;
* ListNode(int x) : val(x), next(NULL) {}
* };
*/
class Solution {
public:
ListNode* getKthFromEnd(ListNode* head, int k) {
ListNode* fast = head;
ListNode* slow = head;
while(fast!=NULL && k > 0){
fast = fast->next;
k--;
}
while(fast!=NULL && slow != NULL){
fast = fast->next;
slow = slow->next;
}
return slow;
}
};剑指 Offer 24. 反转链表定义一个函数,输入一个链表的头节点,反转该链表并输出反转后链表的头节点。示例:输入: 1->2->3->4->5->NULL输出: 5->4->3->2->1->NULL限制:0 <= 节点个数 <= 5000/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* ListNode *next;
* ListNode(int x) : val(x), next(NULL) {}
* };
*/
class Solution {
public:
ListNode* reverseList(ListNode* head) {
ListNode *pre = NULL;
ListNode *cur = head;
while(cur){
ListNode* next = cur->next;
cur->next = pre;
pre = cur;
cur = next;
}
return pre;
}
};剑指 Offer 25. 合并两个排序的链表输入两个递增排序的链表,合并这两个链表并使新链表中的节点仍然是递增排序的。示例1:输入:1->2->4, 1->3->4输出:1->1->2->3->4->4限制:0 <= 链表长度 <= 1000/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* ListNode *next;
* ListNode() : val(0), next(nullptr) {}
* ListNode(int x) : val(x), next(nullptr) {}
* ListNode(int x, ListNode *next) : val(x), next(next) {}
* };
*/
class Solution {
public:
ListNode* mergeTwoLists(ListNode* l1, ListNode* l2) {
ListNode ans;
ListNode* pre = &ans;
while(l1 != nullptr && l2 != nullptr){
if(l1->val <= l2->val){
pre->next = l1;
pre = pre->next;
l1 = l1->next;
}else{
pre->next = l2;
pre = pre->next;
l2 = l2->next;
}
}
if(l1 != nullptr){
pre->next = l1;
}
if(l2 != nullptr){
pre->next = l2;
}
return ans.next;
}
};剑指 Offer 26. 树的子结构输入两棵二叉树A和B,判断B是不是A的子结构。(约定空树不是任意一个树的子结构)B是A的子结构, 即 A中有出现和B相同的结构和节点值。例如:给定的树 A: 3
/ \4 5 / \ 1 2给定的树 B:4 / 1返回 true,因为 B 与 A 的一个子树拥有相同的结构和节点值。示例 1:输入:A = [1,2,3], B = [3,1]输出:false示例 2:输入:A = [3,4,5,1,2], B = [4,1]输出:true限制:0 <= 节点个数 <= 10000/**
* Definition for a binary tree node.
* struct TreeNode {
* int val;
* TreeNode *left;
* TreeNode *right;
* TreeNode(int x) : val(x), left(NULL), right(NULL) {}
* };
*/
class Solution {
public:
bool isSubStructure(TreeNode* A, TreeNode* B) {
if(A==NULL || B==NULL)return false;
return issame(A,B) || isSubStructure(A->left, B) || isSubStructure(A->right, B);
}
//判断树B是否与以A为根的子树相同
bool issame(TreeNode* A, TreeNode* B){
if(B==NULL)return true;
if(A==NULL)return false;
return (A->val)==(B->val) && issame(A->left,B->left) && issame(A->right, B->right);
}
};剑指 Offer 27. 二叉树的镜像请完成一个函数,输入一个二叉树,该函数输出它的镜像。例如输入: 4/ \ 2 7 / \ / \1 3 6 9镜像输出: 4/ \ 7 2 / \ / \9 6 3 1示例 1:输入:root = [4,2,7,1,3,6,9]输出:[4,7,2,9,6,3,1]限制:0 <= 节点个数 <= 1000/**
* Definition for a binary tree node.
* struct TreeNode {
* int val;
* TreeNode *left;
* TreeNode *right;
* TreeNode(int x) : val(x), left(NULL), right(NULL) {}
* };
*/
class Solution {
public:
TreeNode* mirrorTree(TreeNode* root) {
if (root == nullptr) return nullptr;
if (root->left == nullptr && root->right == nullptr) {
return root;
}
TreeNode *left = mirrorTree(root->left);
TreeNode *right = mirrorTree(root->right);
root->right = left;
root->left = right;
return root;
}
};剑指 Offer 28. 对称的二叉树请实现一个函数,用来判断一棵二叉树是不是对称的。如果一棵二叉树和它的镜像一样,那么它是对称的。例如,二叉树 [1,2,2,3,4,4,3] 是对称的。1/ \ 2 2 / \ / \3 4 4 3但是下面这个 [1,2,2,null,3,null,3] 则不是镜像对称的:1/ \ 2 2 \ \ 3 3示例 1:输入:root = [1,2,2,3,4,4,3]输出:true示例 2:输入:root = [1,2,2,null,3,null,3]输出:false限制:0 <= 节点个数 <= 1000/**
* Definition for a binary tree node.
* struct TreeNode {
* int val;
* TreeNode *left;
* TreeNode *right;
* TreeNode(int x) : val(x), left(NULL), right(NULL) {}
* };
*/
class Solution {
public:
bool check(TreeNode *p, TreeNode *q){//双指针
if(p==nullptr && q==nullptr)return true;
if(p==nullptr || q==nullptr)return false;
return p->val==q->val && check(p->left,q->right) && check(p->right,q->left);
}
bool isSymmetric(TreeNode* root) {
return check(root,root);
}
};剑指 Offer 29. 顺时针打印矩阵输入一个矩阵,按照从外向里以顺时针的顺序依次打印出每一个数字。示例 1:输入:matrix = [[1,2,3],[4,5,6],[7,8,9]]输出:[1,2,3,6,9,8,7,4,5]示例 2:输入:matrix = [[1,2,3,4],[5,6,7,8],[9,10,11,12]]输出:[1,2,3,4,8,12,11,10,9,5,6,7]限制:0 <= matrix.length <= 1000 <= matrix[i].length <= 100class Solution {
public:
vector<int> spiralOrder(vector<vector<int>>& matrix) {
if(matrix.size() == 0 || matrix[0].size() == 0)return {};
int n = matrix.size(), m = matrix[0].size();
int x = 0, y = 0, tot = n*m-1;
vector<int>res; res.push_back(matrix[0][0]); matrix[0][0] = -999;
while(tot){
while(y+1<m && matrix[x][y+1]!=-999){y++; res.push_back(matrix[x][y]); matrix[x][y] = -999; tot--;}
while(x+1<n && matrix[x+1][y]!=-999){ x++; res.push_back(matrix[x][y]); matrix[x][y] = -999; tot--;}
while(y-1>=0 && matrix[x][y-1]!=-999){y--; res.push_back(matrix[x][y]); matrix[x][y] = -999; tot--;}
while(x-1>=0 && matrix[x-1][y]!=-999){x--; res.push_back(matrix[x][y]); matrix[x][y] = -999; tot--;}
}
return res;
}
};剑指 Offer 30. 包含min函数的栈定义栈的数据结构,请在该类型中实现一个能够得到栈的最小元素的 min 函数在该栈中,调用 min、push 及 pop 的时间复杂度都是 O(1)。示例:MinStack minStack = new MinStack();minStack.push(-2);minStack.push(0);minStack.push(-3);minStack.min(); --> 返回 -3.minStack.pop();minStack.top(); --> 返回 0.minStack.min(); --> 返回 -2.提示:各函数的调用总次数不超过 20000 次class MinStack {
public:
/** initialize your data structure here. */
int mi = INT_MAX;
stack<int>st;
MinStack() {
}
//对于push,放入x和加入x前的最小值
void push(int x) {
st.push(mi); //加入前的最小值
if(x<mi)mi=x; //加入后的最小值
st.push(x);
}
void pop() {
st.pop(); //去掉当前值
mi = st.top(); //拿出加入前的最小值
st.pop(); //去掉当前值
}
int top() {
return st.top();
}
int min() {
return mi;
}
};
/**
* Your MinStack object will be instantiated and called as such:
* MinStack* obj = new MinStack();
* obj->push(x);
* obj->pop();
* int param_3 = obj->top();
* int param_4 = obj->min();
*/剑指 Offer 31. 栈的压入、弹出序列输入两个整数序列,第一个序列表示栈的压入顺序,请判断第二个序列是否为该栈的弹出顺序。假设压入栈的所有数字均不相等。例如,序列 {1,2,3,4,5} 是某栈的压栈序列,序列 {4,5,3,2,1} 是该压栈序列对应的一个弹出序列,但 {4,3,5,1,2} 就不可能是该压栈序列的弹出序列。示例 1:输入:pushed = [1,2,3,4,5], popped = [4,5,3,2,1]输出:true解释:我们可以按以下顺序执行:push(1), push(2), push(3), push(4), pop() -> 4,push(5), pop() -> 5, pop() -> 3, pop() -> 2, pop() -> 1示例 2:输入:pushed = [1,2,3,4,5], popped = [4,3,5,1,2]输出:false解释:1 不能在 2 之前弹出。提示:0 <= pushed.length == popped.length <= 10000 <= pushed[i], popped[i] < 1000pushed 是 popped 的排列。class Solution {
public:
bool validateStackSequences(vector<int>& pushed, vector<int>& popped) {
stack<int>st;
int cur = 0;
for(int i = 0; i < pushed.size(); i++){
st.push(pushed[i]);
while(!st.empty() && st.top()==popped[cur]){
st.pop(); cur++;
}
}
return st.empty();
}
};剑指 Offer 32 - I. 从上到下打印二叉树从上到下打印出二叉树的每个节点,同一层的节点按照从左到右的顺序打印。例如:给定二叉树: [3,9,20,null,null,15,7],3/ \ 9 20/ \15 7返回:[3,9,20,15,7]提示:节点总数 <= 1000/**
* Definition for a binary tree node.
* struct TreeNode {
* int val;
* TreeNode *left;
* TreeNode *right;
* TreeNode(int x) : val(x), left(NULL), right(NULL) {}
* };
*/
class Solution {
public:
vector<int> levelOrder(TreeNode* root) {
if(root==NULL)return {};
vector<int>res;
queue<TreeNode*>q;
q.push(root);
while(q.size()){
TreeNode* t = q.front(); q.pop();
if(t->left)q.push(t->left);
if(t->right)q.push(t->right);
res.push_back(t->val);
}
return res;
}
};剑指 Offer 32 - II. 从上到下打印二叉树 II从上到下按层打印二叉树,同一层的节点按从左到右的顺序打印,每一层打印到一行。例如:给定二叉树: [3,9,20,null,null,15,7],3/ \ 9 20/ \15 7返回其层次遍历结果:[ [3], [9,20], [15,7]]提示:节点总数 <= 1000//开个队列往里面丢就是了,简称bfs
/**
* Definition for a binary tree node.
* struct TreeNode {
* int val;
* TreeNode *left;
* TreeNode *right;
* TreeNode() : val(0), left(nullptr), right(nullptr) {}
* TreeNode(int x) : val(x), left(nullptr), right(nullptr) {}
* TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {}
* };
*/
class Solution {
public:
vector<vector<int>> levelOrder(TreeNode* root) {
vector<vector<int>>res;
if(!root)return res;
queue<TreeNode*>q;
q.push(root);
while(q.size()){
int len = q.size();
res.push_back(vector<int>());
for(int i = 1; i <= len; i++){
auto t = q.front(); q.pop();
res.back().push_back(t->val);
if(t->left)q.push(t->left);
if(t->right)q.push(t->right);
}
}
return res;
}
};剑指 Offer 32 - III. 从上到下打印二叉树 III请实现一个函数按照之字形顺序打印二叉树,即第一行按照从左到右的顺序打印,第二层按照从右到左的顺序打印,第三行再按照从左到右的顺序打印,其他行以此类推。例如:给定二叉树: [3,9,20,null,null,15,7],3/ \ 9 20/ \15 7返回其层次遍历结果:[ [3], [20,9], [15,7]]提示:节点总数 <= 1000/**
* Definition for a binary tree node.
* struct TreeNode {
* int val;
* TreeNode *left;
* TreeNode *right;
* TreeNode(int x) : val(x), left(NULL), right(NULL) {}
* };
*/
class Solution {
public:
vector<vector<int>> levelOrder(TreeNode* root) {
vector<vector<int>>ans;
if(root==NULL)return ans;
queue<TreeNode*>q;
q.push(root);
int isleft = 0;
while(q.size()){
int n = q.size();
vector<int>tmp;
for(int i = 0; i < n; i++){
TreeNode* t = q.front(); q.pop();
tmp.push_back(t->val);
if(t->left)q.push(t->left);
if(t->right)q.push(t->right);
}
isleft = !isleft;
if(isleft)ans.push_back(tmp);
else ans.push_back(vector<int>(tmp.rbegin(),tmp.rend()));
}
return ans;
}
};剑指 Offer 33. 二叉搜索树的后序遍历序列输入一个整数数组,判断该数组是不是某二叉搜索树的后序遍历结果。如果是则返回 true,否则返回 false。假设输入的数组的任意两个数字都互不相同。参考以下这颗二叉搜索树: 5
/ \2 6 / \ 1 3示例 1:输入: [1,6,3,2,5]输出: false示例 2:输入: [1,3,2,6,5]输出: true提示:数组长度 <= 1000class Solution {
public:
//二叉搜索树: 根节点大于左子树,小于右子树
bool verifyPostorder(vector<int>& postorder) {
return dfs(postorder, 0, postorder.size()-1);
}
bool dfs(vector<int>&p, int l, int r){
if(l > r)return true;
int rt = l; while(p[rt]<p[r])rt++; //左子树都小于根p[r]
int rt2 = rt; while(p[rt2]>p[r])rt2++; //右子树都大于根p[r]
return rt2==r && dfs(p,l,rt-1) && dfs(p, rt, r-1); //递归检查
}
};剑指 Offer 34. 二叉树中和为某一值的路径给你二叉树的根节点 root 和一个整数目标和 targetSum ,找出所有 从根节点到叶子节点 路径总和等于给定目标和的路径。叶子节点 是指没有子节点的节点。示例 1:输入:root = [5,4,8,11,null,13,4,7,2,null,null,5,1], targetSum = 22输出:[[5,4,11,2],[5,8,4,5]]示例 2:输入:root = [1,2,3], targetSum = 5输出:[]示例 3:输入:root = [1,2], targetSum = 0输出:[]提示:树中节点总数在范围 [0, 5000] 内-1000 <= Node.val <= 1000-1000 <= targetSum <= 1000/**
* Definition for a binary tree node.
* struct TreeNode {
* int val;
* TreeNode *left;
* TreeNode *right;
* TreeNode() : val(0), left(nullptr), right(nullptr) {}
* TreeNode(int x) : val(x), left(nullptr), right(nullptr) {}
* TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {}
* };
*/
class Solution {
public:
vector<vector<int>>res;
vector<int>path;
void dfs(TreeNode* p, int target){
if(p==nullptr)return ;
path.push_back(p->val);
target -= p->val;
if(p->left==nullptr && p->right==nullptr && target==0)res.push_back(path);
dfs(p->left, target);
dfs(p->right, target);
path.pop_back();
}
vector<vector<int>> pathSum(TreeNode* root, int target) {
dfs(root,target);
return res;
}
};剑指 Offer 35. 复杂链表的复制请实现 copyRandomList 函数,复制一个复杂链表。在复杂链表中,每个节点除了有一个 next 指针指向下一个节点,还有一个 random 指针指向链表中的任意节点或者 null。示例 1:输入:head = [[7,null],[13,0],[11,4],[10,2],[1,0]]输出:[[7,null],[13,0],[11,4],[10,2],[1,0]]示例 2:输入:head = [[1,1],[2,1]]输出:[[1,1],[2,1]]示例 3:输入:head = [[3,null],[3,0],[3,null]]输出:[[3,null],[3,0],[3,null]]示例 4:输入:head = []输出:[]解释:给定的链表为空(空指针),因此返回 null。提示:-10000 <= Node.val <= 10000Node.random 为空(null)或指向链表中的节点。节点数目不超过 1000 。/*
// Definition for a Node.
class Node {
public:
int val;
Node* next;
Node* random;
Node(int _val) {
val = _val;
next = NULL;
random = NULL;
}
};
*/
class Solution {
public:
Node* copyRandomList(Node* head) {
if(head==NULL)return NULL;
//1. 复制节点,构建拼接链表
Node* tmp;
for(Node* cur = head; cur != NULL; cur = tmp->next){
tmp = new Node(cur->val);
tmp->next = cur->next;
tmp->random = cur->random;
cur->next = tmp;
}
//2. 构建新节点的random
for(Node* cur = head; cur != NULL; cur = cur->next->next){
if(cur->random == NULL)continue;
cur->next->random = cur->random->next;
}
//3. 拆分链表
Node *res = head->next;
Node *pre = head, *cur = head->next;
for( ; cur->next != NULL; pre = pre->next, cur = cur->next){
pre->next = pre->next->next;
cur->next = cur->next->next;
}
pre->next = NULL;
return res;
}
};剑指 Offer 36. 二叉搜索树与双向链表输入一棵二叉搜索树,将该二叉搜索树转换成一个排序的循环双向链表。要求不能创建任何新的节点,只能调整树中节点指针的指向。为了让您更好地理解问题,以下面的二叉搜索树为例:我们希望将这个二叉搜索树转化为双向循环链表。链表中的每个节点都有一个前驱和后继指针。对于双向循环链表,第一个节点的前驱是最后一个节点,最后一个节点的后继是第一个节点。下图展示了上面的二叉搜索树转化成的链表。“head” 表示指向链表中有最小元素的节点。特别地,我们希望可以就地完成转换操作。当转化完成以后,树中节点的左指针需要指向前驱,树中节点的右指针需要指向后继。还需要返回链表中的第一个节点的指针。/*
// Definition for a Node.
class Node {
public:
int val;
Node* left;
Node* right;
Node() {}
Node(int _val) {
val = _val;
left = NULL;
right = NULL;
}
Node(int _val, Node* _left, Node* _right) {
val = _val;
left = _left;
right = _right;
}
};
*/
class Solution {
public:
Node* pre, *head;
Node* treeToDoublyList(Node* root) {
if(root==NULL)return NULL;
dfs(root);
head->left = pre; //头尾相连
pre->right = head;
return head;
}
//中序遍历树
void dfs(Node* cur){
if(cur==NULL)return ;
dfs(cur->left);
if(pre==NULL)head = cur; //第1个头结点, 存到head
else pre->right = cur; //上一个节点->right=当前
cur->left = pre; //当前->left = 上一个节点
pre = cur; //更新上一个节点
dfs(cur->right);
}
};剑指 Offer 37. 序列化二叉树请实现两个函数,分别用来序列化和反序列化二叉树。你需要设计一个算法来实现二叉树的序列化与反序列化。这里不限定你的序列 / 反序列化算法执行逻辑,你只需要保证一个二叉树可以被序列化为一个字符串并且将这个字符串反序列化为原始的树结构。提示:输入输出格式与 LeetCode 目前使用的方式一致,详情请参阅 LeetCode 序列化二叉树的格式。你并非必须采取这种方式,你也可以采用其他的方法解决这个问题。示例:输入:root = [1,2,3,null,null,4,5]输出:[1,2,3,null,null,4,5]/**
* Definition for a binary tree node.
* struct TreeNode {
* int val;
* TreeNode *left;
* TreeNode *right;
* TreeNode(int x) : val(x), left(NULL), right(NULL) {}
* };
*/
class Codec {
public:
// Encodes a tree to a single string.
void enc(TreeNode* u, string &res){
if(u==nullptr)res += "None,";
else{
res += to_string(u->val)+",";
enc(u->left,res);
enc(u->right,res);
}
}
string serialize(TreeNode* root) {
string res;
enc(root,res);
return res;
}
// Decodes your encoded data to tree.
TreeNode* dec(list<string>& li){
if(li.front()=="None"){
li.erase(li.begin());
return nullptr;
}
TreeNode* u = new TreeNode(stoi(li.front()));
li.erase(li.begin());
u->left = dec(li);
u->right = dec(li);
return u;
}
TreeNode* deserialize(string data) {
list<string>li;
string tmp="";
for(char ch : data){
if(ch==','){
li.push_back(tmp);
tmp.clear();
}else{
tmp.push_back(ch);
}
}
if(!tmp.empty())li.push_back(tmp);
return dec(li);
}
};
// Your Codec object will be instantiated and called as such:
// Codec ser, deser;
// TreeNode* ans = deser.deserialize(ser.serialize(root));剑指 Offer 38. 字符串的排列输入一个字符串,打印出该字符串中字符的所有排列。你可以以任意顺序返回这个字符串数组,但里面不能有重复元素。示例:输入:s = "abc"输出:["abc","acb","bac","bca","cab","cba"]限制:1 <= s 的长度 <= 8class Solution {
public:
vector<string> permutation(string s) {
sort(s.begin(), s.end());
vector<string>res;
do res.push_back(s);
while(next_permutation(s.begin(),s.end()));
return res;
}
};剑指 Offer 39. 数组中出现次数超过一半的数字数组中有一个数字出现的次数超过数组长度的一半,请找出这个数字。你可以假设数组是非空的,并且给定的数组总是存在多数元素。示例 1:输入: [1, 2, 3, 2, 2, 2, 5, 4, 2]输出: 2限制:1 <= 数组长度 <= 50000class Solution {
public:
int majorityElement(vector<int>& nums) {
int x = -1, cc = 0;
for(int num : nums){
if(num==x)cc++;
else cc--;
if(cc < 0){
x = num;
cc = 1;
}
}
return x;
}
};剑指 Offer 40. 最小的k个数输入整数数组 arr ,找出其中最小的 k 个数。例如,输入4、5、1、6、2、7、3、8这8个数字,则最小的4个数字是1、2、3、4。示例 1:输入:arr = [3,2,1], k = 2输出:[1,2] 或者 [2,1]示例 2:输入:arr = [0,1,2,1], k = 1输出:[0]限制:0 <= k <= arr.length <= 100000 <= arr[i] <= 10000class Solution {
public:
vector<int> getLeastNumbers(vector<int>& arr, int k) {
sort(arr.begin(),arr.end());
return vector<int>(arr.begin(),arr.begin()+k);
}
};剑指 Offer 41. 数据流中的中位数如何得到一个数据流中的中位数?如果从数据流中读出奇数个数值,那么中位数就是所有数值排序之后位于中间的数值。如果从数据流中读出偶数个数值,那么中位数就是所有数值排序之后中间两个数的平均值。例如,[2,3,4] 的中位数是 3[2,3] 的中位数是 (2 + 3) / 2 = 2.5设计一个支持以下两种操作的数据结构:void addNum(int num) - 从数据流中添加一个整数到数据结构中。double findMedian() - 返回目前所有元素的中位数。示例 1:输入:["MedianFinder","addNum","addNum","findMedian","addNum","findMedian"][[],[1],[2],[],[3],[]]输出:[null,null,null,1.50000,null,2.00000]示例 2:输入:["MedianFinder","addNum","findMedian","addNum","findMedian"][[],[2],[],[3],[]]输出:[null,null,2.00000,null,2.50000]限制:最多会对 addNum、findMedian 进行 50000 次调用。class MedianFinder {
public:
MedianFinder() {}
priority_queue<int, vector<int>, less<int> >qmi; //小于中位数,大根堆
priority_queue<int, vector<int>, greater<int> >qmx;//大于中位数,小根堆
void addNum(int num) {
if(qmi.empty() || num<=qmi.top()){
qmi.push(num);
if(qmi.size()>qmx.size()+1)qmx.push(qmi.top()), qmi.pop();
}else{
qmx.push(num);
if(qmx.size()>qmi.size())qmi.push(qmx.top()), qmx.pop();
}
}
double findMedian() {
if(qmi.size() > qmx.size())return qmi.top(); //qmi多一个
return (qmi.top()+qmx.top())/2.0;
}
};
/**
* Your MedianFinder object will be instantiated and called as such:
* MedianFinder* obj = new MedianFinder();
* obj->addNum(num);
* double param_2 = obj->findMedian();
*/剑指 Offer 42. 连续子数组的最大和输入一个整型数组,数组中的一个或连续多个整数组成一个子数组。求所有子数组的和的最大值。要求时间复杂度为O(n)。示例1:输入: nums = [-2,1,-3,4,-1,2,1,-5,4]输出: 6解释: 连续子数组 [4,-1,2,1] 的和最大,为 6。提示:1 <= arr.length <= 10^5-100 <= arr[i] <= 100class Solution {
public:
int maxSubArray(vector<int>& nums) {
//以i结尾的连续子数组的最大和,f[i] = max{a[i], f[i-1]+a[i]}, res取max{f[i]}即可
int f = 0, res = nums[0];
for(int x : nums){
f = max(f+x, x);
res = max(res, f);
}
return res;
}
};剑指 Offer 43. 1~n 整数中 1 出现的次数输入一个整数 n ,求1~n这n个整数的十进制表示中1出现的次数。例如,输入12,1~12这些整数中包含1 的数字有1、10、11和12,1一共出现了5次。示例 1:输入:n = 12输出:5示例 2:输入:n = 13输出:6限制:1 <= n < 2^31class Solution {
public:
int countDigitOne(int n) {
//数位dp
long long res = 0, dp = 0; //到当前位为止能取到的数的个数
for(long long i = 1; i <= n; i*=10){
int a = n%i, b = (n%(i*10))/i;
if(b>1)res += b*dp+i; //当前位>1
if(b==1)res += dp+a+1; //当前位=1
dp = dp*10+i;
}
return (int)res;
}
};剑指 Offer 44. 数字序列中某一位的数字数字以0123456789101112131415…的格式序列化到一个字符序列中。在这个序列中,第5位(从下标0开始计数)是5,第13位是1,第19位是4,等等。请写一个函数,求任意第n位对应的数字。示例 1:输入:n = 3输出:3示例 2:输入:n = 11输出:0限制:0 <= n < 2^31class Solution {
public:
int findNthDigit(int n) {
if(n<10)return n;
vector<vector<int>>mp{
{0,0}, {0,9}, {10,189}, {190,2889}, {2890,38889}, {38890,488889}, {488890,5888889},
{5888890,68888889}, {68888890,788888889}, {788888890,2147483647}
};
int i = 1;
for(i = 2; i <= 9; i++){
if(n >= mp[i][0] && n <= mp[i][1]){
n -= mp[i][0]; break;
}
}
int mi = pow(10,i-1);
return to_string(mi+(n/i))[n%i]-'0';
}
};剑指 Offer 45. 把数组排成最小的数输入一个非负整数数组,把数组里所有数字拼接起来排成一个数,打印能拼接出的所有数字中最小的一个。示例 1:输入: [10,2]输出: "102"示例 2:输入: [3,30,34,5,9]输出: "3033459"提示:0 < nums.length <= 100说明:输出结果可能非常大,所以你需要返回一个字符串而不是整数拼接起来的数字可能会有前导 0,最后结果不需要去掉前导 0class Solution {
public:
static bool cmp(string a, string b){ return a+b<b+a; }
string minNumber(vector<int>& nums) {
vector<string>str;
for(int x : nums)str.push_back(to_string(x));
sort(str.begin(),str.end(),cmp);
string res = "";
for(string x : str)res += x;
return res;
}
};剑指 Offer 46. 把数字翻译成字符串给定一个数字,我们按照如下规则把它翻译为字符串:0 翻译成 “a” ,1 翻译成 “b”,……,11 翻译成 “l”,……,25 翻译成 “z”。一个数字可能有多个翻译。请编程实现一个函数,用来计算一个数字有多少种不同的翻译方法。示例 1:输入: 12258输出: 5解释: 12258有5种不同的翻译,分别是"bccfi", "bwfi", "bczi", "mcfi"和"mzi"提示:0 <= num < 231class Solution {
public:
int translateNum(int num) {
if(num<=9)return 1;
int r = num%100;
if(r<=9 || r>=26)return translateNum(num/10);
else return translateNum(num/10)+translateNum(num/100); //[10-25]
}
};剑指 Offer 47. 礼物的最大价值在一个 m*n 的棋盘的每一格都放有一个礼物,每个礼物都有一定的价值(价值大于 0)。你可以从棋盘的左上角开始拿格子里的礼物,并每次向右或者向下移动一格、直到到达棋盘的右下角。给定一个棋盘及其上面的礼物的价值,请计算你最多能拿到多少价值的礼物?示例 1:输入: [ [1,3,1], [1,5,1], [4,2,1]]输出: 12解释: 路径 1→3→5→2→1 可以拿到最多价值的礼物提示:0 < grid.length <= 2000 < grid[0].length <= 200class Solution {
public:
int maxValue(vector<vector<int>>& grid) {
int n = grid.size(), m = grid[0].size();
vector<int>dp(m+1);
for(int i = 1; i <= n; i++){
for(int j = 1; j <= m; j++){
dp[j] = max(dp[j], dp[j-1])+grid[i-1][j-1];
}
}
return dp[m];
}
};剑指 Offer 48. 最长不含重复字符的子字符串请从字符串中找出一个最长的不包含重复字符的子字符串,计算该最长子字符串的长度。示例 1:输入: "abcabcbb"输出: 3 解释: 因为无重复字符的最长子串是 "abc",所以其长度为 3。示例 2:输入: "bbbbb"输出: 1解释: 因为无重复字符的最长子串是 "b",所以其长度为 1。示例 3:输入: "pwwkew"输出: 3解释: 因为无重复字符的最长子串是 "wke",所以其长度为 3。 请注意,你的答案必须是 子串 的长度,"pwke" 是一个子序列,不是子串。
提示:s.length <= 40000class Solution {
public:
int lengthOfLongestSubstring(string s) {
int l = 0, r = 0, res = 0;
unordered_set<char>se;
se.insert(s[0]);
while(r < s.size()){
res = max(res, (int)se.size());
r++;
if(se.count(s[r])){
while(s[l]!=s[r]){ se.erase(s[l]); l++; }
se.erase(s[l]); l++;
}
se.insert(s[r]);
}
return res;
}
};剑指 Offer 49. 丑数我们把只包含质因子 2、3 和 5 的数称作丑数(Ugly Number)。求按从小到大的顺序的第 n 个丑数。示例:输入: n = 10输出: 12解释: 1, 2, 3, 4, 5, 6, 8, 9, 10, 12 是前 10 个丑数。说明: 1 是丑数。n 不超过1690。class Solution {
public:
int nthUglyNumber(int n) {
vector<int>vc = {2,3,5};
unordered_set<long>se; se.insert(1L);
priority_queue<long, vector<long>, greater<long>>q; q.push(1L);
for(int i = 0; i < n-1; i++){
long t = q.top(); q.pop();
for(int x : vc){
long nx = x*t;
if(!se.count(nx)){se.insert(nx); q.push(nx); }
}
}
return (int)q.top();
}
};剑指 Offer 50. 第一个只出现一次的字符在字符串 s 中找出第一个只出现一次的字符。如果没有,返回一个单空格。 s 只包含小写字母。示例 1:输入:s = "abaccdeff"输出:'b'示例 2:输入:s = "" 输出:' '限制:0 <= s 的长度 <= 50000class Solution {
public:
char firstUniqChar(string s) {
vector<int>z(27);
for(char ch : s)z[ch-'a']++;
for(char ch : s){
if(z[ch-'a']==1)return ch;
}
return ' ';
}
};剑指 Offer 51. 数组中的逆序对在数组中的两个数字,如果前面一个数字大于后面的数字,则这两个数字组成一个逆序对。输入一个数组,求出这个数组中的逆序对的总数。示例 1:输入: [7,5,6,4]输出: 5限制:0 <= 数组长度 <= 50000class BIT {
private: int n; vector<int> tree;
public: BIT(int _n): n(_n), tree(_n + 1) {}
int query(int x){int res=0; while(x){res+=tree[x];x-=x&(-x);} return res;}
void add(int x, int v){ while(x<=n){tree[x]+=v; x+=x&(-x);} }
};
class Solution {
public:
int reversePairs(vector<int>& nums) {
int n = nums.size();
vector<int>tmp = nums;
//离散化
sort(tmp.begin(), tmp.end());
for(int& num : nums){
num = lower_bound(tmp.begin(),tmp.end(), num)-tmp.begin()+1;
}
//树状数组
BIT bit(n);
int ans = 0;
for (int i = n-1; i >= 0; i--) {
ans += bit.query(nums[i]-1);
bit.add(nums[i], 1);
}
return ans;
}
};剑指 Offer 52. 两个链表的第一个公共节点输入两个链表,找出它们的第一个公共节点。如下面的两个链表:在节点 c1 开始相交。示例 1:输入:intersectVal = 8, listA = [4,1,8,4,5], listB = [5,0,1,8,4,5], skipA = 2, skipB = 3输出:Reference of the node with value = 8输入解释:相交节点的值为 8 (注意,如果两个列表相交则不能为 0)。从各自的表头开始算起,链表 A 为 [4,1,8,4,5],链表 B 为 [5,0,1,8,4,5]。在 A 中,相交节点前有 2 个节点;在 B 中,相交节点前有 3 个节点。示例 2:输入:intersectVal = 2, listA = [0,9,1,2,4], listB = [3,2,4], skipA = 3, skipB = 1输出:Reference of the node with value = 2输入解释:相交节点的值为 2 (注意,如果两个列表相交则不能为 0)。从各自的表头开始算起,链表 A 为 [0,9,1,2,4],链表 B 为 [3,2,4]。在 A 中,相交节点前有 3 个节点;在 B 中,相交节点前有 1 个节点。示例 3:输入:intersectVal = 0, listA = [2,6,4], listB = [1,5], skipA = 3, skipB = 2输出:null输入解释:从各自的表头开始算起,链表 A 为 [2,6,4],链表 B 为 [1,5]。由于这两个链表不相交,所以 intersectVal 必须为 0,而 skipA 和 skipB 可以是任意值。解释:这两个链表不相交,因此返回 null。注意:如果两个链表没有交点,返回 null.在返回结果后,两个链表仍须保持原有的结构。可假定整个链表结构中没有循环。程序尽量满足 O(n) 时间复杂度,且仅用 O(1) 内存。/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* ListNode *next;
* ListNode(int x) : val(x), next(NULL) {}
* };
*/
class Solution {
public:
ListNode *getIntersectionNode(ListNode *headA, ListNode *headB) {
unordered_set<ListNode *> se;
ListNode* tmp = headA;
while(tmp != nullptr){
se.insert(tmp);
tmp = tmp->next;
}
tmp = headB;
while(tmp != nullptr){
if(se.count(tmp))return tmp;
tmp = tmp->next;
}
return nullptr;
}
};剑指 Offer 53 - I. 在排序数组中查找数字 I统计一个数字在排序数组中出现的次数。示例 1:输入: nums = [5,7,7,8,8,10], target = 8输出: 2示例 2:输入: nums = [5,7,7,8,8,10], target = 6输出: 0提示:0 <= nums.length <= 105-109 <= nums[i] <= 109nums 是一个非递减数组-109 <= target <= 109class Solution {
public:
int search(vector<int>& nums, int target) {
if(nums.size()==0)return 0;
int p = upper_bound(nums.begin(),nums.end(), target)-nums.begin()-1;
int cnt = 0;
while(p>=0 && nums[p]==target)cnt++, p--;
return cnt;
}
};剑指 Offer 53 - II. 0~n-1中缺失的数字一个长度为n-1的递增排序数组中的所有数字都是唯一的,并且每个数字都在范围0~n-1之内。在范围0~n-1内的n个数字中有且只有一个数字不在该数组中,请找出这个数字。示例 1:输入: [0,1,3]输出: 2示例 2:输入: [0,1,2,3,4,5,6,7,9]输出: 8限制:1 <= 数组长度 <= 10000class Solution {
public:
int missingNumber(vector<int>& nums) {
int l = 0, r = nums.size()-1;
while(l <= r){
int mid = l+r>>1;
if(nums[mid]==mid)l = mid+1;
else r = mid-1;
}
return l;
}
};剑指 Offer 54. 二叉搜索树的第k大节点给定一棵二叉搜索树,请找出其中第 k 大的节点的值。示例 1:输入: root = [3,1,4,null,2], k = 1 3 / \ 1 4 \ 2输出: 4示例 2:输入: root = [5,3,6,2,4,null,null,1], k = 3 5
/ \
3 6
/ \2 4 / 1输出: 4限制:1 ≤ k ≤ 二叉搜索树元素个数/**
* Definition for a binary tree node.
* struct TreeNode {
* int val;
* TreeNode *left;
* TreeNode *right;
* TreeNode(int x) : val(x), left(NULL), right(NULL) {}
* };
*/
class Solution {
public:
//二叉搜索树:根小于左子树, 大于右子树。 中序遍历输出第k个即可
vector<int>vc;
void dfs(TreeNode *p){
if(p==NULL)return ;
dfs(p->right);
vc.push_back(p->val);
dfs(p->left);
}
int kthLargest(TreeNode* root, int k) {
dfs(root);
return vc[k-1];
}
};剑指 Offer 55 - I. 二叉树的深度输入一棵二叉树的根节点,求该树的深度。从根节点到叶节点依次经过的节点(含根、叶节点)形成树的一条路径,最长路径的长度为树的深度。例如:给定二叉树 [3,9,20,null,null,15,7],3/ \ 9 20/ \15 7返回它的最大深度 3 。提示:节点总数 <= 10000/**
* Definition for a binary tree node.
* struct TreeNode {
* int val;
* TreeNode *left;
* TreeNode *right;
* TreeNode(int x) : val(x), left(NULL), right(NULL) {}
* };
*/
class Solution {
public:
int maxDepth(TreeNode* root) {
if(root==nullptr)return 0;
return max(maxDepth(root->left), maxDepth(root->right))+1;
}
};剑指 Offer 55 - II. 平衡二叉树输入一棵二叉树的根节点,判断该树是不是平衡二叉树。如果某二叉树中任意节点的左右子树的深度相差不超过1,那么它就是一棵平衡二叉树。示例 1:给定二叉树 [3,9,20,null,null,15,7]3/ \ 9 20/ \15 7返回 true 。示例 2:给定二叉树 [1,2,2,3,3,null,null,4,4] 1
/ \
2 2
/ \3 3 / \ 4 4返回 false 。限制:0 <= 树的结点个数 <= 10000/**
* Definition for a binary tree node.
* struct TreeNode {
* int val;
* TreeNode *left;
* TreeNode *right;
* TreeNode(int x) : val(x), left(NULL), right(NULL) {}
* };
*/
class Solution {
public:
int dfs(TreeNode *p){ //返回左右子树的最大深度
if(p==NULL)return 0;
int l = dfs(p->left), r = dfs(p->right);
if(l==-1 || r==-1 || abs(l-r)>1)return -1;
return max(l,r)+1;
}
bool isBalanced(TreeNode* root) {
return dfs(root) != -1;
}
};剑指 Offer 56 - I. 数组中数字出现的次数一个整型数组 nums 里除两个数字之外,其他数字都出现了两次。请写程序找出这两个只出现一次的数字。要求时间复杂度是O(n),空间复杂度是O(1)。示例 1:输入:nums = [4,1,4,6]输出:[1,6] 或 [6,1]示例 2:输入:nums = [1,2,10,4,1,4,3,3]输出:[2,10] 或 [10,2]限制:2 <= nums.length <= 10000class Solution {
public:
vector<int> singleNumbers(vector<int>& nums) {
int x = 0;
for(int y : nums)x ^= y;
x = x&(-x);
int a = 0, b = 0;
for(int y : nums){
if(x&y)a ^= y;
else b ^= y;
}
return vector<int>{a,b};
}
};剑指 Offer 56 - II. 数组中数字出现的次数 II在一个数组 nums 中除一个数字只出现一次之外,其他数字都出现了三次。请找出那个只出现一次的数字。示例 1:输入:nums = [3,4,3,3]输出:4示例 2:输入:nums = [9,1,7,9,7,9,7]输出:1限制:1 <= nums.length <= 100001 <= nums[i] < 2^31class Solution {
public:
int mp[32];
int singleNumber(vector<int>& nums) {
for(int x : nums){
for(int j = 30; j >= 0; j--){
if((x>>j)&1)mp[j]++;
}
}
int res = 0;
for(int i = 30; i >= 0 ; i--){
if(mp[i]%3!=0){
res |= (1<<i);
}
}
return res;
}
};剑指 Offer 57 - II. 和为s的连续正数序列输入一个递增排序的数组和一个数字s,在数组中查找两个数,使得它们的和正好是s。如果有多对数字的和等于s,则输出任意一对即可。示例 1:输入:nums = [2,7,11,15], target = 9输出:[2,7] 或者 [7,2]示例 2:输入:nums = [10,26,30,31,47,60], target = 40输出:[10,30] 或者 [30,10]限制:1 <= nums.length <= 10^51 <= nums[i] <= 10^6class Solution {
public:
vector<int> twoSum(vector<int>& nums, int target) {
unordered_set<int>se;
for(int x : nums){
if(se.count(target-x)){
return {x, target-x};
}
se.insert(x);
}
return {};
}
};剑指 Offer 57. 和为s的两个数字输入一个正整数 target ,输出所有和为 target 的连续正整数序列(至少含有两个数)。序列内的数字由小到大排列,不同序列按照首个数字从小到大排列。示例 1:输入:target = 9输出:[[2,3,4],[4,5]]示例 2:输入:target = 15输出:[[1,2,3,4,5],[4,5,6],[7,8]]限制:1 <= target <= 10^5class Solution {
public:
vector<vector<int>> findContinuousSequence(int target) {
vector<vector<int>>vec;
vector<int> res;
for (int l = 1, r = 2; l < r;){
int sum = (l + r) * (r - l + 1) / 2;
if (sum == target) {
res.clear();
for (int i = l; i <= r; ++i) {
res.push_back(i);
}
vec.push_back(res);
l++;
} else if (sum < target) {
r++;
} else {
l++;
}
}
return vec;
}
};剑指 Offer 58 - I. 翻转单词顺序输入一个英文句子,翻转句子中单词的顺序,但单词内字符的顺序不变。为简单起见,标点符号和普通字母一样处理。例如输入字符串"I am a student. ",则输出"student. a am I"。示例 1:输入: "the sky is blue"输出: "blue is sky the"示例 2:输入: " hello world! "输出: "world! hello"解释: 输入字符串可以在前面或者后面包含多余的空格,但是反转后的字符不能包括。示例 3:输入: "a good example"输出: "example good a"解释: 如果两个单词间有多余的空格,将反转后单词间的空格减少到只含一个。说明:无空格字符构成一个单词。输入字符串可以在前面或者后面包含多余的空格,但是反转后的字符不能包括。如果两个单词间有多余的空格,将反转后单词间的空格减少到只含一个。class Solution {
public:
string reverseWords(string s) {
stringstream ss(s);
string res = "", t;
while(ss>>t){
res = t+" "+res;
}
return res.substr(0,res.size()-1);
}
};剑指 Offer 58 - II. 左旋转字符串字符串的左旋转操作是把字符串前面的若干个字符转移到字符串的尾部。请定义一个函数实现字符串左旋转操作的功能。比如,输入字符串"abcdefg"和数字2,该函数将返回左旋转两位得到的结果"cdefgab"。示例 1:输入: s = "abcdefg", k = 2输出: "cdefgab"示例 2:输入: s = "lrloseumgh", k = 6输出: "umghlrlose"限制:1 <= k < s.length <= 10000class Solution {
public:
string reverseLeftWords(string s, int n) {
return s.substr(n,s.size()-n)+s.substr(0,n);
}
};剑指 Offer 59 - I. 滑动窗口的最大值给定一个数组 nums 和滑动窗口的大小 k,请找出所有滑动窗口里的最大值。示例:输入: nums = [1,3,-1,-3,5,3,6,7], 和 k = 3输出: [3,3,5,5,6,7] 解释: 滑动窗口的位置 最大值[1 3 -1] -3 5 3 6 7 3 1 [3 -1 -3] 5 3 6 7 3 1 3 [-1 -3 5] 3 6 7 5 1 3 -1 [-3 5 3] 6 7 5 1 3 -1 -3 [5 3 6] 7 6 1 3 -1 -3 5 [3 6 7] 7提示:你可以假设 k 总是有效的,在输入数组 不为空 的情况下,1 ≤ k ≤ nums.length。class Solution {
public:
vector<int> maxSlidingWindow(vector<int>& nums, int k) {
int n = nums.size();
priority_queue<pair<int,int> >q;
for(int i = 0; i < k; i++){
q.push(make_pair(nums[i],i));
}
vector<int>ans = {q.top().first};
for(int i = k; i < n; i++){
q.push(make_pair(nums[i],i));
while(q.top().second<=i-k)q.pop();
ans.push_back(q.top().first);
}
return ans;
}
};剑指 Offer 59 - II. 队列的最大值请定义一个队列并实现函数 max_value 得到队列里的最大值,要求函数max_value、push_back 和 pop_front 的均摊时间复杂度都是O(1)。若队列为空,pop_front 和 max_value 需要返回 -1示例 1:输入: ["MaxQueue","push_back","push_back","max_value","pop_front","max_value"][[],[1],[2],[],[],[]]输出: [null,null,null,2,1,2]示例 2:输入: ["MaxQueue","pop_front","max_value"][[],[],[]]输出: [null,-1,-1]限制:1 <= push_back,pop_front,max_value的总操作数 <= 100001 <= value <= 10^5class MaxQueue {
public:
MaxQueue() {
}
queue<int>q;
deque<int>d; //单调递减队列
int max_value() {
if(d.empty())return -1;
return d.front();
}
void push_back(int value) {
q.push(value);
while(!d.empty() && d.back()<value)d.pop_back();
d.push_back(value);
}
int pop_front() {
if(q.empty())return -1;
int x = q.front(); q.pop();
if(x==d.front())d.pop_front();
return x;
}
};
/**
* Your MaxQueue object will be instantiated and called as such:
* MaxQueue* obj = new MaxQueue();
* int param_1 = obj->max_value();
* obj->push_back(value);
* int param_3 = obj->pop_front();
*/剑指 Offer 60. n个骰子的点数把n个骰子扔在地上,所有骰子朝上一面的点数之和为s。输入n,打印出s的所有可能的值出现的概率。你需要用一个浮点数数组返回答案,其中第 i 个元素代表这 n 个骰子所能掷出的点数集合中第 i 小的那个的概率。示例 1:输入: 1输出: [0.16667,0.16667,0.16667,0.16667,0.16667,0.16667]示例 2:输入: 2输出: [0.02778,0.05556,0.08333,0.11111,0.13889,0.16667,0.13889,0.11111,0.08333,0.05556,0.02778]限制:1 <= n <= 11class Solution {
public:
vector<double> dicesProbability(int n) {
vector<double>f(6, 1.0/6);
for(int i = 2; i <= n; i++){ //前i个骰子和为j的概率
vector<double>tmp(5*i+1, 0); //和的范围为[n,6n], 共5n+1
for(int j = 0; j < f.size(); j++){
for(int k = 0; k < 6; k++){ //本次摇出k
tmp[j+k] += f[j]/6.0;
}
}
f = tmp;
}
return f;
}
};剑指 Offer 61. 扑克牌中的顺子从若干副扑克牌中随机抽 5 张牌,判断是不是一个顺子,即这5张牌是不是连续的。2~10为数字本身,A为1,J为11,Q为12,K为13,而大、小王为 0 ,可以看成任意数字。A 不能视为 14。示例 1:输入: [1,2,3,4,5]输出: True示例 2:输入: [0,0,1,2,5]输出: True限制:数组长度为 5 数组的数取值为 [0, 13] .class Solution {
public:
//排序后差的和<5(不算万能牌0,不接受对子)
bool isStraight(vector<int>& nums) {
sort(nums.begin(),nums.end());
int sum = 0;
for(int i = 1; i < 5; i++){
if(nums[i-1]==0)continue;
if(nums[i]==nums[i-1])return false;
sum += nums[i]-nums[i-1];
}
return sum < 5;
}
};剑指 Offer 62. 圆圈中最后剩下的数字0,1,···,n-1这n个数字排成一个圆圈,从数字0开始,每次从这个圆圈里删除第m个数字(删除后从下一个数字开始计数)。求出这个圆圈里剩下的最后一个数字。例如,0、1、2、3、4这5个数字组成一个圆圈,从数字0开始每次删除第3个数字,则删除的前4个数字依次是2、0、4、1,因此最后剩下的数字是3。示例 1:输入: n = 5, m = 3输出: 3示例 2:输入: n = 10, m = 17输出: 2限制:1 <= n <= 10^51 <= m <= 10^6class Solution {
public:
int lastRemaining(int n, int m) {
int ans = 0;
for(int i = 2; i <= n; i++){
ans = (ans+m)%i;
}
return ans;
}
};剑指 Offer 63. 股票的最大利润假设把某股票的价格按照时间先后顺序存储在数组中,请问买卖该股票一次可能获得的最大利润是多少?示例 1:输入: [7,1,5,3,6,4]输出: 5解释: 在第 2 天(股票价格 = 1)的时候买入,在第 5 天(股票价格 = 6)的时候卖出,最大利润 = 6-1 = 5 。 注意利润不能是 7-1 = 6, 因为卖出价格需要大于买入价格。示例 2:输入: [7,6,4,3,1]输出: 0解释: 在这种情况下, 没有交易完成, 所以最大利润为 0。限制:0 <= 数组长度 <= 10^5class Solution {
public:
int maxProfit(vector<int>& prices) {
int mi = 1e9, mx = 0;
for(int x : prices){
mx = max(mx, x-mi);
mi = min(mi, x);
}
return mx;
}
};剑指 Offer 64. 求1+2+…+n求 1+2+...+n ,要求不能使用乘除法、for、while、if、else、switch、case等关键字及条件判断语句(A?B:C)。示例 1:输入: n = 3输出: 6示例 2:输入: n = 9输出: 45限制:1 <= n <= 10000class Solution {
public:
int sumNums(int n) {
bool a[n][n+1];
return sizeof(a)>>1;
}
};剑指 Offer 65. 不用加减乘除做加法写一个函数,求两个整数之和,要求在函数体内不得使用 “+”、“-”、“*”、“/” 四则运算符号。示例:输入: a = 1, b = 1输出: 2提示:a, b 均可能是负数或 0结果不会溢出 32 位整数class Solution {
public:
//两个简易的位运算结论
int add(int a, int b) {
return a+b;
return (a&b)+(a|b);
return (a^b)+(a&b)+(a&b);
}
};剑指 Offer 66. 构建乘积数组给定一个数组 A[0,1,…,n-1],请构建一个数组 B[0,1,…,n-1],其中 B[i] 的值是数组 A 中除了下标 i 以外的元素的积, 即 B[i]=A[0]×A[1]×…×A[i-1]×A[i+1]×…×A[n-1]。不能使用除法。示例:输入: [1,2,3,4,5]输出: [120,60,40,30,24]提示:所有元素乘积之和不会溢出 32 位整数a.length <= 100000class Solution {
public:
vector<int> constructArr(vector<int>& a) {
int sum = 1, havz = 0;
for(int i = 0; i < a.size(); i++) if(a[i]!=0)sum *= a[i];else havz++;
for(int i = 0; i < a.size(); i++) {
if(havz){
if(a[i]==0 && havz==1)a[i] = sum;
else a[i] = 0;
}else{
a[i] = sum/a[i];
}
}
return a;
}
};剑指 Offer 67. 把字符串转换成整数写一个函数 StrToInt,实现把字符串转换成整数这个功能。不能使用 atoi 或者其他类似的库函数。首先,该函数会根据需要丢弃无用的开头空格字符,直到寻找到第一个非空格的字符为止。当我们寻找到的第一个非空字符为正或者负号时,则将该符号与之后面尽可能多的连续数字组合起来,作为该整数的正负号;假如第一个非空字符是数字,则直接将其与之后连续的数字字符组合起来,形成整数。该字符串除了有效的整数部分之后也可能会存在多余的字符,这些字符可以被忽略,它们对于函数不应该造成影响。注意:假如该字符串中的第一个非空格字符不是一个有效整数字符、字符串为空或字符串仅包含空白字符时,则你的函数不需要进行转换。在任何情况下,若函数不能进行有效的转换时,请返回 0。说明:假设我们的环境只能存储 32 位大小的有符号整数,那么其数值范围为 [−231, 231 − 1]。如果数值超过这个范围,请返回 INT_MAX (231 − 1) 或 INT_MIN (−231) 。示例 1:输入: "42"输出: 42示例 2:输入: " -42"输出: -42解释: 第一个非空白字符为 '-', 它是一个负号。 我们尽可能将负号与后面所有连续出现的数字组合起来,最后得到 -42 。示例 3:输入: "4193 with words"输出: 4193解释: 转换截止于数字 '3' ,因为它的下一个字符不为数字。示例 4:输入: "words and 987"输出: 0解释: 第一个非空字符是 'w', 但它不是数字或正、负号。 因此无法执行有效的转换。示例 5:输入: "-91283472332"输出: -2147483648解释: 数字 "-91283472332" 超过 32 位有符号整数范围。 因此返回 INT_MIN (−231) 。
class Solution {
public:
int strToInt(string str) {
string s = str;
long long f = 1, num = 0;
int i = 0;
while(s[i]==' ')i++;
if(s[i]=='-')f = -1, i++;
else if(s[i]=='+')i++;
for( ; s[i]>='0'&&s[i]<='9'; i++){
int x = s[i]-'0';
num = num*10+x*f;
if(num>=INT_MAX)return INT_MAX;
if(num<=INT_MIN)return INT_MIN;
}
return (int)num;
}
};剑指 Offer 68 - I. 二叉搜索树的最近公共祖先给定一个二叉搜索树, 找到该树中两个指定节点的最近公共祖先。百度百科中最近公共祖先的定义为:“对于有根树 T 的两个结点 p、q,最近公共祖先表示为一个结点 x,满足 x 是 p、q 的祖先且 x 的深度尽可能大(一个节点也可以是它自己的祖先)。”例如,给定如下二叉搜索树: root = [6,2,8,0,4,7,9,null,null,3,5]示例 1:输入: root = [6,2,8,0,4,7,9,null,null,3,5], p = 2, q = 8输出: 6 解释: 节点 2 和节点 8 的最近公共祖先是 6。示例 2:输入: root = [6,2,8,0,4,7,9,null,null,3,5], p = 2, q = 4输出: 2解释: 节点 2 和节点 4 的最近公共祖先是 2, 因为根据定义最近公共祖先节点可以为节点本身。说明:所有节点的值都是唯一的。p、q 为不同节点且均存在于给定的二叉搜索树中。/**
* Definition for a binary tree node.
* struct TreeNode {
* int val;
* TreeNode *left;
* TreeNode *right;
* TreeNode(int x) : val(x), left(NULL), right(NULL) {}
* };
*/
class Solution {
public:
//二叉搜索树, 有性质的(
TreeNode* lowestCommonAncestor(TreeNode* root, TreeNode* p, TreeNode* q) {
if(root==NULL)return NULL;
//都在 左树或右树
if(p->val<root->val && q->val<root->val)return lowestCommonAncestor(root->left, p,q);
if(p->val>root->val && q->val>root->val)return lowestCommonAncestor(root->right,p,q);
return root; //在两侧,root就是LCA
}
};剑指 Offer 68 - II. 二叉树的最近公共祖先给定一个二叉树, 找到该树中两个指定节点的最近公共祖先。百度百科中最近公共祖先的定义为:“对于有根树 T 的两个结点 p、q,最近公共祖先表示为一个结点 x,满足 x 是 p、q 的祖先且 x 的深度尽可能大(一个节点也可以是它自己的祖先)。”例如,给定如下二叉树: root = [3,5,1,6,2,0,8,null,null,7,4]示例 1:输入: root = [3,5,1,6,2,0,8,null,null,7,4], p = 5, q = 1输出: 3解释: 节点 5 和节点 1 的最近公共祖先是节点 3。示例 2:输入: root = [3,5,1,6,2,0,8,null,null,7,4], p = 5, q = 4输出: 5解释: 节点 5 和节点 4 的最近公共祖先是节点 5。因为根据定义最近公共祖先节点可以为节点本身。说明:所有节点的值都是唯一的。p、q 为不同节点且均存在于给定的二叉树中。/**
* Definition for a binary tree node.
* struct TreeNode {
* int val;
* TreeNode *left;
* TreeNode *right;
* TreeNode(int x) : val(x), left(NULL), right(NULL) {}
* };
*/
class Solution {
public:
TreeNode* ans;
int dfs(TreeNode *root, TreeNode *p, TreeNode *q){
if(root==NULL)return NULL;
int l = dfs(root->left, p, q);
int r = dfs(root->right, p, q);
if((l&&r) || ((root->val==p->val||root->val==q->val) && (l||r))){
ans = root;
}
return l || r || root->val==p->val || root->val==q->val;
}
TreeNode* lowestCommonAncestor(TreeNode* root, TreeNode* p, TreeNode* q) {
dfs(root, p, q);
return ans;
}
};
作业评讲|学习笔记
开发者学堂课程【Scala 核心编程-基础:作业评讲】学习笔记,与课程紧密联系,让用户快速学习知识。课程地址:https://developer.aliyun.com/learning/course/609/detail/8992作业评讲 内容介绍一、对象二、继承 一、对象1、编写一个 Conversions 对象,加入 inchesToCentimeters,gallonsToLiters 和 milesToKilometers 方法。解析:本题比较简单,查找单位之间的换算,写一个 objet Conversions 对象,再定义三个方法就可解答。答案:object Conversions{//英寸->厘米,英寸和厘米的单位转换是2.54。def inchesToSantimeters(value:Double)=value * 2.54//加仑->升,加仑和升单位转换是3.78541178。def gallonsToLiters(value:Double)=value* 3.78541178//英里->公里,英里和公里的单位转换是1.609344。def milesToKilometers(value:Double)=value * 1.609344}2、定义一个 Point 类和一个伴生对象,使得可以不用 new 而直接用 Point(3,4)来构造 Point 实例 apply 方法的使用。解析:构建对象有三种形式,第一种是 new,第二种是 apply,第三种是匿名子类。这里在 point 中加一个 apply 方法,apply 方法中可以接受两个指数,把 new隐藏在 apply 方法中。答案://Point 类class Point(x:Int = 0,y:Int=o) { //在类中定义了两个属性,使用主构造器的方式初始化属性。var x1=xvar y1=y}object Point {//因为在 apply 方法中调用了 new point ,所以就会在 apply 方法中返回一个 Point 。def apply(x:Int=0,y:Int =0)= new Point(x,y)测试:object Exercise02 {def main(args: Array[string]): unit = {//两个值分别代表横坐标和纵坐标。//触发 apply 方法。Val point=point(3,4).Println(point)}}运行结果:返回了一个 point 实例。3、编写一个 Scala 应用程序,使用 App 特质,以反序打印命令行参数,用空格隔开。举例来说,scala Reverse Hello World 应该打印 World Hello。解析:App 特质中包含了 main 方法。所以继承了 App 特质后就可以直接执行语句,当作程序的入口。答案:object Exercise03 { extends App{//app 中隐藏了一个主函数 def main(args:Array[String]):unit= 。因为 app 中已经定义好了一个主函数,并且参数就是 args,所以这里可以直接使用参数 args。Val args2=args.reversePrintln(args2)}//首先可以随便打印一个内容使 idea 能够被识别。object Exercise03 { extends App{Println(“hello~~”)}找到菜单栏的 Edit Configurations ,点击 program arguments 程序参数,在其中添加内容:hello world abc。运行程序,打印出一个地址:[L java.lang.String;@3498ed。重新修改代码为 Println(args2.mkstring(“”)),打印结果:abc world hello。4、编写一个扑克牌4种花色的枚举,让其 toString 方法分别返回,,,,并实现一个函数,检查某张牌的花色是否为红色。解析:Type 的用法讲解。//type 用于给类型起别名。Type myint =int 表示 myint 可以当作 int 使用,相当于给 int 取了一个别名,或者自定义了一个类型。Type myint =int//把888赋给了 num1,类型是 myint,myint 就是 int 。Val num1:myint=888Println(“num1=”+num1)}}运行代码输出:num1=888答案:// Enumeration 是枚举。object Suits extends Enumeration{//给 Value 类型取别名。type Suits = Value///使用 value 中的apply方法创建了一个实例,此 value 是从Enumeration 中继承来的。val Spade = Value("")val Club = Value("") val Heart = Value("���")val Diamond = value(“”)//这是 Suits 中的四个实例//重写 toString 方法override def toStringo:String={Suits.values.mkString(",") }}//打印结果。Println(suits)}Suits 是一个 object ,这样相当于输出了 suits 对应的隐藏在底层的module$。由于重写了 to string 方法,所以就会调用 Suits.values 方法,而 Suits.values 则会将所有的实例都取出来。运行结果:输出了四个花色,如果没有 Suits.values.mkString(",") 则表示不输出任何内容。//验证花色是否为红色。def isRed(card: Suits)= card == Heart || card == Diamond,在 isRed 中接受一个 Suits然后判断是否为���或者。def isRed(suits:suits):Boolean={if suit== Heart || suit==Diamond)true}else{False}}}这样的写法不够简便,所以直接写 def isRed(card: Suits)= card == Heart || card == Diamond 即可。测试:object 中的方法可以直接调用。Suits.isRed() 要求传入花色的一种,而花色已经在 suits 中了,所以调用时直接点击 suits 就会显示出现四个实例 club,Diamond,Heart,Spade。//传入一个红心,应该返回 true。Suits.isRed(suits.Heart)//传入一个黑桃,应该返回 false。Suits.isRed(suits.spade)//注意用 println 函数打印,否则不能输出结果。Println(Suits.isRed(suits.Heart))Println(Suits.isRed(suits.spade))运行结果:truefalse 二、继承1、扩展如下的 BankAccount 类,新类 CheckingAccount 对每次存款和取款都收取1美元的手续费。解析:题目已经给出了 BankAccount 类,要求根据这个类写一个新类 CheckingAccount。 相当于 BankAccount 是普通银行账号类,而 checkingAccount 是基于 BankAccount 进行扩展的账号类。这是题目给出的普通账号类://初始化余额class BankAccount (initialBalance:Double){private var balance = initialBalance//存款,直接在余额上加上存款。def deposit (amount :Double)= { balane += amount; balance}//取款,直接从余额减去取款额度并且返回余额。//此代码还可以加入对余额的判断。def withdraw(amount : Double)= {balance -= amount; balance}}答案:新类 CheckingAccount 对每次存款和取款都收取1美元的手续费。 BankAccount 类的代码都可以继承过来,但是存款和取款的方法需要重写。//继承 BankAccount,将 deposit,withdraw进行 overwrite。比如存款存了9元,那么在余额中体现为8元,取款取了10元,但实际上余额会减掉11元。class BankAccount (initialBalance: Double){private var balance = initialBalancedef deposit(amount:Double)={balance+=amount;balance}def withdraw(amount:Double)={balance-=amount;balance}}//利用主构造器调用了 BankAccount 的主构造器。class CheckingAccount(initialBalance:Double) extends BankAccount (initialBalance){//重写方法时不需要完全重写,用 super 继承即可。override def deposit(amount:Double)=super.deposit (amount-1)override def withdraw(amount:Double)=super.withdraw (amount+1)}//为了测试结果是否正确,在主类下加入一个查询余额的方法。Def query():unit={Ptintln(“当前余额为”+this.balance)测试:object BankAccountDemo {def main(args:Array[String]):unit={//存入100元。运行结果:当前余额为100.0val checkingAccount = new checkingAccount(100)checkingAccount.query()//取出10元并查询余额。由于手续费是1美元,所以取出10元后余额应该是89元。运行结果:当前余额为89.0checkingAccount.withdraw(10)checkingAccount.query()//存入10元并查询余额。由于手续费是1美元,所以存入10元后余额应该是98元。运行结果:当前余额为98.0checkingAccount.deposit(10)checkingAccount.query()}}2、扩展前一个练习的 BankAccount 类,新类 SavingsAccount 每个月都有利息产生(earnMonthlyInterest方法被调用),并且有每月三次免手续费的存款或取款。在 earnMonthlyInterest 方法中重置交易计数。答案://普通账号class BankAccount(initialBalance: Double){private var balance = initialBalancedef deposit(amount:Double)={balance+=amount;balance}def withdraw(amount:Double)={balance=amount;balance}}Class savingsAccount (initialBalance:Double) extendsBankAccount(initialBalance){//定义了一个免手续费的次数private var num:Int=_//每月初,系统调用该方法计算利息,同时将 num=3。def earmonthlyinterest()={num=3 //一般利息计算应该是余额*利率,这里假定利息是一美元。super.deposit(1)}//取款时如果 num<0就收手续费,否则不收手续费。override def deposit(amount:Double):Double ={num-=1if(num<0)super.deposit(amount-1)elsesuper.deposit(amount)}//取款逻辑和存款一样,如果 num<0存款就会收手续费,否则不收。override def withdraw(amount:Double):Double ={num-=1if(num<0) super. withdraw(amount-1) else super. Withdraw (amount)}}解决该问题需要有思维的转换,充分利用父类对子类进行扩展。测试://查看当前账户余额应该是100元。val savingAccount = new savingAccount(100)savingAccount.query()//月初计算利息,账户余额应为101元。savingAccount.earnMonthlyinterest()//取款三次,每次取出10元。savingAccount.withdraw(10)savingAccount.withdraw(10)savingAccount.withdraw(10)//查询账户余额,此时账户余额应是71元。savingAccount.query()运行结果:当前余额为100.0当前余额为71.0//再次取出10元,此时还应该扣除手续费,所以账户余额应该是71-10-1=60。savingAccount.withdraw(10)savingAccount.query()}}运行结果:当前余额为60.03、设计一个 point 类,其 X 和 Y 坐标可以通过构造器提供。提供一个子类 LabeledPoint,其构造器接受一个标签值和 x,y 坐标,比如:newLabeledPoint(“Black Thursday” ,1929,230.07)解析:本题主要考察构造器的使用。答案:class Point(x:Double,y:Double) //使用了扩展,可以接收两个 double 和一个字符串。//表示在构建的同时,把 x 和 y 交给父类进行构建。class LabeledPoint(tag:string x:Double,y:Double) extends Point(x,y)4、定义一个抽象类 shape,一个抽象方法 centerPoint(求出中心点),以及该抽象类的子类 Rectangle 和 Circle。为子类提供合适的构造器,并重写 enterPoint 方法。解析:本题主要考察抽象类的使用。由于 Rectangle 和 Circle 没有给出具体的要求,所以从语法层面给出方法即可。答案://抽象类abstract class Shape{//抽象方法def centerPoint()}//定义了一个 Rectangle 类,Rectangle 类中有它的坐标。//这里只给出了方法,内容是空着的。class Rectangle(startX:Int,startY:Int,endX:Int,endY:Int) extends Shape{def centerPoint(){}}// Circle 以及半径,内容也是空的。class Circle(x:Int,y:Int,radius:Double) extends shape{def centerPoint(){}}5、提供一个 Square 类,扩展自 java.awt.Rectangle 并且是三个构造器(考察点:根据需求,写出不同的构造器):一个以给定的端点和宽度构造正方形,一个以(0,0)为端点和给定的宽度构造正方形,一个以(0,0)为端点,0为宽度构造正方形。import java.awt.{Point, Rectangle}class Square(point:Point,width:Int) extends Rectangle(point.x,point.y,width,width){def this(){this(new Point(0,0),0)}def this(width:Int){this(new Point(0,0),width)}}
剑指offer(41-50题)详解
41 和为S的连续正数序列题目描述小明很喜欢数学,有一天他在做数学作业时,要求计算出9~16的和,他马上就写出了正确答案是100。但是他并不满足于此,他在想究竟有多少种连续的正数序列的和为100(至少包括两个数)。没多久,他就得到另一组连续正数和为100的序列:18,19,20,21,22。现在把问题交给你,你能不能也很快的找出所有和为S的连续正数序列? Good Luck!输出描述:输出所有和为S的连续正数序列。序列内按照从小至大的顺序,序列间按照开始数字从小到大的顺序思路:这题几个要求:大于等于两个序列,正整数,连续。至于对list长度返回的要求直接重写个排序接口即可。所以这题笔者大概想到2个思路:方法一:动态穷举试探(较为简单)这个穷举并不是所谓的暴力遍历,不从每个数都进行循环试起。如果从每个数都试起那么整个进行的次数太多。我们用一个类似拉链的动态滑动试探。我们都知道:连续相加和相等,如果数很小那么就很长,如果数很大就很短。所以我们用left和right标记左右,用一个value记录这个区间的和。如果value大了那就左面往右试探一位,如果value小了那么right就往右加变长一位。重复上述操作。一直到中间两位长度的截至,记录所有满足题意的条件。法一的ac的代码为:import java.util.*;
public class Solution {
public static ArrayList<ArrayList<Integer> > FindContinuousSequence(int sum)
{
ArrayList<ArrayList<Integer> > list=new ArrayList<ArrayList<Integer>>();
int left=1,right=2,value=3;
while(left<=sum/2)
{
if(value==sum)
{
ArrayList<Integer>list1=new ArrayList<Integer>();
for(int i=left;i<=right;i++)
{
list1.add(i);
}
list.add(list1);
value-=left;left++;
}
else if(value>sum)
{
value-=left;left++;
}
else {
right++;value+=right;
}
}
list.sort(compare);
return list;
}
static Comparator<ArrayList<Integer>>compare=new Comparator<ArrayList<Integer>>() {
public int compare(ArrayList<Integer> o1, ArrayList<Integer> o2) {
// TODO Auto-generated method stub
return o2.size()-o1.size();
}
};
}
方法二:数学转化既然是连续的数字之和。那么请看下面的分析:/**
* 分析 a + (a+1) --- + b =(a+b) * n/2 =sum 其中n是a-b的个数
* (a+b) * (b-a+1)=sum * 2
* 令a+b=m a=a b=m-a
* m*(m-2a+1)=sum * 2
* 利用求sum * 2 的因数遍历m 求出a,b若a大于0、且不为小数、a!=b即为一个序列
*/
大致分析是这样的:首先连续相加是可以转化成乘法的(有时间可以了解唯一分解定理)然后比如 sum * 2 =a * b 可能其中a是平均数,也可能b是平均数。但是你需要考虑所有和都大于0. 且为整数。比如 求和为15.右[1, 2, 3, 4, 5], [4, 5, 6], [7, 8]三组数据。而求和分别为((1+5)/2)*5=15=>6*5=30,((4+6)/2)*3=15=>10*3=30,((7+8)/2)*2=15=>15*2=30.其实你就是需要将所有a*b=30所有情况列举,然后看看能否复原成功。复原公式在上面的。并且其中a,b需要换位考虑能否成功的。当然求因数的一些技巧不需要详细讲述了,只需要遍历根号(n)+1即可。具体代码为:import java.util.*;
public class Solution {
public static ArrayList<ArrayList<Integer> > FindContinuousSequence(int sum) {
int value=sum*2;
ArrayList<ArrayList<Integer> > list=new ArrayList<ArrayList<Integer>>();
int a=0,b=0;
for(int i=1;i*i<value+1;i++)
{
if(value%i==0)
{
a=(i+1-value/i)/2;b=i-a;
if(a>0&&i-a*2+1==value/i&&b>a)
{
ArrayList<Integer>list2=new ArrayList<Integer>();
for(int j=a;j<=b;j++)
{
list2.add(j);
}
list.add(list2);
}
a=(value/i+1-i)/2;b=value/i-a;
if(a>0&&value/i-a*2+1==i&&b>a)
{
ArrayList<Integer>list2=new ArrayList<Integer>();
for(int j=a;j<=b;j++)
{
list2.add(j);
}
list.add(list2);
}
}
}
list.sort(compare);
return list;
}
static Comparator<ArrayList<Integer>>compare=new Comparator<ArrayList<Integer>>() {
public int compare(ArrayList<Integer> o1, ArrayList<Integer> o2) {
// TODO Auto-generated method stub
return o2.size()-o1.size();
}
};
}
42 和为S的两个数字题目描述输入一个递增排序的数组和一个数字S,在数组中查找两个数,使得他们的和正好是S,如果有多对数字的和等于S,输出两个数的乘积最小的。输出描述对应每个测试案例,输出两个数,小的先输出。思路:这题和上一题的方案一思想差不多。用个左右标记位置。从两边向中间逼近。两个数相加如果大于目标,那么最右侧位置right往左移动一下,如果小于目标,左侧的left向右移动增加,当出现第一个满足条件的即可停止。因为要求乘积最小嘛。实现代码为:import java.util.ArrayList;
public class Solution {
public ArrayList<Integer> FindNumbersWithSum(int [] array,int sum) {
int small=0,big=0;//小大两个数
int l=0, r=array.length-1;
while(l<r)
{
if(array[l]+array[r]==sum)
{
small=array[l];big=array[r];
break;
}
else if (array[l]+array[r]<sum) {
l++;
}
else if (array[l]+array[r]>sum) {
r--;
}
}
ArrayList<Integer>list=new ArrayList<Integer>();
if(small==big)return list;
list.add(small);
list.add(big);
return list;
}
}
43 左旋转字符串题目描述汇编语言中有一种移位指令叫做循环左移(ROL),现在有个简单的任务,就是用字符串模拟这个指令的运算结果。对于一个给定的字符序列S,请你把其循环左移K位后的序列输出。例如,字符序列S=”abcXYZdef”,要求输出循环左移3位后的结果,即“XYZdefabc”。是不是很简单?OK,搞定它!思路:可以看似循环链表类似的问题。但是直接求就好了。因为只旋转一次,相当于把前面一部分挪到后面就可以了。要考虑旋转的长度是否大于整个字符串的长度。实现代码:public class Solution {
public String LeftRotateString(String str,int n) {
if(str.equals(""))return str;
n=n%str.length();
return str.substring(n)+str.substring(0,n);
}
}
44 翻转单词顺序列题目描述牛客最近来了一个新员工Fish,每天早晨总是会拿着一本英文杂志,写些句子在本子上。同事Cat对Fish写的内容颇感兴趣,有一天他向Fish借来翻看,但却读不懂它的意思。例如,“student. a am I”。后来才意识到,这家伙原来把句子单词的顺序翻转了,正确的句子应该是“I am a student.”。Cat对一一的翻转这些单词顺序可不在行,你能帮助他么?思路:经典字符串处理题。字符串问题就是有很多坑点、边界问题、特殊情况等等。要考虑空串、一个单词等等情况。当然,具体处理就是字符一个个读取,遇到空格的时候特殊处理一下。处理方式可能不唯一。也可以考虑字符串分割等等。实现代码为:public class Solution {
public static String ReverseSentence(String str) {
String value="";
StringBuilder sBuilder=new StringBuilder();
for(int i=0;i<str.length();i++)
{
if(str.charAt(i)!=' ')
{
sBuilder.append(str.charAt(i));
}
else {
value=sBuilder.toString()+" "+value;
sBuilder=new StringBuilder();
}
}
if(sBuilder.length()>0)
{
value=sBuilder.toString()+" " +value;
value=value.substring(0,value.length()-1);
}
return value;
}
}
45 扑克牌顺序题目描述LL今天心情特别好,因为他去买了一副扑克牌,发现里面居然有2个大王,2个小王(一副牌原本是54张_)…他随机从中抽出了5张牌,想测测自己的手气,看看能不能抽到顺子,如果抽到的话,他决定去买体育彩票,嘿嘿!!“红心A,黑桃3,小王,大王,方片5”,“Oh My God!”不是顺子…LL不高兴了,他想了想,决定大\小 王可以看成任何数字,并且A看作1,J为11,Q为12,K为13。上面的5张牌就可以变成“1,2,3,4,5”(大小王分别看作2和4),“So Lucky!”。LL决定去买体育彩票啦。 现在,要求你使用这幅牌模拟上面的过程,然后告诉我们LL的运气如何, 如果牌能组成顺子就输出true,否则就输出false。为了方便起见,你可以认为大小王是0。思路:首先要理清楚题意。就是给你五张牌让你判断是不是顺子,然而这五张可能有若干张鬼(0)可以随意的陪。这也有点小贪心的思想的。我们的处理思路事这样的:先给五张牌进行排序。然后前面用个数值记录有几个鬼。然后从非鬼开始判断是否连续,如果和前一个不连续如果有鬼用鬼前一个数往后补一张一直到连续,如果没鬼了还不连续那就说明那是真的完成不了。如果最后未能遍历所有牌。或者有相同牌(非鬼)那么一定不是顺子。实现代码为:import java.util.Arrays;
public class Solution {
public static boolean isContinuous(int [] numbers) {
if(numbers.length<5)return false;
Arrays.sort(numbers);
System.out.println(Arrays.toString(numbers));
int wang=0;
int pre=0;
for(int i=0;i<5;i++)
{
if(numbers[i]==0)wang++;
else {
if(pre==0) {pre=numbers[i];}//第一个数
else if(numbers[i]==pre) {return false;}//相同牌
else if(numbers[i]==pre+1) {pre++;}
else {
while(numbers[i]>pre+1)
{
if(wang>0){wang--;pre++;}
else if(wang<=0) {return false;}
}
pre++;
}
}
}
return true;
}
}
46 孩子们的游戏题目描述每年六一儿童节,牛客都会准备一些小礼物去看望孤儿院的小朋友,今年亦是如此。HF作为牛客的资深元老,自然也准备了一些小游戏。其中,有个游戏是这样的:首先,让小朋友们围成一个大圈。然后,他随机指定一个数m,让编号为0的小朋友开始报数。每次喊到m-1的那个小朋友要出列唱首歌,然后可以在礼品箱中任意的挑选礼物,并且不再回到圈中,从他的下一个小朋友开始,继续0…m-1报数…这样下去…直到剩下最后一个小朋友,可以不用表演,并且拿到牛客名贵的“名侦探柯南”典藏版(名额有限哦!!_)。请你试着想下,哪个小朋友会得到这份礼品呢?(注:小朋友的编号是从0到n-1)如果没有小朋友,请返回-1思路:经典约瑟夫环问题。可用链表进行模拟,也可用List借助数学方法一个个移出效率会高一些。笔者这里用了个链表过了。后面用数学模拟链表的方法效率更高,大过年的先不写了,后面会补的!实现代码为:public class Solution {
static class node
{
int val;
public node(int value) {
this.val=value;
}
node next;
}
public static int LastRemaining_Solution(int n, int m) {
if(n<=0||m<2)return -1;
node head=new node(0);
node team=head;
for(int i=1;i<n;i++)
{
team.next=new node(i);
team=team.next;
}
team.next=head;
int index=0;
while (head.next!=head) {
if(index==m-2)
{
head.next=head.next.next;
index=0;
}
else {
index++;
}
head=head.next;
}
return head.val;
}
}
至于笔者最初写的效率并不高。至于数学模拟,笔者最初记得还是分类讨论的:是否越界之类。而可以直接加上一段size长度求余相当于接上那么一段!47 求1+2+。。。+n题目描述求1+2+3+…+n,要求不能使用乘除法、for、while、if、else、switch、case等关键字及条件判断语句(A?B:C)。思路:这个真的没想到,看了下评论区的方法真的是恍然大悟。虽然我们不能用逻辑语句实现我们的逻辑,但是我们可以用&&之类的运算符帮助我们实现逻辑。a&&b这个逻辑当a是false的时候后面就不会执行了。这就是逻辑与的短路特性。我们调用递归或者其他方法借助逻辑与的短路特性就可以实现停止了。实现代码为:public class Solution {
public int Sum_Solution(int n) {
boolean jud= (n>0)&&((n+= Sum_Solution(n-1))>0);
return n;
}
}
参考评论区:对于评论区还有一些其他方法也挺精妙,但是可能自己很难有那么高认识,先了解吧。48 不用加减乘除做加法★题目描述写一个函数,求两个整数之和,要求在函数体内不得使用+、-、*、/四则运算符号。思路:这个我以前就知道跟二进制有关,知道有这个东西不过没仔细研究。我觉得这题大家可以自己独立研究一下。能够加深对二进制和位运算的印象和认识。当然,做这题前,建议稍微了解下四种常用的位运算与或非 异或。对于二进制的运算:0+0=0,0+1=1,1+1=0(进位)对于加法的一个二进制运算。如果不进位那么就是非常容易的。这时候相同位都为0则为0,0和1则为1.满足这种运算的异或(不相同取1,相同取0)和或(有一个1则为1)都能满足.但事实那像这么容易?肯定有进位的运算啊!对于进位的两数相加,这种核心思想为:用两个数,一个正常m相加(不考虑进位的)。用异或a^b就是满足这种要求,先不考虑进位(如果没进位那么就是最终结果)。另一个专门考虑进位的n。两个1需要进位。所以我们用a&b与记录需要进位的。但是还有个问题,进位的要往上面进位,所以就变成这个需要进位的数左移一位。然后就变成m+n重新开始上面直到不需要进位的(会停止的,不可能一直进位)。实现代码为:public class Solution {
public int Add(int num1,int num2) {
/*
* 5+3 5^3 5&3
* 0101 0110 0001
* 0011
*/
int a=num1^num2;
int b=num1&num2;
b=b<<1;
if(b==0)return a;
else {
return Add(a, b);
}
}
}
49 把字符串转换成整数题目描述将一个字符串转换成一个整数,要求不能使用字符串转换整数的库函数。 数值为0或者字符串不是一个合法的数值则返回0输入描述:输入一个字符串,包括数字字母符号,可以为空输出描述:如果是合法的数值表达则返回该数字,否则返回0示例1输入+21474836471a33输出21474836470思路:这题是个麻烦题,主要是有几个越界的字符串需要处理。你又不能使用api,建议你使用long型转换成功判断是否越界可能更方便些。但是笔者挑战了下自己用int型硬刚过的。你要考虑正负(整数最小和负数最大),需要考虑是否为非法字符串。数值是否操作越界(尤其注重int类型最大最小附近)。我的处理思路这样的:记录正负,每次都进行加操作,记录操作前数值,如果操作后符号变化那就越界直接返回0。否则进行到最后返回。实现代码为:public class Solution {
public static int StrToInt(String str) {
if(str.equals(""))return 0;
int check = 0;//判断首位是不是符号
int zhengfu = 1;//正负号用1和-1表示
if (str.charAt(0) == '+' || str.charAt(0) == '-')// 符号位判断下
{
check=1;
zhengfu=str.charAt(0)=='+'?1:-1;
}
int index = check == 0 ? 0 : 1;// 初始位置
int value = 0;
int team=zhengfu;
for (; index < str.length(); index++) {
if (str.charAt(index) >= '0' && str.charAt(index) <= '9') {
value = value * 10 + (str.charAt(index) - '0')*zhengfu;
if(value/team<0)return 0;
team=value;
//System.out.println(value+" "+zhengfu);
} else {
return 0;
}
}
return value;
}
}
50 数组中重复的数字题目描述在一个长度为n的数组里的所有数字都在0到n-1的范围内。 数组中某些数字是重复的,但不知道有几个数字是重复的。也不知道每个数字重复几次。请找出数组中任意一个重复的数字。 例如,如果输入长度为7的数组{2,3,1,0,2,5,3},那么对应的输出是第一个重复的数字2。思路:因为数组中的数范围已经确定了,也不大,不需要用hashmap。直接用一个数组即可。遍历一个加一个,这个位置的数为2返回停止。实现代码为:public class Solution {
// Parameters:
// numbers: an array of integers
// length: the length of array numbers
// duplication: (Output) the duplicated number in the array number,length of duplication array is 1,so using duplication[0] = ? in implementation;
// Here duplication like pointor in C/C++, duplication[0] equal *duplication in C/C++
// 这里要特别注意~返回任意重复的一个,赋值duplication[0]
// Return value: true if the input is valid, and there are some duplications in the array number
// otherwise false
public boolean duplicate(int numbers[],int length,int [] duplication) {
int count[]=new int[length+1];
for(int i=0;i<length;i++)
{
if(++count[numbers[i]]>1)
{
duplication[0]=numbers[i];
return true;
}
}
return false;
}
}
最后欢迎大家关注公众号:bigsai长期交流、分享!
洗牌算法和它的应用场景 7月20日 【今日算法】
有一次参加面试,面试官问我:“会玩牌吧?”
内心:“咋滴,这是要玩德州扑克(或者炸金花),赢了他就能通过面试么?”
结果……
没想到面试官的下一句话:“给我讲讲洗牌算法以及它的应用场景吧!”
##背景
本文产生背景是看到了 一枝花算不算浪漫 同学的这篇 Eureka注册中心集群如何实现客户端请求负载及故障转移?文章想到的。其实本人觉得那篇文中提到的负责均衡的重点就是本文要说的洗牌算法。
好了,回到题目上来。
这确实也是一道面试题,我曾经多次面试中都有遇到这个题目或者这个题目的变种。
你不妨花 1 秒,想想?
##什么是洗牌算法
从名字上来看,就是给你一副牌让你洗呗,用怎样的方法才能洗得均匀呢?请大佬表演一下。
其实洗牌算法就是一种随机算法,你在斗地主的时候,随机把牌的顺序打乱就行。一个足够好的洗牌算法最终结果应该是可以让牌的顺序足够随机。好像有点绕~ 这么来说吧,一副牌大家斗地主的话用 54 张(不考虑你们打配配牌的情形哈),那么这 54 张牌的顺序的话,按照排列组合算法,应该是有 54! 这么多种,然后你的洗牌算法就是从这 **54! **种排列中,随机选 1 种。
无聊的石头算了一下,54 的阶乘有多大呢?大概就是这么大一长串数字,2308436973392413804720927448683027581083278564571807941132288000000000000L,准确答案看下图:
我们还是以 4 张牌作为例子吧。
4 张牌,JQKA,所有的排列有 4!=432*1=24 种,分别如下:
('J', 'Q', 'K', 'A')
('J', 'Q', 'A', 'K')
('J', 'K', 'Q', 'A')
('J', 'K', 'A', 'Q')
('J', 'A', 'Q', 'K')
('J', 'A', 'K', 'Q')
('Q', 'J', 'K', 'A')
('Q', 'J', 'A', 'K')
('Q', 'K', 'J', 'A')
('Q', 'K', 'A', 'J')
('Q', 'A', 'J', 'K')
('Q', 'A', 'K', 'J')
('K', 'J', 'Q', 'A')
('K', 'J', 'A', 'Q')
('K', 'Q', 'J', 'A')
('K', 'Q', 'A', 'J')
('K', 'A', 'J', 'Q')
('K', 'A', 'Q', 'J')
('A', 'J', 'Q', 'K')
('A', 'J', 'K', 'Q')
('A', 'Q', 'J', 'K')
('A', 'Q', 'K', 'J')
('A', 'K', 'J', 'Q')
('A', 'K', 'Q', 'J')
那么,一个均匀的洗牌算法,就是每次洗牌完后,获得上面每种顺序的概率是相等的,都等于1/24。感觉已经出来了一种算法了,那就是先像前文所述把所有的排列情况都枚举出来,分别标上号 1-24 号,然后从 24 中随机取一个数字(先不考虑如何能做到随机取了,这个话题好像也没那么容易),获取其中这个数字对应的号的排列。
这个算法复杂度是多少?假设为** N** 张牌的话,应该就是 1/N!(注意是阶乘,这里可不是感叹号),显然复杂度太高了。
有没有更好的方法呢?答案当然是肯定的。
##经典的洗牌算法
洗牌算法实际上是一个很经典的算法,在经典书籍《算法导论》里面很靠前的部分就有讲解和分析。
我们把这个洗牌过程用更加“程序员”的语言描述一下,就是假设有一个 n 个元素的数组 Array[n],通过某种方式,随机产生一个另外一个序列Array'[n]让数组的每个元素 Array[i] 在数组中的每个位置出现的概率都是1/n。
其实方法可以这样,依次从 Array 中随机选择 1 个,依次放到 Array' 中即可。证明一下:
Array[0],在新数组的第 0 个位置处的概率为:1/n,因为随机选,只能是1/n的概率能选中; Array[1],在新数组的第 1 个位置处的概率为:1/n,因为 第一次没选中 Array[1]的概率为 n-1/n,再结合第二次(只剩下n-1个了,所以分母为n-1)选中的概率为:1/n-1,因此概率为:(n-1)/n * 1/(n-1)=1/n。 依此类推,可以证明前述问题。
其实,我们也可以不用另外找个数组来存结果,Array'也可以理解为还是前面的这个 Array,只不过里面元素的顺序变化了。
这其实可以理解为一个 “排序”(其实是乱序) 过程,算法如下:
void shuffle(List list) {
int n = list.size();
for (int i = 0; i
接下来是如何证明呢?不能你说随机就随机吧,你说等概率就等概率吧。下面还是跟着石头哥一起来看看如何证明吧(这也是面试中的考察点)。
我们假设经过排序后,某个元素 Array[x] 恰好排在位置 x 处的概率为Px,则该元素恰好排在第 x 处的概率是前 x-1 次时都没有被随机到,并且第 x 次时,恰好 random(x, n) = x了。
还是在循环中列举几项,更好理解一些(写完,才发现跟前面的解释差不多):
i = 0, random(0, n) 没有返回 x,共 n 个数,肯定返回了其他 n-1 个中的一个,因此概率为(n-1)/ni = 1, ramdom(1, n) 没有返回 x,共 n - 1 个数,肯定返回了其他 n-2 个中的一个,即该为(n-2)/(n-1)依此类推……i = x-1, random(x-1, n) 没有返回 x,共 n - (x-1) 个数,肯定返回了其他n-(x-1)-1 个中的一个,即为(n-(x-1)-1)/(n-(x-1))=(n-x)/(n-x+1)i = x,random(x, n) 恰好返回了 x,共 n-x 个数,概率为1/(n-x)
Px=(n-1)/n·(n-2)/(n-1)·...·(n-x)/(n-x+1)·1/(n-x)=1/n
因此,到这算是简单证明了任何元素出现在任何位置的概率是相等的。
注意说明一下,这是理论上的值,概率类的问题在量不大的情况下很有可能有随机性的。就像翻硬币,正反面理论上的值都是一半一半的,但你不能说一定是正反面按照次序轮着来。
##看看 JDK 中的实现
我们还是来看看 JDK 中的实现。JDK 中 Collections 中有如下的实现方法 shuffle:
public static void shuffle(List list, Random rnd) {
int size = list.size();
// 石头备注:本机特定版本中的常量 SHUFFLE_THRESHOLD=5
if (size 1; i--)
swap(list, i-1, rnd.nextInt(i));
} else {
Object arr[] = list.toArray();
// Shuffle array
for (int i=size; i>1; i--)
swap(arr, i-1, rnd.nextInt(i));
ListIterator it = list.listIterator();
for (int i=0; i
基本上能看懂大概,不过是不是看看源码还是能获得新技能的。
上面条件分支大概分两类:
如果是数组类型,就是可以 O(1)随机访问的List;或者传入的 list 小于 SHUFFLE_THRESHOLD。否则的话不能随机访问的链表类型,则花 O(n) 转成数组,再 shuffle,最后又回滚回链表。转成数组的目的很简单,可以快速定位某个下标的元素。
第一步的这个 SHUFFLE_THRESHOLD 其实就是一个经验调优值,即便假设不能通过快速下标定位某个元素(即需要遍历的方式定位),当输入的 size 比较小的时候,和先花 O(n)转成数组最后又转回成链表 相比,也能有更快的速度。
另外多说一句,其实这种参数化调优方式在各种语言实现的时候很常见的,比如你去看排序算法的实现中,比如 Java 中 Arrays.sort 就是用的 DualPivotQuicksort(源码在java.util.DualPivotQuicksort中),里面实现逻辑中,当数组大小较小时也是用的其他如 的插入排序,如下图所示。
洗牌算法的应用
回到本篇标题说的应用场景上来,比如开篇提到的 Eureka 注册中心的 Client 就是通过把server 的 IPList 打乱顺序,然后挨个取来实现理论上的均匀的负载均衡。
代码(在 github: Netflix/eureka 中,公众号就不单独贴出来了)在这里com.netflix.discovery.shared.resolver.ResolverUtils。看代码如下,是不是跟前文的算法差不多?(具体写法不一样而已)
public static List randomize(List list) {
List randomList = new ArrayList<>(list);
if (randomList.size()
其实,在任何需要打乱顺序的场景里面都可以用这个算法,举个例子,播放器一般都有随机播放的功能,比如你自己有个歌单 list,但想随机播放里面的歌曲,就也可以用这个方法来实现。
还有,就比如名字中的“洗牌”,那些棋牌类的游戏,当然会用到名副其实的“洗牌”算法了。其实在各种游戏的随机场景中应该都可以用这个算法的。
扩展一下,留道作业题
跟这个问题类似的,还有一些常见的面试题,本人之前印象中也被问到过(石头特地去翻了翻当年校招等找工作的时候收集和积累的面试题集)。
以下题目来源于网络,因为是当初准备面试时候收集的,具体来源不详了。
题目 1
给你一个文本文件,设计一个算法随机从文本文件中抽取一行,要保证每行被抽取到的概率一样。
最简单的思路其实就是:先把文件每一行读取出来,假设有 n 行,这个时候随机从 1-n生成一个数,读取对应的行即可。
这种方法当然可以解决,咱们加深一下难度,假设文件很大很大很大呢,或者直接要求只能遍历该文件内容一遍,怎么做到呢?
题目 2
其实题目 1 还可以扩展一下,不是选择 1 行了,是选择 k 行,又应该怎么做呢?
来源 | 程序猿石头
作者 | 码农唐磊
重磅分享:微软等数据结构+算法面试100题全部答案完整亮相
引言
无私分享造就开源的辉煌。
今是二零一一年十月十三日,明日14日即是本人刚好开博一周年。在一周年之际,特此分享出微软面试全部100题答案的完整版,以作为对读者的回馈。
一 年之前的10月14日,一个名叫July的人在一个叫csdn的论坛上开帖分享微软等公司数据结构+算法面试100题,自此,与上千网友一起思考,解答这 些面试题目,最终成就了一个名为:结构之法算法之道的面试编程与算法研究并重的博客,如今,此博客影响力逐步渗透到海外,及至到整个互联网。
在此之前,由于本人笨拙,这微软面试100题的答案只整理到了前60题,故此,常有朋友留言或来信询问后面40题的答案。只是因个人认为:一、答案只是作 为一个参考,不可太过依赖;二、常常因一些事情耽搁。自此,后面40题的答案迟迟未得整理。且个人已经整理的前60题的答案,在我看来,是有诸多问题与弊 端的,甚至很多答案都是错误的。
互联网总是能带来惊喜。前几日,一位现局美国加州的名叫阿财的朋友发来一封邮件,并把他自己做的全部100题的答案一并发予给我,自此,便似遇见了知己。十分感谢。
话不絮烦。本只需贴出后面40题的答案,因为前60题的答案本人早已整理上传至网上,但多一种思路多一种参考亦未尝不可。把阿财的答案再稍加整理番,特此,全部100题的答案现今都贴出来。有任何问题,欢迎不吝指正。谢谢。
上千上万的人都关注过此100题,且大都都各自贡献了自己的思路,或回复于微软100题的维护帖子上,或回复于本博客内,人数众多,无法一一标明,特此向他们诸位表示敬意和感谢。谢谢大家,诸君的努力足以影响整个互联网,咱们已经迎来一个分享互利的新时代。
微软面试100题全部答案
个人整理的前60题的答案可参见以下三篇文章:
微软100题第1题-20题答案 http://blog.csdn.net/v_JULY_v/archive/2011/01/10/6126406.aspx [博文 I]
微软100题第21-40题答案 http://blog.csdn.net/v_JULY_v/archive/2011/01/10/6126444.aspx [博文II]
微软100题第41-60题答案 http://blog.csdn.net/v_JULY_v/archive/2011/02/01/6171539.aspx [博文III]
最新整理的全部100题的答案参见如下(重复的,以及一些无关紧要的题目跳过):
1.把二元查找树转变成排序的双向链表
题目:
输入一棵二元查找树,将该二元查找树转换成一个排序的双向链表。
要求不能创建任何新的结点,只调整指针的指向。
10
/ \
6 14
/ \ / \
4 8 12 16
转换成双向链表
4=6=8=10=12=14=16。
首先我们定义的二元查找树节点的数据结构如下:
struct BSTreeNode
{
int m_nValue; // value of node
BSTreeNode *m_pLeft; // left child of node
BSTreeNode *m_pRight; // right child of node
};
ANSWER:
This is a traditional problem that can be solved using recursion.
For each node, connect the double linked lists created from left and right child node to form a full list.
/**
* @param root The root node of the tree
* @return The head node of the converted list.
*/
BSTreeNode * treeToLinkedList(BSTreeNode * root) {
BSTreeNode * head, * tail;
helper(head, tail, root);
return head;
}
void helper(BSTreeNode *& head, BSTreeNode *& tail, BSTreeNode *root) {
BSTreeNode *lt, *rh;
if (root == NULL) {
head = NULL, tail = NULL;
return;
}
helper(head, lt, root->m_pLeft);
helper(rh, tail, root->m_pRight);
if (lt!=NULL) {
lt->m_pRight = root;
root->m_pLeft = lt;
} else {
head = root;
}
if (rh!=NULL) {
root->m_pRight=rh;
rh->m_pLeft = root;
} else {
tail = root;
}
}
2.设计包含min 函数的栈。
定义栈的数据结构,要求添加一个min 函数,能够得到栈的最小元素。
要求函数min、push 以及pop 的时间复杂度都是O(1)。
ANSWER:
Stack is a LIFO data structure. When some element is popped from the stack, the status will recover to the original status as before that element was pushed. So we can recover the minimum element, too.
struct MinStackElement {
int data;
int min;
};
struct MinStack {
MinStackElement * data;
int size;
int top;
}
MinStack MinStackInit(int maxSize) {
MinStack stack;
stack.size = maxSize;
stack.data = (MinStackElement*) malloc(sizeof(MinStackElement)*maxSize);
stack.top = 0;
return stack;
}
void MinStackFree(MinStack stack) {
free(stack.data);
}
void MinStackPush(MinStack stack, int d) {
if (stack.top == stack.size) error(“out of stack space.”);
MinStackElement* p = stack.data[stack.top];
p->data = d;
p->min = (stack.top==0?d : stack.data[top-1]);
if (p->min > d) p->min = d;
top ++;
}
int MinStackPop(MinStack stack) {
if (stack.top == 0) error(“stack is empty!”);
return stack.data[--stack.top].data;
}
int MinStackMin(MinStack stack) {
if (stack.top == 0) error(“stack is empty!”);
return stack.data[stack.top-1].min;
}
3.求子数组的最大和
题目:
输入一个整形数组,数组里有正数也有负数。
数组中连续的一个或多个整数组成一个子数组,每个子数组都有一个和。
求所有子数组的和的最大值。要求时间复杂度为O(n)。
例如输入的数组为1, -2, 3, 10, -4, 7, 2, -5,和最大的子数组为3, 10, -4, 7, 2,
因此输出为该子数组的和18。
ANSWER:
A traditional greedy approach.
Keep current sum, slide from left to right, when sum < 0, reset sum to 0.
int maxSubarray(int a[], int size) {
if (size<=0) error(“error array size”);
int sum = 0;
int max = - (1 << 31);
int cur = 0;
while (cur < size) {
sum += a[cur++];
if (sum > max) {
max = sum;
} else if (sum < 0) {
sum = 0;
}
}
return max;
}
4.在二元树中找出和为某一值的所有路径
题目:输入一个整数和一棵二元树。
从树的根结点开始往下访问一直到叶结点所经过的所有结点形成一条路径。
打印出和与输入整数相等的所有路径。
例如输入整数22 和如下二元树
10
/ \
5 12
/ \
4 7
则打印出两条路径:10, 12 和10, 5, 7。
二元树节点的数据结构定义为:
struct BinaryTreeNode // a node in the binary tree
{
int m_nValue; // value of node
BinaryTreeNode *m_pLeft; // left child of node
BinaryTreeNode *m_pRight; // right child of node
};
ANSWER:
Use backtracking and recurison. We need a stack to help backtracking the path.
struct TreeNode {
int data;
TreeNode * left;
TreeNode * right;
};
void printPaths(TreeNode * root, int sum) {
int path[MAX_HEIGHT];
helper(root, sum, path, 0);
}
void helper(TreeNode * root, int sum, int path[], int top) {
path[top++] = root.data;
sum -= root.data;
if (root->left == NULL && root->right==NULL) {
if (sum == 0) printPath(path, top);
} else {
if (root->left != NULL) helper(root->left, sum, path, top);
if (root->right!=NULL) helper(root->right, sum, path, top);
}
top --;
sum -= root.data;
}
5.查找最小的k 个元素
题目:输入n 个整数,输出其中最小的k 个。
例如输入1,2,3,4,5,6,7 和8 这8 个数字,则最小的4 个数字为1,2,3 和4。
ANSWER:
This is a very traditional question...
O(nlogn): cat I_FILE | sort -n | head -n K
O(kn): do insertion sort until k elements are retrieved.
O(n+klogn): Take O(n) time to bottom-up build a min-heap. Then sift-down k-1 times.
So traditional that I don’t want to write the codes...
Only gives the siftup and siftdown function.
/**
*@param i the index of the element in heap a[0...n-1] to be sifted up
void siftup(int a[], int i, int n) {
while (i>0) {
int j=(i&1==0 ? i-1 : i+1);
int p=(i-1)>>1;
if (j<n && a[j]<a[i]) i = j;
if (a[i] < a[p]) swap(a, i, p);
i = p;
}
}
void siftdown(int a[], int i, int n) {
while (2*i+1<n){
int l=2*i+1;
if (l+1<n && a[l+1] < a[l]) l++;
if (a[l] < a[i]) swap(a, i, l);
i=l;
}
}
第6 题
腾讯面试题:
给你10 分钟时间,根据上排给出十个数,在其下排填出对应的十个数
要求下排每个数都是先前上排那十个数在下排出现的次数。
上排的十个数如下:
【0,1,2,3,4,5,6,7,8,9】
举一个例子,
数值: 0,1,2,3,4,5,6,7,8,9
分配: 6,2,1,0,0,0,1,0,0,0
0 在下排出现了6 次,1 在下排出现了2 次,
2 在下排出现了1 次,3 在下排出现了0 次....
以此类推..
ANSWER:
I don’t like brain teasers. Will skip most of them...
第7 题
微软亚院之编程判断俩个链表是否相交
给出俩个单向链表的头指针,比如h1,h2,判断这俩个链表是否相交。
为了简化问题,我们假设俩个链表均不带环。
问题扩展:
1.如果链表可能有环列?
2.如果需要求出俩个链表相交的第一个节点列?
ANSWER:
struct Node {
int data;
int Node *next;
};
// if there is no cycle.
int isJoinedSimple(Node * h1, Node * h2) {
while (h1->next != NULL) {
h1 = h1->next;
}
while (h2->next != NULL) {
h2 = h2-> next;
}
return h1 == h2;
}
// if there could exist cycle
int isJoined(Node *h1, Node * h2) {
Node* cylic1 = testCylic(h1);
Node* cylic2 = testCylic(h2);
if (cylic1+cylic2==0) return isJoinedSimple(h1, h2);
if (cylic1==0 && cylic2!=0 || cylic1!=0 &&cylic2==0) return 0;
Node *p = cylic1;
while (1) {
if (p==cylic2 || p->next == cylic2) return 1;
p=p->next->next;
cylic1 = cylic1->next;
if (p==cylic1) return 0;
}
}
Node* testCylic(Node * h1) {
Node * p1 = h1, *p2 = h1;
while (p2!=NULL && p2->next!=NULL) {
p1 = p1->next;
p2 = p2->next->next;
if (p1 == p2) {
return p1;
}
}
return NULL;
}
第8 题
此贴选一些比较怪的题,,由于其中题目本身与算法关系不大,仅考考思维。特此并作一题。
1.有两个房间,一间房里有三盏灯,另一间房有控制着三盏灯的三个开关,
这两个房间是分割开的,从一间里不能看到另一间的情况。
现在要求受训者分别进这两房间一次,然后判断出这三盏灯分别是由哪个开关控制的。
有什么办法呢?
ANSWER:
Skip.
2.你让一些人为你工作了七天,你要用一根金条作为报酬。金条被分成七小块,每天给出一
块。
如果你只能将金条切割两次,你怎样分给这些工人?
ANSWER:
1+2+4;
3. 用一种算法来颠倒一个链接表的顺序。现在在不用递归式的情况下做一遍。
ANSWER:
Node * reverse(Node * head) {
if (head == NULL) return head;
if (head->next == NULL) return head;
Node * ph = reverse(head->next);
head->next->next = head;
head->next = NULL;
return ph;
}
Node * reverseNonrecurisve(Node * head) {
if (head == NULL) return head;
Node * p = head;
Node * previous = NULL;
while (p->next != NULL) {
p->next = previous;
previous = p;
p = p->next;
}
p->next = previous;
return p;
}
用一种算法在一个循环的链接表里插入一个节点,但不得穿越链接表。
ANSWER:
I don’t understand what is “Chuanyue”.
用一种算法整理一个数组。你为什么选择这种方法?
ANSWER:
What is “Zhengli?”
用一种算法使通用字符串相匹配。
ANSWER:
What is “Tongyongzifuchuan”... a string with “*” and “?”? If so, here is the code.
int match(char * str, char * ptn) {
if (*ptn == ‘\0’) return 1;
if (*ptn == ‘*’) {
do {
if (match(str++, ptn+1)) return 1;
} while (*str != ‘\0’);
return 0;
}
if (*str == ‘\0’) return 0;
if (*str == *ptn || *ptn == ‘?’) {
return match(str+1, ptn+1);
}
return 0;
}
颠倒一个字符串。优化速度。优化空间。
void reverse(char *str) {
reverseFixlen(str, strlen(str));
}
void reverseFixlen(char *str, int n) {
char* p = str+n-1;
while (str < p) {
char c = *str;
*str = *p; *p=c;
}
}
颠倒一个句子中的词的顺序,比如将“我叫克丽丝”转换为“克丽丝叫我”,
实现速度最快,移动最少。
ANSWER:
Reverse the whole string, then reverse each word. Using the reverseFixlen() above.
void reverseWordsInSentence(char * sen) {
int len = strlen(sen);
reverseFixlen(sen, len);
char * p = str;
while (*p!=’\0’) {
while (*p == ‘ ‘ && *p!=’\0’) p++;
str = p;
while (p!= ‘ ‘ && *p!=’\0’) p++;
reverseFixlen(str, p-str);
}
}
找到一个子字符串。优化速度。优化空间。
ANSWER:
KMP? BM? Sunday? Using BM or sunday, if it’s ASCII string, then it’s easy to fast access the auxiliary array. Otherwise an hashmap or bst may be needed. Lets assume it’s an ASCII string.
int bm_strstr(char *str, char *sub) {
int len = strlen(sub);
int i;
int aux[256];
memset(aux, sizeof(int), 256, len+1);
for (i=0; i<len; i++) {
aux[sub[i]] = len - i;
}
int n = strlen(str);
i=len-1;
while (i<n) {
int j=i, k=len-1;
while (k>=0 && str[j--] == sub[k--])
;
if (k<0) return j+1;
if (i+1<n)
i+=aux[str[i+1]];
else
return -1;
}
}
However, this algorithm, as well as BM, KMP algorithms use O(|sub|) space. If this is not acceptable, Rabin-carp algorithm can do it. Using hashing to fast filter out most false matchings.
#define HBASE 127
int rc_strstr(char * str, char * sub) {
int dest= 0;
char * p = sub;
int len = 0;
int TO_REDUCE = 1;
while (*p!=’\0’) {
dest = HBASE * dest + (int)(*p);
TO_REDUCE *= HBASE;
len ++;
}
int hash = 0;
p = str;
int i=0;
while (*p != ‘\0’) {
if (i++<len) hash = HBASE * dest + (int)(*p);
else hash = (hash - (TO_REDUCE * (int)(*(p-len))))*HBASE + (int)(*p);
if (hash == dest && i>=len && strncmp(sub, p-len+1, len) == 0) return i-len;
p++;
}
return -1;
}
比较两个字符串,用O(n)时间和恒量空间。
ANSWER:
What is “comparing two strings”? Just normal string comparison? The natural way use O(n) time and O(1) space.
int strcmp(char * p1, char * p2) {
while (*p1 != ‘\0’ && *p2 != ‘\0’ && *p1 == *p2) {
p1++, p2++;
}
if (*p1 == ‘\0’ && *p2 == ‘\0’) return 0;
if (*p1 == ‘\0’) return -1;
if (*p2 == ‘\0’) return 1;
return (*p1 - *p2); // it can be negotiated whether the above 3 if’s are necessary, I don’t like to omit them.
}
假设你有一个用1001 个整数组成的数组,这些整数是任意排列的,但是你知道所有的整数都在1 到1000(包括1000)之间。此外,除一个数字出现两次外,其他所有数字只出现一次。假设你只能对这个数组做一次处理,用一种算法找出重复的那个数 字。如果你在运算中使用了辅助的存储方式,那么你能找到不用这种方式的算法吗?
ANSWER:
Sum up all the numbers, then subtract the sum from 1001*1002/2.
Another way, use A XOR A XOR B = B:
int findX(int a[]) {
int k = a[0];
for (int i=1; i<=1000;i++)
k ~= a[i]~i;
}
return k;
}
不用乘法或加法增加8 倍。现在用同样的方法增加7 倍。
ANSWER:
n<<3;
(n<<3)-n;
第9 题
判断整数序列是不是二元查找树的后序遍历结果
题目:输入一个整数数组,判断该数组是不是某二元查找树的后序遍历的结果。
如果是返回true,否则返回false。
例如输入5、7、6、9、11、10、8,由于这一整数序列是如下树的后序遍历结果:
8
/ \
6 10
/ \ / \
5 7 9 11
因此返回true。
如果输入7、4、6、5,没有哪棵树的后序遍历的结果是这个序列,因此返回false。
ANSWER:
This is an interesting one. There is a traditional question that requires the binary tree to be re-constructed from mid/post/pre order results. This seems similar. For the problems related to (binary) trees, recursion is the first choice.
In this problem, we know in post-order results, the last number should be the root. So we have known the root of the BST is 8 in the example. So we can split the array by the root.
int isPostorderResult(int a[], int n) {
return helper(a, 0, n-1);
}
int helper(int a[], int s, int e) {
if (e==s) return 1;
int i=e-1;
while (a[e]>a[i] && i>=s) i--;
if (!helper(a, i+1, e-1))
return 0;
int k = l;
while (a[e]<a[i] && i>=s) i--;
return helper(a, s, l);
}
第10 题
翻转句子中单词的顺序。
题目:输入一个英文句子,翻转句子中单词的顺序,但单词内字符的顺序不变。
句子中单词以空格符隔开。为简单起见,标点符号和普通字母一样处理。
例如输入“I am a student.”,则输出“student. a am I”。
Answer:
Already done this. Skipped.
第11 题
求二叉树中节点的最大距离...
如果我们把二叉树看成一个图,父子节点之间的连线看成是双向的,
我们姑且定义"距离"为两节点之间边的个数。
写一个程序,
求一棵二叉树中相距最远的两个节点之间的距离。
ANSWER:
This is interesting... Also recursively, the longest distance between two nodes must be either from root to one leaf, or between two leafs. For the former case, it’s the tree height. For the latter case, it should be the sum of the heights of left and right subtrees of the two leaves’ most least ancestor.
The first case is also the sum the heights of subtrees, just the height + 0.
int maxDistance(Node * root) {
int depth;
return helper(root, depth);
}
int helper(Node * root, int &depth) {
if (root == NULL) {
depth = 0; return 0;
}
int ld, rd;
int maxleft = helper(root->left, ld);
int maxright = helper(root->right, rd);
depth = max(ld, rd)+1;
return max(maxleft, max(maxright, ld+rd));
}
第12 题
题目:求1+2+…+n,
要求不能使用乘除法、for、while、if、else、switch、case 等关键字以及条件判断语句
(A?B:C)。
ANSWER:
1+..+n=n*(n+1)/2=(n^2+n)/2
it is easy to get x/2, so the problem is to get n^2
though no if/else is allowed, we can easilly go around using short-pass.
using macro to make it fancier:
#define T(X, Y, i) (Y & (1<<i)) && X+=(Y<<i)
int foo(int n){
int r=n;
T(r, n, 0); T(r, n,1); T(r, n, 2); … T(r, n, 31);
return r >> 1;
}
第13 题:
题目:输入一个单向链表,输出该链表中倒数第k 个结点。链表的倒数第0 个结点为链表的尾指针。
链表结点定义如下:
struct ListNode
{
int m_nKey;
ListNode* m_pNext;
};
Answer:
Two ways. 1: record the length of the linked list, then go n-k steps. 2: use two cursors.
Time complexities are exactly the same.
Node * lastK(Node * head, int k) {
if (k<0) error(“k < 0”);
Node *p=head, *pk=head;
for (;k>0;k--) {
if (pk->next!=NULL) pk = pk->next;
else return NULL;
}
while (pk->next!=NULL) {
p=p->next, pk=pk->next;
}
return p;
}
第14 题:
题目:输入一个已经按升序排序过的数组和一个数字,
在数组中查找两个数,使得它们的和正好是输入的那个数字。
要求时间复杂度是O(n)。如果有多对数字的和等于输入的数字,输出任意一对即可。
例如输入数组1、2、4、7、11、15 和数字15。由于4+11=15,因此输出4 和11。
ANSWER:
Use two cursors. One at front and the other at the end. Keep track of the sum by moving the cursors.
void find2Number(int a[], int n, int dest) {
int *f = a, *e=a+n-1;
int sum = *f + *e;
while (sum != dest && f < e) {
if (sum < dest) sum = *(++f);
else sum = *(--e);
}
if (sum == dest) printf(“%d, %d\n”, *f, *e);
}
第15 题:
题目:输入一颗二元查找树,将该树转换为它的镜像,
即在转换后的二元查找树中,左子树的结点都大于右子树的结点。
用递归和循环两种方法完成树的镜像转换。
例如输入:
8
/ \
6 10
/\ /\
5 7 9 11
输出:
8
/ \
10 6
/\ /\
11 9 7 5
定义二元查找树的结点为:
struct BSTreeNode // a node in the binary search tree (BST)
{
int m_nValue; // value of node
BSTreeNode *m_pLeft; // left child of node
BSTreeNode *m_pRight; // right child of node
};
ANSWER:
This is the basic application of recursion.
PS: I don’t like the m_xx naming convension.
void swap(Node ** l, Node ** r) {
Node * p = *l;
*l = *r;
*r = p;
}
void mirror(Node * root) {
if (root == NULL) return;
swap(&(root->left), &(root->right));
mirror(root->left);
mirror(root->right);
}
void mirrorIteratively(Node * root) {
if (root == NULL) return;
stack<Node*> buf;
buf.push(root);
while (!stack.empty()) {
Node * n = stack.pop();
swap(&(root->left), &(root->right));
if (root->left != NULL) buf.push(root->left);
if (root->right != NULL) buf.push(root->right);
}
}
第16 题:
题目(微软):
输入一颗二元树,从上往下按层打印树的每个结点,同一层中按照从左往右的顺序打印。
例如输入
7
8
/ \
6 10
/ \ / \
5 7 9 11
输出8 6 10 5 7 9 11。
ANSWER:
The nodes in the levels are printed in the similar manner their parents were printed. So it should be an FIFO queue to hold the level. I really don’t remember the function name of the stl queue, so I will write it in Java...
void printByLevel(Node root) {
Node sentinel = new Node();
LinkedList<Node> q=new LinkedList<Node>();
q.addFirst(root); q.addFirst(sentinel);
while (!q.isEmpty()) {
Node n = q.removeLast();
if (n==sentinel) {
System.out.println(“\n”);
q.addFirst(sentinel);
} else {
System.out.println(n);
if (n.left() != null) q.addFirst(n.left());
if (n.right()!=null) q.addFirst(n.right());
}
}
}
第17 题:
题目:在一个字符串中找到第一个只出现一次的字符。如输入abaccdeff,则输出b。
分析:这道题是2006 年google 的一道笔试题。
ANSWER:
Again, this depends on what is “char”. Let’s assume it as ASCII.
char firstSingle(char * str) {
int a[255];
memset(a, 0, 255*sizeof(int));
char *p=str;
while (*p!=’\0’) {
a[*p] ++;
p++;
}
p = str;
while (*p!=’\0’) {
if (a[*p] == 1) return *p;
}
return ‘\0’; // this must the one that occurs exact 1 time.
}
第18 题:
题目:n 个数字(0,1,…,n-1)形成一个圆圈,从数字0 开始,
每次从这个圆圈中删除第m 个数字(第一个为当前数字本身,第二个为当前数字的下一个数
字)。
当一个数字删除后,从被删除数字的下一个继续删除第m 个数字。
求出在这个圆圈中剩下的最后一个数字。
July:我想,这个题目,不少人已经见识过了。
ANSWER:
Actually, although this is a so traditional problem, I was always to lazy to think about this or even to search for the answer.(What a shame...). Finally, by google I found the elegant solution for it.
The keys are:
1) if we shift the ids by k, namely, start from k instead of 0, we should add the result by k%n
2) after the first round, we start from k+1 ( possibly % n) with n-1 elements, that is equal to an (n-1) problem while start from (k+1)th element instead of 0, so the answer is (f(n-1, m)+k+1)%n
3) k = m-1, so f(n,m)=(f(n-1,m)+m)%n.
finally, f(1, m) = 0;
Now this is a O(n) solution.
int joseph(int n, int m) {
int fn=0;
for (int i=2; i<=n; i++) {
fn = (fn+m)%i; }
return fn;
}
hu...长出一口气。。。
第19 题:
题目:定义Fibonacci 数列如下:
/ 0 n=0
f(n)= 1 n=1
\ f(n-1)+f(n-2) n=2
输入n,用最快的方法求该数列的第n 项。
分析:在很多C 语言教科书中讲到递归函数的时候,都会用Fibonacci 作为例子。
因此很多程序员对这道题的递归解法非常熟悉,但....呵呵,你知道的。。
ANSWER:
This is the traditional problem of application of mathematics...
let A=
{1 1}
{1 0}
f(n) = A^(n-1)[0,0]
this gives a O(log n) solution.
int f(int n) {
int A[4] = {1,1,1,0};
int result[4];
power(A, n, result);
return result[0];
}
void multiply(int[] A, int[] B, int _r) {
_r[0] = A[0]*B[0] + A[1]*B[2];
_r[1] = A[0]*B[1] + A[1]*B[3];
_r[2] = A[2]*B[0] + A[3]*B[2];
_r[3] = A[2]*B[1] + A[3]*B[3];
}
void power(int[] A, int n, int _r) {
if (n==1) { memcpy(A, _r, 4*sizeof(int)); return; }
int tmp[4];
power(A, n>>1, _r);
multiply(_r, _r, tmp);
if (n & 1 == 1) {
multiply(tmp, A, _r);
} else {
memcpy(_r, tmp, 4*sizeof(int));
}
}
第20 题:
题目:输入一个表示整数的字符串,把该字符串转换成整数并输出。
例如输入字符串"345",则输出整数345。
ANSWER:
This question checks how the interviewee is familiar with C/C++? I’m so bad at C/C++...
int atoi(char * str) {
int neg = 0;
char * p = str;
if (*p == ‘-’) {
p++; neg = 1;
} else if (*p == ‘+’) {
p++;
}
int num = 0;
while (*p != ‘\0’) {
if (*p>=0 && *p <= 9) {
num = num * 10 + (*p-’0’);
} else {
error(“illegal number”);
}
p++;
}
return num;
}
PS: I didn’t figure out how to tell a overflow problem easily.
第21 题
2010 年中兴面试题
编程求解:
输入两个整数n 和m,从数列1,2,3.......n 中随意取几个数,
使其和等于m ,要求将其中所有的可能组合列出来.
ANSWER
This is a combination generation problem.
void findCombination(int n, int m) {
if (n>m) findCombination(m, m);
int aux[n];
memset(aux, 0, n*sizeof(int));
helper(m, 0, aux);
}
void helper(int dest, int idx, int aux[], int n) {
if (dest == 0)
dump(aux, n);
if (dest <= 0 || idx==n) return;
helper(dest, idx+1, aux, n);
aux[idx] = 1;
helper(dest-idx-1, idx+1, aux, n);
aux[idx] = 0;
}
void dump(int aux[], int n) {
for (int i=0; i<n; i++)
if (aux[i]) printf(“%3d”, i+1);
printf(“\n”);
}
PS: this is not an elegant implementation, however, it is not necessary to use gray code or other techniques for such a problem, right?
第22 题:
有4 张红色的牌和4 张蓝色的牌,主持人先拿任意两张,再分别在A、B、C 三人额头上贴任意两张牌,A、B、C 三人都可以看见其余两人额头上的牌,看完后让他们猜自己额头上是什么颜色的牌,A 说不知道,B 说不知道,C 说不知道,然后A 说知道了。
请教如何推理,A 是怎么知道的。如果用程序,又怎么实现呢?
ANSWER
I dont’ like brain teaser. As an AI problem, it seems impossible to write the solution in 20 min...
It seems that a brute-force edge cutting strategy could do. Enumerate all possibilities, then for each guy delete the permutation that could be reduced if failed (for A, B, C at 1st round), Then there should be only one or one group of choices left.
But who uses this as an interview question?
第23 题:
用最简单,最快速的方法计算出下面这个圆形是否和正方形相交。"
3D 坐标系原点(0.0,0.0,0.0)
圆形:
半径r = 3.0
圆心o = (*.*, 0.0, *.*)
正方形:
4 个角坐标;
1:(*.*, 0.0, *.*)
2:(*.*, 0.0, *.*)
3:(*.*, 0.0, *.*)
4:(*.*, 0.0, *.*)
ANSWER
Crap... I totally cannot understand this problem... Does the *.* represent any possible number?
第24 题:
链表操作,
(1).单链表就地逆置,
(2)合并链表
ANSWER
Reversing a linked list. Already done.
What do you mean by merge? Are the original lists sorted and need to be kept sorted? If not, are there any special requirements?
I will only do the sorted merging.
Node * merge(Node * h1, Node * h2) {
if (h1 == NULL) return h2;
if (h2 == NULL) return h1;
Node * head;
if (h1->data>h2->data) {
head = h2; h2=h2->next;
} else {
head = h1; h1=h1->next;
}
Node * current = head;
while (h1 != NULL && h2 != NULL) {
if (h1 == NULL || (h2!=NULL && h1->data>h2->data)) {
current->next = h2; h2=h2->next; current = current->next;
} else {
current->next = h1; h1=h1->next; current = current->next;
}
}
current->next = NULL;
return head;
}
第25 题:
写一个函数,它的原形是int continumax(char *outputstr,char *intputstr)
功能:
在字符串中找出连续最长的数字串,并把这个串的长度返回,
并把这个最长数字串付给其中一个函数参数outputstr 所指内存。
例如:"abcd12345ed125ss123456789"的首地址传给intputstr 后,函数将返回9,
outputstr 所指的值为123456789
ANSWER:
int continumax(char *outputstr, char *inputstr) {
int len = 0;
char * pstart = NULL;
int max = 0;
while (1) {
if (*inputstr >= ‘0’ && *inputstr <=’9’) {
len ++;
} else {
if (len > max) pstart = inputstr-len;
len = 0;
}
if (*inputstr++==’\0’) break;
}
for (int i=0; i<len; i++)
*outputstr++ = pstart++;
*outputstr = ‘\0’;
return max;
}
26.左旋转字符串
题目:
定义字符串的左旋转操作:把字符串前面的若干个字符移动到字符串的尾部。
如把字符串abcdef 左旋转2 位得到字符串cdefab。请实现字符串左旋转的函数。
要求时间对长度为n 的字符串操作的复杂度为O(n),辅助内存为O(1)。
ANSWER
Have done it. Using reverse word function above.
27.跳台阶问题
题目:一个台阶总共有n 级,如果一次可以跳1 级,也可以跳2 级。
求总共有多少总跳法,并分析算法的时间复杂度。
这道题最近经常出现,包括MicroStrategy 等比较重视算法的公司
都曾先后选用过个这道题作为面试题或者笔试题。
ANSWER
f(n)=f(n-1)+f(n-2), f(1)=1, f(2)=2, let f(0) = 1, then f(n) = fibo(n-1);
28.整数的二进制表示中1 的个数
题目:输入一个整数,求该整数的二进制表达中有多少个1。
例如输入10,由于其二进制表示为1010,有两个1,因此输出2。
分析:
这是一道很基本的考查位运算的面试题。
包括微软在内的很多公司都曾采用过这道题。
ANSWER
Traditional question. Use the equation xxxxxx10000 & (xxxxxx10000-1) = xxxxxx00000
Note: for negative numbers, this also hold, even with 100000000 where the “-1” leading to an underflow.
int countOf1(int n) {
int c=0;
while (n!=0) {
n=n & (n-1);
c++;
}
return c;
}
another solution is to lookup table. O(k), k is sizeof(int);
int countOf1(int n) {
int c = 0;
if (n<0) { c++; n = n & (1<<(sizeof(int)*8-1)); }
while (n!=0) {
c+=tab[n&0xff];
n >>= 8;
}
return c;
}
29.栈的push、pop 序列
题目:输入两个整数序列。其中一个序列表示栈的push 顺序,
判断另一个序列有没有可能是对应的pop 顺序。
为了简单起见,我们假设push 序列的任意两个整数都是不相等的。
比如输入的push 序列是1、2、3、4、5,那么4、5、3、2、1 就有可能是一个pop 系列。
因为可以有如下的push 和pop 序列:
push 1,push 2,push 3,push 4,pop,push 5,pop,pop,pop,pop,
这样得到的pop 序列就是4、5、3、2、1。
但序列4、3、5、1、2 就不可能是push 序列1、2、3、4、5 的pop 序列。
ANSWER
This seems interesting. However, a quite straightforward and promising way is to actually build the stack and check whether the pop action can be achieved.
int isPopSeries(int push[], int pop[], int n) {
stack<int> helper;
int i1=0, i2=0;
while (i2 < n) {
while (stack.empty() || stack.peek() != pop[i2])
if (i1<n)
stack.push(push[i1++]);
else
return 0;
while (!stack.empty() && stack.peek() == pop[i2]) {
stack.pop(); i2++;
}
}
return 1;
}
30.在从1 到n 的正数中1 出现的次数
题目:输入一个整数n,求从1 到n 这n 个整数的十进制表示中1 出现的次数。
例如输入12,从1 到12 这些整数中包含1 的数字有1,10,11 和12,1 一共出现了5 次。
分析:这是一道广为流传的google 面试题。
ANSWER
This is complicated... I hate it...
Suppose we have N=ABCDEFG.
if G<1, # of 1’s in the units digits is ABCDEF, else ABCDEF+1
if F<1, # of 1’s in the digit of tens is (ABCDE)*10, else if F==1: (ABCDE)*10+G+1, else (ABCDE+1)*10
if E<1, # of 1’s in 3rd digit is (ABCD)*100, else if E==1: (ABCD)*100+FG+1, else (ABCD+1)*100
… so on.
if A=1, # of 1 in this digit is BCDEFG+1, else it’s 1*1000000;
so to fast access the digits and helper numbers, we need to build the fast access table of prefixes and suffixes.
int countOf1s(int n) {
int prefix[10], suffix[10], digits[10]; //10 is enough for 32bit integers
int i=0;
int base = 1;
while (base < n) {
suffix[i] = n % base;
digit[i] = (n % (base * 10)) - suffix[i];
prefix[i] = (n - suffix[i] - digit[i]*base)/10;
i++, base*=10;
}
int count = 0;
base = 1;
for (int j=0; j<i; j++) {
if (digit[j] < 1) count += prefix;
else if (digit[j]==1) count += prefix + suffix + 1;
else count += prefix+base;
base *= 10;
}
return count;
}
31.华为面试题:
一类似于蜂窝的结构的图,进行搜索最短路径(要求5 分钟)
ANSWER
Not clear problem. Skipped. Seems a Dijkstra could do.
int dij
32.
有两个序列a,b,大小都为n,序列元素的值任意整数,无序;
要求:通过交换a,b 中的元素,使[序列a 元素的和]与[序列b 元素的和]之间的差最小。
例如:
var a=[100,99,98,1,2, 3];
var b=[1, 2, 3, 4,5,40];
ANSWER
If only one swap can be taken, it is a O(n^2) searching problem, which can be reduced to O(nlogn) by sorting the arrays and doing binary search.
If any times of swaps can be performed, this is a double combinatorial problem.
In the book <<beauty of codes>>, a similar problem splits an array to halves as even as possible. It is possible to take binary search, when SUM of the array is not too high. Else this is a quite time consuming brute force problem. I cannot figure out a reasonable solution.
33.
实现一个挺高级的字符匹配算法:
给一串很长字符串,要求找到符合要求的字符串,例如目的串:123
1******3***2 ,12*****3 这些都要找出来
其实就是类似一些和谐系统。。。。。
ANSWER
Not a clear problem. Seems a bitset can do.
34.
实现一个队列。
队列的应用场景为:
一个生产者线程将int 类型的数入列,一个消费者线程将int 类型的数出列
ANSWER
I don’t know multithread programming at all....
35.
求一个矩阵中最大的二维矩阵(元素和最大).如:
1 2 0 3 4
2 3 4 5 1
1 1 5 3 0
中最大的是:
4 5
5 3
要求:(1)写出算法;(2)分析时间复杂度;(3)用C 写出关键代码
ANSWER
This is the traditional problem in Programming Pearls. However, the best result is too complicated to achieve. So lets do the suboptimal one. O(n^3) solution.
1) We have know that the similar problem for 1 dim array can be done in O(n) time. However, this cannot be done in both directions in the same time. We can only calculate the accumulations for all the sublist from i to j, (0<=i<=j<n) for each array in one dimension, which takes O(n^2) time. Then in the other dimension, do the tradtional greedy search.
3) To achieve O(n^2) for accumulation for each column, accumulate 0 to i (i=0,n-1) first, then calcuate the result by acc(i, j) = acc(0, j)-acc(0,i-1)
//acc[i*n+j] => acc(i,j)
void accumulate(int a[], int n, int acc[]) {
int i=0;
acc[i] = a[i];
for (i=1;i<n; i++) {
acc[i] = acc[i-1]+a[i];
}
for (i=1; i<n; i++) {
for (j=i; j<n; j++) {
acc[i*n+j] = acc[j] - acc[i-1];
}
}
}
第36 题-40 题(有些题目搜集于CSDN 上的网友,已标明):
36.引用自网友:longzuo
谷歌笔试:
n 支队伍比赛,分别编号为0,1,2。。。。n-1,已知它们之间的实力对比关系,
存储在一个二维数组w[n][n]中,w[i][j] 的值代表编号为i,j 的队伍中更强的一支。
所以w[i][j]=i 或者j,现在给出它们的出场顺序,并存储在数组order[n]中,
比如order[n] = {4,3,5,8,1......},那么第一轮比赛就是4 对3, 5 对8。.......
胜者晋级,败者淘汰,同一轮淘汰的所有队伍排名不再细分,即可以随便排,
下一轮由上一轮的胜者按照顺序,再依次两两比,比如可能是4 对5,直至出现第一名
编程实现,给出二维数组w,一维数组order 和用于输出比赛名次的数组result[n],
求出result。
ANSWER
This question is like no-copying merge, or in place matrix rotation.
* No-copying merge: merge order to result, then merge the first half from order, and so on.
* in place matrix rotation: rotate 01, 23, .. , 2k/2k+1 to 02...2k, 1,3,...2k+1...
The two approaches are both complicated. However, notice one special feature that the losers’ order doesn’t matter. Thus a half-way merge is much simpler and easier:
void knockOut(int **w, int order[], int result[], int n) {
int round = n;
memcpy(result, order, n*sizeof(int));
while (round>1) {
int i,j;
for (i=0,j=0; i<round; i+=2) {
int win= (i==round-1) ? i : w[i][i+1];
swap(result, j, win);
j++;
}
}
}
37.
有n 个长为m+1 的字符串,
如果某个字符串的最后m 个字符与某个字符串的前m 个字符匹配,则两个字符串可以联接,
问这n 个字符串最多可以连成一个多长的字符串,如果出现循环,则返回错误。
ANSWER
This is identical to the problem to find the longest acylic path in a directed graph. If there is a cycle, return false.
Firstly, build the graph. Then search the graph for the longest path.
#define MAX_NUM 201
int inDegree[MAX_NUM];
int longestConcat(char ** strs, int m, int n) {
int graph[MAX_NUM][MAX_NUM];
int prefixHash[MAX_NUM];
int suffixHash[MAX_NUM];
int i,j;
for (i=0; i<n; i++) {
calcHash(strs[i], prefixHash[i], suffixHash[i]);
graph[i][0] = 0;
}
memset(inDegree, 0, sizeof(int)*n);
for (i=0; i<n; i++) {
for (j=0; j<n; j++) {
if (suffixHash[i]==prefixHash[j] && strncmp(strs[i]+1, strs[j], m) == 0) {
if (i==j) return 0; // there is a self loop, return false.
graph[i][0] ++;
graph[i][graph[i*n]] = j;
inDegree[j] ++;
}
}
}
return longestPath(graph, n);
}
/**
* 1. do topological sort, record index[i] in topological order.
* 2. for all 0-in-degree vertexes, set all path length to -1, do relaxation in topological order to find single source shortest path.
*/
int visit[MAX_NUM];
int parent[MAX_NUM];
// -1 path weight, so 0 is enough.
#define MAX_PATH 0
int d[MAX_NUM];
int longestPath(int graph[], int n) {
memset(visit, 0, n*sizeof(int));
if (topSort(graph) == 0) return -1; //topological sort failed, there is cycle.
int min = 0;
for (int i=0; i<n; i++) {
if (inDegree[i] != 0) continue;
memset(parent, -1, n*sizeof(int));
memset(d, MAX_PATH, n*sizeof(int));
d[i] = 0;
for (int j=0; j<n; j++) {
for (int k=1; k<=graph[top[j]][0]; k++) {
if (d[top[j]] - 1 < d[graph[top[j]][k]]) { // relax with path weight -1
d[graph[top[j]][k]] = d[top[j]] - 1;
parent[graph[top[j]][k]] = top[j];
if (d[graph[top[j]][k]] < min) min = d[graph[top[j]][k]];
}
}
}
}
return -min;
}
int top[MAX_NUM];
int finished[MAX_NUM];
int cnt = 0;
int topSort(int graph[]){
memset(visit, 0, n*sizeof(int));
memset(finished, 0, n*sizeof(int));
for (int i=0; i<n; i++) {
if (topdfs(graph, i) == 0) return 0;
}
return 1;
}
int topdfs(int graph[], int s) {
if (visited[s] != 0) return 1;
for (int i=1; i<=graph[s][0]; i++) {
if (visited[graph[s][i]]!=0 && finished[graph[s][i]]==0) {
return 0; //gray node, a back edge;
}
if (visited[graph[s][i]] == 0) {
visited[graph[s][i]] = 1;
dfs(graph, graph[s][i]);
}
}
finished[s] = 1;
top[cnt++] = s;
return 1;
}
Time complexity analysis:
Hash calculation: O(nm)
Graph construction: O(n*n)
Toplogical sort: as dfs, O(V+E)
All source longest path: O(kE), k is 0-in-degree vetexes number, E is edge number.
As a total, it’s a O(n*n+n*m) solution.
A very good problem. But I really doubt it as a solve-in-20-min interview question.
38.
百度面试:
1.用天平(只能比较,不能称重)从一堆小球中找出其中唯一一个较轻的,使用x 次天平,
最多可以从y 个小球中找出较轻的那个,求y 与x 的关系式。
ANSWER:
x=1, y=3: if a=b, c is the lighter, else the lighter is the lighter...
do this recursively. so y=3^x;
2.有一个很大很大的输入流,大到没有存储器可以将其存储下来,
而且只输入一次,如何从这个输入流中随机取得m 个记录。
ANSWER
That is, keep total number count N. If N<=m, just keep it.
For N>m, generate a random number R=rand(N) in [0, N), replace a[R] with new number if R falls in [0, m).
3.大量的URL 字符串,如何从中去除重复的,优化时间空间复杂度
ANSWER
1. Use hash map if there is enough memory.
2. If there is no enough memory, use hash to put urls to bins, and do it until we can fit the bin into memory.
39.
网易有道笔试:
(1).
求一个二叉树中任意两个节点间的最大距离,
两个节点的距离的定义是这两个节点间边的个数,
比如某个孩子节点和父节点间的距离是1,和相邻兄弟节点间的距离是2,优化时间空间复
杂度。
ANSWER
Have done this.
(2).
求一个有向连通图的割点,割点的定义是,如果除去此节点和与其相关的边,
有向图不再连通,描述算法。
ANSWER
Do dfs, record low[i] as the lowest vertex that can be reached from i and i’s successor nodes. For each edge i, if low[i] = i and i is not a leaf in dfs tree, then i is a cut point. The other case is the root of dfs, if root has two or more children ,it is a cut point.
/**
* g is defined as: g[i][] is the out edges, g[i][0] is the edge count, g[i][1...g[i][0]] are the other end points.
*/
int cnt = 0;
int visited[MAX_NUM];
int lowest[MAX_NUM];
void getCutPoints(int *g[], int cuts[], int n) {
memset(cuts, 0, sizeof(int)*n);
memset(visited, 0, sizeof(int)*n);
memset(lowest, 0, sizeof(int)*n);
for (int i=0; i<n; i++) {
if (visited[i] == 0) {
visited[i] = ++cnt;
dfs(g, cuts, n, i, i);
}
}
int dfs(int *g[], int cuts[], int n, int s, int root) {
int out = 0;
int low = visit[s];
for (int i=1; i<=g[s][0]; i++) {
if (visited[g[s][i]] == 0) {
out++;
visited[g[s][i]] = ++cnt;
int clow = dfs(g, cuts, n, g[s][i], root);
if (clow < low) low = clow;
} else {
if (low > visit[g[s][i]]) {
low = visit[g[s][i]];
}
}
}
lowest[s] = low;
if (s == root && out > 1) {
cuts[s] = 1;
}
return low;
}
40.百度研发笔试题
引用自:zp155334877
1)设计一个栈结构,满足一下条件:min,push,pop 操作的时间复杂度为O(1)。
ANSWER
Have done this.
2)一串首尾相连的珠子(m 个),有N 种颜色(N<=10),
设计一个算法,取出其中一段,要求包含所有N 中颜色,并使长度最短。
并分析时间复杂度与空间复杂度。
ANSWER
Use a sliding window and a counting array, plus a counter which monitors the num of zero slots in counting array. When there is still zero slot(s), advance the window head, until there is no zero slot. Then shrink the window until a slot comes zero. Then one candidate segment of (window_size + 1) is achieved. Repeat this. It is O(n) algorithm since each item is swallowed and left behind only once, and either operation is in constant time.
int shortestFullcolor(int a[], int n, int m) {
int c[m], ctr = m;
int h=0, t=0;
int min=n;
while (1) {
while (ctr > 0 && h<n) {
if (c[a[h]] == 0) ctr --;
c[a[h]] ++;
h++;
}
if (h>=n) return min;
while (1) {
c[a[t]] --;
if (c[a[t]] == 0) break;
t++;
}
if (min > h-t) min = h-t;
t++; ctr++;
}
}
3)设计一个系统处理词语搭配问题,比如说中国和人民可以搭配,
则中国人民人民中国都有效。要求:
*系统每秒的查询数量可能上千次;
*词语的数量级为10W;
*每个词至多可以与1W 个词搭配
当用户输入中国人民的时候,要求返回与这个搭配词组相关的信息。
ANSWER
This problem can be solved in three steps:
1. identify the words
2. recognize the phrase
3. retrieve the information
Solution of 1: The most trivial way to efficiently identify the words is hash table or BST. A balanced BST with 100 words is about 17 levels high. Considering that 100k is not a big number, hashing is enough.
Solution of 2: Since the phrase in this problem consists of only 2 words, it is easy to split the words. There won’t be a lot of candidates. To find a legal combination, we need the “matching” information. So for each word, we need some data structure to tell whether a word can co-occur with it. 100k is a bad number -- cannot fit into a 16bit digit. However, 10k*100k is not too big, so we can simply use array of sorted array to do this. 1G integers, or 4G bytes is not a big number, We can also use something like VInt to save a lot of space. To find an index in a 10k sorted array, 14 comparisons are enough.
Above operation can be done in any reasonable work-station's memory very fast, which should be the result of execution of about a few thousands of simple statements.
Solution of 3: The information could be to big to fit in the memory. So a B-tree may be adopted to index the contents. Caching techniques is also helpful. Considering there are at most 10^9 entries, a 3 or 4 level of B-tree is okay, so it will be at most 5 disk access. However, there are thousands of requests and we can only do hundreds of disk seeking per second. It could be necessary to dispatch the information to several workstations.
41.求固晶机的晶元查找程序
晶元盘由数目不详的大小一样的晶元组成,晶元并不一定全布满晶元盘,
照相机每次这能匹配一个晶元,如匹配过,则拾取该晶元,
若匹配不过,照相机则按测好的晶元间距移到下一个位置。
求遍历晶元盘的算法求思路。
ANSWER
Dont understand.
42.请修改append 函数,利用这个函数实现:
两个非降序链表的并集,1->2->3 和2->3->5 并为1->2->3->5
另外只能输出结果,不能修改两个链表的数据。
ANSWER
I don’t quite understand what it means by “not modifying linked list’s data”. If some nodes will be given up, it is weird for this requirement.
Node * head(Node *h1, Node * h2) {
if (h1==NULL) return h2;
if (h2==NULL) return h1;
Node * head;
if (h1->data < h2->data) {
head =h1; h1=h1->next;
} else {
head = h2; h2=h2->next;
}
Node * p = head;
while (h1!=NULL || h2!=NULL) {
Node * candi;
if (h1!=NULL && h2 != NULL && h1->data < h2->data || h2==NULL) {
candi = h1; h1=h1->next;
} else {
candi = h2; h2=h2->next;
}
}
if (candi->data == p->data) delete(candi);
else {
p->next = candi; p=candi;
}
}
return head;
}
43.递归和非递归俩种方法实现二叉树的前序遍历。
ANSWER
void preorderRecursive(TreeNode * node) {
if (node == NULL) return;
visit(node);
preorderRecursive(node->left);
preorderRecursive(node->right);
}
For non-recursive traversals, a stack must be adopted to replace the implicit program stack in recursive programs.
void preorderNonrecursive(TreeNode * node) {
stack<TreeNode *> s;
s.push(node);
while (!s.empty()) {
TreeNode * n = s.pop();
visit(n);
if (n->right!=NULL) s.push(n->right);
if (n->left!=NULL) s.push(n->left);
}
}
void inorderNonrecursive(TreeNode * node) {
stack<TreeNode *> s;
TreeNode * current = node;
while (!s.empty() || current != NULL) {
if (current != NULL) {
s.push(current);
current = current->left;
} else {
current = s.pop();
visit(current);
current = current->right;
}
}
}
Postorder nonrecursive traversal is the hardest one. However, a simple observation helps that the node first traversed is the node last visited. This recalls the feature of stack. So we could use a stack to store all the nodes then pop them out altogether.
This is a very elegant solution, while takes O(n) space.
Other very smart methods also work, but this is the one I like the most.
void postorderNonrecursive(TreeNode * node) {
// visiting occurs only when current has no right child or last visited is his right child
stack<TreeNode *> sTraverse, sVisit;
sTraverse.push(node);
while (!sTraverse.empty()) {
TreeNode * p = sTraverse.pop();
sVisit.push(p);
if (p->left != NULL) sTraverse.push(p->left);
if (p->right != NULL) sTraverse.push(p->right);
}
while (!sVisit.empty()) {
visit(sVisit.pop);
}
}
44.腾讯面试题:
1.设计一个魔方(六面)的程序。
ANSWER
This is a problem to test OOP.
The object MagicCube must have following features
1) holds current status
2) easily doing transform
3) judge whether the final status is achieved
4) to test, it can be initialized
5) output current status
public class MagicCube {
// 6 faces, 9 chips each face
private byte chips[54];
static final int X = 0;
static final int Y = 1;
static final int Z = 1;
void transform(int direction, int level) {
switch direction: {
X : { transformX(level); break; }
Y : { transformY(level); break; }
Z : { transformZ(level); break; }
default: throw new RuntimeException(“what direction?”);
}
void transformX(int level) { … }
}
}
// really tired of making this...
}
2.有一千万条短信,有重复,以文本文件的形式保存,一行一条,有重复。
请用5 分钟时间,找出重复出现最多的前10 条。
ANSWER
10M msgs, each at most 140 chars, that’s 1.4G, which can fit to memory.
So use hash map to accumulate occurrence counts.
Then use a heap to pick maximum 10.
3.收藏了1 万条url,现在给你一条url,如何找出相似的url。(面试官不解释何为相似)
ANSWER
What a SB interviewer... The company name should be claimed and if I met such a interviewer, I will contest to HR. The purpose of interview is to see the ability of communication. This is kind of single side shutdown of information exchange.
My first answer will be doing edit distance to the url and every candidate. Then it depends on what interviewer will react. Other options includes: fingerprints, tries...
45.雅虎:
1.对于一个整数矩阵,存在一种运算,对矩阵中任意元素加一时,需要其相邻(上下左右)
某一个元素也加一,现给出一正数矩阵,判断其是否能够由一个全零矩阵经过上述运算得到。
ANSWER
A assignment problem. Two ways to solve. 1: duplicate each cell to as many as its value, do Hungarian algorithm. Denote the sum of the matrix as M, the edge number is 2M, so the complexity is 2*M*M; 2: standard maximum flow. If the size of matrix is NxN, then the algorithm using Ford Fulkerson algorithm is M*N*N.
too complex... I will do this when I have time...
2.一个整数数组,长度为n,将其分为m 份,使各份的和相等,求m 的最大值
比如{3,2,4,3,6} 可以分成{3,2,4,3,6} m=1;
{3,6}{2,4,3} m=2
{3,3}{2,4}{6} m=3 所以m 的最大值为3
ANSWER
Two restrictions on m, 1) 1 <= m <= n; 2) Sum(array) mod m = 0
NOTE: no hint that a[i]>0, so m could be larger than sum/max;
So firstly prepare the candidates, then do a brute force search on possible m’s.
In the search , a DP is available, since if f(array, m) = OR_i( f(array-subset(i), m) ), where Sum(subset(i)) = m.
int maxShares(int a[], int n) {
int sum = 0;
int i, m;
for (i=0; i<n; i++) sum += a[i];
for (m=n; m>=2; m--) {
if (sum mod m != 0) continue;
int aux[n]; for (i=0; i<n; i++) aux[i] = 0;
if (testShares(a, n, m, sum, sum/m, aux, sum/m, 1)) return m;
}
return 1;
}
int testShares(int a[], int n, int m, int sum, int groupsum, int[] aux, int goal, int groupId) {
if (goal == 0) {
groupId++;
if (groupId == m+1) return 1;
}
for (int i=0; i<n; i++) {
if (aux[i] != 0) continue;
aux[i] = groupId;
if (testShares(a, n, m, sum, groupsum, aux, goal-a[i], groupId)) {
return 1;
}
aux[i] = 0;
}
}
Please do edge cutting yourself, I’m quite enough of this...
46.搜狐:
四对括号可以有多少种匹配排列方式?比如两对括号可以有两种:()()和(())
ANSWER:
Suppose k parenthesis has f(k) permutations, k is large enough. Check the first parenthesis, if there are i parenthesis in it then, the number of permutations inside it and out of it are f(i) and f(k-i-1), respectively. That is
f(k) = Sum_i=[0,k-1]_(f(i)*f(k-i-1));
which leads to the k’th Catalan number.
47.创新工场:
求一个数组的最长递减子序列比如{9,4,3,2,5,4,3,2}的最长递减子序列为{9,5,
4,3,2}
ANSWER:
Scan from left to right, maintain a decreasing sequence. For each number, binary search in the decreasing sequence to see whether it can be substituted.
int[] findDecreasing(int[] a) {
int[] ds = new int[a.length];
Arrays.fill(ds, 0);
int dsl = 0;
int lastdsl = 0;
for (int i=0; i<a.length; i++) {
// binary search in ds to find the first element ds[j] smaller than a[i]. set ds[j] = a[i], or append a[i] at the end of ds
int s=0, t=dsl-1;
while (s<=t) {
int m = s+(t-s)/2;
if (ds[m] < a[i]) {
t = m - 1;
} else {
s = m + 1;
}
}
// now s must be at the first ds[j]<a[i], or at the end of ds[]
ds[s] = a[i];
if (s > dsl) { dsl = s; lastdsl = i; }
}
// now trace back.
for (int i=lastdsl-1, j=dsl-1; i>=0 && j >= 0; i--) {
if (a[i] == ds[j]) { j --; }
else if (a[i] < ds[j]) { ds[j--] = a[i]; }
}
return Arrays.copyOfRange(ds, 0, dsl+1);
}
48.微软:
一个数组是由一个递减数列左移若干位形成的,比如{4,3,2,1,6,5}
是由{6,5,4,3,2,1}左移两位形成的,在这种数组中查找某一个数。
ANSWER:
The key is that, from the middle point of the array, half of the array is sorted, and the other half is a half-size shifted sorted array. So this can also be done recursively like a binary search.
int shiftedBinarySearch(int a[], int k) {
return helper(a, k, 0, n-1);
}
int helper(int a[], int k, int s, int t) {
if (s>t) return -1;
int m = s + (t-s)/2;
if (a[m] == k) return m;
else if (a[s] >= k && k > a[m]) return helper(a, k, s, m-1);
else return helper(a, k, m+1, e);
}
49.一道看上去很吓人的算法面试题:
如何对n 个数进行排序,要求时间复杂度O(n),空间复杂度O(1)
ANSWER:
So a comparison sort is not allowed. Counting sort’s space complexity is O(n).
More ideas must be exchanged to find more conditions, else this is a crap.
50.网易有道笔试:
1.求一个二叉树中任意两个节点间的最大距离,两个节点的距离的定义是这两个节点间边
的个数,
比如某个孩子节点和父节点间的距离是1,和相邻兄弟节点间的距离是2,优化时间空间复
杂度。
ANSWER:
Have done this before.
2.求一个有向连通图的割点,割点的定义是,
如果除去此节点和与其相关的边,有向图不再连通,描述算法。
ANSWER:
Have done this before.
-------------------------------------------------------------------
51.和为n 连续正数序列。
题目:输入一个正数n,输出所有和为n 连续正数序列。
例如输入15,由于1+2+3+4+5=4+5+6=7+8=15,所以输出3 个连续序列1-5、4-6 和7-8。
分析:这是网易的一道面试题。
ANSWER:
It seems that this can be solved by factorization. However, factorization of large n is impractical!
Suppose n=i+(i+1)+...+(j-1)+j, then n = (i+j)(j-i+1)/2 = (j*j - i*i + i + j)/2
=> j^2 + j + (i-i^2-2n) = 0 => j=sqrt(i^2-i+1/4+2n) - 1/2
We know 1 <= i < j <= n/2 + 1
So for each i in [1, n/2], do this arithmetic to check if there is a integer answer.
int findConsecutiveSequence(int n) {
count = 0;
for (int i=1; i<=n/2; i++) {
int sqroot = calcSqrt(4*i*i+8*n-4*i+1);
if (sqroot == -1) continue;
if ((sqroot & 1) == 1) {
System.out.println(i+”-” + ((sqroot-1)/2));
count ++;
}
}
return count;
}
Use binary search to calculate sqrt, or just use math functions.
52.二元树的深度。
题目:输入一棵二元树的根结点,求该树的深度。
从根结点到叶结点依次经过的结点(含根、叶结点)形成树的一条路径,最长路径的长度为
树的深度。
例如:输入二元树:
10
/ \
6 14
/ / \
4 12 16
输出该树的深度3。
二元树的结点定义如下:
struct SBinaryTreeNode // a node of the binary tree
{
int m_nValue; // value of node
SBinaryTreeNode *m_pLeft; // left child of node
SBinaryTreeNode *m_pRight; // right child of node
};
分析:这道题本质上还是考查二元树的遍历。
ANSWER:
Have done this.
53.字符串的排列。
题目:输入一个字符串,打印出该字符串中字符的所有排列。
例如输入字符串abc,则输出由字符a、b、c 所能排列出来的所有字符串
abc、acb、bac、bca、cab 和cba。
分析:这是一道很好的考查对递归理解的编程题,
因此在过去一年中频繁出现在各大公司的面试、笔试题中。
ANSWER:
Full permutation generation. I will use another technique that swap two neighboring characters each time. It seems that all the characters are different. I need to think about how to do it when duplications is allowed. Maybe simple recursion is better for that.
void generatePermutation(char s[], int n) {
if (n>20) { error(“are you crazy?”); }
byte d[n];
int pos[n], dpos[n]; // pos[i], the position of i’th number, dpos[i] the number in s[i] is the dpos[i]’th smallest
qsort(s); // I cannot remember the form of qsort in C...
memset(d, -1, sizeof(byte)*n);
for (int i=0; i<n; i++) pos[i]=i, dpos[i]=i;
int r;
while (r = findFirstAvailable(s, d, pos, n)) {
if (r== -1) return;
swap(s, pos, dpos, d, r, r+d[r]);
for (int i=n-1; i>dpos[r]; i--)
d[i] = -d[i];
}
}
int findFirstAvailable(char s[], byte d[], int pos[], int n) {
for (int i=n-1; i>1; i--) {
if (s[pos[i]] > s[pos[i]+d[pos[i]]]) return pos[i];
}
return -1;
}
#define aswap(ARR, X, Y) {int t=ARR[X]; ARR[X]=ARR[y]; ARR[Y]=t;}
void swap(char s[], int pos[], int dpos[], byte d[], int r, int s) {
aswap(s, r, s);
aswap(d, r, s);
aswap(pos, dpos[r], dpos[s]);
aswap(dpos, r, s);
}
Maybe full of bugs. Please refer to algorithm manual for explansion.
Pros: Amotized O(1) time for each move. Only two characters change position for each move.
Cons: as you can see, very complicated. Extra space needed.
54.调整数组顺序使奇数位于偶数前面。
题目:输入一个整数数组,调整数组中数字的顺序,使得所有奇数位于数组的前半部分,
所有偶数位于数组的后半部分。要求时间复杂度为O(n)。
ANSWER:
This problem makes me recall the process of partition in quick sort.
void partition(int a[], int n) {
int i=j=0;
while (i < n && (a[i] & 1)==0) i++;
if (i==n) return;
swap(a, i++, j++);
while (i<n) {
if ((a[i] & 1) == 1) {
swap(a, i, j++);
}
i++;
}
}
55. 题目:类CMyString 的声明如下:
class CMyString
{
public:
CMyString(char* pData = NULL);
CMyString(const CMyString& str);
~CMyString(void);
CMyString& operator = (const CMyString& str);
private:
char* m_pData;
};
请实现其赋值运算符的重载函数,要求异常安全,即当对一个对象进行赋值时发生异常,对
象的状态不能改变。
ANSWER
I don’t know C++...
56.最长公共字串。
题目:如果字符串一的所有字符按其在字符串中的顺序出现在另外一个字符串二中,
则字符串一称之为字符串二的子串。
注意,并不要求子串(字符串一)的字符必须连续出现在字符串二中。
请编写一个函数,输入两个字符串,求它们的最长公共子串,并打印出最长公共子串。
例如:输入两个字符串BDCABA 和ABCBDAB,字符串BCBA 和BDAB 都是是它们的最长公共子串,则输出它们的长度4,并打印任意一个子串。
分析:求最长公共子串(Longest Common Subsequence, LCS)是一道非常经典的动态规划
题,因此一些重视算法的公司像MicroStrategy 都把它当作面试题。
ANSWER:
Standard DP...
lcs(ap1, bp2) = max{ lcs(p1,p2)+1, lcs(p1, bp2), lcs(ap1, p2)}
int LCS(char *p1, char *p2) {
int l1= strlen(p1)+1, l2=strlen(p2)+1;
int a[l1*l2];
for (int i=0; i<l1; i++) a[i*l2] = 0;
for (int i=0; i<l2; i++) a[i] = 0;
for (int i=1; i<l1; i++) {
for (int j=1; j<l2; j++) {
int max = MAX(a[(i-1)*l2+l1], a[i*l2+l1-1]);
if (p1[i-1] == p2[j-1]) {
max = (max > 1 + a[(i-1)*l2+j-1]) ? max : 1+a[(i-1)*l2+j-1];
}
}
}
return a[l1*l2-1];
}
57.用俩个栈实现队列。
题目:某队列的声明如下:
template<typename T> class CQueue
{
public:
CQueue() {}
~CQueue() {}
void appendTail(const T& node); // append a element to tail
void deleteHead(); // remove a element from head
private:
Stack<T> m_stack1;
Stack<T> m_stack2;
};
分析:从上面的类的声明中,我们发现在队列中有两个栈。
因此这道题实质上是要求我们用两个栈来实现一个队列。
相信大家对栈和队列的基本性质都非常了解了:栈是一种后入先出的数据容器,
因此对队列进行的插入和删除操作都是在栈顶上进行;队列是一种先入先出的数据容器,
我们总是把新元素插入到队列的尾部,而从队列的头部删除元素。
ANSWER
Traditional problem in CLRS.
void appendTail(const T& node) {
m_stack1.push(node);
}
T getHead() {
if (!m_stack2.isEmpty()) {
return m_stack2.pop();
}
if (m_stack1.isEmpty()) error(“delete from empty queue”);
while (!m_stack1.isEmpty()) {
m_stack2.push(m_stack1.pop());
}
return m_stack2.pop();
}
58.从尾到头输出链表。
题目:输入一个链表的头结点,从尾到头反过来输出每个结点的值。链表结点定义如下:
struct ListNode
{
int m_nKey;
ListNode* m_pNext;
};
分析:这是一道很有意思的面试题。
该题以及它的变体经常出现在各大公司的面试、笔试题中。
ANSWER
Have answered this...
59.不能被继承的类。
题目:用C++设计一个不能被继承的类。
分析:这是Adobe 公司2007 年校园招聘的最新笔试题。
这道题除了考察应聘者的C++基本功底外,还能考察反应能力,是一道很好的题目。
ANSWER:
I don’t know c++.
Maybe it can be done by implement an empty private default constructor.
60.在O(1)时间内删除链表结点。
题目:给定链表的头指针和一个结点指针,在O(1)时间删除该结点。链表结点的定义如下:
struct ListNode
{
int m_nKey;
ListNode* m_pNext;
};
函数的声明如下:
void DeleteNode(ListNode* pListHead, ListNode* pToBeDeleted);
分析:这是一道广为流传的Google 面试题,能有效考察我们的编程基本功,还能考察我们
的反应速度,
更重要的是,还能考察我们对时间复杂度的理解。
ANSWER:
Copy the data from tobedeleted’s next to tobedeleted. then delete tobedeleted. The special case is tobedelete is the tail, then we must iterate to find its predecessor.
The amortized time complexity is O(1).
-------------------------------------------------------------------------
61.找出数组中两个只出现一次的数字
题目:一个整型数组里除了两个数字之外,其他的数字都出现了两次。
请写程序找出这两个只出现一次的数字。要求时间复杂度是O(n),空间复杂度是O(1)。
分析:这是一道很新颖的关于位运算的面试题。
ANSWER:
XOR.
62.找出链表的第一个公共结点。
题目:两个单向链表,找出它们的第一个公共结点。
链表的结点定义为:
struct ListNode
{
int m_nKey;
ListNode* m_pNext;
};
分析:这是一道微软的面试题。微软非常喜欢与链表相关的题目,
因此在微软的面试题中,链表出现的概率相当高。
ANSWER:
Have done this.
63.在字符串中删除特定的字符。
题目:输入两个字符串,从第一字符串中删除第二个字符串中所有的字符。例如,输入”They are students.”和”aeiou”, 则删除之后的第一个字符串变成”Thy r stdnts.”。
分析:这是一道微软面试题。在微软的常见面试题中,与字符串相关的题目占了很大的一部
分,因为写程序操作字符串能很好的反映我们的编程基本功。
ANSWER:
Have done this? Use a byte array / character hash to record second string. then use two pointers to shrink the 1st string.
64. 寻找丑数。
题目:我们把只包含因子2、3 和5 的数称作丑数(Ugly Number)。例如6、8 都是丑数,
但14 不是,因为它包含因子7。习惯上我们把1 当做是第一个丑数。求按从小到大的顺序的第1500 个丑数。
分析:这是一道在网络上广为流传的面试题,据说google 曾经采用过这道题。
ANSWER:
TRADITIONAL.
Use heap/priority queue.
int no1500() {
int heap[4500];
heap[0] = 2; heap[1] = 3; heap[2] = 5;
int size = 3;
for (int i=1; i<1500; i++) {
int s = heap[0];
heap[0] = s*2; siftDown(heap, 0, size);
heap[size] = s*3; siftUp(heap, size, size+1);
heap[size+1] = s*5; siftUp(heap, size+1, size+2);
size+=2;
}
}
void siftDown(int heap[], int from, int size) {
int c = from * 2 + 1;
while (c < size) {
if (c+1<size && heap[c+1] < heap[c]) c++;
if (heap[c] < heap[from]) swap(heap, c, from);
from = c; c=from*2+1;
}
}
void siftUp(int heap[], int from, int size) {
while (from > 0) {
int p = (from - 1) / 2;
if (heap[p] > heap[from]) swap(heap, p, from);
from = p;
}
}
65.输出1 到最大的N 位数
题目:输入数字n,按顺序输出从1 最大的n 位10 进制数。比如输入3,则输出1、2、3 一直到最大的3 位数即999。
分析:这是一道很有意思的题目。看起来很简单,其实里面却有不少的玄机。
ANSWER:
So maybe n could exceed i32? I cannot tell where is the trick...
Who will output 2*10^9 numbers...
66.颠倒栈。
题目:用递归颠倒一个栈。例如输入栈{1, 2, 3, 4, 5},1 在栈顶。
颠倒之后的栈为{5, 4, 3, 2, 1},5 处在栈顶。
ANSWER:
Interesting...
void reverse(Stack stack) {
if (stack.size() == 1) return;
Object o = stack.pop();
reverse(stack);
putToBottom(stack, o);
}
void putToBottom(Stack stack, Object o) {
if (stack.isEmpty()) {
stack.push(o);
return;
}
Object o2 = stack.pop();
putToBottom(stack, o);
stack.push(o2);
}
67.俩个闲玩娱乐。
1.扑克牌的顺子
从扑克牌中随机抽5 张牌,判断是不是一个顺子,即这5 张牌是不是连续的。2-10 为数字本身,A 为1,J 为11,Q 为12,K 为13,而大小王可以看成任意数字。
ANSWER:
// make king = 0
boolean isStraight(int a[]) {
Arrays.sort(a);
if (a[0] > 0) return checkGaps(a, 0, 4, 0);
if (a[0] == 0 && a[1] != 0) return checkGaps(a, 1, 4, 1);
return checkGaps(a, 2, 4, 2);
}
boolean checkGaps(int []a, int s, int e, int allowGaps) {
int i=s;
while (i<e) {
allowGaps -= a[i+1] - a[i] - 1;
if (allowGaps < 0) return false;
i++;
}
return true;
}
2.n 个骰子的点数。把n 个骰子扔在地上,所有骰子朝上一面的点数之和为S。输入n,
打印出S 的所有可能的值出现的概率。
ANSWER:
All the possible values includes n to 6n. All the event number is 6^n.
For n<=S<=6n, the number of events is f(S, n)
f(S,n) = f(S-6, n-1) + f(S-5, n-1) + … + f(S-1, n-1)
number of events that all dices are 1s is only 1, and thus f(k, k) = 1, f(1-6, 1) = 1, f(x, 1)=0 where x<1 or x>6, f(m, n)=0 where m<n
Can do it in DP.
void listAllProbabilities(int n) {
int[][] f = new int[6*n+1][];
for (int i=0; i<=6*n; i++) {
f[i] = new int[n+1];
}
for (int i=1; i<=6; i++) {
f[i][1] = 1;
}
for (int i=1; i<=n; i++) {
f[i][i] = 1;
}
for (int i=2; i<=n; i++) {
for (int j=i+1; j<=6*i; j++) {
for (int k=(j-6<i-1)?i-1:j-6; k<j-1; k++)
f[j][i] += f[k][i-1];
}
}
double p6 = Math.power(6, n);
for (int i=n; i<=6*n; i++) {
System.out.println(“P(S=”+i+”)=”+((double)f[i][n] / p6));
}
}
68.把数组排成最小的数。
题目:输入一个正整数数组,将它们连接起来排成一个数,输出能排出的所有数字中最小的
一个。
例如输入数组{32, 321},则输出这两个能排成的最小数字32132。
请给出解决问题的算法,并证明该算法。
分析:这是09 年6 月份百度的一道面试题,
从这道题我们可以看出百度对应聘者在算法方面有很高的要求。
ANSWER:
Actually this problem has little to do with algorithm...
The concern is, you must figure out how to arrange to achieve a smaller figure.
The answer is, if ab < ba, then a < b, and this is a total order.
String smallestDigit(int a[]) {
Integer aux[] = new Integer[a.length];
for (int i=0; i<a.length; a++) aux[i] = a[i];
Arrays.sort(aux, new Comparator<Integer>(){
int compareTo(Integer i1, Integer i2) {
return (“”+i1+i2).compare(“”+i2+i1);
}
});
StringBuffer sb = new StringBuffer();
for (int i=0; i<aux.length, i++) {
sb.append(aux[i]);
}
return sb.toString();
}
69.旋转数组中的最小元素。
题目:把一个数组最开始的若干个元素搬到数组的末尾,我们称之为数组的旋转。输入一个
排好序的数组的一个旋转,
输出旋转数组的最小元素。例如数组{3, 4, 5, 1, 2}为{1, 2, 3, 4, 5}的一个旋转,该数
组的最小值为1。
分析:这道题最直观的解法并不难。从头到尾遍历数组一次,就能找出最小的元素,时间复杂度显然是O(N)。但这个思路没有利用输入数组的特性,我们应该能找到更好的解法。
ANSWER
This is like the shifted array binary search problem. One blind point is that you may miss the part that the array is shifted by 0(or kN), that is not shifted.
int shiftedMinimum(int a[], int n) {
return helper(a, 0, n-1);
}
int helper(int a[], int s, int t) {
if (s == t || a[s] < a[t]) return a[s];
int m = s + (t-s)/2;
if (a[s]>a[m]) return helper(a, s, m);
else return helper(a, m+1, t);
}
70.给出一个函数来输出一个字符串的所有排列。
ANSWER 简单的回溯就可以实现了。当然排列的产生也有很多种算法,去看看组合数学,
还有逆序生成排列和一些不需要递归生成排列的方法。
印象中Knuth 的<TAOCP>第一卷里面深入讲了排列的生成。这些算法的理解需要一定的数学功底,也需要一定的灵感,有兴趣最好看看。
ANSWER:
Have done this.
71.数值的整数次方。
题目:实现函数double Power(double base, int exponent),求base 的exponent 次方。
不需要考虑溢出。
分析:这是一道看起来很简单的问题。可能有不少的人在看到题目后30 秒写出如下的代码:
double Power(double base, int exponent)
{
double result = 1.0;
for(int i = 1; i <= exponent; ++i)
result *= base;
return result;
}
ANSWER
…
double power(double base, int exp) {
if (exp == 1) return base;
double half = power(base, exp >> 1);
return (((exp & 1) == 1) ? base : 1.0) * half * half;
}
72. 题目:设计一个类,我们只能生成该类的一个实例。
分析:只能生成一个实例的类是实现了Singleton 模式的类型。
ANSWER
I’m not good at multithread programming... But if we set a lazy initialization, the “if” condition could be interrupted thus multiple constructor could be called, so we must add synchronized to the if judgements, which is a loss of efficiency. Putting it to the static initialization will guarantee that the constructor only be executed once by the java class loader.
public class Singleton {
private static Singleton instance = new Singleton();
private synchronized Singleton() {
}
public Singleton getInstance() {
return instance();
}
}
This may not be correct. I’m quite bad at this...
73.对策字符串的最大长度。
题目:输入一个字符串,输出该字符串中对称的子字符串的最大长度。比如输入字符串“google”,由于该字符串里最长的对称子字符串是“goog”,因此输出4。
分析:可能很多人都写过判断一个字符串是不是对称的函数,这个题目可以看成是该函数的
加强版。
ANSWER
Build a suffix tree of x and inverse(x), the longest anagram is naturally found.
Suffix tree can be built in O(n) time so this is a linear time solution.
74.数组中超过出现次数超过一半的数字
题目:数组中有一个数字出现的次数超过了数组长度的一半,找出这个数字。
分析:这是一道广为流传的面试题,包括百度、微软和Google 在内的多家公司都
曾经采用过这个题目。要几十分钟的时间里很好地解答这道题,
除了较好的编程能力之外,还需要较快的反应和较强的逻辑思维能力。
ANSWER
Delete every two different digits. The last one that left is the one.
int getMajor(int a[], int n) {
int x, cnt=0;
for (int i=0; i<n; i++) {
if (cnt == 0) {
x = a[i]; cnt++;
} else if (a[i]==x) {
cnt ++;
} else {
cnt --;
}
}
return x;
}
75.二叉树两个结点的最低共同父结点
题目:二叉树的结点定义如下:
struct TreeNode
{
int m_nvalue;
TreeNode* m_pLeft;
TreeNode* m_pRight;
};
输入二叉树中的两个结点,输出这两个结点在数中最低的共同父结点。
分析:求数中两个结点的最低共同结点是面试中经常出现的一个问题。这个问题至少有两个
变种。
ANSWER
Have done this. Do it again for memory...
TreeNode* getLCA(TreeNode* root, TreeNode* X, TreeNode *Y) {
if (root == NULL) return NULL;
if (X == root || Y == root) return root;
TreeNode * left = getLCA(root->m_pLeft, X, Y);
TreeNode * right = getLCA(root->m_pRight, X, Y);
if (left == NULL) return right;
else if (right == NULL) return left;
else return root;
}
76.复杂链表的复制
题目:有一个复杂链表,其结点除了有一个m_pNext 指针指向下一个结点外,还有一个m_pSibling 指向链表中的任一结点或者NULL。其结点的C++定义如下:
struct ComplexNode
{
int m_nValue;
ComplexNode* m_pNext;
ComplexNode* m_pSibling;
};
下图是一个含有5 个结点的该类型复杂链表。
图中实线箭头表示m_pNext 指针,虚线箭头表示m_pSibling 指针。为简单起见,指向NULL 的指针没有画出。请完成函数ComplexNode* Clone(ComplexNode* pHead),以复制一个复杂链表。
分析:在常见的数据结构上稍加变化,这是一种很新颖的面试题。
要在不到一个小时的时间里解决这种类型的题目,我们需要较快的反应能力,
对数据结构透彻的理解以及扎实的编程功底。
ANSWER
Have heard this before, never seriously thought it.
The trick is like this: take use of the old pSibling, make it points to the new created cloned node, while make the new cloned node’s pNext backup the old pSibling.
ComplexNode * Clone(ComplexNode* pHead) {
if (pHead == NULL) return NULL;
preClone(pHead);
inClone(pHead);
return postClone(pHead);
}
void preClone(ComplexNode* pHead) {
ComplexNode * p = new ComplexNode();
p->m_pNext = pHead->m_pSibling;
pHead->m_pSibling = p;
if (pHead->m_pNext != NULL) preClone(pHead->m_pNext);
}
void inClone(ComplexNode * pHead) {
ComplexNode * pSib = pNew->m_pNext;
if (pSib == NULL) { pNew->m_pSibling = NULL; }
else { pNew->m_pSibling = pSib->m_pSibling; }
if (pHead->m_pNext != NULL) inClone(pHead->m_pNext);
}
ComplexNode * postClone(ComplexNode * pHead) {
ComplexNode * pNew = pHead->m_pSibling;
ComplexNode * pSib = pNew->m_pNext;
if (pHead->m_pNext != NULL) {
pNew->m_pNext = pHead->m_pNext->m_pSibling;
pHead->m_pSibling = pSib;
postClone(pHead->m_pNext);
} else {
pNew->pNext = NULL;
pHead->m_pSibling = NULL;
}
return pNew;
}
77.关于链表问题的面试题目如下:
1.给定单链表,检测是否有环。
使用两个指针p1,p2 从链表头开始遍历,p1 每次前进一步,p2 每次前进两步。如果p2 到
达链表尾部,说明无环,否则p1、p2 必然会在某个时刻相遇(p1==p2),从而检测到链表中有环。
2. 给定两个单链表(head1, head2),检测两个链表是否有交点,如果有返回第一个交点。如果head1==head2,那么显然相交,直接返回head1。否则,分别从 head1,head2 开始遍历两个链表获得其长度len1 与len2,假设len1>=len2,那么指针p1 由head1 开始向后移动len1-len2 步,指针p2=head2,下面p1、p2 每次向后前进一步并比较p1p2 是否相等,如果相等即返回该结点,否则说明两个链表没有交点。
3.给定单链表(head),如果有环的话请返回从头结点进入环的第一个节点。
运 用题一,我们可以检查链表中是否有环。如果有环,那么p1p2 重合点p 必然在环中。从p 点断开环,方法为:p1=p, p2=p->next, p->next=NULL。此时,原单链表可以看作两条单链表,一条从head 开始,另一条从p2 开始,于是运用题二的方法,我们找到它们的第一个交点即为所求。
4.只给定单链表中某个结点p(并非最后一个结点,即p->next!=NULL)指针,删除该结点。办法很简单,首先是放p 中数据,然后将p->next 的数据copy 入p 中,接下来删除p->next即可。
5.只给定单链表中某个结点p(非空结点),在p 前面插入一个结点。办法与前者类似,首先分配一个结点q,将q 插入在p 后,接下来将p 中的数据copy 入q中,然后再将要插入的数据记录在p 中。
78.链表和数组的区别在哪里?
分析:主要在基本概念上的理解。
但是最好能考虑的全面一点,现在公司招人的竞争可能就在细节上产生,谁比较仔细,谁获胜的机会就大。
ANSWER
1. Besides the common staff, linked list is more abstract and array is usually a basic real world object. When mentioning “linked list”, it doesn’t matter how it is implemented, that is, as long as it supports “get data” and “get next”, it is a linked list. But almost all programming languages provides array as a basic data structure.
2. So array is more basic. You can implement a linked list in an array, but cannot in the other direction.
79.
1.编写实现链表排序的一种算法。说明为什么你会选择用这样的方法?
ANSWER
For linked list sorting, usually mergesort is the best choice. Pros: O(1) auxilary space, compared to array merge sort. No node creation, just pointer operations.
Node * linkedListMergeSort(Node * pHead) {
int len = getLen(pHead);
return mergeSort(pHead, len);
}
Node * mergeSort(Node * p, int len) {
if (len == 1) { p->next = NULL; return p; }
Node * pmid = p;
for (int i=0; i<len/2; i++) {
pmid = pmid->next;
}
Node * p1 = mergeSort(p, len/2);
Node * p2 = mergeSort(pmid, len - len/2);
return merge(p1, p2);
}
Node * merge(Node * p1, Node * p2) {
Node * p = NULL, * ph = NULL;
while (p1!=NULL && p2!=NULL) {
if (p1->data<p2->data) {
if (ph == NULL) {ph = p = p1;}
else { p->next = p1; p1 = p1->next; p = p->next;}
} else {
if (ph == NULL) {ph = p = p2;}
else { p->next = p2; p2 = p2->next; p = p->next;}
}
}
p->next = (p1==NULL) ? p2 : p1;
return ph;
}
2.编写实现数组排序的一种算法。说明为什么你会选择用这样的方法?
ANSWER
Actually, it depends on the data. If arbitrary data is given in the array, I would choose quick sort. It is asy to implement, fast.
3.请编写能直接实现strstr()函数功能的代码。
ANSWER
Substring test? Have done this.
80.阿里巴巴一道笔试题
问题描述:
12 个高矮不同的人,排成两排,每排必须是从矮到高排列,而且第二排比对应的第一排的人
高,问排列方式有多少种?
这个笔试题,很YD,因为把某个递归关系隐藏得很深。
ANSWER
Must be
1 a b … …
c d e … …
c could be 2th to 7th ( has to be smaller than d, e... those 5 numbers),
so f(12) = 6 f(10) = 6* 5 f(8) = 30 * 4f(6) = 120*3f(4) = 360*2f(2) = 720
81.第1 组百度面试题
1.一个int 数组,里面数据无任何限制,要求求出所有这样的数a[i],其左边的数都小于等于它,右边的数都大于等于它。能否只用一个额外数组和少量其它空间实现。
ANSWER
Sort the array to another array, compare it with the original array, all a[i] = b[i] are answers.
2.一个文件,内含一千万行字符串,每个字符串在1K 以内,要求找出所有相反的串对,如abc 和cba。
ANSWER
So we have ~10G data. It is unlikely to put them all into main memory. Anyway, calculate the hash of each line in the first round, at the second round calculate the hash of the reverse of the line and remembers only the line number pairs that the hashes of the two directions collides. The last round only test those lines.
3.STL 的set 用什么实现的?为什么不用hash?
ANSWER
I don’t quite know. Only heard of that map in stl is implemented with red-black tree. One good thing over hash is that you don’t need to re-hash when data size grows.
82.第2 组百度面试题
1.给出两个集合A 和B,其中集合A={name},
集合B={age、sex、scholarship、address、...},
要求:
问题1、根据集合A 中的name 查询出集合B 中对应的属性信息;
问题2、根据集合B 中的属性信息(单个属性,如age<20 等),查询出集合A 中对应的name。
ANSWER
SQL? Not a good defined question.
2.给出一个文件,里面包含两个字段{url、size},即url 为网址,size 为对应网址访问的次数
要求:
问题1、利用Linux Shell 命令或自己设计算法,查询出url 字符串中包含“baidu”子字符串对应的size 字段值;
问题2、根据问题1 的查询结果,对其按照size 由大到小的排列。
(说明:url 数据量很大,100 亿级以上)
ANSWER
1. shell: gawk ‘ /baidu/ { print $2 } ’ FILE
2. shell: gawk ‘ /baidu/ {print $2}’ FILE | sort -n -r
83.第3 组百度面试题
1.今年百度的一道题目
百度笔试:给定一个存放整数的数组,重新排列数组使得数组左边为奇数,右边为偶数。
要求:空间复杂度O(1),时间复杂度为O(n)。
ANSWER
Have done this.
2.百度笔试题
用C 语言实现函数void * memmove(void *dest, const void *src, size_t n)。memmove 函数的功能是拷贝src 所指的内存内容前n 个字节到dest 所指的地址上。
分析:
由于可以把任何类型的指针赋给void 类型的指针, 这个函数主要是实现各种数据类型的拷贝。
ANSWER
//To my memory, usually memcpy doesn’t check overlap, memmove do
void * memmove(void * dest, const void * src, size_t n) {
if (dest==NULL || src == NULL) error(“NULL pointers”);
byte * psrc = (byte*)src;
byte * pdest = (byte*)dest;
int step = 1;
if (dest < src + n) {
psrc = (byte*)(src+n-1);
pdest = (byte*)(dest+n-1);
step = -1;
}
for (int i=0; i<n; i++) {
pdest = psrc;
pdest += step; psrc += step;
}
}
84.第4 组百度面试题
2010 年3 道百度面试题[相信,你懂其中的含金量]
1.a~z 包括大小写与0~9 组成的N 个数, 用最快的方式把其中重复的元素挑出来。
ANSWER
By fastest, so memory is not the problem, hash is the first choice. Or trie will do.
Both run in O(Size) time, where size is the total size of the imput.
2. 已知一随机发生器,产生0 的概率是p,产生1 的概率是1-p,现在要你构造一个发生器,使得它构造0 和1 的概率均为1/2;构造一个发生器,使得它构造1、2、3 的概率均为1/3;...,构造一个发生器,使得它构造1、2、3、...n 的概率均为1/n,要求复杂度最低。
ANSWER
Run rand() twice, we got 00, 01, 10 or 11. If it’s 00 or 11, discard it, else output 0 for 01, 1 for 10.
Similarly, assume C(M, 2) >= n and C(M-1, 2) < n. Do M rand()’s and get a binary string of M length. Assign 1100...0 to 1, 1010...0 to 2, ...
3.有10 个文件,每个文件1G,
每个文件的每一行都存放的是用户的query,每个文件的query 都可能重复。
要求按照query 的频度排序.
ANSWER
If there is no enough memory, do bucketing first. For each bucket calculate the frequency of each query and sort. Then combine all the frequencies with multiway mergesort.
85.又见字符串的问题
1.给出一个函数来复制两个字符串A 和B。字符串A 的后几个字节和字符串B 的前几个字节重叠。分析:记住,这种题目往往就是考你对边界的考虑情况。
ANSWER
Special case of memmove.
2.已知一个字符串,比如asderwsde,寻找其中的一个子字符串比如sde 的个数,如果没有返回0,有的话返回子字符串的个数。
ANSWER
ANSWER
int count_of_substr(const char* str, const char * sub) {
int count = 0;
char * p = str;
int n = strlen(sub);
while ( *p != ‘\0’ ) {
if (strncmp(p, sub, n) == 0) count ++;
p++;
}
return count;
}
Also recursive way works. Possible optimizations like Sunday algorithm or Rabin-Karp algorithm will do.
86.
怎样编写一个程序,把一个有序整数数组放到二叉树中?
分 析:本题考察二叉搜索树的建树方法,简单的递归结构。关于树的算法设计一定要联想到递归,因为树本身就是递归的定义。而,学会把递归改称非递归也是一种必 要的技术。毕竟,递归会造成栈溢出,关于系统底层的程序中不到非不得以最好不要用。但是对某些数学问题,就一定要学会用递归去解决。
ANSWER
This is the first question I’m given in a google interview.
Node * array2Tree(int[] array) {
return helper(array, 0, n-1);
}
Node * helper(int[] array, int start, int end) {
if (start > end) return NULL;
int m = start + (end-start)/2;
Node * root = new Node(array[m]);
root->left = helper(array, start, m-1);
root->right = helper(array, m+1, end);
return root;
}
87.
1.大整数数相乘的问题。(这是2002 年在一考研班上遇到的算法题)
ANSWER
Do overflow manually.
final static long mask = (1 << 31) - 1;
ArrayList<Integer> multiply(ArrayList <Integer> a, ArrayList<Integer> b) {
ArrayList<Integer> result = new ArrayList<Integer>(a.size()*b.size()+1);
for (int i=0; i<a.size(); i++) {
multiply(b, a.get(i), i, result);
}
return result;
}
void multiply(ArrayList<Integer> x, int a, int base, ArrayList<Integer> result) {
if (a == 0) return;
long overflow = 0;
int i;
for (i=0; i<x.size(); i++) {
long tmp = x.get(i) * a + result.get(base+i) + overflow;
result.set(base+i, (int)(mask & tmp));
overflow = (tmp >> 31);
}
while (overflow != 0) {
long tmp = result.get(base+i) + overflow;
result.set(base+i, (int) (mask & tmp));
overflow = (tmp >> 31);
}
}
2.求最大连续递增数字串(如“ads3sl456789DF3456ld345AA”中的“456789”)
ANSWER
Have done this.
3.实现strstr 功能,即在父串中寻找子串首次出现的位置。
(笔试中常让面试者实现标准库中的一些函数)
ANSWER
Have done this.
88.2005 年11 月金山笔试题。编码完成下面的处理函数。
函数将字符串中的字符'*'移到串的前部分,前面的非'*'字符后移,但不能改变非'*'字符的先后顺序,函数返回串中字符'*'的数量。如原始串为:ab**cd**e*12,处理后为*****abcde12,函数并返回值为5。(要求使用尽量少的时间和辅助空间)
ANSWER
It’s like partition in quick sort. Just keep the non-* part stable.
int partitionStar(char a[]) {
int count = 0;
int i = a.length-1, j=a.length-1; // i for the cursor, j for the first non-* char
while (i >= 0) {
if (a[i] != ‘*’) {
swap(a, i--, j--);
} else {
i--; count ++;
}
}
return count;
}
89.神州数码、华为、东软笔试题
1.2005 年11 月15 日华为软件研发笔试题。实现一单链表的逆转。
ANSWER
Have done this.
2.编码实现字符串转整型的函数(实现函数atoi 的功能),据说是神州数码笔试题。如将字符串”+123”123, ”-0123”-123, “123CS45”123, “123.45CS”123, “CS123.45”0
ANSWER
int atoi(const char * a) {
if (*a==’+’) return atoi(a+1);
else if (*a==’-’) return - atoi(a+1);
char *p = a;
int c = 0;
while (*p >= ‘0’ && *p <= ‘9’) {
c = c*10 + (*p - ‘0’);
}
return c;
}
3.快速排序(东软喜欢考类似的算法填空题,又如堆排序的算法等)
ANSWER
Standard solution. Skip.
4.删除字符串中的数字并压缩字符串。如字符串”abc123de4fg56”处理后变为”abcdefg”。注意空间和效率。(下面的算法只需要一次遍历,不需要开辟新空间,时间复杂度为O(N))
ANSWER
Also partition, keep non-digit stable.
char * partition(const char * str) {
char * i = str; // i for cursor, j for the first digit char;
char * j = str;
while (*i != ‘\0’) {
if (*i > ‘9’ || *i < ‘0’) {
*j++ = *i++;
} else {
*i++;
}
}
*j = ‘\0’;
return str;
}
5.求两个串中的第一个最长子串(神州数码以前试题)。
如"abractyeyt","dgdsaeactyey"的最大子串为"actyet"。
ANSWER
Use suffix tree. The longest common substring is the longest prefix of the suffixes.
O(n) to build suffix tree. O(n) to find the lcs.
90.
1.不开辟用于交换数据的临时空间,如何完成字符串的逆序
(在技术一轮面试中,有些面试官会这样问)。
ANSWER
Two cursors.
2.删除串中指定的字符
(做此题时,千万不要开辟新空间,否则面试官可能认为你不适合做嵌入式开发)
ANSWER
Have done this.
3.判断单链表中是否存在环。
ANSWER
Have done this.
91
1.一道著名的毒酒问题
有1000 桶酒,其中1 桶有毒。而一旦吃了,毒性会在1 周后发作。现在我们用小老鼠做实验,要在1 周内找出那桶毒酒,问最少需要多少老鼠。
ANSWER
Have done this. 10 mices.
2.有趣的石头问题
有一堆1 万个石头和1 万个木头,对于每个石头都有1 个木头和它重量一样,
把配对的石头和木头找出来。
ANSWER
Quick sort.
92.
1.多人排成一个队列,我们认为从低到高是正确的序列,但是总有部分人不遵守秩序。如果说,前面的人比后面的人高(两人身高一样认为是合适的), 那么我们就认为这两个人是一对“捣乱分子”,比如说,现在存在一个序列:
176, 178, 180, 170, 171
这些捣乱分子对为
<176, 170>, <176, 171>, <178, 170>, <178, 171>, <180, 170>, <180, 171>,
那么,现在给出一个整型序列,请找出这些捣乱分子对的个数(仅给出捣乱分子对的数目即可,不用具体的对)
要求:
输入:
为一个文件(in),文件的每一行为一个序列。序列全为数字,数字间用”,”分隔。
输出:
为一个文件(out),每行为一个数字,表示捣乱分子的对数。
详细说明自己的解题思路,说明自己实现的一些关键点。
并给出实现的代码,并分析时间复杂度。
限制:
输入每行的最大数字个数为100000 个,数字最长为6 位。程序无内存使用限制。
ANSWER
The answer is the swap number of insertion sort. The straightforward method is to do insertion sort and accumulate the swap numbers, which is slow: O(n^2)
A sub-quadratic solution can be done by DP.
f(n) = f(n-1) + Index(n)
Index(n), which is to determine how many numbers is smaller than a[n] in a[0..n-1], can be done in log(n) time using BST with subtree size.
93. 在一个int 数组里查找这样的数,它大于等于左侧所有数,小于等于右侧所有数。直观想法是用两个数组a、b。a[i]、b[i]分别保存从前到i 的最大的数和从后到i 的最小的数,一个解答:这需要两次遍历,然后再遍历一次原数组,将所有data[i]>=a[i-1]&&data[i]& lt;=b[i]的data[i]找出即可。给出这个解答后,面试官有要求只能用一个辅助数组,且要求少遍历一次。
ANSWER
It is natural to improve the hint... just during the second traversal, do the range minimum and picking together. There is no need to store the range minimums.
94.微软笔试题
求随机数构成的数组中找到长度大于=3 的最长的等差数列, 输出等差数列由小到大:
如果没有符合条件的就输出
格式:
输入[1,3,0,5,-1,6]
输出[-1,1,3,5]
要求时间复杂度,空间复杂度尽量小
ANSWER
Firstly sort the array. Then do DP: for each a[i], update the length of the arithmetic sequences. That’s a O(n^3) solution. Each arithmetic sequence can be determined by the last item and the step size.
95.华为面试题
1 判断一字符串是不是对称的,如:abccba
ANSWER
Two cursors.
2.用递归的方法判断整数组a[N]是不是升序排列
ANSWER
boolean isAscending(int a[]) {
return isAscending(a, 0);
}
boolean isAscending(int a[], int start) {
return start == a.length - 1 || isAscending(a, start+1);
}
96.08 年中兴校园招聘笔试题
1.编写strcpy 函数
已知strcpy 函数的原型是
char *strcpy(char *strDest, const char *strSrc);
其中strDest 是目的字符串,strSrc 是源字符串。不调用C++/C 的字符串库函数,请编写函数strcpy
ANSWER
char *strcpy(char *strDest, const char *strSrc) {
if (strSrc == NULL) return NULL;
char *i = strSrc, *j = strDest;
while (*i != ‘\0’) {
*j++ = *i++;
}
*j = ‘\0’;
return strDest;
}
Maybe you need to check if src and dest overlaps, then decide whether to copy from tail to head.
最后压轴之戏,终结此微软等100 题系列V0.1 版。
那就,
连续来几组微软公司的面试题,让你一次爽个够:
======================
97.第1 组微软较简单的算法面试题
1.编写反转字符串的程序,要求优化速度、优化空间。
ANSWER
Have done this.
2.在链表里如何发现循环链接?
ANSWER
Have done this.
3.编写反转字符串的程序,要求优化速度、优化空间。
ANSWER
Have done this.
4.给出洗牌的一个算法,并将洗好的牌存储在一个整形数组里。
ANSWER
Have done this.
5.写一个函数,检查字符是否是整数,如果是,返回其整数值。
(或者:怎样只用4 行代码编写出一个从字符串到长整形的函数?)
ANSWER
Char or string?
have done atoi;
98.第2 组微软面试题
1.给出一个函数来输出一个字符串的所有排列。
ANSWER
Have done this...
2.请编写实现malloc()内存分配函数功能一样的代码。
ANSWER
Way too hard as an interview question...
Please check wikipedia for solutions...
3.给出一个函数来复制两个字符串A 和B。字符串A 的后几个字节和字符串B 的前几个字节重叠。
ANSWER
Copy from tail to head.
4.怎样编写一个程序,把一个有序整数数组放到二叉树中?
ANSWER
Have done this.
5.怎样从顶部开始逐层打印二叉树结点数据?请编程。
ANSWER
Have done this...
6.怎样把一个链表掉个顺序(也就是反序,注意链表的边界条件并考虑空链表)?
ANSWER
Have done this...
99.第3 组微软面试题
1.烧一根不均匀的绳,从头烧到尾总共需要1 个小时。现在有若干条材质相同的绳子,问如何用烧绳的方法来计时一个小时十五分钟呢?
ANSWER
May have done this... burn from both side gives ½ hour.
2.你有一桶果冻,其中有黄色、绿色、红色三种,闭上眼睛抓取同种颜色的两个。抓取多少个就可以确定你肯定有两个同一颜色的果冻?(5 秒-1 分钟)
ANSWER
4.
3.如果你有无穷多的水,一个3 公升的提捅,一个5 公升的提捅,两只提捅形状上下都不均
匀,问你如何才能准确称出4 公升的水?(40 秒-3 分钟)
ANSWER
5 to 3 => 2
2 to 3, remaining 1
5 to remaining 1 => 4
一个岔路口分别通向诚实国和说谎国。
来了两个人,已知一个是诚实国的,另一个是说谎国的。
诚实国永远说实话,说谎国永远说谎话。现在你要去说谎国,
但不知道应该走哪条路,需要问这两个人。请问应该怎么问?(20 秒-2 分钟)
ANSWER
Seems there are too many answers.
I will pick anyone to ask: how to get to your country? Then pick the other way.
100.第4 组微软面试题,挑战思维极限
1.12 个球一个天平,现知道只有一个和其它的重量不同,问怎样称才能用三次就找到那个
球。13 个呢?(注意此题并未说明那个球的重量是轻是重,所以需要仔细考虑)(5 分钟-1 小时)
ANSWER
Too complicated. Go find brain teaser answers by yourself.
2.在9 个点上画10 条直线,要求每条直线上至少有三个点?(3 分钟-20 分钟)
3.在一天的24 小时之中,时钟的时针、分针和秒针完全重合在一起的时候有几次?都分别是什么时间?你怎样算出来的?(5 分钟-15 分钟)
30
终结附加题:
微软面试题,挑战你的智商
==========
说明:如果你是第一次看到这种题,并且以前从来没有见过类似的题型,
并且能够在半个小时之内做出答案,说明你的智力超常..)
1.第一题. 五个海盗抢到了100 颗宝石,每一颗都一样大小和价值连城。他们决定这么分:
抽签决定自己的号码(1、2、3、4、5)
首先,由1 号提出分配方案,然后大家表决,当且仅当超过半数的人同意时,
按照他的方案进行分配,否则将被扔进大海喂鲨鱼
如果1 号死后,再由2 号提出分配方案,然后剩下的4 人进行表决,
当且仅当超过半数的人同意时,按照他的方案进行分配,否则将被扔入大海喂鲨鱼。
依此类推
条件:每个海盗都是很聪明的人,都能很理智地做出判断,从而做出选择。
问题:第一个海盗提出怎样的分配方案才能使自己的收益最大化?
Answer:
A traditional brain teaser.
Consider #5, whatever #4 proposes, he won’t agree, so #4 must agree whatever #3 proposes. So if there are only #3-5, #3 should propose (100, 0, 0). So the expected income of #3 is 100, and #4 and #5 is 0 for 3 guy problem. So whatever #2 proposes, #3 won’t agree, but if #2 give #4 and #5 $1, they can get more than 3-guy subproblem. So #2 will propose (98, 0, 1, 1). So for #1, if give #2 less than $98, #2 won’t agree. But he can give #3 $1 and #4 or #5 $2, so this is a (97, 0, 1, 2, 0) solution.
2.一道关于飞机加油的问题,已知:
每个飞机只有一个油箱,
飞机之间可以相互加油(注意是相互,没有加油机)
一箱油可供一架飞机绕地球飞半圈,
问题:
为使至少一架飞机绕地球一圈回到起飞时的飞机场,至少需要出动几架飞机?
(所有飞机从同一机场起飞,而且必须安全返回机场,不允许中途降落,中间没有飞机场)
-------------------------------------------------------------------------------------------------------------------------------
更多面试题,请参见:
微软、谷歌、百度等公司经典面试100题[第1-60题] (微软100题第二版前60题)
微软、Google等公司非常好的面试题及解答[第61-70题] (微软100题第二版第61-70题)
十道海量数据处理面试题与十个方法大总结 (十道海量数据处理面试题)
海量数据处理面试题集锦与Bit-map详解 (十七道海量数据处理面试题)
九月腾讯,创新工场,淘宝等公司最新面试十三题(2011年度9月最新面试30题)
十月百度,阿里巴巴,迅雷搜狗最新面试十一题(2011年度十月最新面试题集锦)
一切的详情,可看此文:
横空出世,席卷Csdn--评微软等数据结构+算法面试100题 (在此文中,你能找到与微软100题所有一切相关的东西)
所有的资源下载(题目+答案)地址:http://v_july_v.download.csdn.net/
本微软等100 题系列V0.1 版,永久维护地址:http://topic.csdn.net/u/20101126/10/b4f12a00-6280-492f-b785-cb6835a63dc9.html
本文转自博客园知识天地的博客,原文链接:重磅分享:微软等数据结构+算法面试100题全部答案完整亮相,如需转载请自行联系原博主。
Flink 必知必会经典课程2:Stream Processing with Apache Flink
作者|崔星灿本篇内容包含三部分展开介绍Stream Processing with Apache Flink:并行处理和编程范式DataStream API概览及简单应用Flink 中的状态和时间一、并行处理和编程范式众所周知,对于计算密集型或数据密集型这样需要计算量比较大的工作,并行计算或分而治之是解决这一类问题非常有效的手段。在这个手段中比较关键的部分是,如何对一个已有任务的划分,或者说如何对计算资源进行合理分配。举例说明,上学期间老师有时会找同学来协助批阅考试试卷。假如卷子里面一共有ABC三个题,那么同学可能会有如下分工协作方式。方式一:将所有试卷的三个题分别交给不同的人来批阅。这种方式,每个批阅的同学批自己负责的题目后就可以把试卷传给下一个批阅同学,从而形成一种流水线的工作效果。因为总共只有三道题目,这种流水线的协作方式会随着同学数量的增加而难以继续扩展。方式二:分工方式一的扩展,同一题目允许多个同学来共同批阅,比如A题目由两个同学共同批阅,B题目由三个同学批阅,C题目只由一个同学批阅。这时候我们就需要考虑怎样进一步的对计算任务做划分。比如,可以把全部同学分成三组,第一组负责A题目,第二个组负责B题目第三个组负责C。第一个组的同学可以再次再组内进行分工,比如A组里第一个同学批一半的卷子,第二个同学批另一半卷子。他们分别批完了之后,再将自己手里的试卷传递给下一个组。像上述按照试卷内题目进行划分,以及讲试卷本身进行划分,就是所谓的计算的并行性和数据并行性。我们可以用上面有向无环图来表示这种并行性。在图中,批阅A题目的同学,假设还承担了一些额外任务,比如把试卷从老师的办公室拿到批阅试卷的地点;负责C题的同学也额外任务,就是等所有同学把试卷批完后,进行总分的统计和记录上交的工作。据此,可以把图中所有的节点划分为三个类别。第一个类别是Source,它们负责获取数据(拿试卷);第二类是数据处理节点,它们大多时候不需要和外部系统打交道;最后一个类别负责将整个计算逻辑写到某个外部系统(统分并上交记录)。这三类节点分别就是Source节点、Transformation节点和Sink节点。DAG图中,节点表示计算,节点之间的连线代表计算之间的依赖。关于编程的一些内容假设有一个数据集,其中包含1~10十个数字,如果把每一个数字都乘以2并做累计求和操作(如上图所示)怎么操作呢?办法有很多。 如果用编程来解决有两个角度:第一种是采取命令式编程方式,一步一步的相当于告诉机器应该怎样生成一些数据结构,怎样的用这些数据结构去存储一些临时的中间结果,怎样把这些中间结果再转换成为最终的结果,相当于一步一步告诉机器如何去做;第二种是声明的方式,声明式编程里通常只需要告诉机器去完成怎样的任务,而不需要像命令式那样详细传递。例如我们可以把原有的数据集转化成一个Stream,然后再把 Stream转化成一个Int类型的Stream,在此过程中,把每一个数字都乘2,最后再调用 Sum方法,就可以获得所有数字的和。声明式编程语言的代码更简洁,而简洁的开发方式,正是计算引擎追求的效果。所以在 Flink 里所有与任务编写相关的API,都是偏向声明式的。二、DataStream API概览及简单应用在详细介绍DataStream API之前,我们先来看一下 Flink API的逻辑层次。在旧版本的 Flink 里,它的API层次遵循上图左侧这样四层的关系。最上层表示我们可以用比较高级的API,或者说声明程度更高的Table API以及SQL的方式来编写逻辑。所有SQL和Table API编写的内容都会被Flink内部翻译和优化成一个用DataStream API实现的程序。再往下一层,DataStream API的程序会被表示成为一系列Transformation,最终 Transformation会被翻译成JobGraph(即上文介绍的DAG)。而在较新版本的 Flink 里发生了一些改变,主要的改变体现在 Table API 和 SQL 这一层上。它不再会被翻译成 DataStream API 的程序,而是直接到达底层 Transformation 一层。换句话说,DataStream API 和 Table API 这两者的关系,从一个下层和上层的关系变为了一个平级的关系,这样流程的简化,会相应地带来一些查询优化方面的好处。接下来我们用一个简单的 DataStream API 程序作为示例来介绍,还是上文乘2再求和的需求。如果用 Flink 表示,它的基本代码如上图所示。看上去比单机的示例要稍微的复杂一点,我们一步一步来分解看。首先,用 Flink 实现任何功能,一定要获取一个相应的运行环境,也就是 Sream Execution Environment;其次,在获取环境后,可以调用环境的 add Source 方法,来为逻辑添加一个最初始数据源的输入;设置完数据源后可以拿到数据源的引用,也就是 Data Source 对象;最后,可以调用一系列的转换方法来对 Data Source 中的数据进行转化。这种转化如图所示,就是把每个数字都×2,随后为了求和我们必须利用 keyBy 对数据进行分组。传入的常数表示把所有的数据都分到一组里边,最后再对这个组里边的所有的数据,按照第一个字段进行累加,最终得到结果。在得到结果后,不能简单的像单机程序那样把它输出,而是需要在整个逻辑里面加一个的 Sink 节点,把所有的数据写到目标位置。上述工作完成后,要去调用 Environment 里面 Execute 方法,把所有上面编写的逻辑统一提交到远程或者本地的一个集群上执行。Flink DataStream API 编写程序和单机程序最大的不同就在于,它前几步的过程都不会触发数据的计算,而像在绘制一个 DAG 图。等整个逻辑的 DAG 图绘制完毕之后,就可以通过 Execute 方法,把整个的图作为一个整体,提交到集群上去执行。介绍到这里,就把Flink DataStream API和DAG图联系在一起了。 事实上,Flink 任务具体的产生过程比上面描述的要复杂得多,它要经过一步步转化和优化等,下图展示了Flink 作业的具体生成过程。DataStream API里提供的转换操作就像上文在示例代码中展示的,每一个 DataStream对象,在被调用相应方法的时候,都会产生一个新的转换。相应的,底层会生成一个新的算子,这个算子会被添加到现有逻辑的DAG图中。相当于添加一条连线来指向现有DAG图的最后一个节点。所有的这些API在调动它的时候都会产生一个新的对象,然后可以在新的对象上去继续调用它的转换方法。就是像这种链式的方式,一步一步把这个DAG图给画出来。 上述解释涉及到了一些高阶函数思想。每去调用 DataStream上的一个转换时,都需要给它传递的一个参数。换句话说,转换决定了你想对这个数据进行怎样的操作,而实际传递的包在算子里面的函数决定了转换操作具体要怎样完成。上图中,除了左边列出来的 API, Flink DataStream API 里面还有两个非常重要的功能,它们是 ProcessFunction以及 CoProcessFunction。这两个函数是作为最底层的处理逻辑提供给用户使用的。上图所有左侧蓝色涉及的转换,理论上来讲都可以用底层的ProcessFunction和CoProcessFunction去完成。关于数据分区数据分区是指在传统的批处理中对数据Shuffle的操作。如果把扑克牌想成数据,传统批处理里的Shuffle操作就相当于理牌的过程。一般情况下在抓牌过程中,我们都会把牌理顺排列好,相同的数字还要放在一起。这样做最大的好处是,出牌时可以一下子找到想出的牌。Shuffle是传统的批处理的方式。因为流处理所有的数据都是动态来的,所以理牌的过程或者说处理数据,进行分组或分区的过程,也是在线来完成的。 例如上图右侧所示,上游有两个算子A的处理实例,下游是三个算子B处理实例。这里展示的流处理等价于Shuffle的操作被称为数据分区或数据路由。它用来表示A处理完数据后,要把结果发到下游B的哪个处理实例上。Flink 里提供的分区策略图X是 Flink 提供的分区策略。需要注意的是, DataStream调用keyBy方法后,可以把整个数据按照一个Key值进行分区。但要严格来讲,其实keyBy并不算是底层物理分区策略,而是一种转换操作,因为从API角度来看,它会把DataStream转化成 KeyedDataStream的类型,而这两者所支持的操作也有所不同。所有这些分区策略里,稍微难理解的可能是Rescale。Rescale涉及到上下游数据本地性的问题,它和传统的Rebalance,即Round-Pobin,轮流分配类似。区别在于Rescale是它会尽量避免数据跨网络的传输。如果所有上述的分区策略都不适用的话,我们还可以自己调用 PartitionCustom去自定义一个数据的分区。值得注意的是,它只是自定义的单播,即对每一个数据只能指定它一个下游所要发送的实例,而没有办法把它复制成多份发送到下游的多个实例中。Flink支持的连接器上文介绍过,图X里有两个关键的节点:A节点,需要去连接外部系统,从外部系统把数据读取到 Flink的处理集群里;C节点,即Sink节点,它需要汇总处理完的结果,然后把这个结果写入到某个外部系统里。这里的外部系统可以是一个文件系统,也可以是一个数据库等。Flink 里的计算逻辑可以没有数据输出,也就是说可以不把最终的数据写出到外部系统,因为Flink里面还有一个State的状态的概念。在中间计算的结果实际上是可以通过 State暴露给外部系统,所以允许没有专门的Sink。但每一个 Flink 应用都肯定有Source,也就是说必须从某个地方把数据读进来,才能进行后续的处理。关于 Source和Sink两类连接器需要关注的点如下:对于Sourse而言,我们往往比较关心是否支持续监测并接入数据更新,然后把相应的更新数据再给传输到这个系统当中来。举例来说,Flink对于文件有相应的FileSystem连接器,例如CSV文件。CSV文件连接器在定义时,可以通过参数指定是否持续监测某个目录的文件变化,并接入更新后的文件。对于Sink来讲,我们往往关心要写出的外部系统是否支持更新已经写出的结果。比如要把数据写到Kafka里,通常情况下数据写入是一种Append-Only,即不能修改已经写入系统里的记录(社区正在利用Kafka Compaction实现Upsert Sink);如果是写入数据库,那么通常可以支持利用主键对现有数据进行更新。以上两个特性,决定了Flink 里连接器是面向静态数据还是面向动态的数据的关键点。提醒,上面截图是 Flink 1.11版本之后的文档,连接器在 Flink 1.11 版本里有较大重构。另外,关于Table、SQL、API这个层面的连接器,比起DataStream层面的连接器,会承担更多的任务。比如是否支持一些谓词或投影操作的下推等等。这些功能可以帮助提高数据处理的整体性能。三、Flink 中的状态和时间如果想要深入地了解DataStream API,状态和时间是必须掌握的要点。所有的计算都可以简单地分为无状态计算和有状态计算。无状态计算相对而言比较容易。假设这里有个加法算子,每进来一组数据,都把它们全部加起来,然后把结果输出去,有点纯函数的味道。纯函数指的是每一次计算结果只和输入数据有关,之前的计算或者外部状态对它不会产生任何影响。这里我们主要讲一下Flink里边的有状态计算。用捡树枝的小游戏来举例。这个游戏在我看来做的非常好的一点是它自己记录了非常多的状态,比如几天没上线,然后再去和里边的 NPC对话的时候,它就会告诉你已经有好久没有上线了。换句话说,它会把之前上线的时间作为一种状态给记录下来,在生成它NPC对话的时候,是会受到这个状态的影响。实现这种有状态的计算,要做的一点就是把之前的状态记录下来,然后再这个状态注入到新的一次计算中,具体实现方式也有下面两种:第一种,把状态数据进入算子之前就给提取出来,然后把这个状态数据和输入数据合并在一起,再把它们同时输入到算子中,得到一个输出。这种方式是被用在 Spark的StructureStreaming里边。其好处是是可以重用已有的无状态算子。第二种,是 Flink 现在的方法,就是算子本身是有状态的,算子在每一次到新数据之后做计算的时候,同时考虑新输数据和已有的状态对计算过程的影响,最终把结果输出出去。计算引擎也应该像上面提到的游戏一样变得越来越智能,可以自动学习数据中潜在的规律,然后来自适应地优化计算逻辑,保持较高的处理性能。Flink 的状态原语Flink的状态原语涉及如何通过代码使用 Flink的状态。其基本思想是在编程的时候抛弃原生语言(例如Java或Scala)提供的数据容器,把它们更换为 Flink 里面的状态原语。作为对状态支持比较好的系统, Flink 内部提供了可以使用的很多种可选的状态原语。从大的角度看,所有状态原语可以分为Keyed State和Operator State两类。Operator State应用相对比较少,我们在这里不展开介绍。下面重点看一下Keyed State。Keyed State,即分区状态。分区状态的好处是可以把已有状态按逻辑提供的分区分成不同的块。块内的计算和状态都是绑定在一起的,而不同的Key值之间的计算和状态的读写都是隔离的。对于每个Key值,只需要管理好自己的计算逻辑和状态就可以了,不需要去考虑其它Key值所对应的逻辑和状态。Keyed State可以进一步划分为下面的5类,它们分别是:比较常用的:ValueState、ListState、MapState不太常用的:ReducingState和AggregationStateKeyed State只能在RichFuction中使用,RichFuction与普通、传统的Function相比,最大的不同就是它有自己的生命周期。Key State的使用方法分为以下四个步骤:第一步,将 State声明为RichFunction里的实例的变量第二步,在RichFunction对应的 open方法中,为 State进行一个初始化的赋值操作。赋值操作要有两步:先创建一个StateDescriptor,在创建中需要给State指定一个名称;然后再去调用RichFuntion中的getRuntimeContext().getState(…),把刚刚定义的StateDescriptor传进去,就可以获取State。提醒:如果此流式应用是第一次运行,那么获得的State会是空内容的;如果State是从某个中间段重启的,它会根据配置和之前保存的数据的基础上进行恢复。 第三步,得到State对象后,就可以在RichFunction里,对对应的State进行读写。如果是ValueState,可以调用它的Value方法来获取对应值。Flink 框架会控制好所有状态的并发访问,并进行限制,所以用户不需要考虑并发的问题。Flink 的时间时间也是 Flink非常重要的一点,它和State是相辅相成的。总体来看 Flink引擎里边提供的时间有两类:第一类是Processing Time;第二类是Event Time。Processing Time表示的是真实世界的时间,Event Time是数据当中包含的时间。数据在生成的过程当中会携带时间戳之类的字段,因为很多时候需要将数据里携带的时间戳作为参考,然后对数据进行分时间的处理。Processing Time处理起来相对简单,因为它不需要考虑乱序等问题;而Event Time处理起来相对复杂。而由于Processing Time在使用时是直接调取系统的时间,考虑到多线程或分布式系统的不确定性,所以它每次运行的结果可能是不确定的;相反,因为Event Time时间戳是被写入每一条数据里的,所以在重放某个数据进行多次处理的时候,携带的这些时间戳不会改变,如果处理逻辑没有改变的话,最后的结果也是比较确定的。Processing Time和Event Time的区别。以上图的数据为例,按照1~7的时间来排列的。对于机器时间而言,每个机器的时间会单调增加。在这种情况下,用Processing Time获得的时间是完美的按照时间从小到大排序的数据。对于Event Time而言,由于延迟或分布式的一些原因,数据到来的顺序可能和它们真实产生的顺序有一定的出入,数据可能存在着一定程度的乱序。这时就要充分利用数据里边携带的时间戳,对数据进行一个粗粒度的划分。例如可以把数据分为三组,第一组里最小的时间是1,第二组最小的时间是4,第三组最小的时间是7。这样划分之后,数据在组和组之间就是按从小到大的顺序排列好的。怎样充分的把一定程度的乱序化解掉,让整个的系统看上去数据进来基本上是有顺序的?一种解决方案是在数据中间插入被称为Watermark的meta数据。在上图的例子中,前三个数据到来之后,假设再没有小于等于3的数据进来了,这时就可以插入一条Watermark 3到整个数据里,系统在看到Watermark 3时就知道,以后都不会有小于或等于3的数据过来了,这时它就可以放心大胆地进行自己的一些处理逻辑。总结一下,Processing Time在使用时,是一个严格递增的;而Event Time会存在一定的乱序,需要通过Watermark这种办法对乱序进行一定缓解。从API的角度来看,怎样去分配Timestamp或生成Watermark也比较容易,有两种方式:第一种,在SourceFunction当中调用内部提供的 collectWithTimestamp方法,把包含时间戳的数据提取出来;还可以在SourceFunction中使用 emitWatermark方法去产生一个Watermark,然后插入到数据流中。 第二种,如果不在SourceFunction中可以调用DateStream.assignTimestampsAndWatermarks这个方法,同时传入两类Watermark生成器:第一类是定期生成,相当在环境里通过配置一个值,比如每隔多长时间(指真实时间)系统会自动调用Watermar生成策略。 第二类是根据特殊记录生成,如果遇到一些特殊数据,可以采取AssignWithPunctuatedWatermarks这个方法来进行时间戳和Watermark的分配。 提醒:Flink 里内置了一些常用的Assigner,即WatermarkAssigner。比如针对一个固定数据,它会把这个数据对应的时间戳减去固定的时间作为一个Watermark。关于Timestamp分配和Watermark生成接口,在后续的版本可能会有一定的改动。 注意,新版本的Flink里面已经统一了上述两类生成器。时间相关APIFlink 在编写逻辑时会用到的与时间相关的 API,下图总结了Event Time和Processing Time相对应的API。在应用逻辑里通过接口支持可以完成三件事:第一,获取记录的时间。Event Time可以调context.getTimestamp,或在SQL算子内从数据字段中把对应的时间给提取出来。Processing Time可以直接调currentProcessingTime完成调取,它的内部是直接调用了获取系统时间的静态方法来返回的值。第二,获取Watermark。其实只有在Event Time里才有Watermark的概念,而Processing Time里是没有的。但在Processing Time中非要把某个东西当成Watermark,其实就是数据时间本身。也就是说第一次调用timerService.currentProcessingTime方法之后获取的值。这个值既是当前记录的这个时间,也是当前的Watermark值,因为时间总是往前流动的,第一次调用了这个值后,第二次调用时这个值肯定不会再比第一次值还小。第三,注册定时器。定时器的作用是清理。比如需要对一个cache在未来某个时间进行清理工作。既然清理工作应该发生在未来的某个时间点,那么可以调用 timerServicerEventTimeTimer或ProcessingTimeTimer方法注册定时器,再在整个方法里添加一个对定时器回调的处理逻辑。当对应的Event Time或者Processing Time的时间超过了定时器设置时间,它就会调用方法自己编写定时器的毁掉逻辑。以上就是关于StreamProcess with Apache Flink的介绍,下一篇内容将着重介绍Flink Runtime Architecture。活动推荐:仅需99元即可体验阿里云基于 Apache Flink 构建的企业级产品-实时计算 Flink 版!点击下方链接了解活动详情:https://www.aliyun.com/product/bigdata/sc?utm_content=g_1000250506
剑指offer刷题记录 其他、回溯(3)
剑指Offer(四十五):扑克牌顺子1、排序2、计算所有相邻数字间隔总数3、计算0的个数4、如果2、3相等,就是顺子5、如果出现对子,则不是顺子import java.util.Arrays;
public class Solution {
public boolean isContinuous(int [] numbers) {
if(numbers.length==0||numbers==null)
return false;
int length=numbers.length;
int ZeroOfNumber=0;
int IntervalOfNumber=0;
Arrays.sort(numbers);
for(int i=0;i<length-1;i++){
if(numbers[i]==0){
ZeroOfNumber++;
continue;
}
if(numbers[i]==numbers[i+1])
return false;
IntervalOfNumber+=numbers[i+1]-numbers[i]-1;
}
if(ZeroOfNumber>=IntervalOfNumber){
return true;
}
return false;
}
}剑指Offer(四十六):孩子们的游戏(圆圈中最后剩下的数)public class Solution {
public int LastRemaining_Solution(int n, int m) {
if(m<1||n<1){
return -1;
}
//针对于这个题,首先捋一下思路;n:表示数组的大小 m:表示
/用数组模拟链表,定义一个环,如果到m,则设置为-1;如果遇到-1则跳过;
int[] a=new int[n]; /定义一个数组模拟环
int i=-1,step=0,count=n;
while(count>0){
i++;
if(i>=n) i=0;
if(a[i]==-1) continue;
step++;
if(step==m){
a[i]=-1;
step=0;
count--;
}
}
return i;
}
}剑指Offer(四十七):求1+2+3+…+n解题思路:
1.需利用逻辑与的短路特性实现递归终止。 2.当n==0时,(n>0)&&((sum+=Sum_Solution(n-1))>0)只执行前面的判断,为false,然后直接返回0;
3.当n>0时,执行sum+=Sum_Solution(n-1),实现递归计算Sum_Solution(n)。
public int Sum_Solution(int n) {
int sum = n;
boolean ans = (n>0)&&((sum+=Sum_Solution(n-1))>0);
return sum;
}&&就是逻辑与,逻辑与有个短路特点,前面为假,后面不计算。剑指Offer(四十八):不用加减乘除的加法public class Solution {
public int Add(int num1,int num2) {
while (num2!=0) {
int temp = num1^num2;
num2 = (num1&num2)<<1;
num1 = temp;
}
return num1;
}
}链接:https://www.nowcoder.com/questionTerminal/59ac416b4b944300b617d4f7f111b215来源:牛客网首先看十进制是如何做的: 5+7=12,三步走第一步:相加各位的值,不算进位,得到2。第二步:计算进位值,得到10. 如果这一步的进位值为0,那么第一步得到的值就是最终结果。第三步:重复上述两步,只是相加的值变成上述两步的得到的结果2和10,得到12。同样我们可以用三步走的方式计算二进制值相加: 5-101,7-111 第一步:相加各位的值,不算进位,得到010,二进制每位相加就相当于各位做异或操作,101^111。第二步:计算进位值,得到1010,相当于各位做与操作得到101,再向左移一位得到1010,(101&111)<<1。第三步重复上述两步, 各位相加 010^1010=1000,进位值为100=(010&1010)<<1。继续重复上述两步:1000^100 = 1100,进位值为0,跳出循环,1100为最终结果。剑指Offer(五十四):字符流中第一个不重复的字符Solution: Java版的,使用一个HashMap来统计字符出现的次数,同时用一个ArrayList来记录输入流,每次返回第一个出现一次的字符都是在这个ArrayList(输入流)中的字符作为key去map中查找。
import java.util.*;
public class Solution {
HashMap<Character, Integer> map=new HashMap();
ArrayList<Character> list=new ArrayList<Character>();
//Insert one char from stringstream
public void Insert(char ch)
{
if(map.containsKey(ch)){
map.put(ch,map.get(ch)+1);
}else{
map.put(ch,1);
}
list.add(ch);
}
//return the first appearence once char in current stringstream
public char FirstAppearingOnce()
{ char c='#';
for(char key : list){
if(map.get(key)==1){
c=key;
break;
}
}
return c;
}
}剑指Offer(六十三):数据流中的中位数import java.util.Comparator;
import java.util.PriorityQueue;
public class Solution {
/首先定义一个 最小堆 即升序
PriorityQueue<Integer> minHeap=new PriorityQueue(); //定义最小堆
/接着定义一个 最大堆 即降序
PriorityQueue<Integer> maxHeap=new PriorityQueue<Integer>(21,new Comparator<Integer>(){ //定义最大堆
public int compare(Integer o1,Integer o2){
return o2-o1;
}
});
int count=0;
public void Insert(Integer num) {
if(count%2==1){ /当为偶数的时候,插入小根堆
maxHeap.offer(num);
int max=maxHeap.poll();
minHeap.offer(max);
}else{ 当为奇数时候,插入大根堆
minHeap.offer(num);
int min=minHeap.poll();
maxHeap.offer(min);
}
count++;
}
public Double GetMedian() {
if(count%2==0){
return new Double(maxHeap.peek()+minHeap.peek())/2;
}else{
return new Double(maxHeap.peek());
}
}
}剑指Offer(六十四):滑动窗口的最大值//经典的暴力破解的方法import java.util.ArrayList;
public class Solution {
public ArrayList<Integer> maxInWindows(int [] num, int size)
{
ArrayList<Integer> list=new ArrayList<Integer>();
if(num.length==0||num==null||(num.length<size)||size==0)
return list;
for(int i=0;i<num.length-size+1;i++){
int temp=num[i];
for(int j=i+1;j<i+size;j++){
if(temp<num[j])
temp=num[j];
}
list.add(temp);
}
return list;
}
}//使用一个队列来解决,让O(kn)变为O(n)import java.util.*;
public class Solution {
public ArrayList<Integer> maxInWindows(int [] num, int size)
{
/*
用一个双端队列,队列第一个位置保存当前窗口的最大值,当窗口欢动一次
1.判断当前最大值是否过期
2.新增加的值从队尾开始比较,把所有比他小的值丢掉
*/
ArrayList<Integer> res=new ArrayList<Integer>(); //定义一个数组,来添加程序
if(size==0) return res; //返回size的大小
int begin;
LinkedList<Integer> q=new LinkedList<Integer>(); //定义一个双边链表
for(int i=0;i<num.length;i++){
begin=i-size+1;
if(q.isEmpty()){
q.add(i); 加进去的是下标
}else if(begin>q.peekFirst()) /判断当前值是否过期
q.pollFirst();
while((!q.isEmpty())&&num[q.peekLast()]<=num[i])
q.pollLast();
q.add(i);
if(begin>=0){
res.add(num[q.peekFirst()]);
}
}
return res;
}
}回溯法(两道)(一):矩阵中的路径【回溯法】占坑,以后写(二):机器人的运动范围【回溯法】占坑,以后写
130道asp.net经典题目和经典智力题目
130道ASP.NET面试题
1. 简述 private、 protected、 public、 internal 修饰符的访问权限。
答 . private : 私有成员, 在类的内部才可以访问。
protected : 保护成员,该类内部和继承类中可以访问。
public : 公共成员,完全公开,没有访问限制。
internal: 在同一命名空间内可以访问。
2 .列举ASP.NET 页面之间传递值的几种方式。
答. 1.使用QueryString, 如....?id=1; response. Redirect()....
2.使用Session变量
3.使用Server.Transfer
3. 一列数的规则如下: 1、1、2、3、5、8、13、21、34...... 求第30位数是多少, 用递归算法实现。
答:public class MainClass
{
public static void Main()
{
Console.WriteLine(Foo(30));
}
public static int Foo(int i)
{
if (i <= 0)
return 0;
else if(i > 0 && i <= 2)
return 1;
else return Foo(i -1) + Foo(i - 2);
}
}
4.C#中的委托是什么?事件是不是一种委托?
答 :
委托可以把一个方法作为参数代入另一个方法。
委托可以理解为指向一个函数的引用。
是,是一种特殊的委托
5.override与重载的区别
答 :
override 与重载的区别。重载是方法的名称相同。参数或参数类型不同,进行多次重载以适应不同的需要
Override 是进行基类中函数的重写。为了适应需要。
6.如果在一个B/S结构的系统中需要传递变量值,但是又不能使用Session、Cookie、Application,您有几种方法进行处理?
答 :
this.Server.Transfer
7.请编程遍历页面上所有TextBox控件并给它赋值为string.Empty?
答:
foreach (System.Windows.Forms.Control control in this.Controls)
{
if (control is System.Windows.Forms.TextBox)
{
System.Windows.Forms.TextBox tb = (System.Windows.Forms.TextBox)control ;
tb.Text = String.Empty ;
}
}
8.请编程实现一个冒泡排序算法?
答:
int [] array = new int
;
int temp = 0 ;
for (int i = 0 ; i < array.Length - 1 ; i++)
{
for (int j = i + 1 ; j < array.Length ; j++)
{
if (array[j] < array)
{
temp = array ;
array = array[j] ;
array[j] = temp ;
}
}
}
9.描述一下C#中索引器的实现过程,是否只能根据数字进行索引?
答:不是。可以用任意类型。
10.求以下表达式的值,写出您想到的一种或几种实现方法: 1-2+3-4+……+m
答:
int Num = this.TextBox1.Text.ToString() ;
int Sum = 0 ;
for (int i = 0 ; i < Num + 1 ; i++)
{
if((i%2) == 1)
{
Sum += i ;
}
else
{
Sum = Sum - I ;
}
}
System.Console.WriteLine(Sum.ToString());
System.Console.ReadLine() ;
11.用.net做B/S结构的系统,您是用几层结构来开发,每一层之间的关系以及为什么要这样分层?
答:一般为3层
数据访问层,业务层,表示层。
数据访问层对数据库进行增删查改。
业务层一般分为二层,业务表观层实现与表示层的沟通,业务规则层实现用户密码的安全等。
表示层为了与用户交互例如用户添加表单。
优点: 分工明确,条理清晰,易于调试,而且具有可扩展性。
缺点: 增加成本。
12.在下面的例子里
using System;
class A
{
public A()
{
PrintFields();
}
public virtual void PrintFields(){}
}
class B:A
{
int x=1;
int y;
public B()
{
y=-1;
}
public override void PrintFields()
{
Console.WriteLine("x={0},y={1}",x,y);
}
当使用new B()创建B的实例时,产生什么输出?
答:X=1,Y=0;x= 1 y = -1
13.什么叫应用程序域?
答:应用程序域可以理解为一种轻量级进程。起到安全的作用。占用资源小。
14.CTS、CLS、CLR分别作何解释?
答:CTS:通用语言系统。CLS:通用语言规范。CLR:公共语言运行库。
15.什么是装箱和拆箱?
答:从值类型接口转换到引用类型装箱。从引用类型转换到值类型拆箱。
16.什么是受管制的代码?
答:unsafe:非托管代码。不经过CLR运行。
17.什么是强类型系统?
答:RTTI:类型识别系统。
18.net中读写数据库需要用到那些类?他们的作用?
答:DataSet:数据存储器。
DataCommand:执行语句命令。
DataAdapter:数据的集合,用语填充。
19.ASP.net的身份验证方式有哪些?分别是什么原理?
答:10。Windwos(默认)用IIS...From(窗体)用帐户....Passport(密钥)
20.什么是Code-Behind技术?
答:代码后植。
21.在.net中,配件的意思是?
答:程序集。(中间语言,源数据,资源,装配清单)
22.常用的调用WebService的方法有哪些?
答:1.使用WSDL.exe命令行工具。
2.使用VS.NET中的Add Web Reference菜单选项
23..net Remoting 的工作原理是什么?
答:服务器端向客户端发送一个进程编号,一个程序域编号,以确定对象的位置。
24.在C#中,string str = null 与 string str = “” 请尽量使用文字或图象说明其中的区别。
答:string str = null 是不给他分配内存空间,而string str = "" 给它分配长度为空字符串的内存空间。
25.请详述在dotnet中类(class)与结构(struct)的异同?
答:Class可以被实例化,属于引用类型,是分配在内存的堆上的,Struct属于值类型,是分配在内存的栈上的.
26.根据委托(delegate)的知识,请完成以下用户控件中代码片段的填写:
namespace test
{
public delegate void OnDBOperate();
public class UserControlBase : System.Windows.Forms.UserControl
{
public event OnDBOperate OnNew;
privatevoidtoolBar_ButtonClick(objectsender,System.Windows.Forms.ToolBarButtonClickEventArgs e)
{
if(e.Button.Equals(BtnNew))
{
//请在以下补齐代码用来调用OnDBOperate委托签名的OnNew事件。
}
}
}
答:if( OnNew != null )
OnNew( this, e );
27.分析以下代码,完成填空
string strTmp = "abcdefg某某某";
int i= System.Text.Encoding.Default.GetBytes(strTmp).Length;
int j= strTmp.Length;
以上代码执行完后,i= j=
答:i=13,j=10
28.SQLSERVER服务器中,给定表 table1 中有两个字段 ID、LastUpdateDate,ID表示更新的事务号, LastUpdateDate表示更新时的服务器时间,请使用一句SQL语句获得最后更新的事务号
答:Select ID FROM table1 Where LastUpdateDate = (Select MAX(LastUpdateDate) FROM table1)
29.根据线程安全的相关知识,分析以下代码,当调用test方法时i>10时是否会引起死锁?并简要说明理由。
public void test(int i)
{
lock(this)
{
if (i>10)
{
i--;
test(i);
}
}
}
答:不会发生死锁,(但有一点int是按值传递的,所以每次改变的都只是一个副本,因此不会出现死锁。但如果把int换做一个object,那么死锁会发生)
30.简要谈一下您对微软.NET 构架下remoting和webservice两项技术的理解以及实际中的应用。
答:WS主要是可利用HTTP,穿透防火墙。而Remoting可以利用TCP/IP,二进制传送提高效率。
31.公司要求开发一个继承System.Windows.Forms.ListView类的组件,要求达到以下的特殊功能:点击ListView各列列头时,能按照点击列的每行值进行重排视图中的所有行 (排序的方式如DataGrid相似)。根据您的知识,请简要谈一下您的思路
答:根据点击的列头,包该列的ID取出,按照该ID排序后,在给绑定到ListView中。
32.给定以下XML文件,完成算法流程图。
< DriverC >
请画出遍历所有文件名(FileName)的流程图(请使用递归算法)。
答:
void FindFile( Directory d )
{
FileOrFolders = d.GetFileOrFolders();
foreach( FileOrFolder fof in FileOrFolders )
{
if( fof is File )
You Found a file;
else if ( fof is Directory )
FindFile( fof );
}
}
33.写出一条Sql语句:取出表A中第31到第40记录(SQLServer,以自动增长的ID作为主键,注意:ID可能不是连续的。
答:解1: select top 10 * from A where id not in (select top 30 id from A)
解2: select top 10 * from A where id > (select max(id) from (select top 30 id from A )as A)
34.面向对象的语言具有________性、_________性、________性
答:封装、继承、多态。
35.能用foreach遍历访问的对象需要实现 ________________接口或声明________________方法的类型。
答:IEnumerable 、 GetEnumerator。
36.GC是什么? 为什么要有GC?
答:GC是**收集器。程序员不用担心内存管理,因为**收集器会自动进行管理。要请求**收集,可以调用下面的方法之一:
System.gc()
Runtime.getRuntime().gc()
37.String s = new String("xyz");创建了几个String Object?
答:两个对象,一个是“xyx”,一个是指向“xyx”的引用对象s。
38.abstract class和interface有什么区别?
答:
声明方法的存在而不去实现它的类被叫做抽象类(abstract class),它用于要创建一个体现某些基本行为的类,并为该类声明方法,但不能在该类中实现该类的情况。不能创建abstract 类的实例。然而可以创建一个变量,其类型是一个抽象类,并让它指向具体子类的一个实例。不能有抽象构造函数或抽象静态方法。Abstract 类的子类为它们父类中的所有抽象方法提供实现,否则它们也是抽象类为。取而代之,在子类中实现该方法。知道其行为的其它类可以在类中实现这些方法。
接口(interface)是抽象类的变体。在接口中,所有方法都是抽象的。多继承性可通过实现这样的接口而获得。接口中的所有方法都是抽象的,没有一个有程序体。接口只可以定义static final成员变量。接口的实现与子类相似,除了该实现类不能从接口定义中继承行为。当类实现特殊接口时,它定义(即将程序体给予)所有这种接口的方法。然后,它可以在实现了该接口的类的任何对象上调用接口的方法。由于有抽象类,它允许使用接口名作为引用变量的类型。通常的动态联编将生效。引用可以转换到接口类型或从接口类型转换,instanceof 运算符可以用来决定某对象的类是否实现了接口。
39.启动一个线程是用run()还是start()?
答:启动一个线程是调用start()方法,使线程所代表的虚拟处理机处于可运行状态,这意味着它可以由JVM调度并执行。这并不意味着线程就会立即运行。run()方法可以产生必须退出的标志来停止一个线程。
40.接口是否可继承接口? 抽象类是否可实现(implements)接口? 抽象类是否可继承实体类(concrete class)?
答:接口可以继承接口。抽象类可以实现(implements)接口,抽象类是否可继承实体类,但前提是实体类必须有明确的构造函数。
41.构造器Constructor是否可被override?
答:构造器Constructor不能被继承,因此不能重写Overriding,但可以被重载Overloading。
42.是否可以继承String类?
答:String类是final类故不可以继承。
43.try {}里有一个return语句,那么紧跟在这个try后的finally {}里的code会不会被执行,什么时候被执行,在return前还是后?
答:会执行,在return前执行。
44.两个对象值相同(x.equals(y) == true),但却可有不同的hash code,这句话对不对?
答:不对,有相同的hash code。
45.swtich是否能作用在byte上,是否能作用在long上,是否能作用在String上?
答:switch(expr1)中,expr1是一个整数表达式。因此传递给 switch 和 case 语句的参数应该是 int、 short、 char 或者 byte。long,string 都不能作用于swtich。
47.当一个线程进入一个对象的一个synchronized方法后,其它线程是否可进入此对象的其它方法?
不能,一个对象的一个synchronized方法只能由一个线程访问。
48.abstract的method是否可同时是static,是否可同时是native,是否可同时是synchronized?
答:都不能。
49.List, Set, Map是否继承自Collection接口?
答:List,Set是Map不是
50.Set里的元素是不能重复的,那么用什么方法来区分重复与否呢? 是用==还是equals()? 它们有何区别?
答:Set里的元素是不能重复的,那么用iterator()方法来区分重复与否。equals()是判读两个Set是否相等。
equals()和==方法决定引用值是否指向同一对象equals()在类中被覆盖,为的是当两个分离的对象的内容和类型相配的话,返回真值。
51.数组有没有length()这个方法? String有没有length()这个方法?
答:数组没有length()这个方法,有length的属性。String有有length()这个方法。
52.sleep() 和 wait() 有什么区别?
答:sleep()方法是使线程停止一段时间的方法。在sleep 时间间隔期满后,线程不一定立即恢复执行。这是因为在那个时刻,其它线程可能正在运行而且没有被调度为放弃执行,除非(a)“醒来”的线程具有更高的优先级
(b)正在运行的线程因为其它原因而阻塞。
wait()是线程交互时,如果线程对一个同步对象x 发出一个wait()调用,该线程会暂停执行,被调对象进入等待状态,直到被唤醒或等待时间到。
53.short s1 = 1; s1 = s1 + 1;有什么错? short s1 = 1; s1 += 1;有什么错?
答:short s1 = 1; s1 = s1 + 1;有错,s1是short型,s1+1是int型,不能显式转化为short型。可修改为s1 =(short)(s1 + 1) 。short s1 = 1; s1 += 1正确。
54.谈谈final, finally, finalize的区别。
答:
final—修饰符(关键字)如果一个类被声明为final,意味着它不能再派生出新的子类,不能作为父类被继承。因此 一个类不能既被声明为 abstract的,又被声明为final的。将变量或方法声明为final,可以保证它们在使用中 不被改变。被声明为final的变量必须在声明时给定初值,而在以后的引用中只能读取,不可修改。被声明为 final的方法也同样只能使用,不能重载
finally—再异常处理时提供 finally 块来执行任何清除操作。如果抛出一个异常,那么相匹配的 catch 子句就会 执行,然后控制就会进入 finally 块(如果有的话)。
finalize—方法名。Java 技术允许使用 finalize() 方法在**收集器将对象从内存中清除出去之前做必要的清理 工作。这个方法是由**收集器在确定这个对象没有被引用时对这个对象调用的。它是在 Object 类中定义的 ,因此所有的类都继承了它。子类覆盖 finalize() 方法以整理系统资源或者执行其他清理工作。finalize() 方法是在**收集器删除对象之前对这个对象调用的。
55.如何处理几十万条并发数据?
答:用存储过程或事务。取得最大标识的时候同时更新..注意主键不是自增量方式这种方法并发的时候是不会有重复主键的..取得最大标识要有一个存储过程来获取.
56.Session有什么重大BUG,微软提出了什么方法加以解决?
答:是iis中由于有进程回收机制,系统繁忙的话Session会丢失,可以用Sate server或SQL Server数据库的方式存储Session不过这种方式比较慢,而且无法捕获Session的END事件。
57.进程和线程的区别?
答:进程是系统进行资源分配和调度的单位;线程是CPU调度和分派的单位,一个进程可以有多个线程,这些线程共享这个进程的资源。
58.堆和栈的区别?
答:
栈:由编译器自动分配、释放。在函数体中定义的变量通常在栈上。
堆:一般由程序员分配释放。用new、malloc等分配内存函数分配得到的就是在堆上。
59.成员变量和成员函数前加static的作用?
答:它们被称为常成员变量和常成员函数,又称为类成员变量和类成员函数。分别用来反映类的状态。比如类成员变量可以用来统计类实例的数量,类成员函数负责这种统计的动作。
60.ASP。NET与ASP相比,主要有哪些进步?
答:asp解释形,aspx编译型,性能提高,有利于保护源码。 61.产生一个int数组,长度为100,并向其中随机插入1-100,并且不能重复。
int[] intArr=new int[100];
ArrayList myList=new ArrayList();
Random rnd=new Random();
while(myList.Count<100)
{
int num=rnd.Next(1,101);
if(!myList.Contains(num))
myList.Add(num);
}
for(int i=0;i<100;i++)
intArr=(int)myList;
62.请说明在.net中常用的几种页面间传递参数的方法,并说出他们的优缺点。
答:session(viewstate) 简单,但易丢失
application 全局
cookie 简单,但可能不支持,可能被伪造
input ttype="hidden" 简单,可能被伪造
url参数 简单,显示于地址栏,长度有限
数据库 稳定,安全,但性能相对弱
63.请指出GAC的含义?
答:全局程序集缓存。
64.向服务器发送请求有几种方式?
答:get,post。get一般为链接方式,post一般为按钮方式。
65.DataReader与Dataset有什么区别?
答:一个是只能向前的只读游标,一个是内存中的表。
66.软件开发过程一般有几个阶段?每个阶段的作用?
答:需求分析,架构设计,代码编写,QA,部署
67.在c#中using和new这两个关键字有什么意义,请写出你所知道的意义?using 指令 和语句 new 创建实例 new 隐藏基类中方法。
答:using 引入名称空间或者使用非托管资源
new 新建实例或者隐藏父类方法
68.需要实现对一个字符串的处理,首先将该字符串首尾的空格去掉,如果字符串中间还有连续空格的话,仅保留一个空格,即允许字符串中间有多个空格,但连续的空格数不可超过一个.
答:string inputStr=" xx xx ";
inputStr=Regex.Replace(inputStr.Trim()," *"," ");
69.下面这段代码输出什么?为什么?
int i=5;
int j=5;
if (Object.ReferenceEquals(i,j))
Console.WriteLine("Equal");
else
Console.WriteLine("Not Equal");
答:不相等,因为比较的是对象
70.什么叫做SQL注入,如何防止?请举例说明。
答:利用sql关键字对网站进行攻击。过滤关键字'等
71.什么是反射?
答:动态获取程序集信息
72.用Singleton如何写设计模式
答:static属性里面new ,构造函数private
73.什么是Application Pool?
答:Web应用,类似Thread Pool,提高并发性能。
74.什么是虚函数?什么是抽象函数?
答:虚函数:没有实现的,可由子类继承并重写的函数。抽象函数:规定其非虚子类必须实现的函数,必须被重写。
75.什么是XML?
答:XML即可扩展标记语言。eXtensible Markup Language.标记是指计算机所能理解的信息符号,通过此种标记,计算机之间可以处理包含各种信息的文章等。如何定义这些标记,即可以选择国际通用的标记语言,比如HTML,也可以使用象XML这样由相关人士自由决定的标记语言,这就是语言的可扩展性。XML是从SGML中简化修改出来的。它主要用到的有XML、XSL和XPath等。
76.什么是Web Service?UDDI?
答:Web Service便是基于网络的、分布式的模块化组件,它执行特定的任务,遵守具体的技术规范,这些规范使得Web Service能与其他兼容的组件进行互操作。
UDDI 的目的是为电子商务建立标准;UDDI是一套基于Web的、分布式的、为Web Service提供的、信息注册中心的实现标准规范,同时也包含一组使企业能将自身提供的Web Service注册,以使别的企业能够发现的访问协议的实现标准。
77.什么是ASP.net中的用户控件?
答:用户控件一般用在内容多为静态,或者少许会改变的情况下..用的比较大..类似ASP中的include..但是功能要强大的多。
78.列举一下你所了解的XML技术及其应用
答:xml用于配置,用于保存静态数据类型.接触XML最多的是web Services..和config
79.ADO.net中常用的对象有哪些?分别描述一下。
答:Connection 数据库连接对象
Command 数据库命令
DataReader 数据读取器
DataSet 数据集
80.什么是code-Behind技术。
答:ASPX,RESX和CS三个后缀的文件,这个就是代码分离.实现了HTML代码和服务器代码分离.方便代码编写和整理.
81.什么是SOAP,有哪些应用。
答:simple object access protocal,简单对象接受协议.以xml为基本编码结构,建立在已有通信协议上(如http,不过据说ms在搞最底层的架构在tcp/ip上的soap)的一种规范Web Service使用的协议..
82.C#中 property 与 attribute的区别,他们各有什么用处,这种机制的好处在哪里?
答:一个是属性,用于存取类的字段,一个是特性,用来标识类,方法等的附加性质
83.XML 与 HTML 的主要区别
答:1. XML是区分大小写字母的,HTML不区分。
2. 在HTML中,如果上下文清楚地显示出段落或者列表键在何处结尾,那么你可以省略
或者之类的结束 标记。在XML中,绝对不能省略掉结束标记。
3. 在XML中,拥有单个标记而没有匹配的结束标记的元素必须用一个 / 字符作为结尾。这样分析器就知道不用 查找结束标记了。
4. 在XML中,属性值必须分装在引号中。在HTML中,引号是可用可不用的。
5. 在HTML中,可以拥有不带值的属性名。在XML中,所有的属性都必须带有相应的值。
84.c#中的三元运算符是?
答:?:。
85.当整数a赋值给一个object对象时,整数a将会被?
答:装箱。
86.类成员有_____种可访问形式?
答:this.;new Class().Method;
87.public static const int A=1;这段代码有错误么?是什么?
答:const不能用static修饰。
88.float f=-123.567F; int i=(int)f;i的值现在是_____?
答:-123。
89.委托声明的关键字是______?
答:delegate.
90.用sealed修饰的类有什么特点?
答:密封,不能继承。
91.在Asp.net中所有的自定义用户控件都必须继承自________?
答:Control。
92.在.Net中所有可序列化的类都被标记为_____?
答:[serializable]
93.在.Net托管代码中我们不用担心内存漏洞,这是因为有了______?
答:GC。
94.下面的代码中有什么错误吗?_______
using System;
class A
{
public virtual void F(){
Console.WriteLine("A.F");
}
}
abstract class B:A
{
public abstract override void F(); 答:abstract override 是不可以一起修饰.
} // new public abstract void F();
95.当类T只声明了私有实例构造函数时,则在T的程序文本外部,___可以___(可以 or 不可以)从T派生出新的类,不可以____(可以 or 不可以)直接创建T的任何实例。
答:不可以,不可以。
96.下面这段代码有错误么?
switch (i){
case(): 答://case()条件不能为空
CaseZero();
break;
case 1:
CaseOne();
break;
case 2:
dufault; 答://wrong,格式不正确
CaseTwo();
break;
}
97.在.Net中,类System.Web.UI.Page 可以被继承么?
答:可以。
98..net的错误处理机制是什么?
答:.net错误处理机制采用try->catch->finally结构,发生错误时,层层上抛,直到找到匹配的Catch为止。
99.利用operator声明且仅声明了==,有什么错误么?
答:要同时修改Equale和GetHash() ? 重载了"==" 就必须重载 "!="
100.在.net(C# or vb.net)中如何用户自定义消息,并在窗体中处理这些消息。
答:在form中重载DefWndProc函数来处理消息:
protected override void DefWndProc ( ref System.WinForms.Message m )
{
switch(m.msg)
{
case WM_Lbutton :
///string与MFC中的CString的Format函数的使用方法有所不同
string message = string.Format("收到消息!参数为:{0},{1}",m.wParam,m.lParam);
MessageBox.Show(message);///显示一个消息框
break;
case USER:
处理的代码
default:
base.DefWndProc(ref m);///调用基类函数处理非自定义消息。
break;
}
}
101.在.net(C# or vb.net)中如何取消一个窗体的关闭。
答:private void Form1_Closing(object sender, System.ComponentModel.CancelEventArgs e)
{
e.Cancel=true;
}
102.在.net(C# or vb.net)中,Appplication.Exit 还是 Form.Close有什么不同?
答:一个是退出整个应用程序,一个是关闭其中一个form。
103.在C#中有一个double型的变量,比如10321.5,比如122235401.21644,作为货币的值如何按各个不同国家的习惯来输出。比如美国用$10,321.50和$122,235,401.22而在英国则为£10 321.50和£122 235 401.22
答:System.Globalization.CultureInfo MyCulture = new System.Globalization.CultureInfo("en-US");
//System.Globalization.CultureInfo MyCulture = new System.Globalization.CultureInfo("en-GB");为英 国 货币类型
decimal y = 9999999999999999999999999999m;
string str = String.Format(MyCulture,"My amount = {0:c}",y);
104.某一密码仅使用K、L、M、N、O共5个字母,密码中的单词从左向右排列,密码单词必须遵循如下规则:
(1) 密码单词的最小长度是两个字母,可以相同,也可以不同
(2) K不可能是单词的第一个字母
(3) 如果L出现,则出现次数不止一次
(4) M不能使最后一个也不能是倒数第二个字母
(5) K出现,则N就一定出现
(6) O如果是最后一个字母,则L一定出现
问题一:下列哪一个字母可以放在LO中的O后面,形成一个3个字母的密码单词?
A) K B)L C) M D) N
答案:B
问题二:如果能得到的字母是K、L、M,那么能够形成的两个字母长的密码单词的总数是多少?
A)1个 B)3个 C)6个 D)9个
答案:A
问题三:下列哪一个是单词密码?
A) KLLN B) LOML C) MLLO D)NMKO
答案:C
8. 62-63=1 等式不成立,请移动一个数字(不可以移动减号和等于号),使得等式成立,如何移动?
答案:62移动成2的6次方
105.对于这样的一个枚举类型:
enum Color:byte
{
Red,
Green,
Blue,
orange
}
答:string[] ss=Enum.GetNames(typeof(Color));
byte[] bb=Enum.GetValues(typeof(Color));
106. C#中 property 与 attribute的区别,他们各有什么用处,这种机制的好处在哪里?
答:attribute:自定义属性的基类;property :类中的属性
107.C#可否对内存进行直接的操作?
答:在.net下,.net引用了**回收(GC)功能,它替代了程序员 不过在C#中,不能直接实现Finalize方法,而是在析构函数中调用基类的Finalize()方法
108.ADO。NET相对于ADO等主要有什么改进?
答:1:ado.net不依赖于ole db提供程序,而是使用.net托管提供的程序,2:不使用com3:不在支持动态游标和服务器端游 4:,可以断开connection而保留当前数据集可用 5:强类型转换 6:xml支持
109.写一个HTML页面,实现以下功能,左键点击页面时显示“您好”,右键点击时显示“禁止右键”。并在2分钟后自动关闭页面。
答: 答:<script language=javascript>
setTimeout('window.close();',3000);
function show()
{
if (window.event.button == 1)
{
alert("左");
}
else if (window.event.button == 2)
{
alert("右");
}
}
</script>
110.大概描述一下ASP。NET服务器控件的生命周期
答:初始化 加载视图状态 处理回发数据 加载 发送回发更改通知 处理回发事件 预呈现 保存状态 呈现 处置 卸载
111.Anonymous Inner Class (匿名内部类) 是否可以extends(继承)其它类,是否可以implements(实现)interface(接口)?
答:不能,可以实现接口
112.Static Nested Class 和 Inner Class的不同,说得越多越好
答:Static Nested Class是被声明为静态(static)的内部类,它可以不依赖于外部类实例被实例化。而通常的内部类需要在外部类实例化后才能实例化。
113.,&和&&的区别。
&是位运算符,表示按位与运算,&&是逻辑运算符,表示逻辑与(and).
114.HashMap和Hashtable的区别。
答:HashMap是Hashtable的轻量级实现(非线程安全的实现),他们都完成了Map接口,主要区别在于HashMap允许空(null)键值(key),由于非线程安全,效率上可能高于Hashtable.
116.Overloaded的方法是否可以改变返回值的类型?
答:Overloaded的方法是可以改变返回值的类型。
117.error和exception有什么区别?
答:error 表示恢复不是不可能但很困难的情况下的一种严重问题。比如说内存溢出。不可能指望程序能处理这样的情况。
exception 表示一种设计或实现问题。也就是说,它表示如果程序运行正常,从不会发生的情况。
118. 和 有什么区别?
答:表示绑定的数据源
是服务器端代码块
119.你觉得ASP.NET 2.0(VS2005)和你以前使用的开发工具(.Net 1.0或其他)有什么最大的区别?你在以前的平台上使用的哪些开发思想(pattern / architecture)可以移植到ASP.NET 2.0上 (或者已经内嵌在ASP.NET 2.0中)
答:1 ASP.NET 2.0 把一些代码进行了封装打包,所以相比1.0相同功能减少了很多代码.
2 同时支持代码分离和页面嵌入服务器端代码两种模式,以前1.0版本,.NET提示帮助只有在分离的代码文件,无 法 在页面嵌入服务器端代码获得帮助提示,
3 代码和设计界面切换的时候,2.0支持光标定位.这个我比较喜欢
4 在绑定数据,做表的分页.Update,Delete,等操作都可以可视化操作,方便了初学者
5 在ASP.NET中增加了40多个新的控件,减少了工作量
120.重载与覆盖的区别?
答:1、方法的覆盖是子类和父类之间的关系,是垂直关系;方法的重载是同一个类中方法之间的关系,是水平关系
2、覆盖只能由一个方法,或只能由一对方法产生关系;方法的重载是多个方法之间的关系。
3、覆盖要求参数列表相同;重载要求参数列表不同。
4、覆盖关系中,调用那个方法体,是根据对象的类型(对象对应存储空间类型)来决定;重载关系,是根据调 用时的实参表与形参表来选择方法体的。
121.描述一下C#中索引器的实现过程,是否只能根据数字进行索引?
答:不是。可以用任意类型。
122.在C#中,string str = null 与 string str = " " 请尽量使用文字或图象说明其中的区别。
答:null是没有空间引用的;
" " 是空间为0的字符串;
123.分析以下代码,完成填空
string strTmp = "abcdefg某某某";
int i= System.Text.Encoding.Default.GetBytes(strTmp).Length;
int j= strTmp.Length;
以上代码执行完后,i= j=
答:i=13.j=10
124.SQLSERVER服务器中,给定表 table1 中有两个字段 ID、LastUpdateDate,ID表示更新的事务号, LastUpdateDate表示更新时的服务器时间,请使用一句SQL语句获得最后更新的事务号
答:Select ID FROM table1 Where LastUpdateDate = (Select MAX(LastUpdateDate) FROM table1)
125.分析以下代码。
public static void test(string ConnectString)
{
System.Data.OleDb.OleDbConnection conn = new System.Data.OleDb.OleDbConnection();
conn.ConnectionString = ConnectString;
try
{
conn.Open();
…….
}
catch(Exception Ex)
{
MessageBox.Show(Ex.ToString());
}
finally
{
if (!conn.State.Equals(ConnectionState.Closed))
conn.Close();
}
}
请问
1)以上代码可以正确使用连接池吗?
答:回答:如果传入的connectionString是一模一样的话,可以正确使用连接池。不过一模一样的意思是,连字符的空格数,顺序完全一致。
2)以上代码所使用的异常处理方法,是否所有在test方法内的异常都可以被捕捉并显示出来?
答:只可以捕捉数据库连接中的异常吧. (finally中,catch中,如果有别的可能引发异常的操作,也应该用try,catch。所以理论上并非所有异常都会被捕捉。)
126.公司要求开发一个继承System.Windows.Forms.ListView类的组件,要求达到以下的特殊功能:点击ListView各列列头时,能按照点击列的每行值进行重排视图中的所有行 (排序的方式如DataGrid相似)。根据您的知识,请简要谈一下您的思路:
答:根据点击的列头,包该列的ID取出,按照该ID排序后,在给绑定到ListView中
127.什么是WSE?目前最新的版本是多少?
答:WSE (Web Service Extension) 包来提供最新的WEB服务安全保证,目前最新版本2.0。
128.在下面的例子里
using System;
class A
{
public A(){
PrintFields();
}
public virtual void PrintFields(){}
}
class B:A
{
int x=1;
int y;
public B(){
y=-1;
}
public override void PrintFields(){
Console.WriteLine("x={0},y={1}",x,y);
}
当使用new B()创建B的实例时,产生什么输出?
答:X=1,Y=0
129.下面的例子中
using System;
class A
{
public static int X;
static A(){
X=B.Y+1;
}
}
class B
{
public static int Y=A.X+1;
static B(){}
static void Main(){
Console.WriteLine("X={0},Y={1}",A.X,B.Y);
}
}
产生的输出结果是什么?
答:x=1,y=2
130.abstract class和interface有什么区别?
答:声明方法的存在而不去实现它的类被叫做抽象类(abstract class),它用于要创建一个体现某些基本行为的类,并为该类声明方法,但不能在该类中实现该类的情况。不能创建abstract 类的实例。然而可以创建一个变量,其类型是一个抽象类,并让它指向具体子类的一个实例。不能有抽象构造函数或抽象静态方法。Abstract 类的子类为它们父类中的所有抽象方法提供实现,否则它们也是抽象类为。取而代之,在子类中实现该方法。知道其行为的其它类可以在类中实现这些方法。
接口(interface)是抽象类的变体。在接口中,所有方法都是抽象的。多继承性可通过实现这样的接口而获得。接口中的所有方法都是抽象的,没有一个有程序体。接口只可以定义static final成员变量。接口的实现与子类相似,除了该实现类不能从接口定义中继承行为。当类实现特殊接口时,它定义(即将程序体给予)所有这种接口的方法。然后,它可以在实现了该接口的类的任何对象上调用接口的方法。由于有抽象类,它允许使用接口名作为引用变量的类型。通常的动态联编将生效。引用可以转换到接口类型或从接口类型转换,instanceof 运算符可以用来决定某对象的类是否实现了接口。
<PIXTEL_MMI_EBOOK_2005>2 </PIXTEL_MMI_EBOOK_2005>
131.a=10,b=15,在不用第三方变量的前提下,把a,b的值互换(需要代码最短)
a=a+b;
b=a-b;
a=a-b;
for(int i=1;i<=5;i++)
{
for(int j=0;j<i;j++)
{
Console.Write(i);
}
Console.WriteLine();
}
133.在sql中having的作用?
答:HAVING子句可以让我们筛选成组后的各组数据。
select AMMETERNO,COUNT(*) from ammeter_pr_daytbl GROUP BY AMMETERNO HAVING AMMETERNO LIKE '350%'
AMMETERNO COUNT(*)
134.如何用sql语句 实现分页查询?
方法1:
适用于 /2005
SELECT TOP 页大小 *
FROM table1
WHERE id NOT IN
(
SELECT TOP 页大小*(页数-1) id FROM table1 ORDER BY id
)
ORDER BY id
方法2:
适用于 /2005
SELECT TOP 页大小 *
FROM table1
WHERE id >
(
SELECT ISNULL(MAX(id),0)
FROM
(
SELECT TOP 页大小*(页数-1) id FROM table1 ORDER BY id
) A
)
ORDER BY id
方法3:
适用于 2005
SELECT TOP 页大小 *
FROM
(
SELECT ROW_NUMBER() OVER (ORDER BY id) AS RowNumber,* FROM table1
) A
WHERE RowNumber > 页大小*(页数-1)
135.SQL分页存储过程。(百万级)
ALTER PROCEDURE GetDataList
(
@TableName varchar(50), --表名
@Fields varchar(5000) = '*', --字段名(全部字段为*)
@OrderField varchar(5000), --排序字段(必须!支持多字段)
@sqlWhere varchar(5000) = Null,--条件语句(不用加where)
@pageSize int, --每页多少条记录
@pageIndex int = 1 , --指定当前为第几页
@TotalPage int output --返回总页数
)
as
begin Begin Tran --开始事务 Declare @sql nvarchar(4000);
Declare @totalRecord int; --计算总记录数
if (@SqlWhere='' or @sqlWhere=NULL)
set @sql = 'select @totalRecord = count(*) from ' + @TableName
else
set @sql = 'select @totalRecord = count(*) from ' + @TableName + ' where ' + @sqlWhere EXEC sp_executesql @sql,N'@totalRecord int OUTPUT',@totalRecord OUTPUT--计算总记录数
--计算总页数
select @TotalPage=CEILING((@totalRecord+0.0)/@PageSize) if (@SqlWhere='' or @sqlWhere=NULL)
set @sql = 'Select * FROM (select ROW_NUMBER() Over(order by ' + @OrderField + ') as rowId,' + @Fields + ' from ' + @TableName
else
set @sql = 'Select * FROM (select ROW_NUMBER() Over(order by ' + @OrderField + ') as rowId,' + @Fields + ' from ' + @TableName + ' where ' + @SqlWhere
--处理页数超出范围情况
if @PageIndex<=0
Set @pageIndex = 1
if @pageIndex>@TotalPage
Set @pageIndex = @TotalPage --处理开始点和结束点
Declare @StartRecord int
Declare @EndRecord int
set @StartRecord = (@pageIndex-1)*@PageSize + 1
set @EndRecord = @StartRecord + @pageSize - 1 --继续合成
set @Sql = @Sql + ') as ' + @TableName + ' where rowId between ' + Convert(varchar(50),@StartRecord) + ' and ' + Convert(varchar(50),@EndRecord)
Exec(@Sql)
---------------------------------------------------
If @@Error <> 0
Begin
RollBack Tran
Return -1
End
Else
Begin
Commit Tran
Return @totalRecord ---返回记录总数
End
end
136.模拟银行创建存储过程。
利用存储过程来实现下面的应用:
从一个账户转指定数额的款项到另一个账户中。
*/ 建表:create table account(accountnum int,uname char(10),total float)
insert into account values(1001,'u1',50000)
insert into account values(1002,'u2',1000)
select * from account
建存储过程:
CREATE PROCEDURE TRANSFER
@outAccount int,
@inAccount int,
@amount
FLOAT AS
DECLARE @totalDeposit FLOAT
BEGIN TRANSACTION /* 检查转出账户的余额 */ SELECT @totalDeposit=total FROM ACCOUNT WHERE ACCOUNTNUM=@outAccount
IF @totalDeposit IS NULL /* 转出账户不存在或账户中没有存款 */ BEGIN ROLLBACK TRANSACTION
PRINT '转出账户不存在或账户中没有存款,不能操作!'
RETURN
END
IF @totalDeposit<@amount /* 转出账户存款不足 */
BEGIN
ROLLBACK TRANSACTION
PRINT '转出账户存款不足,不能操作!' /* 建议使用函数RAISERROR('转出账户存款不足,不能操作!',10,1) */
RETURN
END
IF exists(SELECT * FROM ACCOUNT WHERE ACCOUNTNUM=@inAccount ) /* 检查转入账户是否存在 */ BEGIN
UPDATE account SET total=total-@amount WHERE ACCOUNTNUM=@outAccount /* 修改转出账户,减去转出额 */
UPDATE account SET total=total+@amount WHERE ACCOUNTNUM=@inAccount /* 修改转入账户,增加转出额 */
COMMIT TRANSACTION /* 提交转账事务 */
PRINT '转账成功,谢谢使用本存储过程!'
RETURN
END
ELSE
BEGIN
ROLLBACK TRANSACTION
PRINT '转入账户不存在,不能操作!'
RETURN
END
智力题
1.(小明一家过桥)
现在小明一家过一座桥,过桥时候是黑夜,所以必须有灯.现在小明过桥要1秒,小明的弟弟要3秒,小明的爸爸要6秒,小明的妈妈要8秒,小明的爷爷要12秒.每次过桥只能过2人,两人过桥的速度以最慢者为定.还有过桥后要已过桥的其中一人送灯也算秒速,而灯在30秒后就会熄灭,过桥时不能没有灯.问小明一家怎样过桥?
答案:1.小明和弟弟过桥(3秒) + 小明回来(1秒)+ 爷爷和妈妈过桥(12秒) + 弟弟回来(3秒)+ 爸爸和小明过桥(6秒) + 小明回来(1秒) + 小明和弟弟过桥(3秒)= 29秒 2.小明和弟弟过桥(3秒) + 弟弟回来(3秒)+ 爷爷和妈妈过桥(12秒) + 小明回来(1秒)+ 爸爸和小明过桥(6秒) + 小明回来(1秒) + 小明和弟弟过桥(3秒)= 29秒
2.有关烧绳子的计时问题烧一根不均匀的绳子,从头烧到尾总共需要1个小时.现有若干条材质相同的绳子,问如何用烧绳的方法来计时一个小时十五分钟?
答案:
一,一根绳子从两头烧,烧完就是半个小时。
二,一根要一头烧,一根从两头烧,两头烧完的时候(30分),将剩下的一根另一端点着,烧尽就是45分钟。再从两头点燃第三根,烧尽就是1时15分。
3.假设排列着100个乒乓球,由两个人轮流拿球装入口袋,能拿到第100个乒乓球的人为胜利者.条件是:每次拿球者至少要拿1个,但最多不能超过5个,问:如果你是最先拿球的人,你该拿几个?以后怎么拿就能保证你能得到第100个乒乓球?
答案:
我先拿,第一次拿4个 剩下96个,96=6*16 接下来,对手拿n个,我就拿6-n个(例如他如果拿1个,我就拿5个;如果他拿4个我就拿2个) 这样就能保证每一轮都拿走6个球 最后一轮,剩下6个,不管他怎么拿,第100个球都是被我拿走.
4.猜牌问题 S先生、P先生、Q先生他们知道桌子的抽屉里有16张扑克牌:红桃A、Q、4,黑桃J、8、4、2、7、3,草花K、Q、5、4、6,方块A、5.约翰教授从这16张牌中挑出一张牌来,并把这张牌的点数告诉P先生,把这张牌的花色告诉Q先生.这时,约翰教授问P先生和Q先生:你们能从已知的点数或花色中推知这张牌是什么牌吗?于是,S先生听到如下的对话: P先生:我不知道这张牌. Q先生:我知道你不知道这张牌. P先生:现在我知道这张牌了. Q先生:我也知道了. 听罢以上的对话,S先生想了一想之后,就正确地推出这张牌是什么牌. 请问:这张牌是什么牌?
方块 5P知道点数却不知道是哪张牌,那么就去掉点数不重复的 J、8、2、7、3、K、6.剩下:红桃 A、Q、4黑桃 4草花 Q、5、4、方块 A、5然后,Q知道花色,并且知道P不知道是哪张牌,那么就排除含有只出现一次数的花色:黑桃、草花.剩下:红桃 A、Q、4方块 A、5然后,P知道点数的,听Q说“我知道你不知道这张牌.”就知道了是哪张牌,所以绝不可能是点数重复的A.于是剩下:红桃:Q、4方块:5最后,Q听P这么说(P知道点数,而Q又可以确定花色),那么只能是方块5
5.一只鸟的飞行速度大约为68千米一小时,一架飞机的速度大约是鸟的13倍.这架飞机从北京出发,直飞广州,用了130分钟,如果鸟也沿着飞机的路线飞行,大约要飞多少分钟?合多少小时?(结果保留整)
答案:
1、68÷60=17/15千米/分钟,鸟的速度2、(17/15)×13=221/15千米/分钟,飞机速度3、(221/15)×130=5746/3千米,北京广州距离4、(5746/3)÷(17/15)=1690分钟≈28小时简便方法:因为飞机的速度,是鸟的速度的13倍,所以,在距离不变的情况下,鸟飞行的时间,是飞机的13倍.130×13=1690分钟≈28小时.
6.如果你有无穷多的水,一个3公升的提捅,一个5公升的提捅,两只提捅形状上下都不均
匀,问你如何才能准确称出4公升的水?
答案:把三公升的桶注满水,倒入五公升桶里去,然后在把三公升的桶倒满,把五公升的桶给注满了倒掉,三公升桶里还有一升,把这一升倒入五公升桶里去,在注入三公升就是四公升了。
7.一个岔路口分别通向诚实国和说谎国。来了两个人,已知一个是诚实国的,另一个是说谎国的,诚实国永远说实话,说谎国永远说谎话。现在你要去说谎国,但不知道应该走哪条路,需要问这两个人。请问应该怎么问?
答案:问他们俩各自来自哪条路,他们肯定所指的都是诚实国,另一条路就是去谎话国的。(两人答案一样。然后在往另一条路走。)
8.有21个小铁球,其中有一个比其他小铁球要重,现在给您一个天秤称,您要称多少次?请说说您的算法吗?
答:三次
算法
第一次将21个分成三份3,9,9然后将两个9放到天秤上
第二次将沉下的9个分成三份3,3,3然后将其中的两份放到天秤上
第三次将沉下的3个分成三份1,1,1然后将其中的两个到天秤上如果平衡就是剩下的个,否则就是沉下的一个
本文转自程序猿博客51CTO博客,原文链接http://blog.51cto.com/haihuiwei/1702078如需转载请自行联系原作者
365850153