求中位数中回文数之和C语言,JS算法题之leetcode(1~10)

JS算法题之leetcode(1~10)

前言

一直以来,前端开发的知识储备在数据结构以及算法层面是有所暂缺的,可能归根于我们的前端开发的业务性质,但是我认为任何的编程岗位都离不开数据结构以及算法。

因此,我作为一名前端菜鸡,打算做一个专栏,就是关于用JavaScript来解答算法题,会持续跟新,希望大家督促。同时,本人才疏学浅,文章内容可能有错误的地方,希望各位大神指出,谢谢。

no more bb, show me the code!

bVbwf5H?w=1772&h=728

两数之和

题目描述

给定一个整数数组 nums 和一个目标值 target,请你在该数组中找出和为目标值的那 两个 整数,并返回他们的数组下标。

示例

给定 nums = [2, 7, 11, 15], target = 9

因为 nums[0] + nums[1] = 2 + 7 = 9

所以返回 [0, 1]

解答

这题不难,遍历nums,用targer减去当前元素,得到的元素如果在数组中,那就完事了。不过要注意统一元素不能用两次

var twoSum = function(nums, target) {

let idx1, idx2;

nums.forEach((ele, index) => {

let tempIdx = nums.indexOf(target - ele);

if(tempIdx !== -1 && tempIdx !== index){

idx1 = index;

idx2 = tempIdx;

}

});

return [idx1, idx2]

};

两数相加

题目描述

给出两个 非空 的链表用来表示两个非负的整数。其中,它们各自的位数是按照 逆序 的方式存储的,并且它们的每个节点只能存储 一位 数字。

如果,我们将这两个数相加起来,则会返回一个新的链表来表示它们的和。

您可以假设除了数字 0 之外,这两个数都不会以 0 开头。

示例

输入:(2 -> 4 -> 3) + (5 -> 6 -> 4)

输出:7 -> 0 -> 8

原因:342 + 465 = 807

解答

这题不难,不过稍微有点复杂,涉及到了链表,同时考擦了js大数的运算情况。

先遍历两个链表获得对应的数字,然后相加,最后反推算出结果对应的链表即可。

function ListNode(val) {

this.val = val;

this.next = null;

return {

val: this.val,

next: null

}

}

function addBigNumber(a, b) {

var res = '',

temp = 0;

a = a.split('');

b = b.split('');

while (a.length || b.length || temp) {

temp += ~~a.pop() + ~~b.pop();

res = (temp % 10) + res;

temp = temp > 9;

}

return res.replace(/^0+/, '');

}

var addTwoNumbers = function(l1, l2) {

let num1 = '', num2 = '', cur;

cur = l1;

while(cur){

num1 += cur.val.toString();

cur = cur.next;

}

cur = l2;

while(cur){

num2 += cur.val.toString();

cur = cur.next;

}

num1 = num1.split('').reverse().join('');

num2 = num2.split('').reverse().join('');

let total;

if(num1.length > 21 || num2.length > 21){

total = addBigNumber(num1, num2)

}

else{

total = Number(num1) + Number(num2)

}

total = total.toLocaleString().toString().split('').reverse().join('').replace(/,/g, '')

console.log(num1, num2, total)

let l3 = ListNode(total[0]);

cur = l3;

for(let i = 1; i < total.length; i++){

let node = ListNode(total[i]);

cur.next = node;

cur = node;

}

return l3;

};

无重复字符的最长子串

题目描述

给定一个字符串,请你找出其中不含有重复字符的 最长子串 的长度。

示例

输入: "abcabcbb"

输出: 3

解释: 因为无重复字符的最长子串是 "abc",所以其长度为 3。

输入: "bbbbb"

输出: 1

解释: 因为无重复字符的最长子串是 "b",所以其长度为 1。

输入: "pwwkew"

输出: 3

解释: 因为无重复字符的最长子串是 "wke",所以其长度为 3。

请注意,你的答案必须是 子串 的长度,"pwke" 是一个子序列,不是子串。

解答

维护一个数组用于存放无重复子串,遍历输入的字符串,若当前字符不在无重复数组中,则添加,否则,无重复数组清空,并push当前字符。

同时要维护另外一个最长无重复子串的数组。

var lengthOfLongestSubstring = function(s) {

let max = 0, maxArr = [], oldArr= [];

s.split('').forEach((ele, index) => {

if(maxArr.indexOf(ele) === -1){

maxArr.push(ele)

if(maxArr.length > max){

max = maxArr.length;

}

}

else{

maxArr = [ele]

let tempItem = oldArr.pop();

while(tempItem != ele){

maxArr.unshift(tempItem)

tempItem = oldArr.pop();

}

}

oldArr = [...maxArr]

})

return max;

};

寻找两个有序数组的中位数

题目描述

给定两个大小为 m 和 n 的有序数组 nums1 和 nums2。

请你找出这两个有序数组的中位数,并且要求算法的时间复杂度为 O(log(m + n))。

你可以假设 nums1 和 nums2 不会同时为空。

示例

nums1 = [1, 3]

nums2 = [2]

则中位数是 2.0

nums1 = [1, 2]

nums2 = [3, 4]

则中位数是 (2 + 3)/2 = 2.5

解答

将两个数组合并然后排序,之后获取中位数即可。问题在于限定时间复杂度为 O(log(m + n))的情况下,如何排序呢?

我们这里直接使用sort()方法,该方法底层原理是将多个排序集于一体,根据数组的长度不同选择不同的排序方法,加上V8引擎的优化,综合来说时间复杂度是能满足的。

好像有点偷鸡摸狗的感觉。。。

sort方法的源码:Array API源码,从710行开始看吧

var findMedianSortedArrays = function(nums1, nums2) {

let num = nums1.concat(nums2);

num = num.sort((a, b) => a - b);

let mid = Math.floor(num.length / 2);

if (num.length % 2 === 0) {

return (num[mid-1] + num[mid])/2

} else {

return num[mid]

}

};

最长回文子串

题目描述

给定一个字符串 s,找到 s 中最长的回文子串。你可以假设 s 的最大长度为 1000。

示例

输入: "babad"

输出: "bab"

注意: "aba" 也是一个有效答案。

输入: "cbbd"

输出: "bb"

解答

这题要用动态规划来做,先是判断出所有长度为1,2,3的子串是否回文。

长度为1,必定回文。

长度为2或者3,取决于首位字符是否相同。

长度大于3,取决于该子串去掉首位字符之后是否回文,并且首位字符是否相同。

核心在于 dp[i][j] == dp[i+1][j-1] && s[i] === s[j]

var longestPalindrome = function(s) {

let dp = [];

for(let i = 0; i < s.length; i++){

dp[i] = [];

}

let max = -1, str = '';

for(let k = 0; k < s.length; k++){

// k为所遍历的子串长度 - 1,即左下标到右下标的距离

for(let i = 0; i + k < s.length; i++){

let j = i + k;

// i为子串开始的左下标,j为子串开始的右下标

if(k == 0){

// 当子串长度为1时,必定是回文

dp[i][j] = true;

}

else if(k <= 2){

// 当子串长度为2时,两字符相同则符合回文,长度为3,首位字符相同则符合回文

if(s[i] == s[j]){

dp[i][j] = true;

}

else{

dp[i][j] = false;

}

}

else{

// 当子串长度超过3,取决于去掉头尾之后的子串是否回文并且首位字符是否相同

if(dp[i+1][j-1] && (s[i] == s[j])){

dp[i][j] = true;

}

else{

dp[i][j] = false;

}

}

if(dp[i][j] && k > max){

max = k;

str = s.substring(i, j + 1)

}

}

}

return str;

};

Z 字形变换

题目描述

将一个给定字符串根据给定的行数,以从上往下、从左到右进行 Z 字形排列。

比如输入字符串为 "LEETCODEISHIRING" 行数为 3 时,排列如下:

L C I R

E T O E S I I G

E D H N

之后,你的输出需要从左往右逐行读取,产生出一个新的字符串,比如:"LCIRETOESIIGEDHN"。

请你实现这个将字符串进行指定行数变换的函数:

string convert(string s, int numRows);

示例

输入: s = "LEETCODEISHIRING", numRows = 3

输出: "LCIRETOESIIGEDHN"

输入: s = "LEETCODEISHIRING", numRows = 4

输出: "LDREOEIIECIHNTSG"

解释:

L D R

E O E I I

E C I H N

T S G

解答

这题目的结构有点怪,但也是有规律可循的,我们发现这个”Z“的字符顺序是这样子的:垂直向下,斜向上,然后再垂直向下。

那其实我们可以直接将该结构简化为一个二维数组,去掉中间的空格,再一行一行地遍历就能获取到答案了。

如:

L D R

E O E I I

E C I H N

T S G

可以变成

L D R

E O E I I

E C I H N

T S G

接着再一行一行读,拼成字符串,便可。

var convert = function(s, numRows) {

if(numRows == 1){

return s;

}

let arr = [], direction = 'down', line = 0, str = '';

for(let i = 0; i < numRows; i++){

arr[i] = [];

}

for(let i = 0; i < s.length; i++){

arr[line].push(s[i]);

if(line == 0){

line++;

direction = 'down'

}

else if(line == numRows - 1){

line--;

direction = 'up'

}

else{

if(direction == 'down'){

line++;

}

else if(direction = 'up'){

line--;

}

}

}

for(let i = 0; i < numRows; i++){

str += arr[i].join("");

}

return str;

};

整数反转

题目描述

给出一个 32 位的有符号整数,你需要将这个整数中每位上的数字进行反转。

注意:

假设我们的环境只能存储得下 32 位的有符号整数,则其数值范围为 [−231,  231 − 1]。请根据这个假设,如果反转后整数溢出那么就返回 0。

示例

输入: 123

输出: 321

输入: -123

输出: -321

输入: 120

输出: 21

解答

这题就很简单了,不过要考虑好边缘溢出情况即可。

var MAX = Math.pow(2, 31) - 1

var MIN = -1 * Math.pow(2, 31)

var reverse = function(x) {

let str = x.toString().split(''), symbolFlag = false;

if(str[0] == '-'){

symbolFlag = true;

str.shift();

}

str = str.reverse();

if(symbolFlag){

str.unshift('-');

}

let num = Number(str.join(''))

if(num < MIN || num > MAX){

return 0

}

else{

return num

}

};

字符串转换整数 (atoi)

题目描述

请你来实现一个 atoi 函数,使其能将字符串转换成整数。

首先,该函数会根据需要丢弃无用的开头空格字符,直到寻找到第一个非空格的字符为止。

当我们寻找到的第一个非空字符为正或者负号时,则将该符号与之后面尽可能多的连续数字组合起来,作为该整数的正负号;假如第一个非空字符是数字,则直接将其与之后连续的数字字符组合起来,形成整数。

该字符串除了有效的整数部分之后也可能会存在多余的字符,这些字符可以被忽略,它们对于函数不应该造成影响。

注意:假如该字符串中的第一个非空格字符不是一个有效整数字符、字符串为空或字符串仅包含空白字符时,则你的函数不需要进行转换。

在任何情况下,若函数不能进行有效的转换时,请返回 0。

说明:

假设我们的环境只能存储 32 位大小的有符号整数,那么其数值范围为 [−231,  231 − 1]。如果数值超过这个范围,qing返回  INT_MAX (231 − 1) 或 INT_MIN (−231) 。

示例

题目很长是吧,没关系,我们直接看示例。

输入: "42"

输出: 42

输入: " -42"

输出: -42

解释: 第一个非空白字符为 '-', 它是一个负号。

我们尽可能将负号与后面所有连续出现的数字组合起来,最后得到 -42 。

输入: "4193 with words"

输出: 4193

解释: 转换截止于数字 '3' ,因为它的下一个字符不为数字。

输入: "words and 987"

输出: 0

解释: 第一个非空字符是 'w', 但它不是数字或正、负号。

因此无法执行有效的转换。

输入: "-91283472332"

输出: -2147483648

解释: 数字 "-91283472332" 超过 32 位有符号整数范围。

因此返回 INT_MIN (−231) 。

解答

这题不难,但是有很多坑。首先我们采取ASCII编码的方式来判断字符为数字还是英文还是别的。

先去空白,去掉空白之后取第一个字符,判断正负符号,若是英文直接返回0,若数字则不取。

从第二个字符开始遍历,若不是数字则退出循环。

最后还要考虑溢出情况。

const MIN = -1 * Math.pow(2, 31);

const MAX = Math.pow(2, 31) - 1;

var myAtoi = function(str) {

str = str.trim();

let result = '', symbol = '';

let idx = 0;

if(str.charCodeAt(0) === 45){

idx++;

symbol = '-';

}

else if(str.charCodeAt(0) === 43){

idx++;

}

else if(str.charCodeAt(0) < 48 || str.charCodeAt(0) > 57){

return 0;

}

for(let i = idx; i < str.length; i++){

if(str.charCodeAt(i) === 46){

break;

}

else if(str.charCodeAt(i) >= 48 && str.charCodeAt(i) <= 57){

result += str[i];

}

else{

break

}

}

result = symbol.toString() + result.toString();

if(Number(result) !== Number(result)){

return 0;

}

else if(Number(result) < MIN){

return MIN;

}

else if(Number(result) > MAX){

return MAX;

}

else{

return Number(result)

}

};

回文数

题目描述

判断一个整数是否是回文数。回文数是指正序(从左向右)和倒序(从右向左)读都是一样的整数。

示例

输入: 121

输出: true

输入: -121

输出: false

解释: 从左向右读, 为 -121 。 从右向左读, 为 121- 。因此它不是一个回文数。

输入: 10

输出: false

解释: 从右向左读, 为 01 。因此它不是一个回文数

解答

这题比较简单,反转对比即可

var isPalindrome = function(x) {

let y = x.toString().split("").reverse().join("");

return x == y

};

正则表达式匹配

题目描述

给你一个字符串 s 和一个字符规律 p,请你来实现一个支持 '.' 和 '*' 的正则表达式匹配。

'.' 匹配任意单个字符

'*' 匹配零个或多个前面的那一个元素

所谓匹配,是要涵盖 整个 字符串 s的,而不是部分字符串。

说明:

s 可能为空,且只包含从 a-z 的小写字母。

p 可能为空,且只包含从 a-z 的小写字母,以及字符 . 和 *。

示例

输入:

s = "aa"

p = "a"

输出: false

解释: "a" 无法匹配 "aa" 整个字符串。

输入:

s = "aa"

p = "a*"

输出: true

解释: 因为 '*' 代表可以匹配零个或多个前面的那一个元素, 在这里前面的元素就是 'a'。因此,字符串 "aa" 可被视为 'a' 重复了一次。

输入:

s = "ab"

p = ".*"

输出: true

解释: "." 表示可匹配零个或多个('')任意字符('.')。

输入:

s = "aab"

p = "cab"

输出: true

解释: 因为 '*' 表示零个或多个,这里 'c' 为 0 个, 'a' 被重复一次。因此可以匹配字符串 "aab"。

输入:

s = "mississippi"

p = "misisp*."

输出: false

解答

这题稍微有点复杂,我们采用了递归方法将两个字符串对比,每次只对比一个字符。

将当前递归p的下一个字符是否为*进行分类比较:

①p的下一个字符是*

若s和p的当前字符相同或者p的当前字符为.,则结果就取决于:

isMatch(s.slice(1), p) || isMatch(s.slice(1), p.slice(2)) || isMatch(s, p.slice(2))

若p的最后两个字符为.*就返回true

若不符合上面两种情况就将取决于

isMatch(s,p.slice(2))

②p的下一个字符不为*

这种情况就简单了

若s和p的当前字符相同或者p的当前字符为.,返回true

否则返回false

var isMatch = function(s, p) {

if(s.length === 0 && p.length === 0){

return true;

}

if(s.length !== 0 && p.length === 0){

return false;

}

let str = s[0], pattern = p[0];

let isNextStart = p[1] === "*";

if(isNextStart){

if(str && (str === pattern || pattern === ".")){

return isMatch(s.slice(1), p) || isMatch(s.slice(1), p.slice(2)) || isMatch(s, p.slice(2))

}

else if(pattern === "." && p.slice(2).length === 0){

return true

}

else{

return isMatch(s,p.slice(2));

}

}

else{

if(str && (str === pattern || pattern === ".")){

return isMatch(s.slice(1), p.slice(1))

}

else{

return false;

}

}

};

总结

本文所有题目均来自乐扣(leetcode),做法不唯一,甚至可能还有所错误,希望各位大神指出,弟弟虚心学习。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值