面试题 01.04. 回文排列
给定一个字符串,编写一个函数判定其是否为某个回文串的排列之一。回文串是指正反两个方向都一样的单词或短语。排列是指字母的重新排列。
回文串不一定是字典当中的单词。
示例1:
输入:“tactcoa” 输出:true(排列有"tacocat"、“atcocta”,等等)
解题思路
回文序列:正着读和倒着读都是一样的,也就是正着和倒着的字符是一样的,除了回文中心外,其他字符是成对出现的,只有中间的回文中心是单的。而回文中心的数量是1个。
也就是:正常的回文只有一个字符的数量是奇数,其余的字符都是偶数,所以,只需要判断字符串中字符个数是奇数的数量有几个,如果多余1个则不是回文,返回false,否则是true
/**
* @param {string} s
* @return {boolean}
*/
var canPermutePalindrome = function(s) {
// 使用哈希表
var mapArr=new Map();
var charS=s.split("");
for(var i=0; i<charS.length; i++){
if(mapArr.has(charS[i])){
mapArr.set(charS[i],mapArr.get(charS[i])+1);
}else{
mapArr.set(charS[i],1);
}
}
//获取map中数量----判断为奇数的字符数量是否大于1,则返回 false
var count=0;
for(var val of mapArr.values()){
if(val % 2 !=0){
count++;
}
}
if(count>1){
return false;
}
return true;
};
面试题 10.02. 变位词组
编写一种方法,对字符串数组进行排序,将所有变位词组合在一起。变位词是指字母相同,但排列不同的字符串。
注意:本题相对原题稍作修改
示例:
输入: [“eat”, “tea”, “tan”, “ate”, “nat”, “bat”],
输出:
[
[“ate”,“eat”,“tea”],
[“nat”,“tan”],
[“bat”]
]
说明:
所有输入均为小写字母。
不考虑答案输出的顺序。
解题思路:
将字符串数组中的每一个字符串装入字典/哈希表,并判断下一个字符串在字典中是否存在,存在则取出装入结果数组,不存在的加入字典,加入结果数组。
关键在于判断,这些字符串字母是相同的,但是顺序不同,这样的情况与字典中现存的元素该怎么比较?
*解决:*在判断之间,将当前字符串排序,在判断字典中是否存在。
*注意:*最后加入字典和结果数组的是原来的字符串,不是排序后的。
代码
/**
* @param {string[]} strs
* @return {string[][]}
*/
var groupAnagrams = function(strs) {
//定义返回结果集
var result=[];
//定义字典map
var map=new Map();
for(var i=0,j=strs.length; i<j; i++ ){
var temp=strs[i].split("").sort().join("");
if(!map.has(temp)){
map.set(temp,result.length);
result.push([strs[i]]);
}
else{
result[map.get(temp)].push(strs[i]);
}
}
return result;
};
面试题 16.02. 单词频率
设计一个方法,找出任意指定单词在一本书中的出现频率。你的实现应该支持如下操作:
WordsFrequency(book)构造函数,参数为字符串数组构成的一本书 get(word)查询指定单词在书中出现的频率
示例:
WordsFrequency wordsFrequency = new WordsFrequency({“i”, “have”, “an”,
“apple”, “he”, “have”, “a”, “pen”}); wordsFrequency.get(“you”);
//返回0,"you"没有出现过 wordsFrequency.get(“have”); //返回2,"have"出现2次
wordsFrequency.get(“an”); //返回1 wordsFrequency.get(“apple”); //返回1
wordsFrequency.get(“pen”); //返回1提示:
book[i]中只包含小写字母 1 <= book.length <= 100000 1 <= book[i].length <= 10 get函数的调用次数不会超过100000
解题思路
1、hash法
统计单词出现在书中的次数,想到字典map,书是一个字符串数组,只需要遍历数组将单词存储到字典中,字典中原来存在,则直接+1,不存在则置为1,然后在get函数中,返回字典中word出现的次数。
代码
/**
* @param {string[]} book
*/
var WordsFrequency = function(book) {
this.map=new Map();
for(var word of book){
if(this.map.has(word)){
this.map.set(word,this.map.get(word)+1);
}else{
this.map.set(word,1);
}
}
};
/**
* @param {string} word
* @return {number}
*/
WordsFrequency.prototype.get = function(word) {
return this.map.get(word) || 0;
};
/**
* Your WordsFrequency object will be instantiated and called as such:
* var obj = new WordsFrequency(book)
* var param_1 = obj.get(word)
*/
2、数组法
与hash法所占用内存就差一点点,但是在时间消耗上数组法所用时间太多
/**
* @param {string[]} book
*/
var WordsFrequency = function(book) {
//数组实现
this.bookArr=[];
for(var word of book){
this.bookArr.push(word);
}
};
/**
* @param {string} word
* @return {number}
*/
WordsFrequency.prototype.get = function(word) {
var num=0;
for(var i=0,j=this.bookArr.length; i<j; i++){
if(this.bookArr[i]==word){
num++;
}
}
return num;
};
/**
* Your WordsFrequency object will be instantiated and called as such:
* var obj = new WordsFrequency(book)
* var param_1 = obj.get(word)
*/
面试题 16.14. 最佳直线
给定一个二维平面及平面上的 N
个点列表Points,其中第i个点的坐标为Points[i]=[Xi,Yi]。请找出一条直线,其通过的点的数目最多。设穿过最多点的直线所穿过的全部点编号从小到大排序的列表为S,你仅需返回[S[0],S[1]]作为答案,若有多条直线穿过了相同数量的点,则选择S[0]值较小的直线返回,S[0]相同则选择S[1]值较小的直线返回。
示例:
输入: [[0,0],[1,1],[1,0],[2,0]] 输出: [0,2] 解释: 所求直线穿过的3个点的编号为[0,2,3]
提示:
2 <= len(Points) <= 300 len(Points[i]) = 2
解题思路
参考大佬的思路:但有一点我没想明白,多条直线穿过点数量相同,返回下标最小点,下面的代码中是怎么实现的。
两点确定一条直线,因此直接让直线通过两点,count=2,然后循环判断该直线是否穿过ppoints中的其他点,如果穿过,则count++,最后更新res结果数组,返回直线穿过的前两个节点。
其中那个长的if语句用到了三点共线的,其实就是数学推导的过程。
方法1:普通的for循环嵌套
/**
* @param {number[][]} points
* @return {number[]}
*/
var bestLine = function (points) {
var max=0;
var res=[];
var l=points.length;
for(var i=0; i<l; i++){
for(var j=i+1; j<l; j++){
var count=2;
for(var k=0; k<l; k++){
if(k==i || k==j){ continue;}
if(
(points[j][0]-points[i][0])*(points[k][1]-points[j][1])==
(points[j][1]-points[i][1])*(points[k][0]-points[j][0])
){
count++;
}
}
if(count>max){
max=count;
res=[i,j];
}
}
}
return res;
}
方法2:hash表
关于哈希这个解法,我并没有仔细看,需要心静下来在仔细看。但是这个方法我个人觉得要比上面的更严谨。
/**
* @param {number[][]} points
* @return {number[]}
*/
var bestLine = function(points) {
var inLine = function (p0, p1, p2) {
return (p1[1] - p0[1]) * (p2[0] - p0[0]) == (p1[0] - p0[0]) * (p2[1] - p0[1])
}
var currentDiff, currentAppend, tempDiff, diffArr = [], diffObj = {}, max = [0, '']
for (var i = 0; i < points.length - 1; i++) {
for (var k = i + 1; k < points.length; k++) {
var key = i + ',' + k
for (var j = k + 1; j < points.length; j++) {
if (inLine(points[i], points[k], points[j])) {
diffObj[key] = diffObj[key] || [i, k]
if (diffObj[key].indexOf(j) == -1) {
diffObj[key].push(j)
diffObj[key] = diffObj[key].sort((a, b) => a - b)
if (diffObj[key].length == max[0]) {
if (diffObj[key][0] < diffObj[max[1]][0] || (diffObj[key][0] == diffObj[max[1]][0] && diffObj[key][1] < diffObj[max[1]][1])) {
max[0] = diffObj[key].length
max[1] = key
}
} else if (diffObj[key].length > max[0]) {
max[0] = diffObj[key].length
max[1] = key
}
}
}
}
}
}
var res = diffObj[max[1]]
if (res && res.length > 2) {
return res.slice(0, 2)
} else {
return [0, 1]
}
};
面试题 16.24. 数对和
设计一个算法,找出数组中两数之和为指定值的所有整数对。一个数只能属于一个数对。
示例 1:
输入: nums = [5,6,5], target = 11 输出: [[5,6]]
示例 2:
输入: nums = [5,6,5,6], target = 11 输出: [[5,6],[5,6]]
提示:
nums.length <= 100000
方法1:排序+双指针
先排序,然后使用双指针,前面一个后面一个,循环判断加和是否等于target,相等则加入结果数组中,大于则r–,小于则l++
注意:在数组的排序中,如果排序元素都是数字,直接使用sortsort函数会出错(因为是按照unicode码排的)),因此需要使用函数重新排。这个方法耗费时间和内存都比较大。
/**
* @param {number[]} nums
* @param {number} target
* @return {number[][]}
*/
var pairSums = function(nums, target) {
//排序和双指针
nums.sort((a,b)=> a-b);
var res=[];
var l=0;
var r=nums.length-1;
while(l<r){
if(nums[l]+nums[r]==target){
var temp=[];
temp.push(nums[l]);
temp.push(nums[r]);
res.push(temp);
l++;
r--;
}else if(nums[l]+nums[r]>target){
r--;
}else l++;
}
return res;
};
关于sort排序对数组中元素都是纯数字的情况,如果直接使用sort()排序,会出错,因为isor函数使用unicode编码,解决方法,就是重写sort函数:
nums.sort(
(a,b)=> a-b
);
使用的箭头函数;
方法2:hash表法
/**
* @param {number[]} nums
* @param {number} target
* @return {number[][]}
*/
var pairSums = function(nums, target) {
var len=nums.length;
var res=[];
var map=new Map();
for(var i=0; i<len; i++){
if(map.has(target-nums[i])){
res.push([nums[i],target-nums[i]]);
map.set(target-nums[i],map.get(target-nums[i])-1);
if(map.get(target-nums[i])<1){
map.delete(target-nums[i]);
}
}else{//将元素都装入map
if(map.get(nums[i])){
map.set(nums[i],map.get(nums[i])+1);
}else{
map.set(nums[i],1);
}
}
}
return res;
};
这个写法是复制其他大佬的:
* @param {number[]} nums
* @param {number} target
* @return {number[][]}
*/
var pairSums = function (nums, target) {
const map = new Map()
for (const num of nums) {
if (!map.has(num)) {
map.set(num, 0)
}
map.set(num, map.get(num) + 1)
}
const res = []
for (const [key, val] of map) {
if (map.has(target - key)) {
let count = Math.min(val, map.get(target - key))
if (key === target - key) {//num和target-num相等时
count = val >> 1
}
while (count) {
res.push([key, target - key])
count--
}
map.delete(key)
}
}
return res
}
不定时补充~