技术 js JS算法之脑洞大开---freeCodeCamp Kitholt Frank 2022-10-07 2025-11-30 JS算法之脑洞大开 freeCodeCamp(部分)
问题描述:寻找两个数组的对称差集
代码块(思路1):
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 function diffArray (arr1, arr2 ) { const newArr = []; let arr1_map = new Map () let arr2_map = new Map () for (let i = 0 ; i < arr1.length ; i++){ arr1_map.set (arr1[i], 1 ) } for (let j = 0 ; j < arr2.length ; j++){ arr2_map.set (arr2[j], 1 ) } for (let key of arr1_map.keys ()){ for (let j = 0 ; j < arr2.length ; j++){ if (key === arr2[j]){ arr1_map.set (key, arr1_map.get (key) + 1 ) arr2_map.set (key, arr2_map.get (key) + 1 ) console .log ("****" , arr1_map) console .log ("++++++++++++++++++" , arr2_map) } } } for (let key of arr1_map.keys ()){ if (arr1_map.get (key) == 1 ){ newArr.push (key) } } for (let key of arr2_map.keys ()){ if (arr2_map.get (key) == 1 ){ newArr.push (key) } } return newArr; } diffArray ([1 , 2 , 3 , 5 ], [1 , 2 , 3 , 4 , 5 ]);
问题描述:删除指定元素
思考:需要遍历接受到的参数(arguments
代码块:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 function destroyer (arr ) { for (let arg of arguments ){ if (arg == arr){ continue } for (let i = 0 ; i < arr.length ; i++){ if (arr[i] == arg){ delete arr[i] } } arr = arr.filter (Boolean ) } return arr; } destroyer ([1 , 2 , 3 , 1 , 2 , 3 ], 2 , 3 );
问题描述:给出一个源对象和一个集合对象,要求在集合对象中筛选出包含源对象所有键值的对象
思考:获取到需要寻找的每个对象的键key;遍历集合中的每个对象,使用filter函数进行过滤操作,过滤有两个条件,首先是判断是否存在这个键,其次是判断对应的值是否正确
使用到的API: Object.keys();obj.hasOwnProperty()
代码块:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 function whatIsInAName (collection, source ) { let arr = []; let source_keys = Object .keys (source) arr = collection.filter (function (obj ) { let signal = 0 for (let i = 0 ; i < source_keys.length ; i++) { if (obj.hasOwnProperty (source_keys[i]) && obj[source_keys[i]] == source[source_keys[i]]) { signal ++ } } if (signal == source_keys.length ){ return true } return false }) console .log (arr) return arr; } whatIsInAName ([{ first : "Romeo" , last : "Montague" }, { first : "Mercutio" , last : null }, { first : "Tybalt" , last : "Capulet" }], { last : "Capulet" });
问题描述:将字符串转为小写,并且用-连接每一个单词
Passed:spinalCase("thisIsSpinalTap") should return the string this-is-spinal-tap.
-
Passed:spinalCase("The_Andy_Griffith_Show") should return the string the-andy-griffith-show.
-
Passed:spinalCase("Teletubbies say Eh-oh") should return the string teletubbies-say-eh-oh.
-
Passed:spinalCase("AllThe-small Things") should return the string all-the-small-things.
思考:关键点是如何切割单词,可以通过比较字母之间的ASCII码来完成。需要使用的方法:str.charCodeAt()
代码块:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 function spinalCase (str ) { let str1 = [] for (let i = 0 ; i < str.length ; i++){ str1.push (str.charAt (i)) } for (let i = 0 ; i < str.length ; i++){ if (str1[i].charCodeAt () > str1[i + 1 ].charCodeAt () && str1[i + 1 ].charCodeAt () <= 90 ){ if (str1[i].charCodeAt () == 95 ){ str1.splice (i, 1 , " " ) } str1.splice (i + 1 , 0 , " " ) i++ } } str1 = str1.join ("" ) str1 = str1.split (/\W+/ ) str1 = str1.join ('-' ) str1 = str1.toLowerCase () console .log (str1) return str1; } spinalCase ('This Is Spinal Tap' );
问题描述:Pig Latin is a way of altering English Words. The rules are as follows:
- If a word begins with a consonant, take the first consonant or consonant cluster, move it to the end of the word, and add ay to it.
- If a word begins with a vowel, just add way at the end.
思考:注意字符串的拼接等操作,重点是对元音字母的判断(位置)
代码块:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 function translatePigLatin (str ) { let vowels = new Set (['a' , 'e' , 'i' , 'o' , 'u' ]) for (let i = 0 ; i < str.length ; i++){ if (vowels.has (str[i])){ if (i == 0 ){ str = str + 'way' return str } else { str = str.split ("" ) let temp_str = str.slice (0 , i) temp_str = temp_str.join ('' ) str.splice (0 , i, '' ) str = str.join ('' ) str = str + temp_str + 'ay' return str } } } return str + 'ay' } translatePigLatin ("consonant" );
问题描述:Perform a search and replace on the sentence using the arguments provided and return the new sentence.
First argument is the sentence to perform the search and replace on.
Second argument is the word that you will be replacing (before).
Third argument is what you will be replacing the second argument with (after).
Note: Preserve the case of the first character in the original word when you are replacing it. For example if you mean to replace the word Book with the word dog, it should be replaced as Dog
思考:先使用indexOf方法找到要替换单词的首字母索引,然后判断要替换单词的位置(是否位于句首,句首的话要将替换的单词首字母大写)
代码块:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 function myReplace (str, before, after ) { let index = str.indexOf (before); if (str[index] === str[index].toUpperCase ()) { after = after.charAt (0 ).toUpperCase () + after.slice (1 ); } else { after = after.charAt (0 ).toLowerCase () + after.slice (1 ); } str = str.replace (before, after); return str; } myReplace ("A quick brown fox jumped over the lazy dog" , "jumped" , "leaped" );
问题描述:Pairs of DNA strands consist of nucleobase pairs. Base pairs are represented by the characters AT and CG , which form building blocks of the DNA double helix.
The DNA strand is missing the pairing element. Write a function to match the missing base pairs for the provided DNA strand. For each character in the provided string, find the base pair character. Return the results as a 2d array.
For example, for the input GCG, return [["G", "C"], ["C","G"], ["G", "C"]]
The character and its pair are paired up in an array, and all the arrays are grouped into one encapsulating array.
思考:碱基对之间的关系 可以用对象形式表示,需要使用到in这个关键字,在遍历DNA单链时(单个碱基),判断其所属与那个关系 。在使用in关键字过程中,要明白格式:(变量 in 对象)
当“对象”为数组时,“变量”指的是数组的“索引”;
当“对象”为对象是,“变量”指的是对象的“属性”。
代码块:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 function pairElement (str ) { let AT_pair = {'A' : 'T' , 'T' : 'A' } let CG_pair = {'C' : 'G' , 'G' : 'C' } let DNA_double_helix = [] let DNA_single_helix = str.split ('' ) for (let d of DNA_single_helix){ if (d in AT_pair){ let AT_pairs = [d, AT_pair[d]] console .log (AT_pairs) DNA_double_helix.push (AT_pairs) } else { let CG_pairs = [d, CG_pair[d]] console .log (CG_pairs) DNA_double_helix.push (CG_pairs) } } return DNA_double_helix; } pairElement ("GCG" );
问题描述:Find the missing letter in the passed letter range and return it.
If all letters are present in the range, return undefined.
思考:ASCII码?相邻的字母?作差?字符转换需要用到的方法charCodeAt(),fromCharCode()
代码块:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 function fearNotLetter (str ) { let ascii_str = [] for (let i = 0 ; i < str.length ; i++){ ascii_str.push (str[i].charCodeAt ()) } for (let i = 0 ; i < ascii_str.length - 1 ; i++){ if (ascii_str[i+1 ] - ascii_str[i] !== 1 ){ console .log (String .fromCharCode (ascii_str[i])) return String .fromCharCode (ascii_str[i]+1 ) } } return undefined ; } fearNotLetter ("abce" );
问题描述:Write a function that takes two or more arrays and returns a new array of unique values in the order of the original provided arrays.
In other words, all values present from all arrays should be included in their original order, but with no duplicates in the final array.
The unique numbers should be sorted by their original order, but the final array should not be sorted in numerical order.
思考:投个只因(机),将数组全部连接起来concat,然后扔进一个集合Set去重,然后在使用Array.from()转换为数组并返回
代码块:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 function uniteUnique (arr ) { let array_group = [] for (let i = 0 ; i < arguments .length ; i++){ array_group = array_group.concat (arguments [i]) } let result_array = new Set (array_group) let final_result = Array .from (result_array) return final_result; } uniteUnique ([1 , 3 , 2 ], [5 , 2 , 1 , 4 ], [2 , 1 ]);
问题描述:Find the smallest common multiple of the provided parameters that can be evenly divided by both, as well as by all sequential numbers in the range between these parameters.
The range will be an array of two numbers that will not necessarily be in numerical order.
For example, if given 1 and 3, find the smallest common multiple of both 1 and 3 that is also evenly divisible by all numbers between 1 and 3. The answer here would be 6.
思考:寻找一个有序序列的最小公倍数,可以从头开始,比如序列abcdef,先寻找a,b的最大公倍数,并将结果保存起来(scm)。遍历序列,重复以上步骤。寻找最小公倍数的关键判断条件(scm * k % num2 !== 0)
代码块:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 function smallestCommons (arr ) { let scm = 0 let num2 = 0 if (arr[0 ] > arr[1 ]){ let temp = arr[0 ] arr[0 ] = arr[1 ] arr[1 ] = temp } scm = arr[0 ] for (let i = arr[0 ]; i < arr[1 ]; i++){ num2 = i + 1 let k = 1 while ((scm * k % num2 !== 0 )){ k++ } scm = scm * k console .log ('scm' , scm, i) } return scm; }
问题描述:Create a function that sums two arguments together. If only one argument is provided, then return a function that expects one argument and returns the sum.
For example, addTogether(2, 3) should return 5, and addTogether(2) should return a function.
Calling this returned function with a single argument will then return the sum:
1 var sumTwoAnd = addTogether (2 );
sumTwoAnd(3) returns 5.
If either argument isn’t a valid number, return undefined.
思考:关键在于typeof的使用,以及闭包的应用(嵌套函数
代码块:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 function addTogether ( ) { let args = arguments if (args.length == 1 ){ if (typeof (args[0 ]) == 'number' ){ return function f (x ){ if (typeof (x) == 'number' ){ return args[0 ] + x } return undefined } } } if (typeof (args[0 ]) == 'number' && typeof (args[1 ]) == 'number' ){ return args[0 ] + args[1 ]; } return undefined }
最大子数组 输入是以数字组成的数组,例如 arr = [1, -2, 3, 4, -9, 6].
任务是:找出所有项的和最大的 arr 数组的连续子数组。
写出函数 getMaxSubSum(arr),用其找出并返回最大和。
JS小案例合集 ①贪吃蛇
HTML:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 <!DOCTYPE html > <html lang ="en" > <head > <meta charset ="UTF-8" > <meta http-equiv ="X-UA-Compatible" content ="IE=edge" > <meta name ="viewport" content ="width=device-width, initial-scale=1.0" > <title > Document</title > <link rel ="stylesheet" href ="./css/index.css" > </head > <body > <div class ="container" > <button class = 'startBtn' > Play</button > <button class = 'pauseBtn' > Pause</button > </div > <script src ="./js/config.js" > </script > <script src ="./js/index.js" > </script > </body > </html >
CSS:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 *{ margin : 0 ; padding : 0 ; } .container { width : 600px ; height : 600px ; background : purple; border : 20px solid royalblue; margin : 20px auto; display : flex; justify-content : center; align-items : center; position : relative; } .container button { border : none; outline : none; } .startBtn { width : 150px ; height : 60px ; border-radius : 15px ; box-shadow : 3px 4px 0px 0px #1564ad ; background :linear-gradient (to bottom, #79bbff 5% , #378de5 100% ); background-color :#79bbff ; color : #fff ; font-size : 18px ; font-weight : bold; display : block; } .pauseBtn { width : 150px ; height : 60px ; border-radius : 15px ; box-shadow : 3px 4px 0px 0px #8a2a21 ; background :linear-gradient (to bottom, #c62d1f 5% , #f24437 100% ); background-color :#c62d1f ; color : #fff ; font-size : 18px ; font-weight : bold; display : none; }
JS:
config.js配置文件
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 let gridData = []let tr = 30 let td = 30 let snakeBody = 20 let directionNum = { left : {x : -1 , y : 0 , flag : 'left' }, right : {x : 1 , y : 0 , flag : 'right' }, top : {x : 0 , y : -1 , flag : 'top' }, bottom : {x : 0 , y : 1 , flag : 'bottom' }, } let snake = { direction : directionNum.right , snakePos : [ {x : 0 , y : 0 , domContent : '' , flag : 'body' }, {x : 1 , y : 0 , domContent : '' , flag : 'body' }, {x : 2 , y : 0 , domContent : '' , flag : 'body' }, {x : 3 , y : 0 , domContent : '' , flag : 'head' }, ] } let food = { x : 0 , y : 0 , domContent : '' , } let score = 0 let timeStopper = null let speed = 60
index.js主程序文件
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 function drawSnake ( ) { for (let i = 0 ; i < snake.snakePos .length ; i++) { if (!snake.snakePos [i].domContent ) { snake.snakePos [i].domContent = document .createElement ('div' ) snake.snakePos [i].domContent .style .position = 'absolute' snake.snakePos [i].domContent .style .width = snakeBody + 'px' snake.snakePos [i].domContent .style .height = snakeBody + 'px' snake.snakePos [i].domContent .style .left = snake.snakePos [i].x * snakeBody + 'px' snake.snakePos [i].domContent .style .top = snake.snakePos [i].y * snakeBody + 'px' if (snake.snakePos [i].flag === 'head' ) { snake.snakePos [i].domContent .style .background = 'red' } else { snake.snakePos [i].domContent .style .background = 'green' } document .querySelector ('.container' ).append (snake.snakePos [i].domContent ) } } } function drawFood ( ) { while (true ) { let isRepeated = false food.x = Math .floor (Math .random () * tr) food.y = Math .floor (Math .random () * td) for (let i = 0 ; i < snake.snakePos .length ; i++) { if (snake.snakePos [i].x === food.x && snake.snakePos [i].y === food.y ) { isRepeated = true break } } if (!isRepeated) { break } } if (!food.domContent ) { food.domContent = document .createElement ('div' ) food.domContent .style .width = snakeBody + 'px' food.domContent .style .height = snakeBody + 'px' food.domContent .style .position = 'absolute' food.domContent .style .backgroundColor = 'yellow' document .querySelector ('.container' ).append (food.domContent ) } food.domContent .style .left = food.x * snakeBody + 'px' food.domContent .style .top = food.y * snakeBody + 'px' } function initGame ( ) { for (let i = 0 ; i < tr; i++) { for (let j = 0 ; j < td; j++) { gridData.push ({ x : j, y : i }) } } drawSnake (snake) drawFood () } function isCollided (newHead ) { let info = { isCollided : false , isEat : false , } if (newHead.x < 0 || newHead.x >= td || newHead.y < 0 || newHead.y >= td) { info.isCollided = true return info } for (let i = 0 ; i < snake.snakePos .length ; i++) { if (newHead.x === snake.snakePos [i].x && newHead.y === snake.snakePos [i].y ) { info.isCollided = true return info } } if (newHead.x === food.x && newHead.y === food.y ) { score++ info.isEat = true return info } return info } function resetConfig ( ) { score = 0 snake = { direction : directionNum.right , snakePos : [ { x : 0 , y : 0 , domContent : '' , flag : 'body' }, { x : 1 , y : 0 , domContent : '' , flag : 'body' }, { x : 2 , y : 0 , domContent : '' , flag : 'body' }, { x : 3 , y : 0 , domContent : '' , flag : 'head' }, ] } food = { x : 0 , y : 0 , domContent : '' , } } function snakeMove ( ) { let oldHead = snake.snakePos [snake.snakePos .length - 1 ] let newHead = { domcontent : '' , x : oldHead.x + snake.direction .x , y : oldHead.y + snake.direction .y , flag : 'head' } let collisionResult = isCollided (newHead) if (collisionResult.isCollided ) { if (window .confirm (` 游戏结束,当前得分为${score} ,是否要重新开始游戏? ` )) { document .querySelector ('.container' ).innerHTML = ` <!-- 开始游戏按钮 --> <button class = 'startBtn' style = 'display:none'>Play</button> <!-- 暂停游戏按钮 --> <button class = 'pauseBtn' style = 'display:none'>Pause</button> ` resetConfig () drawSnake () drawFood () } else { document .onkeydown = null document .querySelector ('.container' ).onclick = null clearInterval (timeStopper) } return } oldHead.flag = 'body' oldHead.domContent .style .background = 'green' snake.snakePos .push (newHead) if (collisionResult.isEat ) { drawFood () } else { document .querySelector ('.container' ).removeChild (snake.snakePos [0 ].domContent ) snake.snakePos .shift () } drawSnake () } function startGame ( ) { timeStopper = setInterval (function ( ) { snakeMove () }, speed) } function bindEvent ( ) { document .onkeydown = function (e ) { console .log (e.key ) if ((e.key === 'ArrowUp' || e.key .toLowerCase () === 'w' ) && snake.direction .flag !== 'bottom' ) { snake.direction = directionNum.top } if ((e.key === 'ArrowDown' || e.key .toLowerCase () === 's' ) && snake.direction .flag !== 'top' ) { snake.direction = directionNum.bottom } if ((e.key === 'ArrowLeft' || e.key .toLowerCase () === 'a' ) && snake.direction .flag !== 'right' ) { snake.direction = directionNum.left } if ((e.key === 'ArrowRight' || e.key .toLowerCase () === 'd' ) && snake.direction .flag !== 'left' ) { snake.direction = directionNum.right } } startGame () document .querySelector ('.container' ).onclick = function (e ) { if (e.target .className === 'container' ) { document .querySelector ('.pauseBtn' ).style .display = 'block' clearInterval (timeStopper) } else { document .querySelector ('.pauseBtn' ).style .display = 'none' startGame () } } } function main ( ) { document .querySelector ('.startBtn' ).onclick = function (e ) { e.stopPropagation () document .querySelector ('.startBtn' ).style .display = 'none' initGame () bindEvent () } } main ()