面试题目总结——剑指Offer

二叉树

*1.重建二叉树

前序遍历数组:[根节点,左子树,右子树]

中序遍历数组:[左子树,根节点,右子树]

前序遍历数组的第一个元素就是树的根节点,就可以根据这个元素找到根节点在中序遍历数组中的下标(我们需要先存储下中序遍历数组中各个元素在中序遍历数组的下标),然后找出左右子树的节点数,继续递归建立节点。

/**
 * Definition for a binary tree node.
 * function TreeNode(val) {
 *     this.val = val;
 *     this.left = this.right = null;
 * }
 */
/**
 * @param {number[]} preorder
 * @param {number[]} inorder
 * @return {TreeNode}
 */

let index=new Map;
let buildNode=function(preorder,inorder,preLeft,preRight,inLeft,inRight){
    if(preLeft>preRight)return null;
    //当前根节点在前序遍历数组中的下标
    let preRoot=preLeft;
    //当前根节点在中序遍历数组中的下标
    let inRoot=index[preorder[preRoot]];
    let node=new TreeNode(preorder[preRoot]);
    //当前节点左子树的节点数
    let leftLen=inRoot-inLeft;
    node.right=buildNode(preorder,inorder,preLeft+leftLen+1,preRight,inRoot+1,inRight);
    node.left=buildNode(preorder,inorder,preLeft+1,preLeft+leftLen,inLeft,inRoot-1);
    return node;
}
var buildTree = function (preorder, inorder) {
    let n=inorder.length;
    //记录下各个节点在中序遍历数组中的下标
    for(let i=0;i<n;i++){
        index[inorder[i]]=i;
    }
    return buildNode(preorder,inorder,0,n-1,0,n-1);
};

*2.树的子结构

/**
 * Definition for a binary tree node.
 * function TreeNode(val) {
 *     this.val = val;
 *     this.left = this.right = null;
 * }
 */
/**
 * @param {TreeNode} A
 * @param {TreeNode} B
 * @return {boolean}
 */
let recur = function (A, B) {
    if (B == null) return true;
    if (A == null || A.val != B.val) return false;
    return recur(A.left, B.left) && recur(A.right, B.right);
}
var isSubStructure = function (A, B) {
    return (A!=null&&B!=null)&&(recur(A,B)||isSubStructure(A.left,B)||isSubStructure(A.right,B));
};

3.二叉树的镜像

/**
 * Definition for a binary tree node.
 * function TreeNode(val) {
 *     this.val = val;
 *     this.left = this.right = null;
 * }
 */
/**
 * @param {TreeNode} root
 * @return {TreeNode}
 */
var mirrorTree = function (root) {
    if(root==null)return null;
   let left = mirrorTree(root.right);
   let right = mirrorTree(root.left);
   root.right=right;
   root.left=left;
   return root;
};

*4.对称的二叉树

/**
 * Definition for a binary tree node.
 * function TreeNode(val) {
 *     this.val = val;
 *     this.left = this.right = null;
 * }
 */
/**
 * @param {TreeNode} root
 * @return {boolean}
 */
var isSymmetric = function (root) {
    return root==null?true:recur(root.left,root.right);
};
let recur =function(l,r){
    if(l==null&&r==null)return true;
    if(l==null||r==null||l.val!=r.val)return false;
    return recur(l.left,r.right)&&recur(r.left,l.right);
}

5.从上到下打印二叉树

层序遍历

/**
 * Definition for a binary tree node.
 * function TreeNode(val) {
 *     this.val = val;
 *     this.left = this.right = null;
 * }
 */
/**
 * @param {TreeNode} root
 * @return {number[]}
 */
let _queue=function() {
    this.data=new Array;
};
_queue.prototype.push=function(x){
    this.data.push(x);
};
_queue.prototype.pop=function(){
    this.data.shift();
};
_queue.prototype.front=function(){
    return this.data[0];
}
_queue.prototype.size=function(){
    return this.data.length;
}
var levelOrder = function(root) {
    let queue=new _queue();
    let ans=new Array();
    if(root!=null)
    queue.push(root);
    while(queue.size()!=0){
        let len=queue.size();
        for(let i=0;i<len;i++){
            let node=queue.front();
            ans.push(node.val);
            if(node.left!=null)queue.push(node.left);
            if(node.right!=null)queue.push(node.right);
            queue.pop()
        }
    }
    return ans;
};

6.从上到下打印二叉树||

/**
 * Definition for a binary tree node.
 * function TreeNode(val) {
 *     this.val = val;
 *     this.left = this.right = null;
 * }
 */
/**
 * @param {TreeNode} root
 * @return {number[]}
 */
let _queue=function() {
    this.data=new Array;
};
_queue.prototype.push=function(x){
    this.data.push(x);
};
_queue.prototype.pop=function(){
    this.data.shift();
};
_queue.prototype.front=function(){
    return this.data[0];
}
_queue.prototype.size=function(){
    return this.data.length;
}
var levelOrder = function(root) {
    let queue=new _queue();
    let ans=new Array();
    if(root!=null)
    queue.push(root);
    while(queue.size()!=0){
        let len=queue.size();
        ans.push(new Array());
        for(let i=0;i<len;i++){
            let node=queue.front();
            ans[ans.length-1].push(node.val);
            if(node.left!=null)queue.push(node.left);
            if(node.right!=null)queue.push(node.right);
            queue.pop()
        }
    }
    return ans;
};

7.从上到下打印二叉树|||

/**
 * Definition for a binary tree node.
 * function TreeNode(val) {
 *     this.val = val;
 *     this.left = this.right = null;
 * }
 */
/**
 * @param {TreeNode} root
 * @return {number[]}
 */
let _queue = function () {
    this.data = new Array;
};
_queue.prototype.push = function (x) {
    this.data.push(x);
};
_queue.prototype.pop = function () {
    this.data.shift();
};
_queue.prototype.front = function () {
    return this.data[0];
}
_queue.prototype.size = function () {
    return this.data.length;
}
var levelOrder = function (root) {
    let queue = new _queue();
    let ans = new Array();
    let dir = 1;//-1从右到左,1从左到右
    if (root != null)
        queue.push(root);
    while (queue.size() != 0) {
        let len = queue.size();
        ans.push(new Array());
        for (let i = 0; i < len; i++) {
            let node = queue.front();
            ans[ans.length - 1].push(node.val);
            if (node.left != null) queue.push(node.left);
            if (node.right != null) queue.push(node.right);
            queue.pop()
        }
        if(dir==-1){
            ans[ans.length-1]=ans[ans.length-1].reverse();
        }
        dir = (dir == -1) ? 1 : -1;
    }
    return ans;
};

*8.二叉搜索树的后序遍历序列

后序遍历数组:[左子树,右子树,根]

二叉搜索树的根大于左子树小于右子树,在节点区间内移动指针找到左子树和右子树的数组区间,并且指针先按左小于根移动,再按右大于根移动,如果最后可以移动到根节点那么这一层顺序是正确的,如果左右子树都是二叉搜索树则为二叉搜索树。

/**
 * @param {number[]} postorder
 * @return {boolean}
 */
var verifyPostorder = function(postorder) {
    return recur(postorder,0,postorder.length-1);
};
let recur=function(postorder,i,j){
    if(i>=j)return true;
    let p=i;
    while(postorder[p]<postorder[j])p++;
    let m=p;
    while(postorder[p]>postorder[j])p++;
    return p==j&&recur(postorder,i,m-1)&&recur(postorder,m,j-1);
} 

*9.二叉树中和为某一值的路径

/**
 * Definition for a binary tree node.
 * function TreeNode(val) {
 *     this.val = val;
 *     this.left = this.right = null;
 * }
 */
/**
 * @param {TreeNode} root
 * @param {number} sum
 * @return {number[][]}
 */


var pathSum = function (root, sum) {
    let ans=new Array();
    let temp=new Array();
    let NodeSum=function(root,sum){
        if(root==null)return;
        temp.push(root.val);
        sum=sum-root.val;
        if(sum==0&&root.left==null&&root.right==null){
            ans.push([...temp]);
        }
        NodeSum(root.left,sum);
        NodeSum(root.right,sum);
        temp.pop();
    }
    NodeSum(root,sum);
    return ans;
};

*10.二叉搜索树与双向链表

设立一个空的头节点

/**
 * // Definition for a Node.
 * function Node(val,left,right) {
 *    this.val = val;
 *    this.left = left;
 *    this.right = right;
 * };
 */
/**
 * @param {Node} root
 * @return {Node}
 */
var treeToDoublyList = function(root) {
    if(root==null)return null;
  let head=new Node(null),node=head;
  let dfs=function(root){
      root.left&&dfs(root.left);
      node.right=root;
      root.left=node
      node=root;
      root.right&&dfs(root.right);
  }
  dfs(root);
  node.right=head.right;
  head.right.left=node;  
  return head.right;
};

*11.二叉搜索树的第K大节点

简单解法

/**
 * Definition for a binary tree node.
 * function TreeNode(val) {
 *     this.val = val;
 *     this.left = this.right = null;
 * }
 */
/**
 * @param {TreeNode} root
 * @param {number} k
 * @return {number}
 */
let arr=new Array;
var kthLargest = function(root, k) {
  dfs(root,k);
  return arr[arr.length-k];
};
let dfs=function(root,k){
  if(root==null)return;
  dfs(root.left,k);
  arr.push(root.val);
  dfs(root.right,k);
}

反中序遍历解法,从右子树向根向左子树遍历

/**
 * @param {TreeNode} root
 * @param {number} k
 * @return {number}
 */
var kthLargest = function(root, k) {
    // 反中序遍历,记录数值第k个值返回
    let num = 0
    let result = null
    const dfs = function(node) {
        if (node === null) {
            return
        }
        dfs(node.right)
        num++
        if (num === k) {
            result = node.val
            return
        }
        dfs(node.left)
    }
    dfs(root)
    return result
};

*12.二叉树的深度

/**
 * Definition for a binary tree node.
 * function TreeNode(val) {
 *     this.val = val;
 *     this.left = this.right = null;
 * }
 */
/**
 * @param {TreeNode} root
 * @return {number}
 */
var maxDepth = function(root) {
  if(root==null)return 0;
  let ld=maxDepth(root.left);
  let rd=maxDepth(root.right);
  let depth=Math.max(ld,rd);
  return depth+1;
};

13.平衡二叉树

var isBalanced = function(root) {
  if(root==null)return true;
  let ld=dfs(root.left);
  let rd=dfs(root.right);
  if(Math.abs(ld-rd)>1)return false;
  return isBalanced(root.left)&&isBalanced(root.right);
};
var dfs = function(root) {
  if(root==null)return 0;  
  return Math.max(dfs(root.left),dfs(root.right))+1;
};

*14.二叉搜索树的最近公共祖先

如果两个节点值都小于当前节点,说明祖先节点在当前节点的左边,如果两个节点值都大于当前节点,说明祖先节点在当前节点的右边,否则当前节点在两节点值的正中间说明当前节点就是两节点的最近祖先节点。

/**
 * Definition for a binary tree node.
 * function TreeNode(val) {
 *     this.val = val;
 *     this.left = this.right = null;
 * }
 */
/**
 * @param {TreeNode} root
 * @param {TreeNode} p
 * @param {TreeNode} q
 * @return {TreeNode}
 */
var lowestCommonAncestor = function(root, p, q) {
  let cur=root;
  while(cur!=null){
    if(p.val<cur.val&&q.val<cur.val){
      cur=cur.left;
    }else if(p.val>cur.val&&q.val>cur.val){
      cur=cur.right;
    }else{
      break;
    }
  }  
  return cur;
};

*15.二叉树的最近公共祖先

最近公共祖先的几种情况:

1.左右子树中分别包含两个节点

2.左子树与根节点包含两个节点

3.右子树与根节点包含两个节点

/**
 * Definition for a binary tree node.
 * function TreeNode(val) {
 *     this.val = val;
 *     this.left = this.right = null;
 * }
 */
/**
 * @param {TreeNode} root
 * @param {TreeNode} p
 * @param {TreeNode} q
 * @return {TreeNode}
 */
   let ans;
   var lowestCommonAncestor = function(root, p, q) {
     dfs(root,p,q);
     return ans;
  };
  let dfs=function(root,p,q){
    if(root==null)return false;
    let lson=dfs(root.left,p,q);
    let rson=dfs(root.right,p,q);
    if((lson&&rson)||((root.val==p.val||root.val==q.val)&&(lson||rson))){
      ans=root;
    }
    return lson||rson||root.val==q.val||root.val==p.val;
  }

链表、栈、队列

1.从头到尾打印链表

var reversePrint = function (head) {
    let arr=new Array;
    for(let p=head;p!=null;p=p.next){
        arr.push(p.val);
    }
    return arr.reverse();
};

2.用两个栈实现队列

 

3.删除链表的节点

/**
 * Definition for singly-linked list.
 * function ListNode(val) {
 *     this.val = val;
 *     this.next = null;
 * }
 */
/**
 * @param {ListNode} head
 * @param {number} val
 * @return {ListNode}
 */

var deleteNode = function (head, val) {
    if(head.val==val){
        return head.next;
    }
    for(let lastp=head,p=head.next;p!=null;lastp=lastp.next,p=p.next){
        if(p.val==val){
            lastp.next=p.next;
        }
    }
    return head;
};

4.链表中倒数第K个节点

/**
 * Definition for singly-linked list.
 * function ListNode(val) {
 *     this.val = val;
 *     this.next = null;
 * }
 */
/**
 * @param {ListNode} head
 * @param {number} k
 * @return {ListNode}
 */
var getKthFromEnd = function(head, k) {
    let len=0;
    for(let p=head;p!=null;p=p.next){
        len++;
    }
    let ok=len-k+1;
    len=0;
    for(let p=head;p!=null;p=p.next){
        len++;
        if(len==ok)return p;
    }
};

*5.反转链表

/**
 * Definition for singly-linked list.
 * function ListNode(val) {
 *     this.val = val;
 *     this.next = null;
 * }
 */
/**
 * @param {ListNode} head
 * @return {ListNode}
 */
var reverseList = function(head) {
    let pre=null,cur=head;
    while(cur!=null){
        let next=cur.next;
        cur.next=pre;
        pre=cur;
        cur=next;
    }
    return pre;
};

6.合并两个排序的链表

/**
 * Definition for singly-linked list.
 * function ListNode(val) {
 *     this.val = val;
 *     this.next = null;
 * }
 */
/**
 * @param {ListNode} l1
 * @param {ListNode} l2
 * @return {ListNode}
 */
var mergeTwoLists = function (l1, l2) {
    let head=new ListNode;
    let p=head;
    while(l1!=null&&l2!=null){
        if(l1.val<l2.val){
            p.next=l1;
            l1=l1.next
        }else{
            p.next=l2;
            l2=l2.next;
        }
        p=p.next;
    }
    if(l1!=null){
        p.next=l1;
    }
    if(l2!=null){
        p.next=l2;
    }
    return head.next;
};

*7.包含min函数的栈

栈中每一位存放两个值,一个是数值,另一个是最小值,每次入栈的时候比对入栈数字和栈顶最小值比对更新栈顶最小值

/**
 * initialize your data structure here.
 */
var MinStack = function() {
    this.stack=new Array;
};

/** 
 * @param {number} x
 * @return {void}
 */
MinStack.prototype.push = function(x) {
    if(this.stack.length==0){
        this.stack.push({val:x,min:x});
    }else{
        let min;
        if(x<this.stack[this.stack.length-1].min){
            min=x;
        }else{
            min=this.stack[this.stack.length-1].min;
        }
        this.stack.push({val:x,min:min});
    }
};

/**
 * @return {void}
 */
MinStack.prototype.pop = function() {
    this.stack.pop();
};

/**
 * @return {number}
 */
MinStack.prototype.top = function() {
    return this.stack[this.stack.length-1].val;
};

/**
 * @return {number}
 */
MinStack.prototype.min = function() {
    return this.stack[this.stack.length-1].min;
};

/**
 * Your MinStack object will be instantiated and called as such:
 * var obj = new MinStack()
 * obj.push(x)
 * obj.pop()
 * var param_3 = obj.top()
 * var param_4 = obj.min()
 */

*8.栈的压入、弹出序列

使用一个辅助栈按照入栈顺序入栈,入栈之后能弹出就弹出,到最后如果辅助栈为空则返回真

/**
 * @param {number[]} pushed
 * @param {number[]} popped
 * @return {boolean}
 */
var validateStackSequences = function(pushed, popped) {
    let stack=new Array;
    let idx=0;
    for(let i=0;i<pushed.length;i++){
        stack.push(pushed[i]);
        while(stack.length!=0&&popped[idx]==stack[stack.length-1]){
            idx++;
            stack.pop();
        }
    }
    return stack.length==0
};

*9.复杂链表的复制

先复制链表

1,2,3,4,5,6=>1,1,2,2,3,3,4,4,5,5,6,6

再完成随机指针的复制,注意新复制的节点要指向新复制的节点

最后分离两个重合的链表

/**
 * // Definition for a Node.
 * function Node(val, next, random) {
 *    this.val = val;
 *    this.next = next;
 *    this.random = random;
 * };
 */

/**
 * @param {Node} head
 * @return {Node}
 */
var copyRandomList = function (head) {
    if (head == null) return head;
    /**复制链表 */
    let cur = head;
    while (cur != null) {
        let copyNode = new Node(cur.val);
        copyNode.next=cur.next;
        cur.next = copyNode;
        cur = cur.next.next;
    }
    /**完成随机指针的复制*/
    cur = head;
    while (cur != null) {
        if (cur.random != null) {
            cur.next.random = cur.random.next;
        }
        cur = cur.next.next;
    }
    /**分离两个链表 */
    let copyHead = head.next;
    cur = head;
    let copyCur = head.next;
    while (cur != null) {
        cur.next = cur.next.next;
        cur = cur.next;
        if (copyCur.next != null) {
            copyCur.next = copyCur.next.next;
            copyCur = copyCur.next;
        }
    }
    
    return copyHead;
};

10.两个链表的第一个公共节点

可以使得2个链表加上彼此,这样就能保证链表长度一样了,即同时遍历,能找到相交点。

/**
 * Definition for singly-linked list.
 * function ListNode(val) {
 *     this.val = val;
 *     this.next = null;
 * }
 */

/**
 * @param {ListNode} headA
 * @param {ListNode} headB
 * @return {ListNode}
 */
var getIntersectionNode = function(headA, headB) {
    var h1 = headA;
    var h2 = headB;

    while(h1 !== h2){ // 如果相交、或者没有相交
        h1 = h1 === null ? headB: h1.next; // h1结束 接入对方
        h2 = h2 === null ? headA: h2.next;  // h2结束 接入对方
    }

    return h1;
};

*11.队列的最大值

使用一个模拟队列,另一个模拟双向队列的数组,一个是正常的队列,另一个是存放最大值的双向队列。

在入队时,如果比双向队列队尾的更大,那么就一直从队尾弹出,再从队尾入队,出队时,如果从正常队列出队的值就是当前最大值,那么就从双向队列的队首出队。

var MaxQueue = function() {
    this.queue1 = [];
    this.queue2 = [];
};

/**
 * @return {number}
 */
MaxQueue.prototype.max_value = function() {
    if (this.queue2.length) {
        return this.queue2[0];
    }
    return -1;
};

/** 
 * @param {number} value
 * @return {void}
 */
MaxQueue.prototype.push_back = function(value) {
    this.queue1.push(value);
    while (this.queue2.length && this.queue2[this.queue2.length - 1] < value) {
        this.queue2.pop();
    }
    this.queue2.push(value);
};

/**
 * @return {number}
 */
MaxQueue.prototype.pop_front = function() {
    if (!this.queue1.length) {
        return -1;
    }
    const value = this.queue1.shift();
    if (value === this.queue2[0]) {
        this.queue2.shift();
    }
    return value;
};

/**
 * Your MaxQueue object will be instantiated and called as such:
 * var obj = new MaxQueue()
 * var param_1 = obj.max_value()
 * obj.push_back(value)
 * var param_3 = obj.pop_front()
 */

其他题型

1.数组中重复的数字

/**
 * @param {number[]} nums
 * @return {number}
 */
var findRepeatNumber = function(nums) {
    let s=new Set();
    for(let i=0;i<nums.length;i++){
        if(!s.has(nums[i])){
            s.add(nums[i]);
        }else{
            return nums[i];
        }
    }
};

*2.二维数组中的查找

一个指针从小到大,另一个指针从大到小的移动

/**
 * @param {number[][]} matrix
 * @param {number} target
 * @return {boolean}
 */
var findNumberIn2DArray = function(matrix, target) {
    let row=matrix.length;
    if(row==0)return false;
    let col=matrix[0].length;
    let r=row-1,c=0;
    while(r>=0&&c<col){
        if(matrix[r][c]==target){
            return true;
        }
        else if(matrix[r][c]<target){
            c++;
        }else{
            r--;
        }
    }
    return false;
};

*3.旋转数组的最小数字

增序数组被旋转之后以最小值为边界两边都是增序的数组

/**
 * @param {number[]} numbers
 * @return {number}
 */
var minArray = function(numbers) {
    for(let i=1;i<numbers.length;i++){
        if(numbers[i]<numbers[i-1]){
            return numbers[i];
        }
    }
    return numbers[0];
};

4.数组中出现次数超过一半的数字

/**
 * @param {number[]} nums
 * @return {number}
 */
var majorityElement = function(nums) {
  if(nums.length==1||nums.length==2)return nums[0];
  nums.sort();
  let i=0;
  while(i<nums.length-1){
    let len=1;
    while(i<nums.length-1&&nums[i]==nums[i+1]){
      len++;
      i++;
    }
    if(len>(nums.length/2))
    return nums[i];
    i++;
  }
};

5.替换空格

/**
 * @param {string} s
 * @return {string}
 */
var replaceSpace = function(s) {
    let newS=[];
    for(let i=0;i<s.length;i++){
        if(s[i]==" "){
            newS.push("%20");
        }else{
            newS.push(s[i]);
        }
    }
    return newS.join("");
};

6.斐波那契数列

/**
 * @param {number} n
 * @return {number}
 */
var fib = function(n) {
    let a=0,b=1;
    for(let i=1;i<=n;i++){
        let k=b;
        b=(a+b)%(1e9+7);
        a=k;
    }
    return a%(1e9+7);
}

7.青蛙跳台阶问题

/**
 * @param {number} n
 * @return {number}
 */
var numWays = function(n) {
    let arr=new Array(n+1);
    arr[0]=1;
    arr[1]=1;
    for(let i=2;i<=n;i++){
        arr[i]=(arr[i-1]+arr[i-2])%(1e9+7);
    }
    return arr[n];
};

*8.矩阵中的路径

先遍历矩阵,枚举每一个格子为起点直到找到目标字符串

在以格子为起点开始搜索的时候如果遍历的格子超出或者不是目标字符的话返回假,然后把遍历成功的格子改为空字符串,然后递归的遍历四周的格子,有一个格子对当前目标字符匹配成功就返回真,然后回溯

当目标字符串的指针移动到最后一个的时候返回真

/**
 * @param {character[][]} board
 * @param {string} word
 * @return {boolean}
 */
var exist = function(board, word) {
    if(board==undefined)return false;
    for(let i=0;i<board.length;i++){
        for(let j=0;j<board[0].length;j++){
            if(dfs(board,word,i,j,0))
            return true;
        }
    }
    return false;
};
let dfs=function(board,word,i,j,k){
    if(i<0||j<0||i>=board.length||j>=board[0].length||board[i][j]!=word[k])return false;
    if(k==word.length-1)return true;
    board[i][j]='\0';
    let res=dfs(board,word,i+1,j,k+1)||
            dfs(board,word,i-1,j,k+1)||
            dfs(board,word,i,j+1,k+1)||
            dfs(board,word,i,j-1,k+1);
    board[i][j]=word[k];
    return res;
};

*9.机器人的运动范围

/**
 * @param {number} m
 * @param {number} n
 * @param {number} k
 * @return {number}
 */
let ifInto=function(m,n,k){
    let ans=0;
    for(let i=m;i!=0;i=parseInt(i/10)){
        ans=ans+i%10;
    }
    for(let i=n;i!=0;i=parseInt(i/10)){
        ans=ans+i%10;
    }
    return ans<=k;
};
let go=function(curm,curn,m,n,k,arr){
    let res=0;
    if(curn<0||curm<0||curn>=n||curm>=m||!ifInto(curm,curn,k)||arr[curm][curn]==1){
        return 0;
    }
    arr[curm][curn]=1;
    res++;
    res=res+go(curm,curn+1,m,n,k,arr);
    res=res+go(curm+1,curn,m,n,k,arr);
    return res;
}
var movingCount = function(m, n, k) {

    let arr=new Array(m);
    for(let i=0;i<m;i++){
        arr[i]=new Array(n);
        for(let j=0;j<n;j++){
            arr[i][j]=0;
        }
    }
    return go(0,0,m,n,k,arr);
};

*10.剪绳子|

被切分完之后的绳子每小段长度不超过4

let cuttingRope=function(n) {
    //以下这样返回的意义是由于绳子必须被切断
    if(n==2)//长度为2的绳子
      return 1;
    if(n==3)//长度为3的绳子
        return 2;
    if(n==4)
       return 4;
    vector<int>dp(n+1,-1);//长度为i的绳子最大的乘积
    dp[1]=1;
    dp[2]=2;
    dp[3]=3;
    dp[4]=4;
    for(let i=5;i<=n;i++)
    {
        for(let j=1;j<4;j++)//这里只循环到4的原因是切分为5的时候,dp[4]=2*2,dp[5]=2*3,就相当于之前讨论过的dp[2]*dp[i-2]了
        {
            dp[i]=max(dp[i],dp[j]*dp[i-j]);
        }
    }
    return dp[n];
}

*11.剪绳子||

这一题与上面的不同点在于取余之后就不能比较大小了

/**
 * @param {number} n
 * @return {number}
 */
var cuttingRope = function(n) {
    const max=(1e9+7);
    if(n<4)return n-1;
    if(n==4)return 4;
    let res=1;
    while(n>=5){
        res=res%max*3;
        n=n-3;
    }
    return res*n%max;
};

*12.二进制中1的个数

%2的操作相当于取出二进制最低位的数字,/2的操作相当于把二进制所有数字整体享有移动一位

/**
 * @param {number} n - a positive integer
 * @return {number}
 */
var hammingWeight = function(n) {
    if(n==0)return 0;
    let res=1;
    while(n!=1){
        let left=n%2;
        if(left==1)
        res++;
        n=parseInt(n/2);
    }
    console.log(res);
    return res;
};

*13.数值的整数次方

比如7次方可以由3次方相乘再乘以一个1次方得到

/**
 * @param {number} x
 * @param {number} n
 * @return {number}
 */
var myPow = function(x, n) {
    let isFu=n<0;
    let res = PowBase(x,Math.abs(n));
    return isFu?1/res:res;
};
let PowBase=function(base,cpt) {
    if(cpt===0){
        return 1; 
    }
    if(cpt===1){
        return base;
    }
    let result=PowBase(base,Math.floor(cpt/2));
    return cpt%2==0?result*result:result*result*base;
}

14.打印从1到最大的n位数

/**
 * @param {number} n
 * @return {number[]}
 */
var printNumbers = function(n) {
    let max=1;
    let num=0;
    for(let i=1;i<=n;i++){
        num=max*9+num;
        max=max*10;
    }
    //console.log(num);
    let arr=new Array(num);
    for(let j=1;j<=num;j++){
        arr[j-1]=j
    }
    return arr;
};

**15.正则表达式匹配

这道题目我觉得还是很重要的,在评论区看见有人说这是微软的二面题,需要使用动态规划的方法做

/**
 * @param {string} s
 * @param {string} p
 * @return {boolean}
 */
/** 
 * .表示任意字符
 * *表示它前面的字符可以出现任意次
 * s是目标串,p是匹配串
*/
let Judge=function (si,pi,s,p) {
    if(si==0){
        return false;
    }
    if(p[pi-1]=='.'){
        return true;
    }
    return s[si-1]==p[pi-1];
}
var isMatch = function(s, p) {
    let sl=s.length,pl=p.length;
    let dp=new Array(sl+1);
    for(let i=0;i<=sl;i++){
        dp[i]=new Array(pl+1).fill(false);
    }

    dp[0][0]=true;
    for(let i=0;i<=sl;i++){
        for(let j=1;j<=pl;j++){
                if(p[j-1]=="*"){
                    dp[i][j]=dp[i][j-2];
                    if(Judge(i,j-1,s,p)){
                        dp[i][j]|=dp[i-1][j];
                    }
                }
                else if(Judge(i,j,s,p)){
                    dp[i][j]=dp[i-1][j-1];
                }
        }
    }
    return dp[sl][pl];
};

16.调整数组顺序使奇数位于偶数前面

4

/**
 * @param {number[]} nums
 * @return {number[]}
 */
var exchange = function(nums) {
    let idx=0;
    for(let i=0;i<nums.length;i++){
        if(nums[i]%2!=0){
            let p=nums[idx];
            nums[idx]=nums[i];
            nums[i]=p;
            idx++;
        }
    }
    return nums; 
};

17.顺时针打印矩阵


class Solution
{
public:
    vector<int> spiralOrder(vector<vector<int>>& matrix)
    {

        int row=matrix.size();
        if(row==0)
        return {};
        int col=matrix[0].size();
        if(col==0)
        return {};
        vector<int>ans(row*col);
        vector<vector<int>>mark(row,vector<int>(col,0));
        vector<int>x={0,1,0,-1};
        vector<int>y={1,0,-1,0};
        int idx=0,i;
        int a=0,b=0;
        for(i=0;i<row*col;i++)
        {
            ans[i]=matrix[a][b];
            mark[a][b]=1;
            if(0<=a+x[idx]&&a+x[idx]<row&&0<=b+y[idx]&&b+y[idx]<col&&mark[a+x[idx]][b+y[idx]]==0)
            {
                a=a+x[idx];
                b=b+y[idx];
            }
            else
            {
                idx=(idx+1)%4;
                a=a+x[idx];
                b=b+y[idx];
            }
        }
        return ans;
    }
};

*18.字符串的排列

如果使用js来写题目,需要注意字符串变量是不能改变的,必须先转换为数组来做修改,再转回去。

指针指到字符串数组最后一个的时候就放入一个排列,set集合的目的是防止有相同的字符会造成重复

从指针的位置开始依次向后互相交换(如果不重复),然后递归的把指针指向下一个位置,然后回溯的换回来

/**
 * @param {string} s
 * @return {string[]}
 */


var permutation = function(s) {
    if(s.length==0)return [];
    if(s.length==1)return[s];
    const list=s.split('');
    let res=[];
    
    let dfs=function(list,begin){
    if(begin==list.length){
        res.push(list.join(''));
    }else{
        let set=new Set;
        for(let i=begin;i<list.length;i++){
            if(set.has(list[i])){continue;}
            set.add(list[i]);

            let temp=list[begin];
            list[begin]=list[i];
            list[i]=temp;
            
            dfs(list,begin+1);
            temp=list[i];
            list[i]=list[begin];
            list[begin]=temp;
        }
    }
}
    dfs(list,0);
    return res;
};

*19.连续子数组的最大和

前缀和,并维护一个当前元素之前最小的一个值

/**
 * @param {number[]} nums
 * @return {number}
 */
var maxSubArray = function(nums) {
    let frontAnd=new Array(nums.length);
    frontAnd[0]=nums[0];
    let min=nums[0]<0?nums[0]:0;
    let res=nums[0];
    for(let i=1;i<nums.length;i++){
        frontAnd[i]=frontAnd[i-1]+nums[i];
        res=Math.max(frontAnd[i]-min,res);
        if(min>frontAnd[i])min=frontAnd[i];
    }
    return res;

};

 

20.礼物的最大价值

动态规划

/**
 * @param {number[][]} grid
 * @return {number}
 */
var maxValue = function(grid) {
    let row=grid.length;
    let col=grid[0].length;
    let dp=new Array(row);
    for(let i=0;i<row;i++){
        dp[i]=new Array(col);
    }
    dp[0][0]=grid[0][0];
    for(let i=1;i<row;i++){
        dp[i][0]=dp[i-1][0]+grid[i][0];
    }
    for(let i=1;i<col;i++){
        dp[0][i]=dp[0][i-1]+grid[0][i];
    }
    for(let i=1;i<row;i++){
        for(let j=1;j<col;j++){
            dp[i][j]=Math.max(dp[i-1][j]+grid[i][j],dp[i][j-1]+grid[i][j]);
        }
    }
    return dp[row-1][col-1];
};

*21.最长不含重复字符的子字符串

滑动窗口的做法,最开始两个指针都在开头,如果后指针没有发生重复,那么就继续向右移动后指针,否则向右移动左指针,统计两指针间距最大的间距

/**
 * @param {string} s
 * @return {number}
 */
var lengthOfLongestSubstring = function(s) {
    let map={};
    let ans=0;
    let i=0,j=0;
    while(i<s.length&&j<s.length){
        if(!map[s[j]]){
            ans=Math.max(j-i+1,ans);
            map[s[j]]=true;
            j++;
        }else{
            map[s[i]]=false;
            i++;
        }
    }
    return ans;
};

*22.丑数

三指针解法:

因为丑数只包含质因数 2, 3, 5,所以对于下个丑数来说,一定是前面某个丑数乘 3、乘 4 或者乘 5 所得。

准备三个指针 ptr2、ptr3、ptr5,它们指向的数只能乘 2、3 和 5。在循环过程中,每次选取 2 * res[ptr2]、3 * res[ptr3] 和 5 * res[ptr5]这三个数中结果最小的数,并且将对应的指针向前移动。有效循环是 n 次,当循环结束后,res 数组中就按从小到大的顺序保存了丑数。

/**
 * @param {number} n
 * @return {number}
 */
var nthUglyNumber = function(n) {
   let dp=new Array(n),a=0,b=0,c=0;
   dp[0]=1;
   for(let i=1;i<n;i++){
       let n2=dp[a]*2,n3=dp[b]*3,n5=dp[c]*5;
       dp[i]=Math.min(n2,n3,n5);
       if(dp[i]==n2)a++;
       if(dp[i]==n3)b++;
       if(dp[i]==n5)c++;
   } 
   return dp[n-1];
};

*23.第一个只出现一次的字符

妙用indexOf和lastindexOf数组方法

/**
 * @param {string} s
 * @return {character}
 */
var firstUniqChar = function(s) {
    for(let x of s){
        if(s.indexOf(x) === s.lastIndexOf(x)) return x
    }
    return ' '
};

*24.构建乘积数组

由于每一个元素的乘积是由两部分组成的。一部分是该元素前面的乘积,另一部分是该元素后面的乘积,使用两次遍历。

/**
 * @param {number[]} a
 * @return {number[]}
 */
var constructArr = function(a) {
  if(a.length==0)return[]
  let b=new Array(a.length);
  b[0]=1;
  let t=1;
  for(let i=1;i<a.length;i++){
    b[i]=b[i-1]*a[i-1]
  }
  for(let i=a.length-2;i>=0;i--){
    t=t*a[i+1];
    b[i]=b[i]*t;
  }
  return b;
};

*25.不用加减乘除做加法

n=a^b,n是非进位和

c=a&b<<1,c是进位和

s=c+n,s是a+b和值

/**
 * @param {number} a
 * @param {number} b
 * @return {number}
 */
var add = function(a, b) {
  if(b==0){
    return a;
  }
  return add(a^b,(a&b)<<1);
};

26.求1+2+....+n


let sumNums=function(n) {
    return n == 0 ? 0 : n + sumNums(n - 1);
}

27.股票的最大利润

/**
 * @param {number[]} prices
 * @return {number}
 */
var maxProfit = function(prices) {
  let min=prices[0];
  let ans=0;
  for(let i=1;i<prices.length;i++){
    if(ans<prices[i]-min)ans=prices[i]-min;
    if(prices[i]<min)min=prices[i];
  }
  return ans;
};

*28.扑克牌中的顺子

如果有重复的,或者最大值比最小值大的超过5,说明不是顺子。

/**
 * @param {number[]} nums
 * @return {boolean}
 */
var isStraight = function(nums) {
  let min=13,max=0;
  let s=new Set;
  for(let i=0;i<nums.length;i++)
  {
    if(nums[i]==0)continue;
    if(s.has(nums[i])){
       return false;}
    max=Math.max(max,nums[i]);
    min=Math.min(min,nums[i]);
    s.add(nums[i]);
  }
  return  max-min<5;
};

*29.滑动窗口的最大值

也是双向队列的解法

/**
 * @param {number[]} nums
 * @param {number} k
 * @return {number[]}
 */
var maxSlidingWindow = function(nums, k) {
    if(nums.length==0)return[];
    let deque=new Array;
    let res=new Array;
    for(let i=0;i<k;i++){
        while(deque.length!=0&&nums[i]>nums[deque[deque.length-1]]){
            deque.pop();
        }
        //这里存放下标是为了下面窗口移动的时候如果超出窗口就从队首弹出队列
        deque.push(i);
    }
    res.push(nums[deque[0]]);
    for(let i=k;i<nums.length;i++){
        while(deque.length!=0&&nums[i]>nums[deque[deque.length-1]]){
            deque.pop();
        }
        deque.push(i);
        while(deque[0]<=i-k){
            deque.shift();
        }
        res.push(nums[deque[0]]);
    }
    return res;
};

*30.左旋转字符串

原地AC:

class Solution {
public:
    string reverseLeftWords(string s, int n) {
        if (n >= s.length() || s.length() < 2 || n == 0) {
            return s;
        }

        reverse(s, n, s.length() - 1); // 翻转 s2 部分
        reverse(s, 0, s.length() - 1); // 翻转整个字符串 s
        reverse(s, s.length() - n, s.length() - 1); // 翻转 s1 部分

        return s;
    }

    void reverse(string& s, int start, int end) {
        if (end >= s.length() || end - start < 1) {
            return;
        }

        while (start < end) {
            char temp = s[start];
            s[start] = s[end];
            s[end] = temp;
            start++; end--;
        }
    }
};

31.翻转单词顺序

使用栈存放单词,然后按顺序添加到返回的字符串中

/**
 * @param {string} s
 * @return {string}
 */
var reverseWords = function(s) {
  let stack=new Array;
  let res='';
  let word='';
  for(let i=0;i<s.length;){
    word="";
    while(s[i]==' '&&i<s.length){
      i++;
      word=" ";
    }
    if(word!=""){
      stack.push(" ");
    }

    word="";
    while(s[i]!=' '&&i<s.length){
      word+=s[i];
      i++
    }
    if(word!=''){
      stack.push(word);
    }
  }
  console.log(stack)
  for(let i=stack.length-1;i>=0;i--){
    if(stack[i]==' '&&(i==stack.length-1||i==0))continue;
    res+=stack[i];
  }
  return res;
};

*32.和为S的连续正数序列

双指针解法,前指针开始指向1,后指针开始指向2,由于是连续的,所以我们可以根据前后指针依据高斯公式计算和。

如果和小于目标值,后指针右移,如果和大于目标值前指针右移,否则把两指针间所有值放入返回数组。

/**
 * @param {number} target
 * @return {number[][]}
 */
var findContinuousSequence = function(target) {
  let res=new Array;
  for(let l=1,r=2;l<r;){
    let sum=(l+r)*(r-l+1)/2;
    if(sum<target)r++;
    else if(sum>target)l++;
    else {
      let line=[];
      for(let i=l;i<=r;i++){
        line.push(i);
      }
      res.push(line)
      //console.log(line);
      l++;
    }
  }
  return res;
};

33.和为S的两个数字

双指针解

/**
 * @param {number[]} nums
 * @param {number} target
 * @return {number[]}
 */
var twoSum = function(nums, target) {
  let ans=new Array(2);
  let l=0,r=nums.length-1;
  console.log(l+"  "+r);
  while(l<r){
    console.log(l+"+"+r);
    if(nums[l]+nums[r]<target){
      l++;
    }else if(nums[l]+nums[r]>target){
      r--;
    }else{
      ans[0]=nums[l];
      ans[1]=nums[r];
      break;
    }
  }
  return ans;
};

**34.数组中数字出现的次数||

由于每一个整数由32位二进制组成,如果数组中的数字出现了三次,那么二进制的对应位之和必是3的倍数,而那个只出现一次的数字对应的二进制位之和必不是3的倍数。

/**
 * @param {number[]} nums
 * @return {number}
 */
var singleNumber = function(nums) {
  let res=0;
  for(let i=0;i<32;i++){
    let count=0;
    for(let j=0;j<nums.length;j++){
      if(nums[j]>>i&1==1){
        count++;
      }
    }
    if(count%3!=0){
      res=res+(1<<i);
    }
  }
  return res;
}

35.0~n-1中缺失的数字

/**
 * @param {number[]} nums
 * @return {number}
 */
var missingNumber = function(nums) {
  let i=0,j=nums.length-1;
  while(i<=j){
    let m=Math.floor((i+j)/2);
    if(nums[m]==m)i=m+1;
    else j=m-1;
  }
  return i;
};

36.在排序数组中查找数字

/**
 * @param {number[]} nums
 * @param {number} target
 * @return {number}
 */
var search = function(nums, target) {
  let ans=0;
  for(let i=0;i<nums.length;i++){
    if(nums[i]==target){
      ans++;
    }
  }
  return ans;
};

 

比较难的题目

1.把字符串转换成整数

2.n个骰子的点数

3.表示数值的字符串

4.序列化二叉树

5.数据流的中位数

6.1~n整数中1出现的次数

7.数字序列中某一位的数字

8.把数组排成最小的数

9.数组中的逆序对

10.数组中数字出现的次数

11.最小的k个数

12.把数字翻译成字符串

13.圆圈中最后剩下的数字

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值