双指针
https://mp.weixin.qq.com/s/Z-oYzx9O1pjiym6HtKqGIQ
在数组中并没有真正意义上的指针,但我们可以把索引当做数组中的指针,这样也可以在数组中施展双指针技巧。
快慢指针
原地修改数组
26.删除有序数组中的重复项
83.删除排序链表中的重复元素
其实和数组去重是一模一样的,唯一的区别是把数组赋值操作变成操作指针而已
27.移除元素
快慢指针
或者左右指针(会打乱顺序)
283.移动零
与上题类似
75. 颜色分类
189. 轮转数组
31. 下一个排列
202. 快乐数
推算得到所有数最后都会进入循环,所以可以使用快慢指针。
/*** @param {number} n* @return {boolean}*/var isHappy = function(n) {function getSquareSum(n){let sum=0;while(n>0){sum+=(n%10)*(n%10);n=parseInt(n/10);}return sum;}let slow=n,fast=n;do{ //使用do whileslow=getSquareSum(slow);fast=getSquareSum(fast);fast=getSquareSum(fast);}while(slow!==fast)return slow===1;};
滑动窗口算法
https://mp.weixin.qq.com/s/ioKXTMZufDECBUwRRp3zaA
数组中另一大类快慢指针的题目就是「滑动窗口算法」。
left指针在后,right指针在前,两个指针中间的部分就是「窗口」,算法通过扩大和缩小「窗口」来解决某些问题。
//滑动窗口算法算法框架 时间复杂度O(N)int left = 0, right = 0;while (right < s.size()) {// 增大窗口window.add(s[right]);right++;while (window needs shrink) {// 缩小窗口window.remove(s[left]);left++;}}
76.最小覆盖子串
找到字符串中所有字母异位词
3. 无重复字符的最长子串
var lengthOfLongestSubstring = function(s) {let result=0;let left=0,right=0;let world="";while(right<s.length){const rightStr=s[right]right++;let index=world.indexOf(rightStr)if(index>-1){left=left+index+1;}const length=right-left;result=length>result?length:result;world=s.substring(left,right);}return result};
左右指针
只要数组有序,就应该想到双指针技巧。
二分查找
167.两数之和 II 可以使用map吗?
翻转数组
回文串判断
977. 有序数组的平方
var sortedSquares = function(nums) {const length=nums.lengthlet left=0,right=length-1;let result=Array(length);let resIndex=length-1;while(left<=right){const leftNum=nums[left],rightNum=nums[right]if(Math.abs(leftNum)<=Math.abs(rightNum)){result[resIndex--]=rightNum*rightNumright--;}else{result[resIndex--]=leftNum*leftNumleft++;}}return result;};
5.最长回文子串
var longestPalindrome = function (s) {let result = '';// 寻找以 s[l] 和 s[r] 为中心的最长回文串function getPalindrome(s, l, r) {while (l >= 0 && r < s.length && s[l] === s[r]) {l--;r++;}return s.substring(l + 1, r);}for (let i = 0; i < s.length; i++) {//找到以 s[i] 为中心的回文串想,奇数回文串const r1 = getPalindrome(s, i, i);//找到以 s[i] 和 s[i+1] 为中心的回文串,偶数回文串const r2 = getPalindrome(s, i, i + 1);result =r1.length > result.length ? r1 :result;result =r2.length > result.length ? r2 :result;}return result;};
合并两个有序数组
var merge = function(nums1, m, nums2, n) {let left=m-1,right=n-1,res=m+n-1;if(n=0) return;while(left>=0 || right>=0){if(left <0 || nums1[left]<=nums2[right] ){nums1[res]=nums2[right]right--;res--;}else{nums1[res]=nums1[left]left--;res--;}}};
二分查找
给定一个 n 个元素有序的(升序)整型数组 nums 和一个目标值 target ,写一个函数搜索 nums 中的 target,如果目标值存在返回下标,否则返回 -1。
看到有序数组查找某一个值,则应立马想到二分查找
704. 二分查找
var search = function(nums, target) {let min=0,max=nums.length-1,mid;while(min<=max){mid=Math.ceil((min+max)/2);if(nums[mid]>target){max=mid-1}else if(nums[mid]<target){min=mid+1}else{return mid}}return -1;};
69. x 的平方根
var mySqrt = function(x) {if(x<2) return x;let left=0,right=x;while(left<=right){let mid=Math.floor((left+right)/2)if((mid+1)*(mid+1)>x){return mid}else{left= mid+1;}}else{right = mid-1;}}};
34. 在排序数组中查找元素的第一个和最后一个位置
var searchRange = function(nums, target) {let min=0,max=nums.length-1;while(min<=max){let mid=Math.ceil((min+max)/2);if(nums[mid]>target){max=mid-1;}else if(nums[mid]<target){min=mid+1}else{//试探周围元素是否相等;let left=mid,right=mid;while(left-1>=0 && nums[left-1]===nums[mid]){left--}while(right+1<nums.length && nums[right+1]===nums[mid]){right++}return [left,right];}}return [-1,-1];};
33. 搜索旋转排序数组
对于有序数组,可以使用二分查找的方法查找元素。
但是这道题中,数组本身不是有序的,进行旋转后只保证了数组的局部是有序的。将数组一分为二,其中一定有一个是有序的,另一个可能是有序,也能是部分有序。 此时有序部分用二分法查找。无序部分再一分为二,其中一个一定有序,另一个可能有序,可能无序。就这样循环.。
var search = function(nums, target) {if(nums.length<1) return -1if(nums.length==1) return target===nums[0] ?0:-1;let left=0,right=nums.length-1;while(left<=right){let mid=Math.floor((left+right)/2);if(target===nums[mid]){return mid;}if(nums[left]<=nums[mid]){if(target>=nums[left] && target<nums[mid]){right = mid-1;}else{left = mid+1;}}else{if(target>nums[mid] && target <=nums[right]){left = mid+1;}else{right = mid-1;}}}return -1;};
539. 最小时间差
function getMinute(str){//转成分钟return Number(str.substr(0,1))*60*10 + Number(str.substr(1,1))*60 + Number(str.substr(3,1))*10+ Number(str.substr(4,1))}var findMinDifference = function(timePoints) {let newTimePoiont=timePoints.sort(); //排序let fisrtTime=getMinute(newTimePoiont[0]);let preTime=fisrtTime,nextTime;let res=Infinityfor(let i =1;i<newTimePoiont.length;i++){nextTime=getMinute(newTimePoiont[i]);res=Math.min(nextTime-preTime,res)preTime=nextTime}//排序好后计算收尾,因为头一定比尾小,相减时需要考虑 5:00 20:00的情况,时间差应为9而不是15res=Math.min(res,fisrtTime+24*60-nextTime);return res;};
前后缀乘积
给你一个长度为 n 的整数数组 nums,其中 n > 1,返回输出数组 output ,其中 output[i] 等于 nums 中除 nums[i] 之外其余各元素的乘积。
示例:
输入: [1,2,3,4]
输出: [24,12,8,6]
提示:题目数据保证数组之中任意元素的全部前缀元素和后缀(甚至是整个数组)的乘积都在 32 位整数范围内。
说明: 请不要使用除法,且在 O(n) 时间复杂度内完成此题。
function getNewList(list){const length=list.length;let left=Array(length); //计算所有前缀和后缀元素的乘积let right=Array(length);let ans=Array(length);left[0]=1;right[length-1]=1;for(leti=1;i<length;i++){left[i]=list[i-1] * left[i-1]}for(leti=length-2;i>=0;i--){right[i]=list[i+1] * right[i+1]ans[i]=left[i]*right[i];}ans[length-1]=left[length-1]*right[length-1];return ans;}
单调栈
单调栈实际上就是栈,只是利用了一些巧妙的逻辑,使得每次新元素入栈后,栈内的元素都保持有序(单调递增或单调递减)。
时间复杂度为O(N)
496. 下一个更大元素 I
739. 每日温度
503. 下一个更大元素 II
如何处理环形数组
接雨水
//木桶效应,需要看短板,得到当前左边和右边的最高,取短板-当前高度//前后缀 动态规划 时间O(N),空间O(N)var trap = function(height) {const length=height.length;let left=Array(length),right=Array(length);let result=0;left[0]=height[0];for(let i=1;i<length;i++){left[i]=Math.max(height[i],left[i-1])}right[length-1]=height[length-1];for(let i=length-2;i>=0;i--){right[i]=Math.max(height[i],right[i+1])}for(let i=0;i<length;i++){result+=Math.min(left[i],right[i])-height[i]}return result;};
空间复杂度还需要优化,将备忘录数组优化为双指针变量,此时空间复杂度为O(N)。
/*** @param {number[]} height* @return {number}*/var trap = function(height) {const length=height.length;let result=0;let left=0,right=length-1;let leftMax=height[left],rightMax=height[right];while(left<=right){if(leftMax<=rightMax){result+=leftMax-height[left]left++;leftMax=Math.max(height[left],leftMax)}else{result+=rightMax-height[right]right--;rightMax=Math.max(height[right],rightMax)}}return result;};
二维矩阵
48. 旋转图像
var rotate = function (matrix) {const n = matrix.length;for (let i = 0; i < Math.floor((n + 1) / 2); ++i) {for (let j = 0; j < Math.floor(n / 2); ++j) {const temp = matrix[i][j];matrix[i][j] = matrix[n - j - 1][i];matrix[n - j - 1][i] = matrix[n - i - 1][n - j - 1];matrix[n - i - 1][n - j - 1] = matrix[j][n - i - 1];matrix[j][n - i - 1] = temp;}}};
54. 螺旋矩阵
var spiralOrder = function (matrix) {let left = 0, right = n - 1, top = 0, bottom = n - 1, k = 1;let result = []while (k <= n * n) {for (let i = left; i <= right; i++) {result.push(matrix[top][i])}top++;for (let i = top; i <= bottom; i++) {result.push(matrix[i][right])}right--;for (let i = right; i >= left; i--) {result.push(matrix[bottom][i])}bottom--;for (let i = bottom; i >= top; i--) {result.push(matrix[i][left])}left++;}return result;};
59. 螺旋矩阵 II
var generateMatrix = function (n) {let left = 0, right = n - 1, top = 0, bottom = n - 1, k = 1;let result = Array.from(Array(n), () => Array(n));while (k <= n * n) {for (let i = left; i <= right; i++) {result[top][i] = k++;}top++;for (let i = top; i <= bottom; i++) {result[i][right] = k++;}right--;for (let i = right; i >= left; i--) {result[bottom][i] = k++;}bottom--;for (let i = bottom; i >= top; i--) {result[i][left] = k++;}left++;}return result;};
74. 搜索二维矩阵
//方法一:var searchMatrix = function (matrix, target) {//左下角的数一定比右上角大。故从左下角开始遍历,小了往上,大了往右。let i = matrix.length - 1, j = 0;while (i >= 0 && j < matrix[0].length) {const num = matrix[i][j]if (num === target) return true;else if (num > target) i--;else j++;}return false;};//方法二 二分搜索//时间复杂度:O(logmn),其中 mm 和 nn 分别是矩阵的行数和列数。//空间复杂度:O(1)。var searchMatrix = function (matrix, target) {const m = matrix.length, n = matrix[0].length;let low = 0, high = m * n - 1;while (low <= high) {const mid = Math.floor((high + low) / 2);const x = matrix[Math.floor(mid / n)][mid % n];if (x < target) {low = mid + 1;} else if (x > target) {high = mid - 1;} else {return true;}}return false;};
