删除链表的倒数第n个节点
思路1:建立两个指针p1,p2 其中p1 先走n步,p2再开始走,等p1到末尾了,p2.next就是要删除的节点 ,注意特殊位置的考虑,删除的是头结点
思路2:用个数组,对链表每个节点值重新声明为节点对象,再存入数组,删除对应元素后,一个一个串起来
var removeNthFromEnd = function(head, n) {
var p1 = head;
// p1先走n步
while (n > 0) {
p1 = p1.next;
n--;
}
// 删除的是头结点
if (!p1) {
return head.next;
}
var p2 = head;
while (p1.next) {
p1 = p1.next;
p2 = p2.next;
}
p2.next = p2.next.next;
return head;
};
不用乘除符,取模符,做除法运算
思路:通过减法计算,每减一次,结果加一,但是显然,当被除数很大时,会运行超时,所以采用位运算加减法来实现,每次除数左移一位,可以将问题规模变成原来的一半。
var divide = function(dividend, divisor) {
var d1=Math.abs(dividend);
var d2=Math.abs(divisor);
var res=0;
if(dividend==0){
return 0;
}
var i=0;
var res=0;
if(d2==1){
if((dividend>0&&divisor<0)||(dividend<0&&divisor>0)){
return -d1;
}
else{
if(d1>2147483647)
{
return 2147483647;
}
else{
return d1;
}
}
}
//关键算法
while(d1>=d2){
var temp=d2;
var cnt=1;
while(temp<d1>>1){
temp=temp<<1;
cnt=cnt<<1;
}
d1=d1-temp;
res+=cnt;
}
if((dividend>0&&divisor<0)||(dividend<0&&divisor>0)){
return -res;
}
else{
return res;
}
};
搜索旋转数组
假设按照升序排序的数组在预先未知的某个点上进行了旋转。
( 例如,数组 [0,1,2,4,5,6,7] 可能变为 [4,5,6,7,0,1,2] )。
搜索一个给定的目标值,如果数组中存在这个目标值,则返回它的索引,否则返回 -1 。
你可以假设数组中不存在重复的元素。
你的算法时间复杂度必须是 O(log n) 级别。
var search = function(nums, target) {
var left=0;
var right=nums.length-1;
while(left<=right){
//注意javascript的除法不是整除,需要自行向下取整
var mid=Math.floor((left+right)/2);
if(nums[mid]==target){
return mid;
}
// mid 在左边递增数组中
if(nums[mid]>=nums[left]){
if(nums[left]==target){
return left;
}
else if(nums[mid]>target&&nums[left]<target){
right=mid-1;
}
else{
left=mid+1;
}
}
// mid在右边递增数组中
else{
if(nums[right]==target){
return right;
}
else if(nums[mid]<target&&nums[right]>target){
left=mid+1;
}
else{
right=mid-1;
}
}
}
return -1;
};
旋转链表
如 1->2->3->4->5 k=2;
旋转后的链表为 4->5->1->2->3,注意k可以大于链表的长度
var rotateRight = function(head, k) {
var p=head;
var count=0;
//计算链表长度
while(p){
count++;
p=p.next;
}
if(count==0){
return null;
}
if(k>=count){
k=k%count;
}
if(k==0){
return head;
}
var phead=new ListNode(0);
var qhead=phead;
var num=count-k;
var i=1;
var q=head;
//获取旋转后的链表头结点q.next
while(i<num){
q=q.next;
i++;
}
phead.next=q.next;
q.next=null;
while(phead.next){
phead=phead.next;
}
//把原来的尾节点串上原来的头结点
phead.next=head;
return qhead.next;
};
二叉树最大深度
var maxDepth = function(root) {
if(!root){
return 0;
}
return 1+Math.max(maxDepth(root.left), maxDepth(root.right));
};
110 高度平衡二叉树
给定一个二叉树,判断它是否是高度平衡的二叉树。
本题中,一棵高度平衡二叉树定义为:
一个二叉树每个节点 的左右两个子树的高度差的绝对值不超过1。
示例 1:
给定二叉树 [3,9,20,null,null,15,7]
返回 true 。
示例 2:
给定二叉树 [1,2,2,3,3,null,null,4,4]
返回 false 。
var depth=function(root){
if(!root){
return 0;
}
return 1+Math.max(depth(root.left),depth(root.right));
}
var isBalanced = function(root) {
if(!root){
return true;
}
var temp1=depth(root.left);
var temp2=depth(root.right);
return Math.abs(temp1-temp2)<2&&isBalanced(root.left)&&isBalanced(root.right);
};
101 对称二叉树
给定一个二叉树,检查它是否是镜像对称的。
例如,二叉树 [1,2,2,3,4,4,3] 是对称的。
但是下面这个 [1,2,2,null,3,null,3] 则不是镜像对称的:
var judge = function(t1, t2) {
if (t1 == null && t2 == null) {
return true;
}
if (t1 != null && t2 != null) {
return (
t1.val == t2.val && judge(t1.right, t2.left) && judge(t1.left, t2.right)
);
}
return false;
};
var isSymmetric = function(root) {
if (!root) {
return true;
}
return judge(root.left, root.right);
};
226翻转二叉树
翻转一棵二叉树。
var invertTree = function(root) {
if (!root) {
return null;
}
var nodeleft = invertTree(root.left);
var noderight = invertTree(root.right);
root.left = noderight;
root.right = nodeleft;
return root;
};
654最大二叉树
给定一个不含重复元素的整数数组。一个以此数组构建的最大二叉树定义如下:
二叉树的根是数组中的最大元素。
左子树是通过数组中最大值左边部分构造出的最大二叉树。
右子树是通过数组中最大值右边部分构造出的最大二叉树。
通过给定的数组构建最大二叉树,并且输出这个树的根节点。
Example 1:
输入: [3,2,1,6,0,5]
输入: 返回下面这棵树的根节点:
var constructMaximumBinaryTree = function(nums) {
if (nums.length == 0) {
return null;
}
var max = nums[0];
var maxpos = 0;
for (var i = 0; i < nums.length; i++) {
if (nums[i] > max) {
max = nums[i];
maxpos = i;
}
}
var root = new TreeNode(max);
root.left = constructMaximumBinaryTree(nums.slice(0, maxpos));
root.right = constructMaximumBinaryTree(nums.slice(maxpos + 1));
return root;
};
617 合并二叉树
给定两个二叉树,想象当你将它们中的一个覆盖到另一个上时,两个二叉树的一些节点便会重叠。
你需要将他们合并为一个新的二叉树。合并的规则是如果两个节点重叠,那么将他们的值相加作为节点合并后的新值,否则不为 NULL 的节点将直接作为新二叉树的节点。
示例 1:
输出:
合并后的树:
var mergeTrees = function(t1, t2) {
if(!t1&&!t2){
return null;
}
if(t1&&!t2){
return t1;
}
if(!t1&&t2){
return t2;
}
var tempVal=t1.val+t2.val;
var newNode=new TreeNode(tempVal);
newNode.left=mergeTrees(t1.left,t2.left);
newNode.right=mergeTrees(t1.right,t2.right);
return newNode;
};
55、跳跃游戏
给定一个非负整数数组,你最初位于数组的第一个位置。
数组中的每个元素代表你在该位置可以跳跃的最大长度。
判断你是否能够到达最后一个位置。
示例 1:
输入: [2,3,1,1,4]
输出: true
解释: 从位置 0 到 1 跳 1 步, 然后跳 3 步到达最后一个位置。
示例 2:
输入: [3,2,1,0,4]
输出: false
解释: 无论怎样,你总会到达索引为 3 的位置。但该位置的最大跳跃步数是 0 ,
所以你永远不可能到达最后一个位置。
思路,从后往前考虑,数组长度为 len,将len-2视为起点,判断其是否有希望到达终点len-1处,若有,仅需将len-2作为终点,再重复上面的思想即可,如果len-2处不可能有机会到len-1,也就是此时的目的地,那就继续考虑前一个元素 len-3处是否有希望跳转到目的地,若有,则将len-3作为下一阶段的目的地
超简短的代码:
var canJump = function(nums) {
var len=nums.length;
// 开始必须至少走一步
var step=1;
if(len===1){
return true;
}
for(var i=len-2;i>=0;i--){
// 当前位置可以跳到目的地,只需考虑是否能从之前的位置跳转到当前位置 将此时的i-1 视为循环起点,i视为终点,只需要1步即可达到,所以step归一
if(nums[i]>=step){
step=1;
}
// 当前位置跳转不到目的地,继续考虑i-1位置是否有可能,此时意味着需要跳的步数需要加 1
else{
step++;
}
}
return step==1;
};
7 整数反转
给出一个 32 位的有符号整数,你需要将这个整数中每位上的数字进行反转。
示例 1:
输入: 123
输出: 321
示例 2:
输入: -123
输出: -321
示例 3:
输入: 120
输出: 21
注意:
假设我们的环境只能存储得下 32 位的有符号整数,则其数值范围为 [−231, 231 − 1]。请根据这个假设,如果反转后整数溢出那么就返回 0。
var reverse = function(x) {
if(x==0){
return 0;
}
//数字变字符串
var num=x.toString();
var res=[];
for(var i=num.length-1;i>0;i--){
res.push(num[i])
}
if (x > 0){
res.push(num[0])
}
else{
res.unshift('-');
}
//数组变字符串
var ans="";
for(var i=0;i<res.length;i++){
ans+=res[i];
}
//字符串变数字
var sol=ans-'0';
//超出范围考虑
if((sol+Math.pow(2,31)<0)||(sol-Math.pow(2,31)>=0)){
return 0;
}
return sol;
};
162 寻找峰值
峰值元素是指其值大于左右相邻值的元素。
给定一个输入数组 nums,其中 nums[i] ≠ nums[i+1],找到峰值元素并返回其索引。
数组可能包含多个峰值,在这种情况下,返回任何一个峰值所在位置即可。
你可以假设 nums[-1] = nums[n] = -∞。
示例 1:
输入: nums = [1,2,3,1]
输出: 2
解释: 3 是峰值元素,
示例 2:
输入: nums = [1,2,1,3,5,6,4]
输出: 1 或 5
解释: 你的函数可以返回索引 1,其峰值元素为 2;
或者返回索引 5,
说明:
你的解法应该是 O(logN) 时间复杂度的。
这题注意是 中等难度的,做之前感觉有点不合理,以为应该是 easy,做之后,发现确实应该是 medium ,看博客基本都说二分,感觉都没说到重点?为什么能用二分?
首先注意复杂度要求,会想到二分,然后又想到数组是无序的,不适合用二分,但是除了二分也不可能满足该复杂度要求了。所以确定用二分。
然后,这题考的是数学。
两端是最低点,所以这题一定有解,而且题目要求任意解即可,因此可以用二分。
这题只要理解了这一点,就能做出来核心思想:取mid值,只要nums[mid-1] 大于nums[mid] ,则mid左边一定有峰值 反之,mid-1右边一定存在峰值,因为两端无穷小,明白不了的画张图理解一下
然后,看代码注释
var findPeakElement = function(nums) {
if(nums.length==1){
return 0;
}
var l=0;
var r=nums.length-1;
//这里需要注意一下 js的 / 号不是整除
var mid=Math.floor((l+r)/2);
while(l<=r){
//最后的结果必然是 l,r相邻,此时取两者之间大的一定是峰值
if(mid==l){
return nums[l]>nums[r]?l:r;
}
else if(nums[mid-1]>nums[mid]){
r=mid;
}
else{
l=mid;
}
mid=Math.floor((l+r)/2);
}
};
leetcode3 无重复字符的最长子串
示例 1:
输入: “abcabcbb”
输出: 3
解释: 因为无重复字符的最长子串是 “abc”,所以其长度为 3。
示例 2:
输入: “bbbbb”
输出: 1
解释: 因为无重复字符的最长子串是 “b”,所以其长度为 1。
示例 3:
输入: “pwwkew”
输出: 3
解释: 因为无重复字符的最长子串是 “wke”,所以其长度为 3。
请注意,你的答案必须是 子串 的长度,“pwke” 是一个子序列,不是子串。
注意。子串是连续的,子序列是不需要连续的
var lengthOfLongestSubstring = function(s) {
if(s==''){
return 0;
}
var len = s.length;
var max=1;
for(var i=0;i<len-max;i++){
var temp=[];
temp.push(s[i]);
var maxTemp=1;
for(var j=i+1;j<len;j++){
if(temp.includes(s[j])){
break;
}
else{
temp.push(s[j]);
maxTemp++;
}
}
if(maxTemp>max){
max=maxTemp;
}
}
return max;
};