前端程序员大厂面试精选100道算法题2

本文精选了多个大厂面试中的算法题目,包括字符串处理、数组、链表、哈希等,详细解析了判断回文、找第一个只出现一次的字符、最长公共前缀、反转字符串、验证IP地址、最长回文子串、大数加法、字符串转整数、KMP算法、最小覆盖子串和最长重复子串等问题,旨在帮助程序员提升算法能力和面试技巧。
摘要由CSDN通过智能技术生成
碎碎念:

亲爱的读者:你好!我的名字叫昌龙 【Changlon】 —— 一个非科班程序员、一个致力于前端的开发者、一个热爱生活且又时有忧郁的思考者。

如果我的文章能给你带来一些收获,你的点赞收藏将是对我莫大的鼓励!

我的邮箱:thinker_changlon@163.com

我的Github: https://github.com/Changlon

无论从事前端后端,不得不说算法才是决定一个程序员水平高低,工资收入的基石。算法能力也是一个程序员未来能走多远的一个决定性因素。小龙这里为大家总结了最新大厂面试题锦,为你的面试推波助力。

程序员之进大厂必刷算法题系列

数组
字符串
链表

哈希
二叉树
二分算法
分治算法(快排序)
动态规划
深度优先,广度优先算法
递归,回溯算法
贪心算法

一、NC141 判断回文

描述

给定一个字符串,请编写一个函数判断该字符串是否回文。如果回文请返回true,否则返回false。

示例1

输入: “absba”

返回值: true

示例2

输入: “ranko”

返回值: false

思路: 判断是否是回文字符串,就是从字符串的左边遍历和从右边遍历的效果是一样的。可以用双指针分别从前向后,和从右向前遍历,加入遇到不一样的字符就说明不是一个回文字符串。

function judge( str ) {
    let left = 0,right  = str.length-1 
	while(left<=right) {
		if(str[left]!=str[right])	 return false 
		left++,right--
	}
	return true
}

二、NC31 第一个只出现一次的字符

描述

在一个字符串(0<=字符串长度<=10000,全部由字母组成)中找到第一个只出现一次的字符,并返回它的位置, 如果没有则返回
-1(需要区分大小写).(从0开始计数)

示例1

输入: “google”

返回值: 4

思路:维护一个map,先遍历一遍字符串,将每个字符以及其出现的次数存入map,接着再次遍历一遍字符串,查询每次遍历的字符出现的次数,如果次数为1则返回。没有返回-1

function FirstNotRepeatingChar(str){
	const map = new Map() 
	for(let char of str) {
		if(!map.get(char)) {
			map.set(char,1)
		}else{
			map.set(char,map.get(char)+1) 
		}
	}
	for(let i =0;i<str.length;++i) {
		const char = str[i] 
		if(map.get(char)==1) {
			return i
		}
	}
	return -1
	
}

三、NC55 最长公共前缀

描述 编写一个函数来查找字符串数组中的最长公共前缀。

示例1

输入: [“abca”,“abc”,“abca”,“abc”,“abcc”]

返回值: “abc”

思路:

  1. 将字符串数组按照字典集排序;
  2. 将第一个字符与最后一个进行比较。
function longestCommonPrefix(strs) {
	  if(!strs||strs.length<=0) return ""
	  if(strs.length==1) return strs[0]
	  strs.sort() 
	  let len = strs.length 
	  let l = Math.min(strs[0].length,strs[len-1].length)
     
	 let maxStr = ""
	  for(let i=0;i<l;i++) {
		  for(let j= i+1;j<l+1;j++) {
			  let subStr = strs[0].substring(i,j) 
			 if(strs[len-1].indexOf(subStr)>=0) { 
				 maxStr = maxStr.length>subStr.length?maxStr:subStr
			 }
		  }
	  }
	 
	 return maxStr
	
 }

四、NC103 反转字符串

描述 写出一个程序,接受一个字符串,然后输出该字符串反转后的字符串。(字符串长度不超过1000)

示例1

输入: “abcd”

返回值: “dcba”

思路1: 倒着遍历目标字符串,每次将字符赋给一个变量,最后就是反转的效果。
时间复杂度: O(n)

function solve( str ) {
	
	let ret = "" 
	for(let i =str.length-1;i>=0;i--) {
		ret = ret + str[i]
	}
	return ret
}

思路2:双指针
时间复杂度: O(n/2)

function solve_( str ) {
	//由于js语言不能操作地址,所以先将str转为数组形式
	let strs = str.split('') 
	let i =0,j = strs.length-1
	while(i<j) {
		let t = strs[i] 
		strs[i] = strs[j] 
		strs[j] = t 
		i++
		j-- 
		
	}
	return strs.join("") 
}

五、NC113 验证IP地址

描述 编写一个函数来验证输入的字符串是否是有效的 IPv4 或 IPv6 地址

IPv4 地址由十进制数和点来表示,每个地址包含4个十进制数,其范围为 0 - 255, 用(".")分割。比如,172.16.254.1;
同时,IPv4 地址内的数不会以 0 开头。比如,地址 172.16.254.01 是不合法的。

IPv6 地址由8组16进制的数字来表示,每组表示 16 比特。这些组数字通过 (":")分割。比如,
2001:0db8:85a3:0000:0000:8a2e:0370:7334 是一个有效的地址。而且,我们可以加入一些以 0
开头的数字,字母可以使用大写,也可以是小写。所以, 2001:db8:85a3:0:0:8A2E:0370:7334 也是一个有效的
IPv6 address地址 (即,忽略 0 开头,忽略大小写)。

然而,我们不能因为某个组的值为 0,而使用一个空的组,以至于出现 (:😃 的情况。 比如,
2001:0db8:85a3::8A2E:0370:7334 是无效的 IPv6 地址。 同时,在 IPv6 地址中,多余的 0
也是不被允许的。比如, 02001:0db8:85a3:0000:0000:8a2e:0370:7334 是无效的。

说明: 你可以认为给定的字符串里没有空格或者其他特殊字符。

示例1

输入: “172.16.254.1”

返回值: “IPv4”

说明: 这是一个有效的 IPv4 地址, 所以返回 “IPv4”

示例2

输入: “2001:0db8:85a3:0:0:8A2E:0370:7334”

返回值: “IPv6”

说明: 这是一个有效的 IPv6 地址, 所以返回 “IPv6”

示例3

输入: “256.256.256.256”

返回值: “Neither”

说明: 这个地址既不是 IPv4 也不是 IPv6 地址

备注:

ip地址的类型,可能为 IPv4, IPv6, Neither

这道题其实没啥可说的,就是用正则匹配两种Ip地址格式

function solve( IP ) {
    // write code here
    	const IPv4Reg = /^(25[0-5]|2[0-4]\d|(0|1[0-9]{0,2}))(\.(25[0-5]|2[0-4]\d|(0|1[0-9]{0,2}))){3}$/ 
		const IPv6Reg = /^([\da-fA-F]{1,4}:){6}((25[0-5]|2[0-4]\d|[01]?\d\d?)\.){3}(25[0-5]|2[0-4]\d|[01]?\d\d?)$|^::([\da-fA-F]{1,4}:){0,4}((25[0-5]|2[0-4]\d|[01]?\d\d?)\.){3}(25[0-5]|2[0-4]\d|[01]?\d\d?)$|^([\da-fA-F]{1,4}:):([\da-fA-F]{1,4}:){0,3}((25[0-5]|2[0-4]\d|[01]?\d\d?)\.){3}(25[0-5]|2[0-4]\d|[01]?\d\d?)$|^([\da-fA-F]{1,4}:){2}:([\da-fA-F]{1,4}:){0,2}((25[0-5]|2[0-4]\d|[01]?\d\d?)\.){3}(25[0-5]|2[0-4]\d|[01]?\d\d?)$|^([\da-fA-F]{1,4}:){3}:([\da-fA-F]{1,4}:){0,1}((25[0-5]|2[0-4]\d|[01]?\d\d?)\.){3}(25[0-5]|2[0-4]\d|[01]?\d\d?)$|^([\da-fA-F]{1,4}:){4}:((25[0-5]|2[0-4]\d|[01]?\d\d?)\.){3}(25[0-5]|2[0-4]\d|[01]?\d\d?)$|^([\da-fA-F]{1,4}:){7}[\da-fA-F]{1,4}$|^:((:[\da-fA-F]{1,4}){1,6}|:)$|^[\da-fA-F]{1,4}:((:[\da-fA-F]{1,4}){1,5}|:)$|^([\da-fA-F]{1,4}:){2}((:[\da-fA-F]{1,4}){1,4}|:)$|^([\da-fA-F]{1,4}:){3}((:[\da-fA-F]{1,4}){1,3}|:)$|^([\da-fA-F]{1,4}:){4}((:[\da-fA-F]{1,4}){1,2}|:)$|^([\da-fA-F]{1,4}:){5}:([\da-fA-F]{1,4})?$|^([\da-fA-F]{1,4}:){6}:$/ 
		if(IPv4Reg.test(IP)){
			return "IPv4"
		}else if(IPv6Reg.test(IP)) {
			return "IPv6"
		}else{
			return "Neither"
		}
}

六、NC17 最长回文子串

描述 对于一个字符串,请设计一个高效算法,计算其中最长回文子串的长度。

给定字符串A以及它的长度n,请返回最长回文子串的长度。

示例1

输入: “abc1234321ab”,12

返回值: 7
思路: 动态规划
参考文章: https://blog.nowcoder.net/n/99462149a22d479ba9a3d5c8e7fd0247?f=comment

我的思路:
上面文章中的思路大概就是定义一个二维数组dp[i][j]来表示字符串A[i:j]是否是一个回文字符串。然后判断A[i:j]是否是回文的条件是A[i+1:j-1]是否是一个回文并且A[i]==A[j]。然后通过计算下标统计最大回文字符串长度。
而我的dp数组表示的则是字符串A[i:j]中回文字符串的长度,通过一个maxLen变量来记录最大值。

function getLongestPalindrome(A,n) {
	 let dp = []  
	 let max = 0
	//js初始化二维数组
	 for(let i =0;i<n;++i){
		 dp[i] = []
		 for(let j=0;j<n;++j) {
			 dp[i][j] = 0
		 }
	 }
	 for(let i =n-1;i>=0;--i) {
		 for(let j =i;j<n;++j) {
			 if(A[i]==A[j]) {
				 if(j-i<2) {
				 //对于单个字符 如a
				//或者两个字符 如 aa 的情况
					 dp[i][j] = j-i+1
				 }else{
				 //dp[i+1][j-1]==(j-i-1)
				 //判断的是字符A[i+1:j-1]是否是一个回文字符串,如果是回文字符串那dp[i+1][j-1]的值应该和字符串的长度相等
					 dp[i][j] = dp[i+1][j-1]==(j-i-1)? dp[i+1][j-1]+2:dp[i+1][j-1] 
				 } 
			 }else{
				 dp[i][j] = dp[i+1][j-1] 
			 }
			 max = Math.max(max,dp[i][j])
		 }
	 }
	return max
}

七、NC1 大数加法

描述 以字符串的形式读入两个数字,编写一个函数计算它们的和,以字符串形式返回。
(字符串长度不大于100000,保证字符串仅由’0’~'9’这10种字符组成)

示例1 输入: “1”,“99”
返回值: “100”
说明: 1+99=100

function solve( s ,  t ) {
    let res = "" 
	let carry = 0 
	let l1 = s.length,l2 = t.length   
	while(l1>0&&l2>0) { 
		let up = Number.parseInt(s[l1-1])  
		let down = Number.parseInt(t[l2-1]) 
		let result = (up+down+carry)%10 //上下两位数相加的结果
		carry = (up+down+carry)>=10?1:0 //上下两个数相加的进位
		res = result + res 
		l1--,l2--
	}
	
	let c = s.length>t.length?l1:l2
	let str = s.length>t.length?s:t
	
	if(carry==0) {
		return str.substring(0,c) + res
	}else if(carry==1&&!l1&&!l2) {
		return "1" + res
	}
		
	return solve(str.substring(0,c),"1") + res
   
}

八、NC100 将字符串转化为整数

描述 实现函数 atoi 。函数的功能为将字符串转化为整数
提示:仔细思考所有可能的输入情况。这个问题没有给出输入的限制,你需要自己考虑所有可能的情况。

function atoi( str ) {
    const INT_MAX = 2147483647
    const INT_MIN = -2147483648
    str = str.trim()
	const numberReg = /[0-9]/
	let ret = 0
	let oper = 1
	while(str[0]=='0'&&str.length>1) 
		str = str.substring(1,str.length) 
		for(let i =0;i<str.length;i++) {
			if(numberReg.test(str[i])) {
				 let  c =  Number.parseInt(str[i])
				 ret = ret*10 +c 
			}else{
				if(str[i]=='-') oper = -1
	            else if(str[i]=='+') oper = 1
				else break
			}
		}
    	if(ret*oper>INT_MAX) return INT_MAX
	    if(ret*oper<INT_MIN) return INT_MIN
		return ret*oper
}

九、NC149 kmp算法

描述 给你一个文本串S,一个非空模板串T,问S在T中出现了多少次

示例1

输入: “ababab”,“abababab” 返回值: 2

示例2

输入: “abab”,“abacabab” 返回值: 1

备注:

空间O(n)时间O(n)的算法

function kmp( S ,  T ) {
    	let ret = 0 
		let next = [] 
		let ni = 1,nj= 0 
		let k = 0
		next[nj] = 0 
		
		//求解next数组
		while(ni<S.length) {
			if(S[nj]==S[ni]) {
				k++ 
				next[ni] = k 
				ni++,nj=k
				
			}else{
				if(nj!=0) {
					nj = next[nj-1] 
					k = next[j] 
					
				}else{
					k = 0 
					next[ni] =k 
					ni++ 
				}
			}
		}
		
		let i = 0,j=0
		for(;i<T.length;i++) {
			while(j>0&&T[i]!=S[j]) 
				j = next[j-1] 
				
			if(T[i]==S[j]) 
				j++ 
			if(j == S.length){ 
				ret++  
				j = next[j-1]	
			}
		}
		
	return ret
}

十、NC28 最小覆盖子串

描述 给出两个字符串 S 和 T,要求在O(n)的时间复杂度内在 S 中找出最短的包含 T中所有字符的子串。 例如:

S =“XDOYEZODEYXNZ”
T =“XYZ”
找出的最短子串为"YXNZ".

注意: 如果 SS 中没有包含 TT 中所有字符的子串,返回空字符串 “”;
满足条件的子串可能有很多,但是题目保证满足条件的最短的子串唯一。

示例1

输入: “XDOYEZODEYXNZ”,“XYZ”

返回值: “YXNZ”

思路: 滑动窗口算法

  1. begin开始指向0, end一直后移,直到begin - end区间包含T中所有字符。
    记录窗口长度d
  2. 然后begin开始后移移除元素,直到移除的字符是T中的字符则停止,此时T中有一个字符没被
    包含在窗口,
  3. 继续后移end,直到T中的所有字符被包含在窗口,重新记录最小的窗口d。
  4. 如此循环知道end到S中的最后一个字符。
function minWindow( S ,  T ) {
    if(S==T) return S
	let window = [],target = [] 
	
	//记录T中个字符出现的次数
	for(let i =0;i<T.length;++i) {
		if(!target[T[i]]) {
			target[T[i]] = 1
		}else{
			target[T[i]]++
		}
	} 
	
	for(let i =0;i<S.length;++i) {
		window[S[i]] = 0
	} 
	
	const MAX_LEN = 999999999
	let left =0,right =0;
	let start = 0, minLen = MAX_LEN 
	let count = 0 
	
	while(right<S.length) {
		 let c = S[right] 
		 ++right 
		 if(target[c]){
			 window[c]++ 
			 if(window[c]<=target[c]) count++ 
		 }
		 //当窗口中包含了T的全部字符
		 while(count==T.length) {
			if(right-left<minLen) {
				start = left 
				minLen = right - left 
			}
			c = S[left] 
			++left 
			if(target[c]) {
				if(window[c]<=target[c]) count-- 
				--window[c]
			}
		 }
	}
	 return minLen==MAX_LEN ?"": S.substr(start,minLen)
}

十 一、NC142 最长重复子串

描述
定义重复字符串是由两个相同的字符串首尾拼接而成,例如 “abcabc"便是长度为6的一个重复字符串,而"abcba”
则不存在重复字符串。

给定一个字符串,请返回其最长重复子串的长度。

若不存在任何重复字符子串,则返回 0 。

示例1

输入: “ababc”

返回值: 4

说明:

abab为最长的重复字符子串,长度为4

示例2

输入: “abcab”

返回值: 0

说明:

该字符串没有重复字符子串 备注: 字符串长度不超过10000,且仅由小写字母组成

思路:
以 "ababc"为例,长度为5,这样如果这个字符串中存在重复字符串,就求出其中一个重复字符串的长度为2,我们要做的就是从字符串头开始两两比对,如果发现两个一样就返回总长度,不一样则继续比对下去。

function solve( a ) {
    if(a==null||a.length<=1) return 0 
	let maxLen = Number.parseInt(a.length/2) 
	while(maxLen>0) {
		for(let  i = 0;i<=a.length-maxLen*2;++i) {
			if(a.slice(i,i+maxLen)===a.slice(i+maxLen,i+maxLen*2)) {
				return maxLen*2 
			}
		}
		maxLen--
	} 
	return 0 
}

OK,本算法系列的字符串部分就精选了这十几道题,有易有难。如果你看完之后觉得不错不要忘了给我评论点赞哦!我一定会继续认真做好后面的章节!


由于本人技术水平有限,博客中难免出现的一些错误,有纰漏之处恳请各位大佬不吝赐教!
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

智者_若愚

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值