c++隐形代码太多,要效率还是c。另外纯计算题,java也很快的。偏计算的代码少,执行次数多,jit会生成机器码,生成之后就跟c语言没啥区别了。
LintCode/LeetCode训练题目&答案详解—基础篇
一、在二叉树中寻找值最大的节点并返回:
给出如下一棵二叉树:
返回值为3的节点。
简析:使用了递归的思想;注意为空的判断;
二、单例
单例是最为最常见的设计模式之一。对于任何时刻,如果某个类只存在且最多存在一个具体的实例,那么我们称这种设计模式为单例。例如,对于classMouse(不是动物的mouse哦),我们应将其设计为singleton模式。
你的任务是设计一个getInstance方法,对于给定的类,每次调用getInstance时,都可得到同一个实例。
样例:
在Java中:
Aa=A.getInstance();
Ab=A.getInstance();
a应等于b.
挑战:
如果并发的调用getInstance,你的程序也可以正确的执行么?
}
注意:实现一个单例有两点注意事项,①将构造器私有,不允许外界通过构造器创建对象;②通过公开的静态方法向外界返回类的唯一实例
参考:单例模式的几种写法对比:
三、整数排序
给一组整数,按照升序排序,使用选择排序,冒泡排序,插入排序或者任何O(n2)的排序算法。
样例:
对于数组[3,2,1,4,5],排序后为:[1,2,3,4,5]。
答案(java版本):
选择排序:
冒泡排序:
答案解析:
各个语言的实现请参考维基百科:
**四、斐波那契数列**
查找斐波纳契数列中第N个数。所谓的斐波纳契数列是指:
前2个数是0和1。
第i个数是第i-1个数和第i-2个数的和。
斐波纳契数列的前10个数字是:
0,1,1,2,3,5,8,13,21,34...
答案:
注意事项:
1、n是从1开始的,而不是0
2、一般我们都会想到用递归的方式实现,但是这个时间开销很大:
3、题目的引申:青蛙跳阶梯,铺方砖
参考:
javaleetcode的题目for循环逻辑求大神解释举个例子:数组nums有5个组,即大小为5.它的元素下标就是01234
你的J如果是jlength-1就是说J4那么j取值就是0123,也就是永远取不到最后一个元素。
leetcode第三题,JAVA小白求教publicintlengthOfLongestSubstring(Strings){
if(s.length()==0)return0;
HashMapCharacter,Integermap=newHashMapCharacter,Integer();
intmax=0;
for(inti=0,j=0;is.length();++i){
if(map.containsKey(s.charAt(i))){
j=Math.max(j,map.get(s.charAt(i))+1);
}
map.put(s.charAt(i),i);
max=Math.max(max,i-j+1);
}
returnmax;
}
耗时7天我终于把LeetCode刷通关:数组十七连,真是不简单大家好,我是老三,一个刷题困难户,接下来我们开始数组类型算法的刷题之旅!
数组
数组基本上是我们最熟悉的数据结构了,刚会写“HelloWorld”不久,接着就是“杨辉三角”之类的练习。
数组结构
上图是一个字符数组的例子。
因为内存空间连续,所以可以直接通过下标获取对应的元素。
但是删除就麻烦一点,相当于填坑,一个元素被移走,留下的坑,需要其它元素来填上。
删除元素
在Java中,多维数组的存储本质上也是一个行优先的一维数组。
我们都知道,在Java中的“=”用在基本数据类型上,是值传递,用在引用数据类型上,是引用传递。
这一块展开可以写一篇文章,我们只简单看个例子:
大家可以看到,newArray改变了,array也跟着变了。
为什么呢?
在Java中,数组是引用数组类型。array、newArray都是存储在栈中的引用,它们指向堆中真正存储的数组对象。
所以改变了newArray,实际是改变了newArray指向的数组。
数组引用传递
这一点是我们刷题需要注意的,复制数组需要在循环中一个个复制。
题目:704.二分查找()
难度:简单
描述:
给定一个n个元素有序的(升序)整型数组nums和一个目标值target,写一个函数搜索nums中的target,如果目标值存在返回下标,否则返回-1。
题目示例
思路:
二分查找可以说我们都很熟了。
因为数组是有序的,所以定义三个指针,low、high、mid,每次与中间指针指向的元素nums[mid]比较,
二分查找
但是这个代码还有一处问题,在哪呢?
intmid=(left+right)/2;
这个地方可能会因为left和right数值太大导致内存溢出,所以应该写为intmid=left+((right-left)1);
修改之后代码如下:
时间复杂度:O(logn)
题目:35.搜索插入位置()
难度:简单
描述:
给定一个排序数组和一个目标值,在数组中找到目标值,并返回其索引。如果目标值不存在于数组中,返回它将会被按顺序插入的位置。
请必须使用时间复杂度为O(logn)的算法。
题目示例
思路:
二分查找比较简单,但写对还要费点功夫,再做一道基本一样的题巩固一下。
这道题基本一样,插入的位置可能有四种情况:
二叉树插入位置
代码如下:
时间复杂度:O(logn)
题目:34.在排序数组中查找元素的第一个和最后一个位置()
难度:中等
描述:
给定一个按照升序排列的整数数组nums,和一个目标值target。找出给定目标值在数组中的开始位置和结束位置。
如果数组中不存在目标值target,返回[-1,-1]。
进阶:
题目示例
思路:
看到时间复杂度O(logn),数组有序,我们知道,二分查找该上场了。
但是这道题有点不一样,它需要寻找边界。
那我们怎么办呢?
这就引入了寻找边界的二分查找。
这道题的思路是什么呢?
我们分别用二分查找来寻找左边界和右边界。
一般的二分查找:
注意,我们这里的返回条件是nums[mid]==target,但是寻找边界的时候就不能这样了,因为我们不能确定mid是不是我们的边界。
以寻找左边界为例,条件是target=nums[mid]的时候,我们接着往左移动。
寻找右边界也类似。
代码如下:
时间复杂度:O(logn)
题目:27.移除元素()
难度:简单
描述:
给你一个数组nums和一个值val,你需要原地移除所有数值等于val的元素,并返回移除后数组的新长度。
不要使用额外的数组空间,你必须仅使用O(1)额外空间并原地修改输入数组。
元素的顺序可以改变。你不需要考虑数组中超出新长度后面的元素。
说明:
为什么返回数值是整数,但输出的答案是数组呢?
请注意,输入数组是以「引用」方式传递的,这意味着在函数里修改输入数组对于调用者是可见的。
你可以想象内部操作如下:
思路
「暴力解法」
暴力解法没什么好说的,和上道题类似,找到要删除的元素,把它后面的元素全部向前移动一位。
暴力解法
这里有两点需要注意:
代码如下:
时间复杂度:O(n?)。
「双指针法」
双指针法,是数组和链表题中非常常用的一种方法。
这道题用双指针法怎么解决呢?
定义两个指针,一个前,一个后。没有找到目标的时候front和after一起移动,找到目标的时候,after停下来,front接着移动,把front指向的值赋给after指向的值。
这样一来,双指针就通过一个循环完成了双循环完成的事情。
双指针法
代码如下:
时间复杂度:O(n)。
题目:27.移除元素()
难度:简单
描述:
给你一个有序数组nums,请你原地删除重复出现的元素,使每个元素只出现一次,返回删除后数组的新长度。
不要使用额外的数组空间,你必须在原地修改输入数组并在使用O(1)额外空间的条件下完成。
说明:
为什么返回数值是整数,但输出的答案是数组呢?
请注意,输入数组是以「引用」方式传递的,这意味着在函数里修改输入数组对于调用者是可见的。
你可以想象内部操作如下:
题目示例
思路
趁着上一道题劲儿还没缓过来,赶紧做一道基本一样的巩固一下。
直接上代码:
时间复杂度:O(n)。
题目:283.移动零()
难度:简单
描述:
给定一个数组nums,编写一个函数将所有0移动到数组的末尾,同时保持非零元素的相对顺序。
「示例:」
「说明」:
思路
继续沿着上一道题的思路。
移动零
代码如下:
时间复杂度:O(n)。
题目:977.有序数组的平方()
难度:简单
描述:
给你一个按「非递减顺序」排序的整数数组nums,返回「每个数字的平方」组成的新数组,要求也按「非递减顺序」排序。
题目示例
思路
「暴力排序法」
这道题一看,最直观的做法是什么呢?
先求数字平方的数组,然后再把新数组排序。
代码也好写:
时间复杂度:遍历时间复杂度O(n),快排时间复杂度O(nlogn),所以时间复杂度O(n+nlogn)。
思路
「双指针法」
我们连写几道双指针了,这道题能不能用双指针实现呢?
我们分析一下,这个数组在取平方之前,是有序的,那么它绝对值最大的数一定是在两端的。
所以我们可以定义两个指针,一个指向最左端,一个指向最右端,比较两者平方的大小,大的平方放入结果数组,并移动指针。
有序数组的平方
代码如下:
时间复杂度:O(n)。
题目:1.两数之和()
难度:简单
描述:给定一个整数数组nums和一个整数目标值target,请你在该数组中找出和为目标值target的那两个整数,并返回它们的数组下标。
你可以假设每种输入只会对应一个答案。但是,数组中同一个元素在答案里不能重复出现。
你可以按任意顺序返回答案。
题目示例
思路:
「暴力解法」
上来我们先来个最简单的暴力解法,大家应该都知道冒泡排序吧,类似的两层循环。
两层循环
代码写起来也很简单:
时间复杂度:看到这个双循环,就知道时间复杂度O(n?)。
「哈希辅助法」
时间复杂度O(n?)多少有点过了,这道题的重点是两个元素相加之和的判断。
我们可以用一个Hash集合把元素存起来,这样一来遍历一遍就够了,例如目标和9,当前元素2,只需要判断集合里是否有元素7就行了。
时间复杂度:从Hash查询和取值时间复杂度都是O(1),所以整体时间复杂度是O(1)。
题目:15.三数之和()
难度:简单
描述:
给你一个包含n个整数的数组nums,判断nums中是否存在三个元素a,b,c,使得a+b+c=0?请你找出所有和为0且不重复的三元组。
注意:答案中不可以包含重复的三元组。
题目示例
思路:
「哈希法」
做完两数之和以后,我们首先想到的就是哈希法。
两层循环,取到a,b,再通过0-(a+b)来确定c。
但是这里还有一个问题,答案中不可以包含重复的三元组。
所以,我们还要想办法去掉Hash里的重复元素。
可以加入一个约束,第三个数的索引大于第二个数才存入。
时间复杂度:双循环,O(n?)。
虽然这么也写出来了,但是,说实话,很难写出没有问题的代码。
我们写了这么多双指针,那么有没有可能用双指针的方式呢?
「双指针法」
首先对数组进行排序,然后遍历数组。
然后再在当前节点后面取左右指针,判断左右指针的值是否等于0-nums[i],然后分别左右移动。
怎么去重呢?
满足条件时,看左指针的值是否和前一个位置相等,右指针的值是否和和它后一个位置的值相等。
双指针法
代码如下:
时间复杂度:O(n?)
题目:18.四数之和()
难度:简单
描述:
给定一个包含n个整数的数组nums和一个目标值target,判断nums中是否存在四个元素a,b,c和d,使得a+b+c+d的值与target相等?找出所有满足条件且不重复的四元组。
注意:答案中不可以包含重复的四元组。
题目示例
思路:
我们延续三数之和的思路,在三数之和外面再套一层循环。
时间复杂度:O(n?)
题目:209.长度最小的子数组()
难度:中等
描述:
给定一个含有n个正整数的数组和一个正整数target。
找出该数组中满足其和target的长度最小的连续子数组[numsl,numsl+1,...,numsr-1,numsr],并返回其长度。如果不存在符合条件的子数组,返回0。
题目示例
思路
这道题是一道经典的滑动窗口问题[4]。
image-20210801164436322
代码如下:
时间复杂度:O(n),虽然循环里套循环了,但是starrt和end各自被移动了n次,所以时间复杂度是O(n)。
题目:219.存在重复元素II()
难度:简单
描述:
给定一个整数数组和一个整数k,判断数组中是否存在两个不同的索引i和j,使得nums[i]=nums[j],并且i和j的差的绝对值至多为k。
题目示例
思路:
上面我们做了一道滑动窗口的题,我们接着再做一道也可以用滑动窗口解决的问题。
这道题的滑动窗口略有区别,上一道题的窗口是活动的,这个是固定的滑动窗口,维护一个长度为k的固定窗口,如果窗口内含有目标值,返回。如果窗口进入新的元素,就需要把头部的元素移除掉,保持窗口的长度。
固定窗口
代码如下:
时间复杂度:O(n)。
题目:1052.爱生气的书店老板()
难度:中等
描述:
今天,书店老板有一家店打算试营业customers.length分钟。每分钟都有一些顾客(customers[i])会进入书店,所有这些顾客都会在那一分钟结束后离开。
在某些时候,书店老板会生气。如果书店老板在第i分钟生气,那么grumpy[i]=1,否则grumpy[i]=0。当书店老板生气时,那一分钟的顾客就会不满意,不生气则他们是满意的。
书店老板知道一个秘密技巧,能抑制自己的情绪,可以让自己连续X分钟不生气,但却只能使用一次。
请你返回这一天营业下来,最多有多少客户能够感到满意。
「示例:」
思路:
这道题是一道固定窗口的问题。
整体思路就是把不生气的部分作为固定窗口,固定窗口把customers分成了三部分,最后求三部分的最大和。
固定窗口
时间复杂度:O(n)。
空间复杂度:O(1)。
题目:面试题3.数组中重复的数字()
难度:复杂
描述:
找出数组中重复的数字。
在一个长度为n的数组nums里的所有数字都在0n-1的范围内。数组中某些数字是重复的,但不知道有几个数字重复了,也不知道每个数字重复了几次。请找出数组中任意一个重复的数字。
「示例1:」
思路:
「哈希法」
这种找重复的数字问题,我们脑子里第一下就想起来,用Hash存储元素,然后进行比对。
代码实现也很简单:
时间复杂度:O(n)。
空间复杂度:O(n)
但今天的主角不是它,而是
「原地置换法」
我们注意到一个条件所有数字都在0n-1的范围内,那就在这方面进行操作,我们可以把元素放到它的值对应的下标的位置。
例如num[2]=1,那我们就把它放到下标1的位置。
接着遍历,元素发现它应该待的坑已经被它的双胞胎兄弟给占了,它就知道,它是多余的那个。
原地置换
代码如下:
时间复杂度:O(n)。
空间复杂度:O(1)
题目:41.缺失的第一个正数()
难度:复杂
描述:
给你一个未排序的整数数组nums,请你找出其中没有出现的最小的正整数。
请你实现时间复杂度为O(n)并且只使用常数级别额外空间的解决方案。
题目示例
思路
「辅助数组」
这道题有一个非常巧妙地的办法![1]
可以引入一个辅助数组,从1开始,在对应的位置存入原数组对应的元素。如原数组num[0]=1,那么这个元素就应该存入辅助数组helper[1]。
然后遍历辅助数组,发现的第一个坑就是缺失的第一个正数。
辅助数组
代码如下:
时间复杂度:O(n)。
空间复杂度:O(n)。
「原地置换法」
我们上面用了原地置换法解决了一个问题,降低了空间复杂度,我们这道题是不是也可以呢?
原地置换没法修改数组长度,我们肯定不能nums[i]存i了,我们左移一下,num[i-1]存i。
原地置换
代码实现如下:
时间复杂度:O(n)。
空间复杂度:O(1)。
题目:54.螺旋矩阵()
难度:中等
描述:
给你一个m行n列的矩阵matrix,请按照「顺时针螺旋顺序」,返回矩阵中的所有元素。
示例1:
示例2
思路
这道题,思路比较容易想,就是上右下左四个方向顺时针遍历数组。
顺时针遍历数组
但是这道题的细节是魔鬼。
有两种,一种是一圈遍历完成,上下左右的位置移动,遍历是左闭右开[的条件。
我们采用的是第二种,每遍历完一条边,就移动对应的位置,遍历就是左闭右闭的条件。
还有一点细节就是值得注意的是,遍历过程中可能会出现出现topbottom||leftright,其中一对边界彼此交错了。
这意味着此时所有项都遍历完了,如果没有及时break,就会重复遍历。
代码如下:
时间复杂度:O(mn),其中m和n分别是输入矩阵的行数和列数。
题目:59.螺旋矩阵II()
难度:中等
描述:
给你一个正整数n,生成一个包含1到n2所有元素,且元素按顺时针顺序螺旋排列的nxn正方形矩阵matrix。
示例
思路
和上面一道题基本一模一样,我们往里面套就行了。
代码如下:
时间复杂度:O(n?)
剑指Offer29.顺时针打印矩阵也是一道类似的题目。
写了个顺口溜总结一下:
logo设计
创造品牌价值
¥500元起
APP开发
量身定制,源码交付
¥2000元起
商标注册
一个好品牌从商标开始
¥1480元起
公司注册
注册公司全程代办
¥0元起
查
看
更
多