一、六种排序算法
排序算法
选择一个目标值,比目标值小的放左边,比目标值大的放右边,目标值的位置已排好,将左右两侧再进行快排。
将大序列二分成小序列,将小序列排序后再将排序后的小序列归并成大序列。
每次排序取一个最大或最小的数字放到前面的有序序列中。
将左侧序列看成一个有序序列,每次将一个数字插入该有序序列。插入时,从有序序列最右侧开始比较,若比较的数较大,后移一位。
循环数组,比较当前元素和下一个元素,如果当前元素比下一个元素大,向上冒泡。下一次循环继续上面的操作,不循环已经排序好的数。
创建一个大顶堆,大顶堆的堆顶一定是最大的元素。交换第一个元素和最后一个元素,让剩余的元素继续调整为大顶堆。从后往前以此和第一个元素交换并重新构建,排序完成。
-
冒泡排序
//思路 循环数组,比较当前元素和下一个元素,如果当前元素比下一个元素大,向上冒泡。 这样一次循环之后最后一个数就是本数组最大的数。 下一次循环继续上面的操作,不循环已经排序好的数。 优化:当一次循环没有发生冒泡,说明已经排序完成,停止循环。 function bubbleSort(array) { for (let j = 0; j < array.length; j++) { let complete = true; for (let i = 0; i < array.length - 1 - j; i++) { // 比较相邻数 if (array[i] > array[i + 1]) { [array[i], array[i + 1]] = [array[i + 1], array[i]]; complete = false; } } // 没有冒泡结束循环 if (complete) { break; } } return array; } //复杂度 时间复杂度:O(n2) 空间复杂度:O(1) // 稳定性 稳定
-
快速排序
//实现步骤: 选择一个基准元素target(一般选择第一个数) 将比target小的元素移动到数组左边,比target大的元素移动到数组右边 分别对target左侧和右侧的元素进行快速排序 function quickSort(array) { if (array.length < 2) { return array; } const target = array[0]; const left = []; const right = []; for (let i = 1; i < array.length; i++) { if (array[i] < target) { left.push(array[i]); } else { right.push(array[i]); } } return quickSort(left).concat([target], quickSort(right)); } //记录一个索引l从数组最左侧开始,记录一个索引r从数组右侧开始 //在l<r的条件下,找到右侧小于target的值array[r],并将其赋值到array[l] //在l<r的条件下,找到左侧大于target的值array[l],并将其赋值到array[r] //这样让l=r时,左侧的值全部小于target,右侧的值全部小于target,将target放到该位置 不需要额外存储空间,写法思路稍复杂(有能力推荐这种写法) function quickSort(array, start, end) { if (end - start < 1) { return; } const target = array[start]; let l = start; let r = end; while (l < r) { while (l < r && array[r] >= target) { r--; } array[l] = array[r]; while (l < r && array[l] < target) { l++; } array[r] = array[l]; } array[l] = target; quickSort(array, start, l - 1); quickSort(array, l + 1, end); return array; }
-
插入排序
//思路 将左侧序列看成一个有序序列,每次将一个数字插入该有序序列。 插入时,从有序序列最右侧开始比较,若比较的数较大,后移一位。
复杂度
时间复杂度:O(n2)
空间复杂度:O(1)
稳定性:稳定
function insertSort(array) { for (let i = 1; i < array.length; i++) { let target = i; for (let j = i - 1; j >= 0; j--) { if (array[target] < array[j]) { [array[target], array[j]] = [array[j], array[target]] target = j; } else { break; } } } return array; }
-
选择排序
//思路 每次循环选取一个最小的数字放到前面的有序序列中。 //复杂度 时间复杂度:O(n2) 空间复杂度:O(1) //稳定性 稳定 function selectionSort(array) { for (let i = 0; i < array.length; i++) { let minIndex = i; for (let j = i + 1; j < array.length; j++) { if (array[j] < array[minIndex]) { minIndex = j; } } [array[minIndex], array[i]] = [array[i], array[minIndex]]; } }
-
堆排序
//思路 创建一个大顶堆,大顶堆的堆顶一定是最大的元素。 交换第一个元素和最后一个元素,让剩余的元素继续调整为大顶堆。 从后往前以此和第一个元素交换并重新构建,排序完成。 //复杂度 时间复杂度:O(nlogn) 空间复杂度:O(1) //稳定性 不稳定 function heapSort(array) { creatHeap(array); console.log(array); // 交换第一个和最后一个元素,然后重新调整大顶堆 for (let i = array.length - 1; i > 0; i--) { [array[i], array[0]] = [array[0], array[i]]; adjust(array, 0, i); } return array; } // 构建大顶堆,从第一个非叶子节点开始,进行下沉操作 function creatHeap(array) { const len = array.length; const start = parseInt(len / 2) - 1; for (let i = start; i >= 0; i--) { adjust(array, i, len); } } // 将第target个元素进行下沉,孩子节点有比他大的就下沉 function adjust(array, target, len) { for (let i = 2 * target + 1; i < len; i = 2 * i + 1) { // 找到孩子节点中最大的 if (i + 1 < len && array[i + 1] > array[i]) { i = i + 1; } // 下沉 if (array[i] > array[target]) { [array[i], array[target]] = [array[target], array[i]] target = i; } else { break; } } }
-
归并排序
//思路 分割: 将数组从中点进行分割,分为左、右两个数组 递归分割左、右数组,直到数组长度小于2 归并: 如果需要合并,那么左右两数组已经有序了。 创建一个临时存储数组temp,比较两数组第一个元素,将较小的元素加入临时数组 若左右数组有一个为空,那么此时另一个数组一定大于temp中的所有元素,直接将其所有元素加入temp //复杂度 时间复杂度:O(nlogn) 空间复杂度:O(n) //稳定性 稳定 function mergeSort(array, left, right, temp) { if (left < right) { const mid = Math.floor((left + right) / 2); mergeSort(array, left, mid, temp) mergeSort(array, mid + 1, right, temp) merge(array, left, right, temp); } return array; } function merge(array, left, right, temp) { const mid = Math.floor((left + right) / 2); let leftIndex = left; let rightIndex = mid + 1; let tempIndex = 0; while (leftIndex <= mid && rightIndex <= right) { if (array[leftIndex] < array[rightIndex]) { temp[tempIndex++] = array[leftIndex++] } else { temp[tempIndex++] = array[rightIndex++] } } while (leftIndex <= mid) { temp[tempIndex++] = array[leftIndex++] } while (rightIndex <= right) { temp[tempIndex++] = array[rightIndex++] } tempIndex = 0; for (let i = left; i <= right; i++) { array[i] = temp[tempIndex++]; } }
二、数组排序
-
随机打乱顺序-(洗牌、音乐播放)
function randomSort(arr) { // 对数组进行随机打乱, // return大于0则交换位置,小于等于0就不交换 // 由于Math.random()产生的数字为0-1之间的数 // 所以0.5-Math.random()的是否大于0是随机结果 // 进而实现数组的随机打乱 var array = arr.slice(); array.sort(function () { return 0.5 - Math.random(); }) // 在控制台输出结果 console.log(array); } // 调用排序方法 randomSort([1,2,3,4,5,6,7]);
-
打乱数组顺序
//打乱数组顺序,没有重复元素 Fisher–Yates shuffle洗牌算法 Step1:从数组末尾开始遍历,选取当前i位置的元素。然后从0-i随机产生一个位置k,交换a[k]和a[i] Step2:每次遍历,都只从当前位置前面生成随机位置,因为后面的元素已经乱序 Math.floor(Math.random() * (arr.length - i ))生成0-i的随机位置 var Solution = function(nums) { this.nums = nums; }; /** * Resets the array to its original configuration and return it. * @return {number[]} */ Solution.prototype.reset = function() { return this.nums; }; /** * Returns a random shuffling of the array. * @return {number[]} */ Solution.prototype.shuffle = function() { //交换数组元素顺序 const swap = (a, i, j) => { [a[i], a[j]] = [a[j], a[i]]; }; let arr = this.nums.slice(); //深拷贝数组,不然会影响reset的输出 for (let i = arr.length - 1; i >= 0; i--) { swap(arr, i, Math.floor(Math.random() * (arr.length - i ))); //swap(arr, i, Math.floor(Math.random() *i )); } return arr; };
-
最小的K个数
输入n个整数,找出其中最小的K个数。例如输入4,5,1,6,2,7,3,8这8个数字,则最小的4个数字是1,2,3,4
//方法一:sort法 sort正序排序,返回前k个元素: function GetLeastNumbers_Solution(input, k) { if (input.length < k) return []; return input.sort((a,b)=>(a-b)).slice(0,k); }
-
合并两个有序数组
给定两个有序整数数组 nums1 和 nums2,将 nums2 合并到 nums1 中,使得 num1 成为一个有序数组
//思路 合并数组的长度为n + m ,从后往前遍历数组,往数组里存入两个数组中的较大的值 数组1的数被存完以后,开始从将数组2的数组依次存入 var merge = function(nums1, m, nums2, n) { //if (m === 0 || n === 0) return [].concat(nums1, nums2); let [i, j, index] = [m - 1, n - 1, n + m - 1]; while (i >= 0 && j >= 0) { nums1[i] >= nums2[j] ? (nums1[index--] = nums1[i--]) : (nums1[index--] = nums2[j--]); } while (j >= 0) { nums1[index--] = nums2[j--]; } };
-
重复数组排序
给定一个包含红色、白色和蓝色,一共 n 个元素的数组,原地对它们进行排序,使得相同颜色的元素相邻,并按照红色、白色、蓝色顺序排列。
你能想出一个仅使用常数空间的一趟扫描算法吗
//方法一:计数法——两次遍历 //思路 首先遍历一遍原数组,分别记录 0,1,2 的个数。 然后更新原数组,按个数分别赋上 0,1,2。 //方法二:双指针分区法——一次遍历 //思路 定义三个指针:low指向0分区的下一个位置,mid指向1分区的下一个位置,high指向2分区的前一个位置。 如图所示,0区块在前,1区块在中间,2区块在后,中间的?代表尚未遍历的数字,值不确定。 mid指针遍历数组 如果 mid为0,交换low和mid的元素,low和mid都下移一位,0分区增加一个元素 如果 mid为1,mid下移一位,1分区增加一个元素 如果 mid为2,交换high和mid的元素,high向前移动一位,2分区增加一个元素,mid不移动,因为mid元素还需要再进行下一轮比较,不一定是1分区的元素 var sortColors = function(nums) { if (nums.length <= 1) return nums; let low = 0, i = 0, high = nums.length - 1; while (i <= high) { switch (nums[i]) { case 0: swap(nums, i++, low++); break; case 2: swap(nums, i, high--); break; case 1: i++; break; } } return nums; }; var swap = function(a, i, j) { [a[i], a[j]] = [a[j], a[i]]; };
-
数组奇偶排序
输入一个整数数组,实现一个函数来调整该数组中数字的顺序,使得所有的奇数位于数组的前半部分,所有的偶数位于数组的后半部分
//思路 设定两个指针 第一个指针start从数组第一个元素出发,向尾部前进 第二个指针end从数组的最后一个元素出发,向头部前进 start遍历到偶数,end遍历到奇数时,交换两个数的位置 当start>end时,完成交换 function reOrderArray(array) { if (Array.isArray(array)) { let start = 0; let end = array.length - 1; while (start < end) { while (array[start] % 2 === 1) { start++; } while (array[end] % 2 === 0) { end--; } if (start < end) { [array[start], array[end]] = [array[end], array[start]] } } } return array; }
-
把数组排成最小的数
//题目描述 输入一个正整数数组,把数组里所有数字拼接起来排成一个数,打印能拼接出的所有数字中最小的一个。
例如输入数组{3,32,321},则打印出这三个数字能排成的最小数字为321323。 //思路:排序规则如下: 若ab > ba 则 a > b, 若ab < ba 则 a < b, 若ab = ba 则 a = b; 然后从小到大拼接即为所求结果 function PrintMinNumber(numbers) { // write code here numbers.sort(function(a,b){ var s1=a+''+b; var s2=b+''+a; for(var i=0;i<s1.length;i++){ if(s1.charAt(i)>s2.charAt(i)){ return 1 }else if(s1.charAt(i)<s2.charAt(i)){ return -1; } } return 1 }) var result=""; numbers.map(function(num){ result=result.concat(num) }) return result; }
-
丑数
编写一个程序判断给定的数是否为丑数。
丑数就是只包含质因数 2, 3, 5 的正整数
//首先,虽然我这个题解可能复杂,但是我觉得我开始的思维是极其简单的。 如果这个 num === 0,那么它不是丑数。 如果这个数 % 2 === 0 或者 % 3 === 0 或者 %5 === 0,那么证明这个数是 2/3/5 的公倍数,那么我们就将它除于 2/3/5,同时这个数还可能是丑数。
(例如 30 / 2 = 15,15 可能是丑数,所以可以继续循环) 循环这个数,直到它不能 %2、%3 或者 %5 得出的结果为 0。 while 后,这个数已经不能整除 2/3/5 了,所以我们最终判断它的值为多少。如果是 2/3/5,表明它是丑数;如果不是,则它不是丑数。 var isUgly = function(num) { if (!num) { return false; } while (num % 2 === 0 || num % 3 === 0 || num % 5 === 0) { if (num % 2 === 0) { num = num / 2; } if (num % 3 === 0) { num = num / 3; } if (num % 5 === 0) { num = num / 5; } } if (num === 1) { return true; } if (num !== 2 || num !== 3 || num !== 5) { return false; } return true; };
-
数组中的逆序对
//在数组中的两个数字,如果前面一个数字大于后面的数字,则这两个数字组成一个逆序对。
输入一个数组,求出这个数组中的逆序对的总数P。并将P对1000000007取模的结果输出。 即输出P%1000000007 //思路:利用归并排序思想,先求出每个组中逆序对数,然后合并、排序并统计 function InversePairs(data) { if(!data||data.length<2) return 0; var copy = data.slice(), count = 0; count = mergeSort(data,copy,0,data.length-1); return count%1000000007; } function mergeSort(data,copy,start,end){ if(end===start) return 0; var mid = (end-start)>>1, left = mergeSort(copy,data,start,start+mid), right = mergeSort(copy,data,start+mid+1,end), count = 0, p = start+mid,//前一个数组的最后一个下标 q = end,//后一个数组的下标 copyIndex = end;//辅助数组下标,从最后一个算起 while(p>=start&&q>=start+mid+1){ if(data[p]>data[q]){ count+=q-start-mid; copy[copyIndex--] = data[p--]; }else{ copy[copyIndex--] = data[q--]; } } while(p>=start){ copy[copyIndex--] = data[p--]; } while(q>=start+mid+1){ copy[copyIndex--] = data[q--]; } return left+right+count; }
-
移动零
//给定一个数组 nums,编写一个函数将所有 0 移动到数组的末尾,同时保持非零元素的相对顺序。 //类似于冒泡排序。如果是0就后移 var moveZeroes = function (nums) { for (let i = 0; i < nums.length; i++) { for (let j = 0; j < nums.length - i - 1; j++) { if (nums[j + 1] !== 0 && nums[j] === 0) { nums[j] = nums[j + 1]; nums[j + 1] = 0; } } } return nums; }
-
两个数组的交集
//在本题中,我们将 nums1 的值,都存储到 Map 中。 然后,遍历 nums2,如果 Map 中包含这个元素,并且数组 result 中不存在这个元素,我们就将它存入到数组中。 最后,根据前面的步骤,我们做到了去重 + 取交集。 var intersection = function(nums1, nums2) { let map = new Map(); let result = []; for (let i = 0; i < nums1.length; i++) { map.set(nums1[i], i); } for (let j = 0; j < nums2.length; j++) { if (map.get(nums2[j]) !== undefined && result.indexOf(nums2[j]) === -1) { result.push(nums2[j]); } } return result; };
三、数组去重
-
对排序数组去重
给定一个排序数组,你需要在原地删除重复出现的元素,使得每个元素只出现一次,返回移除后数组的新长度
//思路 给定的数组 nums是一个排过序的数组,那么,重复元素一定是在数组中相邻的元素。也就是说,我们可以通过遍历数组,找出相邻相同项,并将其从数组中移除即可 需要原地删除,那么我们就不能创建一个副本,而是只能在原数组上进行操作 参考:https://blog.csdn.net/qq_30216191/article/details/81348501 特殊情况 数组长度小于等于1,return 遍历数组,比较当前和下一位 如果相等删除一位,并把i--,否则会跳过下个元素 遍历完成,返回数组长度 var removeDuplicates = function(nums) { if (nums.length <= 1) { return nums.length; } for (let i = 0; i < nums.length; i++) { if (nums[i] === nums[i + 1]) { nums.splice(i, 1); i--; } } return nums.length; }; removeDuplicates([1, 1, 2]);
-
检查是否存在重复元素
//方法1、对象键值法 var containsDuplicate = function(nums) { if(nums.length<=1){ return false; } let obj = {};//对照对象 for (var i = 0; i < nums.length; i++) { // 判断当前项是否遍历过,是则删除,否存入obj以作对照 if (obj[nums[i]]) { return true //数组删除了一项,要把i回退一下,不然会跳过下一项不去遍历 } else { obj[nums[i]] = 1; } } return false; }; //方法二:set法,比较去重数组和原数组的长度 var containsDuplicate = function(nums) { if(nums.length<=1){ return false; } let uniq=[...new Set(nums)]; return !(uniq.length===nums.length); };
-
检查相邻k个元素是否存在重复元素
给定一个整数数组和一个整数 k,判断数组中是否存在两个不同的索引 i 和 j,使得 nums [i] = nums [j],并且 i 和 j 的差的绝对值最大为 k。
//some() 方法会依次执行数组的每个元素: 如果有一个元素满足条件,则表达式返回true , 剩余的元素不会再执行检测。 如果没有满足条件的元素,则返回false。 array.some(function(currentValue,index,arr),thisValue) //includes() 方法用来判断一个数组是否包含一个指定的值,如果是返回 true,否则false。 arr.includes(searchElement, fromIndex) //是否存在重复元素2 /** * @param {number[]} nums * @param {number} k * @return {boolean} */ var containsNearbyDuplicate = function(nums, k) { //some遍历得到第一个函数的返回值为true,就会return true,如果q return nums.some((item, index) => { return nums.slice(index + 1, index + k + 1).includes(item); }); };
四、查找数组中的元素
-
大数相加
function add(n, m) { // 操作数类型判断, 要求为字符串。不满足条件则抛出异常。 if (typeof n !== 'string' || typeof m !== 'string') { throw new Error('数据类型错误, 大数相加操作数为字符串!'); } // 数据反转, 方便后面的遍历求和 n = n.split('').reverse(); m = m.split('').reverse(); // 获取较长的位数, 并作为后面对应位数遍历的最大值 const maxLength = Math.max(n.length, m.length); // 计算过程中, 临时变量 let tempN = 0; // 临时存储第1个操作数 let tempM = 0; // 临时存储第2个操作数 let tempAdd = 0; // 临时存储对应位上的数相加的和 let extNum = 0; // 满10进1,保存进的值(1 或 0) // 计算结果 const res = []; // 应位上的书相加的和 // 遍历每一位上的数字,并求和。记录满十进一 for (let index = 0; index < maxLength; index++) { // 缺位补0 tempN = n[index] || 0; tempM = m[index] || 0; // 对应位上的数字求和 tempAdd = Number(tempN) + Number(tempM); // 进一(extNum 为进 1) if (extNum) { tempAdd += extNum; } // 满十(存储需要进的 1) extNum = tempAdd >= 10 ? 1 : 0; // 最后一位满十进的一直接保存在当前求得的和中, 非最后一位则取 %10 后的值 if (index === (maxLength - 1) && extNum) { res.push(tempAdd); } else { res.push(tempAdd % 10); } } // 返回计算后的数时注意翻转 return res.reverse().join(''); }
-
找出数组中第二大的值
var arr=[5,2,10,8,0,4,7,11,9,1]; function array2(){ var temp,min; for(var i=0;i<arr.length-1;i++){ min=i; for(var j=i+1;j<arr.length;j++){ if(arr[j]>arr[i]){ temp= arr[i]; arr[i] = arr[j]; arr[j] = temp; } } } alert(arr[1]); } array2();
-
查找数组中是否包含某些特定字符串
function search(Arr,str){ var newArr = []; for(var k in Arr){ if(Arr[k].indexOf(str) != -1){ newArr.push(Arr[k]); } } return newArr.length !=0 ? newArr : "未找到"; } var arr = ["abc","acd","bcf"]; var str1 = "b"; var result = search(arr,str1); console.log(result);//["abc","bcf"] var str2 = "s"; var result1 = search(arr,str2); console.log(result1);//"未找到"
-
字符串里面最长的数字串
//利用正则匹配找出数字 var str="1a12ab123abc1234abcd12345abcde123456abcdef"; var n=[]; var newStr=str.replace(/\d+/g, function () { //调用方法时内部会产生 this 和 arguments return n.push(arguments[0]); }); var i=0; for(let j=0;j<n.length;j++){ if(n[j].length>=i){ i=n[j].length; var max=j; } } console.log(n[max]); console.log(i);
-
两数之和
给定一个整数数组 nums 和一个目标值 target,请你在该数组中找出和为目标值的那 两个 整数,并返回他们的数组下标
//解法一:for() //思路 第一遍过滤 nums 数组,标记为 i。 第二遍再次过滤 nums 数组,标记为 i + 1,因为我们是对数组中的两个数字相加,所以不能重复使用同一个数字。 判断第二次遍历的数字中,它是否等于 target - nums[i],如果成立就返回两个数字的索引。(并不考虑后面还有可成立的答案)。 var twoSum = function(nums, target) { for (let i = 0; i < nums.length; i++) { for (let j = i + 1; j < nums.length; j++) { if (nums[j] === target - nums[i]) { return [i, j]; } } } }; //解法2:indexOf() //思路 首先,我们开辟一块内存 result。 然后,我们通过 map() 遍历 nums,并使用 indexOf() 寻找除当前 item 的 index 之外和 item 相加之和为 target 的结果。 最后,我们返回查找的最新结果,该结果进行了排序([4, 2] 的返回通过 sort() 排序变成 [2, 4]) var twoSum = function(nums, target) { let result = []; nums.map((item, index) => { if (nums.indexOf(target - item) > -1 && nums.indexOf(target - item) != index) { result = [index, nums.indexOf(target - item)].sort((a, b) => a > b); } }); return result; }; //解法3:map //思路 首先,我们需要了解 Map 这个对象。 它可以通过 set() 的形式,以 [key, value] 的形式保存一组数据。(题目中对应 key 就是存入的 target - nums[i] 值,value 就是索引) 它可以通过 get() 的形式,获取到传入 key 值对应的 value。 它可以通过 has() 的形式,判断 Map 对象里面是否存储了传入 key 对应的 value。 然后,我们遍历 nums 数组。 最后,我们判断 nums[i] 是否存在于 Map 对象中。没有的话,就存入 target - nums[i] 到 Map 中。
有的话,因为上次存入的是 target- nums[i],有点类似于解题的钥匙,既然我们看到 nums[i] 存在于 Map 中,
它是解题的钥匙,所以我们只需要返回 [map.get(nums[i]), i] 这组值即可。 var twoSum = function(nums, target) { let map = new Map(); for (let i = 0; i < nums.length; i++) { if (map.has(nums[i])) { return [map.get(nums[i]), i]; } else { map.set(target - nums[i], i); } } };
-
数组中出现次数超过一半的数字
//思路 特殊情况:长度为1/0 新建map,遍历数组,依次储存数组值和频率 如果map里有item这一项,频率value+1,并判断是否超过Math.floor(numbers.length / 2),超过则return, 注意需要循环内return,所以必须使用for循环,不能使用foreach 如果map里没有item这一项,频率value为1 循环结束没有返回,说明数组不存在这样的元素 function MoreThanHalfNum_Solution(numbers) { // write code here if (numbers.length === 0) return 0; if (numbers.length === 1) return numbers[0]; let map = new Map(); for (let i = 0; i < numbers.length; i++) { //map里有item这一项 if (map.has(numbers[i])) { map.set(numbers[i], map.get(numbers[i]) + 1); //频率value+1 if (map.get(numbers[i]) > Math.floor(numbers.length / 2)) return numbers[i]; //频率value>一半长度,返回 } //map里没有item这一项 else { map.set(numbers[i], 1); } //频率value为1 } //循环结束没有返回,说明数组不存在这样的元素 return 0; } MoreThanHalfNum_Solution([1, 2, 3, 2, 2, 2, 5, 4, 2]);
-
数组旋转
把一个数组最开始的若干个元素搬到数组的末尾,我们称之为数组的旋转。
输入一个非递减排序的数组的一个旋转,输出旋转数组的最小元素。例如数组{3,4,5,1,2}为{1,2,3,4,5}的一个旋转,该数组的最小值为1。
NOTE:给出的所有元素都大于0,若数组大小为0,请返回0。
//思路 数组可以被分成两个不减的子数组,最小值就是第二个子数组的开头元素,使用二分法寻找这个元素,使用三个指针:左指针,右指针,中间指针。 没有重复元素的数组,比较mid和right的大小, 如果mid小于right,说明mid-right是单调递增(这中间不会有最小值),min在left~mid中间; 如果mid大于right,说明mid-right不是单调递增(mid不会是最小值,但是这中间会有最小值),min在mid+1~right中间; mid=right,说明存在重复元素,不能直接判断单调性,右指针左移一位,依次比较。 function minNumberInRotateArray(rotateArray) { // write code here // 空数组/单元素数组 if (!rotateArray || rotateArray.length === 1) { return rotateArray[0] || 0; } let left = 0, //左指针 right = rotateArray.length - 1; //右指针 while (left < right) { let mid = Math.floor((left + right) / 2); //mid和right相等,最小值一定在right元素的右边 if (rotateArray[mid] === rotateArray[right]) { right--; //右指针左移动一位,依次比较 } //mid-right非递增,最小值一定在mid元素的右边 else if (rotateArray[mid] > rotateArray[right]) { left = mid + 1; //左指针移动到mid右边第一位 } //mid-mid递增,最小值一定在mid/mid元素的 else right = mid; //右指针移动到mid } // left和right相遇退出循环,该位置就是最小值 return rotateArray[right]; }
-
统计一个数字在排序数组中出现的次数。
function GetNumberOfK(data, k) { // write code here var start = data.indexOf(k), count = 0, i = start; while(i < data.length && data[i] == k){ count++; i++; } return count; } //二分查找方法:先看中间元素与k相比,小于则在左半部分,大于则在右半部分 function GetNumberOfK(data, k) { // write code here var l = 0,r = data.length,mid; while(l < r){ mid = Math.floor((l+r)/2); if(data[mid] < k ){ while(data[mid] == data[mid+1]){ mid++; } l = ++mid; }else if(data[mid] > k){ while(data[mid] == data[mid-1]){ mid--; } r = --mid; }else{ var sign1 = mid,sign2 = mid; while(sign1 <= r && data[sign1] == data[sign1+1]){ sign1++; } while(sign2 >= l && data[sign2] == data[sign2-1]){ sign2--; } return sign1-sign2+1; } } return 0; }
-
数组中只出现一次的两个数字
//思想 第一步: 把所有的元素进行异或操作,最终得到一个异或值。因为是不同的两个数字,所以这个值必定不为0; 第二步: 取异或值最后一个二进制位为1的数字作为mask,如果是1则表示两个数字在这一位上不同。 第三步: 通过与这个mask进行与操作,如果为0的分为一个数组,为1的分为另一个数组。
这样就把问题降低成了:“有一个数组每个数字都出现两次,有一个数字只出现了一次,求出该数字”。对这两个子问题分别进行全异或就可以得到两个解。也就是最终的数组了。 var singleNumber = function (nums) { if (nums.length < 2 || !nums) { return } if (nums.length === 2) { return nums } let xor = 0 for (let i = 0; i < nums.length; i++) { xor ^= nums[i] } let lastOne = -xor & xor let numOne = 0 let numTwo = 0 let arr1 = [] let arr2 = [] for (let i = 0; i < nums.length; i++) { if ((nums[i] & lastOne) === 0) { numOne ^= nums[i] } else { numTwo ^= nums[i] } } return [numOne, numTwo] }
-
给定一个排序数组和一个目标值,在数组中找到目标值,并返回其索引。如果目标值不存在于数组中,返回它将会被按顺序插入的位置。
//解题思路: 首先,我们需要了解的是,一个数 / 2,大概率返回的是小数,而我们的索引需要的是整数,所以我们通过 Math.round() 来四舍五入获取整数。 然后,就是 while 的逻辑判断: nums:[1, 3, 5, 6] target:2 最后,我们需要知道的是,如果 target 是 2,那么返回的 [left, right] 是:[1, 0];
如果 target 是 4,那么返回的 [left, right] 是 [2, 1]。因为循环结束的条件是 left > right,所以无疑 left 是更接近中间值的。 var searchInsert = function(nums, target) { let left = 0; let right = nums.length - 1; while (left <= right) { let middle = Math.round((left + right) / 2); if (target === nums[middle]) { return middle; } else if (target < nums[middle]) { right = middle - 1; } else if (target > nums[middle]) { left = middle + 1; } } return left; };
-
盛水最多的容器
var maxArea = function(height) { let max = 0 for (let i = 0, j = height.length-1; i< j; ) { // 因为容量受限于矮的一边,所以在向内收缩时,移动矮的一侧 // 如果新边足够高的话,效果有可能大于宽度收缩带来的负效果 let minHeight = height[i] > height[j] ? height[j--] : height[i++] // 因为上面--或者++了,所有要补个+1 max = Math.max(max, (j-i+1)* minHeight ) } return max };
-
数组的最小移动次数
给定一个长度为 n 的非空整数数组,找到让数组所有元素相等的最小移动次数。每次移动可以使 n - 1 个元素增加 1
//思路 移动次数等于所有值减去最小值。 var minMoves = function(nums) { let min = Math.min.apply(null, nums); let count = 0; for (let i = 0; i < nums.length; i++) { count += nums[i] - min; } return count; };
-
孩子们的游戏(圆圈中最后剩下的数)
//题目描述 每年六一儿童节,牛客都会准备一些小礼物去看望孤儿院的小朋友,今年亦是如此。 HF作为牛客的资深元老,自然也准备了一些小游戏。其中,有个游戏是这样的: 首先,让小朋友们围成一个大圈。然后,他随机指定一个数m,让编号为0的小朋友开始报数。 每次喊到m-1的那个小朋友要出列唱首歌,然后可以在礼品箱中任意的挑选礼物,并且不再回到圈中,从他的下一个小朋友开始,继续0…m-1报数….这样下去…. 直到剩下最后一个小朋友,可以不用表演,并且拿到牛客名贵的“名侦探柯南”典藏版(名额有限哦!!_)。请你试着想下,哪个小朋友会得到这份礼品呢?(注:小朋友的编号是从0到n-1) //思路1:创立数组,每当到m-1位置时,删除数组中元素,然后将位置计0重新开始 function LastRemaining_Solution(n, m) { // write code here if(n<1||m<1) return -1; var arr=[]; for(var i=0;i<n;i++) arr.push(i); var idx=0; var start=0; while(arr.length>1){ for(var i=1;i<m;i++){//找到第m个位置 idx=(idx+1)%arr.length; } arr.splice(idx,1) } return arr[0]; } //方法2 function LastRemaining_Solution(n, m) { var last=0; for(var i=2;i<=n;i++){ last=(last+m)%i } return last }
-
二维数组中的查找
在一个二维数组中,每一行都按照从左到右递增的顺序排序,每一列都按照从上到下递增的顺序排序。
请完成一个函数,输入这样的一个二维数组和一个整数,判断数组中是否含有该整数
function Find(target, array) { var row = array.length-1; for(var i=row,j=0; i>=0 && j<array[i].length;){ if(target == array[i][j]){ return true; }else if(target > array[i][j]){ j++; continue; }else if(target < array[i][j]){ i--; continue; } } return false; }
五、数组子序列
-
连续子数组的最大和
输入一个整型数组,数组里有正数也有负数
。数组中一个或连续的多个整数组成一个子数组。求所有子数组的和的最大值。要求时间复杂度为O(n)
//思路: 找最大子序列之和,等价于一段找连续的子序列,和是最大值,因为和为负的子序列会减少和,所以直接抛弃这个子序列和更大 特殊情况:数组为空/单元素 数组全是负数时,最大和为数组中的最大值 数组有正数时,从头到尾逐个累加数组中的每个数字,存放到curSum,如果curSum小于0,抛弃之前的累加值,重新开始计数,curSum=item; 比较curSum和 maxSum,取最大值,存进 maxSum,为当前历史序列和的最大值 function FindGreatestSumOfSubArray(array) { // write code here //数组为空/单元素 if (array.length === 0) return 0; if (array.length === 1) return array[0]; //全是负数时,最大和为数组中的最大值 if(array.every(item => item < 0)) { return Math.max(...array); } //数组有正数时,要遍历比较最大和 let curSum, maxSum = 0; array.forEach((item, index) => { curSum = curSum > 0 ? curSum + item : item; maxSum = Math.max(curSum, maxSum); }); return maxSum; }
-
买卖股票的最佳时机
给定一个数组,它的第 i 个元素是一支给定股票第 i 天的价格。如果你最多只允许完成一笔交易(即买入和卖出一支股票),
设计一个算法来计算你所能获取的最大利润。注意你不能在买入股票前卖出股票。
//思路: 等价于找一个数组的两个元素子序列,使得他们差值最大,要求小元素在前,大元素在后 遍历数组,i指向卖出天数 maxProfit保存最大利润,是0~i-1之间的差值的最大值,每次最大值只能在当前元素-当前元素之前的最小买入价以及上次保存的最大利润中产生。
因为要使用当前元素之前的minPrice,所以先比较更新maxProfit minPrice保存最小买入价,是0~i-1之间的最小值,每次比较当前值和上次保存的最小值 遍历完成以后的 maxProfit就是整个数组中的最大差值 var maxProfit = function(prices) { if (prices.length <= 1) return 0; let minPrice = prices[0], //最小买入价,初始化为第一个价格 maxProfit = 0; //最大利润,初始为0 for (let i = 0; i < prices.length; i++) { //和当前价和最小买入价之差比较,更新最大利润 maxProfit = Math.max(maxProfit, prices[i] - minPrice); //和当前价格比较,更新最小买入价 minPrice = Math.min(minPrice, prices[i]); } return maxProfit; };
-
买卖股票的最佳时机
给定一个数组,它的第 i 个元素是一支给定股票第 i 天的价格。设计一个算法来计算你所能获取的最大利润。
你可以尽可能地完成更多的交易(多次买卖一支股票)//思路 比较数组相邻元素,后一位比较大,就把差值累加到结果上 var maxProfit = function(prices) { if (prices.length <= 1) return 0; let res=0; for (let i = 1; i < prices.length; i++) { prices[i] > prices[i - 1] ? (res += prices[i] - prices[i - 1]) : ""; } return res; };
-
输入一个正数
S
,打印出所有和为S的连续正数序列。
例如:输入15,有序1+2+3+4+5 = 4+5+6 = 7+8 = 15 所以打印出3个连续序列1-5,5-6和7-8。 //思路 创建一个容器child,用于表示当前的子序列,初始元素为1,2 记录子序列的开头元素small和末尾元素big big向右移动子序列末尾增加一个数 small向右移动子序列开头减少一个数 当子序列的和大于目标值,small向右移动,子序列的和小于目标值,big向右移动 function FindContinuousSequence(sum) { const result = []; const child = [1, 2]; let big = 2; let small = 1; let currentSum = 3; while (big < sum) { while (currentSum < sum && big < sum) { child.push(++big); currentSum += big; } while (currentSum > sum && small < big) { child.shift(); currentSum -= small++; } if (currentSum === sum && child.length > 1) { result.push(child.slice()); child.push(++big); currentSum += big; } } return result; }
-
杨辉三角
//给定一个非负整数 numRows,生成杨辉三角的前 numRows 行。 在杨辉三角中,每个数是它左上方和右上方的数的和。 function combination(m,n){ if(n == 0) return 1;//第一个数为1 else if(m == n) return 1; //最后一个数为1 else return combination(m-1,n-1)+combination(m-1,n);//中间的数为前一行的两个数相加 } function Print(n){ for( var i = 0 ; i < n ; i++ ){ let arr=[];//用来放第i行的数 for ( var j = 0 ; j <= i ; j++ ) { arr.push(combination(i,j)); } console.log(arr.join(' '));//字符串形式输出 }
-
给定一个非负索引 k,其中 k ≤ 33,返回杨辉三角的第 k 行。在杨辉三角中,每个数是它左上方和右上方的数的和。
//思路 如果 rowIndex 为 0,返回 [1] 如果 rowIndex 为 1,返回 [1, 1] 递归 getRow(rowIndex - 1),然后求出中间的和,并 return 出来 [1, ...result, 1] var getRow = function(rowIndex) { if (!rowIndex) { return [1]; } if (rowIndex === 1) { return [1, 1]; } let recursion = getRow(rowIndex - 1); let result = []; for (let i = 0; i < rowIndex; i++) { if (recursion[i] && recursion[i + 1]) { result.push(recursion[i] + recursion[i + 1]); } } return [1, ...result, 1]; };
六、字符串
-
1+2+3+……+n
//求1+2+3+…+n,要求不能使用乘除法、for、while、if、else、switch、case等关键字及条件判断语句(A?B:C)。 function Sum_Solution(n) { // write code here var sum=n; //短路运算,n!==0,如果等于0,则不会进行后续的运算 var a=(n!=0)&&((sum+=Sum_Solution(n-1))>0) return sum }
-
不用加减乘除做加法
//题目描述 写一个函数,求两个整数之和,要求在函数体内不得使用+、-、*、/四则运算符号。 //思路:不考虑进位直接相加可以看出,1+0=1,1+1=0,0+0=0,这是异或操作(^),
对加 0 、0 加 1 、1 加 0 而言, 都不会产生进位,只有 1 加 1 时,会向前产生一个进位。此时我们可以想象成是两个数先做位与运算,然后再向左移动一位 function Add(num1, num2) { // write code here var sum,carry; do{ sum=num1 ^ num2; carry=(num1&num2)<<1; num1=sum; num2=carry; }while(num2!=0) return sum; }
-
整数反转
//思路 首先,将传入的数字 x 转换成字符串,并分割成数组。 然后,遍历该数组,将最后一个取出来放到 result 中。 最后,判断这个 result 是否超过题目限制,如果超过则变成 0 。 var reverse = function(x) { // 转数组 let numberToArray = String(Math.abs(x)).split(''); // 转字符串 let result = ''; for (const i = 0; i < numberToArray.length; ) { result += numberToArray.pop(); } result = x > 0 ? Number(result) : - Number(result); // 超 [-Math.pow(2, 31), Math.pow(2, 31) - 1] 判断 if (result > Math.pow(2, 31) - 1 || result < - Math.pow(2, 31)) { result = 0; } return result; };
-
罗马数字转整数
//思路 首先,设置 Map,将正常情况存下来。 然后,遍历字符串,判断特殊情况,如果是特殊情况,需要跳过下一次循环,否则直接获取 Map 中对应的值。 最后,将结果通过 result 给 return 出去。 var romanToInt = function(s) { /** * 特殊情况 * IV === 4 * IX === 9 * XL === 40 * XC === 90 * CD === 400 * CM === 900 * 正常情况 * I === 1 * V === 5 * X === 10 * L === 50 * C === 100 * D === 500 * M === 1000 */ let map = new Map(); map.set('I', 1); map.set('V', 5); map.set('X', 10); map.set('L', 50); map.set('C', 100); map.set('D', 500); map.set('M', 1000) let result = 0; for (let i = 0; i < s.length; i++) { if (s[i] + s[i+1] === 'IV') { result += 4; i = i + 1; } else if(s[i] + s[i+1] === 'IX') { result += 9; i = i + 1; } else if(s[i] + s[i+1] === 'XL') { result += 40; i = i + 1; } else if(s[i] + s[i+1] === 'XC') { result += 90; i = i + 1; } else if(s[i] + s[i+1] === 'CD') { result += 400 i = i + 1; } else if(s[i] + s[i+1] === 'CM') { result += 900; i = i + 1; } else { result += map.get(s[i]); } } return result; };
-
快乐数
//编写一个算法来判断一个数是不是“快乐数”。 一个“快乐数”定义为:对于一个正整数,每一次将该数替换为它每个位置上的数字的平方和, 然后重复这个过程直到这个数变为 1,也可能是无限循环但始终变不到 1。 如果可以变为 1,那么这个数就是快乐数。 //思路 在 1-9 中,1 和 7 是快乐数,即它们计算后的最终结果是 1。除此之外,都不是快乐数,返回 false。 一个数,假设是 89。那么,第一次遍历,将它磨 10,得到的是个位数 9;除以 10(JS 没有强类型,需要借助 Math.floor()),那么缩短 1 位变成 8。
第二次遍历,将它磨 10,得到 8,再除以 10,得到的数小于 1,终止循环。 在这过程中,我们得到 sum = 9 * 9 + 8 * 8,即将一个数彻底分解。递归往复,在结果为 10 之内的时候,就可以返回 true 或者 false 了。 var isHappy = function(n) { if (n == 1 || n == 7) { return true; } if (n < 10) { return false; } let sum = 0; while (n >= 1) { let d = n % 10; sum += d * d; n = Math.floor(n / 10); } return isHappy(sum); };
-
缺失数
给定一个包含 0, 1, 2, ..., n 中 n 个数的序列,找出 0 .. n 中没有出现在序列中的那个数
//直接对前n项进行求和与从1到n的数字之和相比,返回差值即可 var missingNumber = function(nums) { let n = nums.length; let sum1 = (n+1)*(n)/2; let sum=0; for(let i=0;i<n;i++) { sum += nums[i]; } return sum1-sum; };
-
js根据数组中对象的某个属性进行排序
let arr = [ {name: '张三',age: 18}, {name: '李四',age: 9}, {name: '王五',age: 28} ] const handle = (property) => { return function(a,b){ const val1 = a[property]; const val2 = b[property]; return val1 - val2; } } arr.sort(handle('age'));
-
同构字符串
//给定两个字符串 s 和 t,判断它们是否是同构的。 如果 s 中的字符可以被替换得到 t ,那么这两个字符串是同构的。 所有出现的字符都必须用另一个字符替换,同时保留字符的顺序。 两个字符不能映射到同一个字符上,但字符可以映射自己本身。 //思路 ndexOf():判断数组中是否存在判断条件中的值。如果存在,则返回第一次出现的索引;如果不存在,则返回 -1。 var isIsomorphic = function(s, t) { for (let i = 0; i < s.length; i++) { if (s.indexOf(s[i]) !== t.indexOf(t[i])) { return false; } } return true; };
-
统计一个字符串出现最多的字母
function findMaxDuplicateChar(str) { if(str.length == 1) { return str; } let charObj = {}; for(let i=0;i<str.length;i++) { if(!charObj[str.charAt(i)]) { charObj[str.charAt(i)] = 1; }else{ charObj[str.charAt(i)] += 1; } } let maxChar = '', maxValue = 1; for(var k in charObj) { if(charObj[k] >= maxValue) { maxChar = k; maxValue = charObj[k]; } } return maxChar; }
-
判断括号是否闭合合法
//思路 遇到左括号进行入栈,遇到右括号判断栈顶元素是否与当前字符匹配,如果匹配则出栈,否则返回false,
最后检查栈是否为空,如果为空说明完全匹配,返回true,否则返回false。JavaScript实现利用数组的push和pop方法实现元素的入栈和出栈。 var isValid = function(s) { var sub = []; var len = s.length; for (var i = 0; i < len; i++) { if (s[i] === '(' || s[i] === '[' || s[i] === '{') { //遇到左括号添加进数组 sub.push(s[i]); } else if (s[i] === ')') { if (sub[sub.length - 1] === '(') { //遇到右括号判断数组最后一个元素是否与当前元素匹配 sub.pop(); //匹配则删除数组最后一个元素 } else { return false; //否则返回false } } else if (s[i] === ']') { if (sub[sub.length - 1] === '[') { sub.pop(); } else { return false; } } else { if (sub[sub.length - 1] === '{') { sub.pop(); } else { return false; } } } if (sub.length == 0) return true; else return false; }; var result = isValid('()');
-
判断一个字符串是不是回文字符串
var isPalindrome = function(x) { if(x >= 0){ var strx = String(x); return strx == strx.split("").reverse().join(""); } return false; };
-
斐波那契数列
function getFibonacci(n) { var fibarr = []; var i = 0; while(i<n) { if(i<=1) { fibarr.push(i); }else{ fibarr.push(fibarr[i-1] + fibarr[i-2]) } i++; } return fibarr; }
-
跳台阶
function jumpFloor(n) { if(n<=2){ return n; } let i = 2; let pre = 1; let current = 2; let result = 0; while(i++ < n){ result = pre + current; pre = current; current = result; } return result; }
-
字符串反转
function ReverseSentence(str) { if(!str){return ''} return str.split(' ').reverse().join(' '); }
-
输入一个字符串,按字典序打印出该字符串中字符的所有排列
例如输入字符串abc
,则打印出由字符a,b,c
所能排列出来的所有字符串abc,acb,bac,bca,cab
和cba
//使用回溯法 记录一个字符(temp),用于存储当前需要进入排列的字符 记录一个字符串(current),用于记录当前已经排列好的字符 记录一个队列(queue),用于存储还未被排列的字符 每次排列将temp添加到current 如果queue为空,则本次排列完成,将curret加入到结果数组中,结束递归 如果queue不为空,说明还有未排列的字符 递归排列queue中剩余的字符 为了不影响后续排列,每次递归完成,将当前递归的字符temp加回队列 function Permutation(str) { const result = []; if (str) { queue = str.split('') PermutationCore(queue, result); } result.sort(); return [... new Set(result)]; } function PermutationCore(queue, result, temp = "", current = "") { current += temp; if (queue.length === 0) { result.push(current); return; } for (let i = 0; i < queue.length; i++) { temp = queue.shift(); PermutationCore(queue, result, temp, current); queue.push(temp); } }
-
随机生成指定长度的字符串
function randomString(n) { let str = 'abcdefghijklmnopqrstuvwxyz9876543210'; let tmp = '', i = 0, l = str.length; for (i = 0; i < n; i++) { tmp += str.charAt(Math.floor(Math.random() * l)); } return tmp; }
-
不借助临时变量,进行两个整数的交换
//主要是利用 + - 去进行运算,类似 a = a + ( b - a) 实际上等同于最后 的 a = b; function swap(a , b) { b = b - a; a = a + b; b = a - b; return [a,b]; } module.exports = swap;
-
替换空格
请实现一个函数,将一个字符串中的空格替换成“%20”
//思路:使用正则表达式,\s+代表多个空格,?则表示取尽可能少的空格,然后通过replace函数替换为%20 function replaceSpace(str) { // write code here return str.replace(/\s+?/g,'%20') }
-
字符流中第一个不重复的字符
//思路:设置一个数组存放初次出现的元素。如果这个元素没有过,就放进数组;如果这个元素有过了,就把他从数组删除。输出数组的第一个元素。 var map = {}; //Init module if you need function Init() { // write code here map = {}; } //Insert one char from stringstream function Insert(ch) { // write code here if (!map[ch]) { map[ch] = 1; } else { map[ch] ++; } } //return the first appearence once char in current stringstream function FirstAppearingOnce() { for (var ch in map) { if (map.hasOwnProperty(ch)) { if (map[ch] === 1) { return ch; } } } return '#'; }
-
给定一个字符串,找到它的第一个不重复的字符,并返回它的索引。如果不存在,则返回 -1。
//思路 那么,我们就有思路破解本题了: 如果出现的第一个位置和出现的最后一个位置相同,那么就是唯一的,返回 i。 如果都不是相同的,说明没有唯一值,返回 -1。 var firstUniqChar = function(s) { for (let i = 0; i < s.length; i++) { if (s.indexOf(s[i]) === s.lastIndexOf(s[i])) { return i; } } return -1; };
-
最长公共前缀
//思路 通过 reduce(),我们可以进行一项累加操作:先比较第一项和第二项,然后找到它们共通值后,剪切并 return;再比较的时候,
使用 return 出来的值和第三项进行比较……依次类推 最后,返回最后一次 return 的值。 var longestCommonPrefix = function(strs) { if (strs.length < 2) { return !strs.length ? '' : strs[0]; } return strs.reduce((prev, next) => { let i = 0; while (prev[i] && next[i] && prev[i] === next[i]) { i++; }; return prev.slice(0, i); }); };
-
最长回文子序列
//https://juejin.im/post/5d39d3e16fb9a07ec42ba020 var longestPalindrome = function(s) { var start = end = 0 for(let i = 0 ; i < s.length; i++){ let len1 = search(s,i,i) let len2 = search(s,i,i+1) let len = Math.max(len1,len2) if(len > end -start){ start = i - Math.floor((len - 1) / 2) end = i + Math.floor(len / 2) } } return s.slice(start,end+1) function search(s,l,r){ let left = l, right = r; while(left >= 0 && right < s.length && s[left] === s[right]){ left--; right++; } return right - left - 1; } };
-
字符串相加
给定两个字符串形式的非负整数 num1 和num2 ,计算它们的和
//思路 如果没有超过最大精度限制,即它们两个相加数字的长度小于 16,我们直接使用 JavaScript 自带的加法运算。 如果超过最大精度限制,那么就实施方案二。 先补位。为什么要补位呢?我们的计算都是从个位数相加的,即 最后一位对齐 原则,通过补位,我们可以将 1234 变成 01234,从而能够跟 12345 对齐相加。 遍历相加。遍历的两个数字,如果相加大于 10,那么前面一位就需要 + 1,我们通过 temp 来记录,同时,如果是最后一个字符串,我们需要给前面的补字符串 1;
如果相加小于 10,说明可以直接添加到字符串中,同时,我们需要清空掉补位数(即 temp)。 var addStrings = function(num1, num2) { // 如果没有超过 JS 最大精度限制 if (num1.length < 16 && num2.length < 16) { return String(Number(num1) + Number(num2)); } // 如果超过 JS 最大精度限制 // 1. 补位:12345 与 01234 let patch = ''; const differenceLength = num1.length > num2.length ? num1.length - num2.length : num2.length - num1.length; for (let i = 0; i < differenceLength; i++) { patch += '0'; } if (num1.length > num2.length) { num2 = patch + num2; } else if(num1.length < num2.length) { num1 = patch + num1; } // 2. 计算相加 let temp = 0; // 补进制 let result = ''; for (let i = num1.length - 1; i >= 0; i--) { let sum = Number(num1[i]) + Number(num2[i]) + temp; if (sum >= 10) { result = sum % 10 + result; // 如果超 10 记得补 1 temp = 1; // 如果在最后一个字符串了,那么记得给前面补 1 if (i === 0) { result = '1' + result; } } else { result = sum + result; temp = 0; // 如果没超 10 记得清 0 } } return result; }; //大神解答 var addStrings = function (num1, num2) { let a = num1.split(''); let b = num2.split(''); let c = 0; let res = ''; while (a.length || b.length || c) { c += ~~a.pop() + ~~b.pop(); res = c % 10 + res; c = c > 9; } return res; };
给定一个非空的字符串,判断它是否可以由它的一个子串重复多次构成。给定的字符串只含有小写英文字母,并且长度不超过10000
//思路 /^(\w+)\1+$/.test(s) (\w+) 表示任意多个字母或数字或下划线,也就是 AZ,az,0~9,_ 中任意多个组成。 \1+ 表明前面的字母可能重复 1+ 次数。 这样,正则表达式直接通过 test() 匹配 s,返回 true 或者 false。
const repeatedSubstringPattern = (s) => { return /^(\w+)\1+$/.test(s); };
-
把字符串转换成整数
function StrToInt(str) { // write code here if(str==null || str.length<=0) return 0; var len=str.length,index=0,flag=false; var arr=str.split('') if(arr[0]=='+'){ index++ }else if(arr[0]=='-'){ flag=true index++ } var num=strToIntCore(arr,index,len,flag) return num; } function strToIntCore(arr,start,length,minus){ var number=0; if(arr==null||start>=length) return number; while(start<length){ if(arr[start]>='0'&& arr[start]<='9'){ var flag=minus?-1:1; number=number*10+(arr[start]-'0')*flag start++ }else{ number=0; break; } } return number; }
-
表示数值的字符串
function isNumeric(s) { // write code here return s.match(/[\+\-]?[0-9]*(\.[0-9]*)?([eE][\+-]?[0-9]+)?/g)[0]==s; }
-
赎金信
//给定一个赎金信 (ransom) 字符串和一个杂志(magazine)字符串, 判断第一个字符串 ransom 能不能由第二个字符串 magazines 里面的字符构成。 如果可以构成,返回 true;否则返回 false。 //思路 将字符串 magazine 拆分成数组。 遍历 ransomNote,判断它每个字符串是否存在于 magazine 中。如果存在,则删掉 magazine 中的这个元素
(相当于杂志剪去这个字母,防止杂志中的字母不够用);如果不存在,表明 magazine 杂志的单词不够完成写赎金信。 var canConstruct = function (ransomNote, magazine) { magazine = magazine.split(''); for (let i = 0; i < ransomNote.length; i++) { if (magazine.indexOf(ransomNote[i]) != -1) { magazine.splice(magazine.indexOf(ransomNote[i]), 1) } else { return false; } } return true; };
-
压缩字符串
//思路 给数组增加长度,然后给末尾添加 0。(为什么不直接从 0 开始,请思考对与否)。 遍历 chars 数组。 将长度赋值到数组中。 var compress = function (chars) { let j = 0, count = 1; chars.push(0); for (let i = 1; i < chars.length; i++) { if (chars[i] !== chars[i - 1]) { chars[j] = chars[i - 1]; j++; if (count > 1) { let temp = count.toString(); for (let k = 0; k < temp.length; k++) { chars[j++] = temp[k]; } } count = 1; } else { count++; } } return j; };