leedcode编程练习总结

一、 Leedcode
链表:
*剑指 Offer 06. 从尾到头打印链表

输入一个链表的头节点,从尾到头反过来返回每个节点的值(用数组返回)。

示例 1:
输入:head = [1,3,2]
输出:[2,3,1]
/**

  • Definition for singly-linked list.
  • public class ListNode {
  • int val;
    
  • ListNode next;
    
  • ListNode(int x) { val = x; }
    
  • }
    */
    class Solution {
    public int[] reversePrint(ListNode head) {
    ArrayList list = new ArrayList();
    while (head != null) {
    list.add(head.val);
    head = head.next;
    }
    int size = list.size();
    int[] array = new int[size];
    for (int i = 0; i < size; i++) {
    array[i] = list.get(size - i -1);
    }
    return array;
    }
    }
  1. 删除链表的倒数第 N 个结点(找到倒数第N个节点的前一个结点)
    给你一个链表,删除链表的倒数第 n 个结点,并且返回链表的头结点。

进阶:你能尝试使用一趟扫描实现吗?

示例 1:

输入:head = [1,2,3,4,5], n = 2
输出:[1,2,3,5]
示例 2:

输入:head = [1], n = 1
输出:[]
示例 3:

输入:head = [1,2], n = 1
输出:[1]
/**

  • Definition for singly-linked list.
  • public class ListNode {
  • int val;
    
  • ListNode next;
    
  • ListNode() {}
    
  • ListNode(int val) { this.val = val; }
    
  • ListNode(int val, ListNode next) { this.val = val; this.next = next; }
    
  • }
    */
    class Solution {
    public ListNode removeNthFromEnd(ListNode head, int n) {
    if(head.next==null){
    return null;
    }
    ListNode dump=new ListNode(0,head);
    ListNode p=dump;
    ListNode q=dump;
    for(int i=0;i<n+1;i++){
    q=q.next;
    }
    while(q!=null){
    p=p.next;
    q=q.next;
    }
    p.next=p.next.next;
    return dump.next;
    }
    }

*剑指 Offer 18. 删除链表的节点
给定单向链表的头指针和一个要删除的节点的值,定义一个函数删除该节点。

返回删除后的链表的头节点。

注意:此题对比原题有改动

示例 1:

输入: head = [4,5,1,9], val = 5
输出: [4,1,9]
解释: 给定你链表中值为 5 的第二个节点,那么在调用了你的函数之后,该链表应变为 4 -> 1 -> 9.
示例 2:

输入: head = [4,5,1,9], val = 1
输出: [4,5,9]
解释: 给定你链表中值为 1 的第三个节点,那么在调用了你的函数之后,该链表应变为 4 -> 5 -> 9.
/**

  • Definition for singly-linked list.
  • public class ListNode {
  • int val;
    
  • ListNode next;
    
  • ListNode(int x) { val = x; }
    
  • }
    */
    class Solution {
    public ListNode deleteNode(ListNode head, int val) {
    if(head.val == val) return head.next;
    ListNode pre = head, cur = head.next;
    while(cur != null && cur.val != val) {
    pre = cur;
    cur = cur.next;
    }
    if(cur != null) pre.next = cur.next;
    return head;
    }
    }
    *剑指 Offer 22. 链表中倒数第k个节点(难度:简单)
    输入一个链表,输出该链表中倒数第k个节点。为了符合大多数人的习惯,本题从1开始计数,即链表的尾节点是倒数第1个节点。

例如,一个链表有 6 个节点,从头节点开始,它们的值依次是 1、2、3、4、5、6。这个链表的倒数第 3 个节点是值为 4 的节点。
示例:
给定一个链表: 1->2->3->4->5, 和 k = 2.
返回链表 4->5.
代码:
/**

  • Definition for singly-linked list.
  • public class ListNode {
  • int val;
    
  • ListNode next;
    
  • ListNode(int x) { val = x; }
    
  • }
    */
    class Solution {
    public ListNode getKthFromEnd(ListNode head, int k) {
    ListNode pre=head;
    ListNode p=new ListNode(); //存储倒数节点链表
    int count=0,i=0;
    while(pre!=null){
    count++;
    pre=pre.next; 统计节点个数count
    }
    while(head!=null){
    if(countk){
    p=head;
    return p; //如果节点个数等于k,直接返回p
    }
    i++;
    head=head.next; //head指针后移
    if(i
    count-k){ //如果等于k
    p=head; //p指向k个节点
    head=head.next;
    }
    }
    return p;
    // ListNode former = head, latter = head;
    // for(int i = 0; i < k; i++)
    // former = former.next;
    // while(former != null) {
    // former = former.next;
    // latter = latter.next;
    // }
    // return latter;
    }
    }
    *剑指 Offer 24. 反转链表(while(前后连接))

定义一个函数,输入一个链表的头节点,反转该链表并输出反转后链表的头节点。

示例:
输入: 1->2->3->4->5->NULL
输出: 5->4->3->2->1->NULL
• /**
• * Definition for singly-linked list.
• * public class ListNode {
• * int val;
• * ListNode next;
• * ListNode(int x) { val = x; }
• * }
• */
• class Solution {
• public ListNode reverseList(ListNode head) {
• // //定义一个带头节点的
• // ListNode resultList = new ListNode(-1);
• // //循环节点
• // ListNode p = head;
• // while(p!= null){
• // //保存插入点之后的数据
• // ListNode tempList = p.next;
• // p.next = resultList.next;
• // resultList.next = p;
• // p = tempList;
• // }
• // return resultList.next;
• ListNode pre = null;
• ListNode next = null;
• while (head != null) {
• next = head.next;
• head.next = pre;
• pre = head;
• head = next;
• }
• return pre;
• }
• }
• 准备两个空结点 pre用来保存先前结点、next用来做临时变量
• 在头结点node遍历的时候此时为1结点
o next = 1结点.next(2结点)
o 1结点.next=pre(null)
o pre = 1结点
o node = 2结点
• 进行下一次循环node=2结点
o next = 2结点.next(3结点)
o 2结点.next=pre(1结点)=>即完成2->1
o pre = 2结点
o node = 3结点
• 进行循环…

*剑指 Offer 25. 合并两个排序的链表
输入两个递增排序的链表,合并这两个链表并使新链表中的节点仍然是递增排序的。

示例1:

输入:1->2->4, 1->3->4
输出:1->1->2->3->4->4
/**

  • Definition for singly-linked list.
  • public class ListNode {
  • int val;
    
  • ListNode next;
    
  • ListNode(int x) { val = x; }
    
  • }
    */
    class Solution {
    public ListNode mergeTwoLists(ListNode l1, ListNode l2) {
    ListNode dum = new ListNode(0), cur = dum;
    while(l1 != null && l2 != null) {
    if(l1.val < l2.val) {
    cur.next = l1;
    l1 = l1.next;
    }
    else {
    cur.next = l2;
    l2 = l2.next;
    }
    cur = cur.next;
    }
    cur.next = l1 != null ? l1 : l2;
    return dum.next;
    }
    }
    *剑指 Offer 52. 两个链表的第一个公共节点(while循环)
    输入两个链表,找出它们的第一个公共节点。

如下面的两个链表:

在节点 c1 开始相交。

示例 1:

输入:intersectVal = 8, listA = [4,1,8,4,5], listB = [5,0,1,8,4,5], skipA = 2, skipB = 3
输出:Reference of the node with value = 8
输入解释:相交节点的值为 8 (注意,如果两个列表相交则不能为 0)。从各自的表头开始算起,链表 A 为 [4,1,8,4,5],链表 B 为 [5,0,1,8,4,5]。在 A 中,相交节点前有 2 个节点;在 B 中,相交节点前有 3 个节点。

示例 2:

输入:intersectVal = 2, listA = [0,9,1,2,4], listB = [3,2,4], skipA = 3, skipB = 1
输出:Reference of the node with value = 2
输入解释:相交节点的值为 2 (注意,如果两个列表相交则不能为 0)。从各自的表头开始算起,链表 A 为 [0,9,1,2,4],链表 B 为 [3,2,4]。在 A 中,相交节点前有 3 个节点;在 B 中,相交节点前有 1 个节点。

示例 3:

输入:intersectVal = 0, listA = [2,6,4], listB = [1,5], skipA = 3, skipB = 2
输出:null
输入解释:从各自的表头开始算起,链表 A 为 [2,6,4],链表 B 为 [1,5]。由于这两个链表不相交,所以 intersectVal 必须为 0,而 skipA 和 skipB 可以是任意值。
解释:这两个链表不相交,因此返回 null。
/**

  • Definition for singly-linked list.
  • public class ListNode {
  • int val;
    
  • ListNode next;
    
  • ListNode(int x) {
    
  •     val = x;
    
  •     next = null;
    
  • }
    
  • }
    */
    public class Solution {
    public ListNode getIntersectionNode(ListNode headA, ListNode headB) {
    ListNode pA = headA, pB = headB;
    while(pA != pB){
    pA = pA == null ? headB : pA.next;
    pB = pB == null ? headA : pB.next;
    }
    return pA;
    }
    }

剑指 Offer 35. 复杂链表的复制
请实现 copyRandomList 函数,复制一个复杂链表。在复杂链表中,每个节点除了有一个 next 指针指向下一个节点,还有一个 random 指针指向链表中的任意节点或者 null。

示例 1:

输入:head = [[7,null],[13,0],[11,4],[10,2],[1,0]]
输出:[[7,null],[13,0],[11,4],[10,2],[1,0]]
示例 2:

输入:head = [[1,1],[2,1]]
输出:[[1,1],[2,1]]
示例 3:

输入:head = [[3,null],[3,0],[3,null]]
输出:[[3,null],[3,0],[3,null]]
示例 4:

输入:head = []
输出:[]
解释:给定的链表为空(空指针),因此返回 null。

提示:

-10000 <= Node.val <= 10000
Node.random 为空(null)或指向链表中的节点。
节点数目不超过 1000 。
/*
// Definition for a Node.
class Node {
int val;
Node next;
Node random;

public Node(int val) {
    this.val = val;
    this.next = null;
    this.random = null;
}

}
*/
class Solution {
public Node copyRandomList(Node head) {
if(head == null) return null;
Node cur = head;
// 1. 复制各节点,并构建拼接链表
while(cur != null) {
Node tmp = new Node(cur.val);
tmp.next = cur.next;
cur.next = tmp;
cur = tmp.next;
}
// 2. 构建各新节点的 random 指向
cur = head;
while(cur != null) {
if(cur.random != null)
cur.next.random = cur.random.next;
cur = cur.next.next;
}
// 3. 拆分两链表
cur = head.next;
Node pre = head, res = head.next;
while(cur.next != null) {
pre.next = pre.next.next;
cur.next = cur.next.next;
pre = pre.next;
cur = cur.next;
}
pre.next = null; // 单独处理原链表尾节点
return res; // 返回新链表头节点
}
}
82. 删除排序链表中的重复元素 II
存在一个按升序排列的链表,给你这个链表的头节点 head ,请你删除链表中所有存在数字重复情况的节点,只保留原始链表中 没有重复出现 的数字。

返回同样按升序排列的结果链表。
示例 1:

输入:head = [1,2,3,3,4,4,5]
输出:[1,2,5]
示例 2:

输入:head = [1,1,1,2,3]
输出:[2,3]
/**

  • Definition for singly-linked list.
  • public class ListNode {
  • int val;
    
  • ListNode next;
    
  • ListNode() {}
    
  • ListNode(int val) { this.val = val; }
    
  • ListNode(int val, ListNode next) { this.val = val; this.next = next; }
    
  • }
    */
    class Solution {
    public ListNode deleteDuplicates(ListNode head) {
    if (head == null) {
    return head;
    }
    ListNode dummy = new ListNode(0, head); //由于头结点有可能被删除,所以设置哑节点
    ListNode cur = dummy;
    while (cur.next != null && cur.next.next!=null) {
    if (cur.next.val == cur.next.next.val) {//找到相同元素的前一个位置
    cur.next = cur.next.next; //删除操作
    int x = cur.next.val; //记录删除元素
    while (cur.next!=null && cur.next.val == x) {
    cur.next = cur.next.next; //删除操作
    }
    } else {
    cur = cur.next;
    }
    }
    return dummy.next;
    }
    }
  1. 删除排序链表中的重复元素
    存在一个按升序排列的链表,给你这个链表的头节点 head ,请你删除所有重复的元素,使每个元素 只出现一次 。

返回同样按升序排列的结果链表。

示例 1:

输入:head = [1,1,2]
输出:[1,2]
示例 2:

输入:head = [1,1,2,3,3]
输出:[1,2,3]

/**

  • Definition for singly-linked list.

  • public class ListNode {

  • int val;
    
  • ListNode next;
    
  • ListNode() {}
    
  • ListNode(int val) { this.val = val; }
    
  • ListNode(int val, ListNode next) { this.val = val; this.next = next; }
    
  • }
    */
    class Solution {
    public ListNode deleteDuplicates(ListNode head) {
    if (head == null) {
    return head;
    }
    ListNode cur = head;
    while (cur.next != null) {
    if (cur.val == cur.next.val) {
    cur.next = cur.next.next;
    } else {
    cur = cur.next;
    }
    }

     return head;
    

    }
    }

  1. 反转链表 II(区间反转链表)
    给你单链表的头指针 head 和两个整数 left 和 right ,其中 left <= right 。请你反转从位置 left 到位置 right 的链表节点,返回 反转后的链表 。

/**

  • Definition for singly-linked list.

  • public class ListNode {

  • int val;
    
  • ListNode next;
    
  • ListNode() {}
    
  • ListNode(int val) { this.val = val; }
    
  • ListNode(int val, ListNode next) { this.val = val; this.next = next; }
    
  • }
    */
    class Solution {
    public ListNode reverseBetween(ListNode head, int left, int right) {
    // 定义一个dummyHead, 方便处理
    ListNode dummyHead = new ListNode(0);
    dummyHead.next = head;

     // 初始化指针
     ListNode g = dummyHead;
     ListNode p = dummyHead.next;
    
     // 将指针移到相应的位置
     for(int step = 0; step < left - 1; step++) {
         g = g.next; p = p.next;
     }
    
     // 头插法插入节点
     for (int i = 0; i < right - left; i++) {
         ListNode removed = p.next;
         p.next = p.next.next;
    
         removed.next = g.next;
         g.next = removed;
     }
    
     return dummyHead.next;
    

    }
    }

*141. 环形链表(空值判断、while(快指针空值判断、快慢指针后移)) 快慢指针
给定一个链表,判断链表中是否有环。

如果链表中有某个节点,可以通过连续跟踪 next 指针再次到达,则链表中存在环。 为了表示给定链表中的环,我们使用整数 pos 来表示链表尾连接到链表中的位置(索引从 0 开始)。 如果 pos 是 -1,则在该链表中没有环。注意:pos 不作为参数进行传递,仅仅是为了标识链表的实际情况。

如果链表中存在环,则返回 true 。 否则,返回 false 。

进阶:

你能用 O(1)(即,常量)内存解决此问题吗?

示例 1:

输入:head = [3,2,0,-4], pos = 1
输出:true
解释:链表中有一个环,其尾部连接到第二个节点。
示例 2:

输入:head = [1,2], pos = 0
输出:true
解释:链表中有一个环,其尾部连接到第一个节点。
示例 3:

输入:head = [1], pos = -1
输出:false
解释:链表中没有环。
/**

  • Definition for singly-linked list.
  • class ListNode {
  • int val;
    
  • ListNode next;
    
  • ListNode(int x) {
    
  •     val = x;
    
  •     next = null;
    
  • }
    
  • }
    */
    public class Solution {
    public boolean hasCycle(ListNode head) {
    if (head == null || head.next == null) {
    return false;
    }
    ListNode slow = head;
    ListNode fast = head.next;
    while (slow != fast) {
    if (fast == null || fast.next == null) {
    return false;
    }
    slow = slow.next;
    fast = fast.next.next;
    }
    return true;
    }
    }
  1. 环形链表 II(hashset+while循环)
    给定一个链表,返回链表开始入环的第一个节点。 如果链表无环,则返回 null。

为了表示给定链表中的环,我们使用整数 pos 来表示链表尾连接到链表中的位置(索引从 0 开始)。 如果 pos 是 -1,则在该链表中没有环。注意,pos 仅仅是用于标识环的情况,并不会作为参数传递到函数中。

说明:不允许修改给定的链表。

进阶:

你是否可以使用 O(1) 空间解决此题?

示例 1:

输入:head = [3,2,0,-4], pos = 1
输出:返回索引为 1 的链表节点
解释:链表中有一个环,其尾部连接到第二个节点。
示例 2:

输入:head = [1,2], pos = 0
输出:返回索引为 0 的链表节点
解释:链表中有一个环,其尾部连接到第一个节点。
示例 3:

输入:head = [1], pos = -1
输出:返回 null
解释:链表中没有环。
/**

  • Definition for singly-linked list.
  • class ListNode {
  • int val;
    
  • ListNode next;
    
  • ListNode(int x) {
    
  •     val = x;
    
  •     next = null;
    
  • }
    
  • }
    */
    public class Solution {
    public ListNode detectCycle(ListNode head) {
    ListNode pos = head;
    Set visited = new HashSet();
    while (pos != null) {
    if (visited.contains(pos)) {
    return pos;
    } else {
    visited.add(pos);
    }
    pos = pos.next;
    }
    return null;
    }
    }
    *237. 删除链表中的节点(val和next后移)
    请编写一个函数,使其可以删除某个链表中给定的(非末尾)节点。传入函数的唯一参数为 要被删除的节点 。

现有一个链表 – head = [4,5,1,9],它可以表示为:

示例 1:

输入:head = [4,5,1,9], node = 5
输出:[4,1,9]
解释:给定你链表中值为 5 的第二个节点,那么在调用了你的函数之后,该链表应变为 4 -> 1 -> 9.
示例 2:

输入:head = [4,5,1,9], node = 1
输出:[4,5,9]
解释:给定你链表中值为 1 的第三个节点,那么在调用了你的函数之后,该链表应变为 4 -> 5 -> 9.
/**

  • Definition for singly-linked list.

  • public class ListNode {

  • int val;
    
  • ListNode next;
    
  • ListNode(int x) { val = x; }
    
  • }
    */
    class Solution {
    public void deleteNode(ListNode node) {
    node.val = node.next.val;
    node.next = node.next.next;

    }
    }
    面试题 02.06. 回文链表(前后指针)
    编写一个函数,检查输入的链表是否是回文的。

示例 1:

输入: 1->2
输出: false
示例 2:

输入: 1->2->2->1
输出: true
/**

  • Definition for singly-linked list.

  • public class ListNode {

  • int val;
    
  • ListNode next;
    
  • ListNode(int x) { val = x; }
    
  • }
    */
    class Solution {
    public boolean isPalindrome(ListNode head) {
    List vals = new ArrayList();

     // 将链表的值复制到数组中
     ListNode cur = head;
     while (cur != null) {
         vals.add(cur.val);
         cur = cur.next;
     }
    
     // 使用双指针判断是否回文
     int front = 0;
     int back = vals.size() - 1;
     while (front < back) {
         if (!vals.get(front).equals(vals.get(back))) {
             return false;
         }
         front++;
         back--;
     }
     return true;
    

    }
    }
    *面试题 02.07. 链表相交(pa和pb)
    给定两个(单向)链表,判定它们是否相交并返回交点。请注意相交的定义基于节点的引用,而不是基于节点的值。换句话说,如果一个链表的第k个节点与另一个链表的第j个节点是同一节点(引用完全相同),则这两个链表相交。

示例 1:

输入:intersectVal = 8, listA = [4,1,8,4,5], listB = [5,0,1,8,4,5], skipA = 2, skipB = 3
输出:Reference of the node with value = 8
输入解释:相交节点的值为 8 (注意,如果两个列表相交则不能为 0)。从各自的表头开始算起,链表 A 为 [4,1,8,4,5],链表 B 为 [5,0,1,8,4,5]。在 A 中,相交节点前有 2 个节点;在 B 中,相交节点前有 3 个节点。

示例 2:

输入:intersectVal = 2, listA = [0,9,1,2,4], listB = [3,2,4], skipA = 3, skipB = 1
输出:Reference of the node with value = 2
输入解释:相交节点的值为 2 (注意,如果两个列表相交则不能为 0)。从各自的表头开始算起,链表 A 为 [0,9,1,2,4],链表 B 为 [3,2,4]。在 A 中,相交节点前有 3 个节点;在 B 中,相交节点前有 1 个节点。

示例 3:

输入:intersectVal = 0, listA = [2,6,4], listB = [1,5], skipA = 3, skipB = 2
输出:null
输入解释:从各自的表头开始算起,链表 A 为 [2,6,4],链表 B 为 [1,5]。由于这两个链表不相交,所以 intersectVal 必须为 0,而 skipA 和 skipB 可以是任意值。
解释:这两个链表不相交,因此返回 null。
/**

  • Definition for singly-linked list.
  • public class ListNode {
  • int val;
    
  • ListNode next;
    
  • ListNode(int x) {
    
  •     val = x;
    
  •     next = null;
    
  • }
    
  • }
    */
    public class Solution {
    public ListNode getIntersectionNode(ListNode headA, ListNode headB) {
    if(headAnull || headBnull){
    return null;
    }
    ListNode pa=headA,pb=headB;
    while(pa!=pb){
    pa=pa!=null?pa.next:headB;
    pb=pb!=null?pb.next:headA;
    }
    return pa;
    }
    }
  1. 合并K个升序链表
    给你一个链表数组,每个链表都已经按升序排列。

请你将所有链表合并到一个升序链表中,返回合并后的链表。

示例 1:

输入:lists = [[1,4,5],[1,3,4],[2,6]]
输出:[1,1,2,3,4,4,5,6]
解释:链表数组如下:
[
1->4->5,
1->3->4,
2->6
]
将它们合并到一个有序链表中得到。
1->1->2->3->4->4->5->6
示例 2:

输入:lists = []
输出:[]
示例 3:

输入:lists = [[]]
输出:[]
/**

  • Definition for singly-linked list.

  • public class ListNode {

  • int val;
    
  • ListNode next;
    
  • ListNode() {}
    
  • ListNode(int val) { this.val = val; }
    
  • ListNode(int val, ListNode next) { this.val = val; this.next = next; }
    
  • }
    */
    class Solution {
    public ListNode mergeKLists(ListNode[] lists) {
    ListNode ans = null;
    for (int i = 0; i < lists.length; ++i) {
    ans = mergeTwoLists(ans, lists[i]);
    }
    return ans;
    }

    public ListNode mergeTwoLists(ListNode l1, ListNode l2) {
    ListNode dum = new ListNode(0), cur = dum;
    while(l1 != null && l2 != null) {
    if(l1.val < l2.val) {
    cur.next = l1;
    l1 = l1.next;
    }
    else {
    cur.next = l2;
    l2 = l2.next;
    }
    cur = cur.next;
    }
    cur.next = l1 != null ? l1 : l2;
    return dum.next;
    }
    }

  1. 两两交换链表中的节点
    给定一个链表,两两交换其中相邻的节点,并返回交换后的链表。
    你不能只是单纯的改变节点内部的值,而是需要实际的进行节点交换。

/**

  • Definition for singly-linked list.
  • public class ListNode {
  • int val;
    
  • ListNode next;
    
  • ListNode() {}
    
  • ListNode(int val) { this.val = val; }
    
  • ListNode(int val, ListNode next) { this.val = val; this.next = next; }
    
  • }
    */
    class Solution {
    public ListNode swapPairs(ListNode head) {
    if (head == null || head.next == null) {
    return head;
    }
    ListNode newHead = head.next;
    head.next = swapPairs(newHead.next);
    newHead.next = head;
    return newHead;
    }
    }
  1. 奇偶链表
    给定一个单链表,把所有的奇数节点和偶数节点分别排在一起。请注意,这里的奇数节点和偶数节点指的是节点编号的奇偶性,而不是节点的值的奇偶性。

请尝试使用原地算法完成。你的算法的空间复杂度应为 O(1),时间复杂度应为 O(nodes),nodes 为节点总数。

示例 1:

输入: 1->2->3->4->5->NULL
输出: 1->3->5->2->4->NULL
示例 2:

输入: 2->1->3->5->6->4->7->NULL
输出: 2->3->6->7->1->5->4->NULL
/**

  • Definition for singly-linked list.
  • public class ListNode {
  • int val;
    
  • ListNode next;
    
  • ListNode() {}
    
  • ListNode(int val) { this.val = val; }
    
  • ListNode(int val, ListNode next) { this.val = val; this.next = next; }
    
  • }
    */
    class Solution {
    public ListNode oddEvenList(ListNode head) {
    if (head == null) {
    return head;
    }
    ListNode evenHead = head.next;
    ListNode odd = head, even = evenHead;
    while (even != null && even.next != null) {
    odd.next = even.next;
    odd = odd.next;
    even.next = odd.next;
    even = even.next;
    }
    odd.next = evenHead;
    return head;
    }
    }
  1. 二叉树展开为链表
    给你二叉树的根结点 root ,请你将它展开为一个单链表:

展开后的单链表应该同样使用 TreeNode ,其中 right 子指针指向链表中下一个结点,而左子指针始终为 null 。
展开后的单链表应该与二叉树 先序遍历 顺序相同。

/**

  • Definition for a binary tree node.

  • public class TreeNode {

  • int val;
    
  • TreeNode left;
    
  • TreeNode right;
    
  • TreeNode() {}
    
  • TreeNode(int val) { this.val = val; }
    
  • TreeNode(int val, TreeNode left, TreeNode right) {
    
  •     this.val = val;
    
  •     this.left = left;
    
  •     this.right = right;
    
  • }
    
  • }
    */
    class Solution {
    public void flatten(TreeNode root) {
    List list = new ArrayList();
    preorderTraversal(root, list);
    int size = list.size();
    for (int i = 1; i < size; i++) {
    TreeNode prev = list.get(i - 1), curr = list.get(i);
    prev.left = null;
    prev.right = curr;
    }
    }

    public void preorderTraversal(TreeNode root, List list) {
    if (root != null) {
    list.add(root);
    preorderTraversal(root.left, list);
    preorderTraversal(root.right, list);
    }
    }
    }

  1. 对链表进行插入排序
    对链表进行插入排序。

插入排序的动画演示如上。从第一个元素开始,该链表可以被认为已经部分排序(用黑色表示)。
每次迭代时,从输入数据中移除一个元素(用红色表示),并原地将其插入到已排好序的链表中。

插入排序算法:

插入排序是迭代的,每次只移动一个元素,直到所有元素可以形成一个有序的输出列表。
每次迭代中,插入排序只从输入数据中移除一个待排序的元素,找到它在序列中适当的位置,并将其插入。
重复直到所有输入数据插入完为止。

示例 1:

输入: 4->2->1->3
输出: 1->2->3->4
示例 2:

输入: -1->5->3->4->0
输出: -1->0->3->4->5
/**

  • Definition for singly-linked list.
  • public class ListNode {
  • int val;
    
  • ListNode next;
    
  • ListNode() {}
    
  • ListNode(int val) { this.val = val; }
    
  • ListNode(int val, ListNode next) { this.val = val; this.next = next; }
    
  • }
    */
    class Solution {
    public ListNode insertionSortList(ListNode head) {
    if (head == null) {
    return head;
    }
    ListNode dummyHead = new ListNode(0);
    dummyHead.next = head;
    ListNode lastSorted = head, curr = head.next;
    while (curr != null) {
    if (lastSorted.val <= curr.val) {
    lastSorted = lastSorted.next;
    } else {
    ListNode prev = dummyHead;
    while (prev.next.val <= curr.val) {
    prev = prev.next;
    }
    lastSorted.next = curr.next;
    curr.next = prev.next;
    prev.next = curr;
    }
    curr = lastSorted.next;
    }
    return dummyHead.next;
    }
    }
    对链表进行插入排序的具体过程如下。

1、首先判断给定的链表是否为空,若为空,则不需要进行排序,直接返回。

2、创建哑节点 dummyHead,令 dummyHead.next = head。引入哑节点是为了便于在 head 节点之前插入节点。

3、维护 lastSorted 为链表的已排序部分的最后一个节点,初始时 lastSorted = head。

4、维护 curr 为待插入的元素,初始时 curr = head.next。

5、比较 lastSorted 和 curr 的节点值。

若 lastSorted.val <= curr.val,说明 curr 应该位于 lastSorted 之后,将 lastSorted 后移一位,curr 变成新的 lastSorted。

否则,从链表的头节点开始往后遍历链表中的节点,寻找插入 curr 的位置。令 prev 为插入 curr 的位置的前一个节点,进行如下操作,完成对 curr 的插入:

lastSorted.next = curr.next
curr.next = prev.next
prev.next = curr

6、令 curr = lastSorted.next,此时 curr 为下一个待插入的元素。

7、重复第 5 步和第 6 步,直到 curr 变成空,排序结束。

8、返回 dummyHead.next,为排序后的链表的头节点。

  1. 排序链表
    给你链表的头结点 head ,请将其按 升序 排列并返回 排序后的链表 。

进阶:

你可以在 O(n log n) 时间复杂度和常数级空间复杂度下,对链表进行排序吗?

/**

  • Definition for singly-linked list.
  • public class ListNode {
  • int val;
    
  • ListNode next;
    
  • ListNode() {}
    
  • ListNode(int val) { this.val = val; }
    
  • ListNode(int val, ListNode next) { this.val = val; this.next = next; }
    
  • }
    */
    class Solution {
    public ListNode sortList(ListNode head) {
    if(headnull||head.nextnull)return head;
    ListNode slow=head;
    ListNode fast=head.next;
    while (fast!=null&&fast.next!=null){
    slow=slow.next;
    fast=fast.next.next;
    }
    ListNode head2=slow.next;
    slow.next=null;
    return merge(sortList(head),sortList(head2));
    }
    public ListNode merge(ListNode head1, ListNode head2) {
    ListNode dummyHead = new ListNode(0);
    ListNode temp = dummyHead, temp1 = head1, temp2 = head2;
    while (temp1 != null && temp2 != null) {
    if (temp1.val <= temp2.val) {
    temp.next = temp1;
    temp1 = temp1.next;
    } else {
    temp.next = temp2;
    temp2 = temp2.next;
    }
    temp = temp.next;
    }
    if (temp1 != null) {
    temp.next = temp1;
    } else if (temp2 != null) {
    temp.next = temp2;
    }
    return dummyHead.next;
    }
    }
  1. 链表的中间结点
    给定一个头结点为 head 的非空单链表,返回链表的中间结点。

如果有两个中间结点,则返回第二个中间结点。

示例 1:

输入:[1,2,3,4,5]
输出:此列表中的结点 3 (序列化形式:[3,4,5])
返回的结点值为 3 。 (测评系统对该结点序列化表述是 [3,4,5])。
注意,我们返回了一个 ListNode 类型的对象 ans,这样:
ans.val = 3, ans.next.val = 4, ans.next.next.val = 5, 以及 ans.next.next.next = NULL.
示例 2:

输入:[1,2,3,4,5,6]
输出:此列表中的结点 4 (序列化形式:[4,5,6])
由于该列表有两个中间结点,值分别为 3 和 4,我们返回第二个结点。
/**

  • Definition for singly-linked list.
  • public class ListNode {
  • int val;
    
  • ListNode next;
    
  • ListNode() {}
    
  • ListNode(int val) { this.val = val; }
    
  • ListNode(int val, ListNode next) { this.val = val; this.next = next; }
    
  • }
    */
    class Solution {
    public ListNode middleNode(ListNode head) {
    ListNode pre = head;
    int count=0,n=0;
    while(pre!=null){
    count++;
    pre=pre.next;
    }
    pre=head;
    while(n<count/2){
    ++n;
    pre=pre.next;
    }
    return pre;
    }
    }
  1. 从链表中删去总和值为零的连续节点
    给你一个链表的头节点 head,请你编写代码,反复删去链表中由 总和 值为 0 的连续节点组成的序列,直到不存在这样的序列为止。

删除完毕后,请你返回最终结果链表的头节点。

你可以返回任何满足题目要求的答案。

(注意,下面示例中的所有序列,都是对 ListNode 对象序列化的表示。)

示例 1:

输入:head = [1,2,-3,3,1]
输出:[3,1]
提示:答案 [1,2,1] 也是正确的。
示例 2:

输入:head = [1,2,3,-3,4]
输出:[1,2,4]
示例 3:

输入:head = [1,2,3,-3,-2]
输出:[1]
/**

  • Definition for singly-linked list.

  • public class ListNode {

  • int val;
    
  • ListNode next;
    
  • ListNode() {}
    
  • ListNode(int val) { this.val = val; }
    
  • ListNode(int val, ListNode next) { this.val = val; this.next = next; }
    
  • }
    */
    class Solution {
    public ListNode removeZeroSumSublists(ListNode head) {
    ListNode dummy = new ListNode(0);
    dummy.next = head;

     Map<Integer, ListNode> map = new HashMap<>();
    
     // 首次遍历建立 节点处链表和<->节点 哈希表
     // 若同一和出现多次会覆盖,即记录该sum出现的最后一次节点
     int sum = 0;
     for (ListNode d = dummy; d != null; d = d.next) {
         sum += d.val;
         map.put(sum, d);
     }
    
     // 第二遍遍历 若当前节点处sum在下一处出现了则表明两结点之间所有节点和为0 直接删除区间所有节点
     sum = 0;
     for (ListNode d = dummy; d != null; d = d.next) {
         sum += d.val;
         d.next = map.get(sum).next;
     }
    
     return dummy.next;
    

    }
    }

二叉树
剑指 Offer 27. 二叉树的镜像(难度:简单)(if(不为空)递归左右、交换左右)
请完成一个函数,输入一个二叉树,该函数输出它的镜像。
例如输入:
4
/
2 7
/ \ /
1 3 6 9
镜像输出:

 4

/
7 2
/ \ /
9 6 3 1

示例 1:

输入:root = [4,2,7,1,3,6,9]
输出:[4,7,2,9,6,3,1]

代码:
/**

  • Definition for a binary tree node.
  • public class TreeNode {
  • int val;
    
  • TreeNode left;
    
  • TreeNode right;
    
  • TreeNode(int x) { val = x; }
    
  • }
    */
    class Solution {
    public TreeNode mirrorTree(TreeNode root) {
    swap(root);
    return root;
    }
    void swap(TreeNode b){
    if(b!=null){
    swap(b.left);//递归交换结点左孩子的左右子树。。。
    swap(b.right);//递归交换结点右孩子的左右子树。。
    TreeNode temp = b.left;//交换左右子树
    b.left = b.right;
    b.right = temp;
    }
    }
    }
    剑指 Offer 28. 对称的二叉树(左&&右,左||右||l.val!=r.val、递归(左,右)&&递归(右、左))
    请实现一个函数,用来判断一棵二叉树是不是对称的。如果一棵二叉树和它的镜像一样,那么它是对称的。

例如,二叉树 [1,2,2,3,4,4,3] 是对称的。

1

/
2 2
/ \ /
3 4 4 3
但是下面这个 [1,2,2,null,3,null,3] 则不是镜像对称的:

1

/
2 2
\
3 3

示例 1:

输入:root = [1,2,2,3,4,4,3]
输出:true
示例 2:

输入:root = [1,2,2,null,3,null,3]
输出:false
/**

  • Definition for a binary tree node.
  • public class TreeNode {
  • int val;
    
  • TreeNode left;
    
  • TreeNode right;
    
  • TreeNode(int x) { val = x; }
    
  • }
    */
    class Solution {
    public boolean isSymmetric(TreeNode root) {
    return root == null ? true : recur(root.left, root.right);
    }
    boolean recur(TreeNode L, TreeNode 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(L.right, R.left);
    }
    }
    *剑指 Offer 32 - I. 从上到下打印二叉树

从上到下打印出二叉树的每个节点,同一层的节点按照从左到右的顺序打印。

例如:
给定二叉树: [3,9,20,null,null,15,7],
3
/
9 20
/
15 7
返回:
[3,9,20,15,7]
/**

  • Definition for a binary tree node.
  • public class TreeNode {
  • int val;
    
  • TreeNode left;
    
  • TreeNode right;
    
  • TreeNode(int x) { val = x; }
    
  • }
    */
    class Solution {
    public int[] levelOrder(TreeNode root) {
    if (root == null) {
    return new int[]{};
    }
    Queue stack = new LinkedList<>();
    ArrayList ans = new ArrayList<>();
    stack.add(root);
    while (!stack.isEmpty()) {
    TreeNode node = stack.poll();
    ans.add(node.val);
    if (node.left != null) {
    stack.add(node.left);
    }
    if (node.right != null) {
    stack.add(node.right);
    }
    }
    int[] res=new int[ans.size()];
    for(int i=0;i<ans.size();i++){
    res[i]=ans.get(i);
    }
    return res;
    }
    }
    *剑指 Offer 32 - II. 从上到下打印二叉树 II
    从上到下按层打印二叉树,同一层的节点按从左到右的顺序打印,每一层打印到一行。

例如:
给定二叉树: [3,9,20,null,null,15,7],

3

/
9 20
/
15 7
返回其层次遍历结果:

[
[3],
[9,20],
[15,7]
]
/**

  • Definition for a binary tree node.
  • public class TreeNode {
  • int val;
    
  • TreeNode left;
    
  • TreeNode right;
    
  • TreeNode(int x) { val = x; }
    
  • }
    */
    class Solution {
    public List<List> levelOrder(TreeNode root) {
    Queue queue = new LinkedList<>();
    List<List> res = new ArrayList<>();
    if(root != null) queue.add(root);
    while(!queue.isEmpty()) {
    List tmp = new ArrayList<>();
    for(int i = queue.size(); i > 0; i–) {
    TreeNode node = queue.poll();
    tmp.add(node.val);
    if(node.left != null) queue.add(node.left);
    if(node.right != null) queue.add(node.right);
    }
    res.add(tmp);
    }
    return res;
    }
    }
    剑指 Offer 32 - III. 从上到下打印二叉树 III
    请实现一个函数按照之字形顺序打印二叉树,即第一行按照从左到右的顺序打印,第二层按照从右到左的顺序打印,第三行再按照从左到右的顺序打印,其他行以此类推。

例如:
给定二叉树: [3,9,20,null,null,15,7],

3

/
9 20
/
15 7
返回其层次遍历结果:

[
[3],
[20,9],
[15,7]
]
/**

  • Definition for a binary tree node.
  • public class TreeNode {
  • int val;
    
  • TreeNode left;
    
  • TreeNode right;
    
  • TreeNode(int x) { val = x; }
    
  • }
    */
    class Solution {
    public List<List> levelOrder(TreeNode root) {
    Queue queue = new LinkedList<>();
    List<List> res = new ArrayList<>();
    if(root != null) queue.add(root);
    while(!queue.isEmpty()) {
    LinkedList tmp = new LinkedList<>();
    for(int i = queue.size(); i > 0; i–) {
    TreeNode node = queue.poll();
    if((res.size()&1) == 0) tmp.addLast(node.val); // 偶数层 -> 队列头部
    else tmp.addFirst(node.val); // 奇数层 -> 队列尾部
    if(node.left != null) queue.add(node.left);
    if(node.right != null) queue.add(node.right);
    }
    res.add(tmp);
    }
    return res;
    }
    }
    剑指 Offer 54. 二叉搜索树的第k大节点
    给定一棵二叉搜索树,请找出其中第k大的节点。

示例 1:

输入: root = [3,1,4,null,2], k = 1
3
/
1 4

2
输出: 4
示例 2:

输入: root = [5,3,6,2,4,null,null,1], k = 3
5
/
3 6
/
2 4
/
1
输出: 4

限制:

1 ≤ k ≤ 二叉搜索树元素个数
/**

  • Definition for a binary tree node.
  • public class TreeNode {
  • int val;
    
  • TreeNode left;
    
  • TreeNode right;
    
  • TreeNode(int x) { val = x; }
    
  • }
    */
    class Solution {
    int res, k;
    public int kthLargest(TreeNode root, int k) {
    this.k = k;
    dfs(root);
    return res;
    }
    void dfs(TreeNode root) {
    if(root == null) return;
    dfs(root.right);
    if(k == 0) return;
    if(–k == 0) res = root.val;
    dfs(root.left);
    }
    }
    *剑指 Offer 55 - I. 二叉树的深度(空值判断、递归左右+1)
    输入一棵二叉树的根节点,求该树的深度。从根节点到叶节点依次经过的节点(含根、叶节点)形成树的一条路径,最长路径的长度为树的深度。

例如:

给定二叉树 [3,9,20,null,null,15,7],

3

/
9 20
/
15 7
返回它的最大深度 3 。
/**

  • Definition for a binary tree node.
  • public class TreeNode {
  • int val;
    
  • TreeNode left;
    
  • TreeNode right;
    
  • TreeNode(int x) { val = x; }
    
  • }
    */
    class Solution {
    public int maxDepth(TreeNode root) {
    if(root == null) return 0;
    return Math.max(maxDepth(root.left), maxDepth(root.right)) + 1;
    }
    }
    剑指 Offer 55 - II. 平衡二叉树(空值判断、递归左、左判断、递归右、右判断、)
    输入一棵二叉树的根节点,判断该树是不是平衡二叉树。如果某二叉树中任意节点的左右子树的深度相差不超过1,那么它就是一棵平衡二叉树。

示例 1:

给定二叉树 [3,9,20,null,null,15,7]

3

/
9 20
/
15 7
返回 true 。

示例 2:

给定二叉树 [1,2,2,3,3,null,null,4,4]

   1
  / \
 2   2
/ \

3 3
/
4 4
返回 false 。
/**

  • Definition for a binary tree node.
  • public class TreeNode {
  • int val;
    
  • TreeNode left;
    
  • TreeNode right;
    
  • TreeNode(int x) { val = x; }
    
  • }
    */
    class Solution {
    public boolean isBalanced(TreeNode root) {
    return Depth(root)!=-1;
    }
    public int Depth(TreeNode root){
    if(rootnull) return 0;
    int left=Depth(root.left);
    if(left
    -1)return -1;//左子树不平衡直接返回-1
    int right=Depth(root.right);
    if(right==-1)return -1;//右子树不平衡直接返回-1
    return Math.abs(left - right) > 1 ? -1 : 1 + Math.max(left, right);
    }
    }
    剑指 Offer 68 - II. 二叉树的最近公共祖先(空值判断、递归左右、左空反右、右空反左)
    给定一个二叉树, 找到该树中两个指定节点的最近公共祖先。

百度百科中最近公共祖先的定义为:“对于有根树 T 的两个结点 p、q,最近公共祖先表示为一个结点 x,满足 x 是 p、q 的祖先且 x 的深度尽可能大(一个节点也可以是它自己的祖先)。”

例如,给定如下二叉树: root = [3,5,1,6,2,0,8,null,null,7,4]

示例 1:

输入: root = [3,5,1,6,2,0,8,null,null,7,4], p = 5, q = 1
输出: 3
解释: 节点 5 和节点 1 的最近公共祖先是节点 3。
示例 2:

输入: root = [3,5,1,6,2,0,8,null,null,7,4], p = 5, q = 4
输出: 5
解释: 节点 5 和节点 4 的最近公共祖先是节点 5。因为根据定义最近公共祖先节点可以为节点本身。
/**

  • Definition for a binary tree node.
  • public class TreeNode {
  • int val;
    
  • TreeNode left;
    
  • TreeNode right;
    
  • TreeNode(int x) { val = x; }
    
  • }
    */
    class Solution {
    public TreeNode lowestCommonAncestor(TreeNode root, TreeNode p, TreeNode q) {
    if(root == null || root == p || root == q) return root;
    TreeNode left = lowestCommonAncestor(root.left, p, q);
    TreeNode right = lowestCommonAncestor(root.right, p, q);
    if(left == null) return right;
    if(right == null) return left;
    return root;
    }
    }
    *94. 二叉树的中序遍历(空值判断、递归左、list.add添加val、递归右)
    给定一个二叉树的根节点 root ,返回它的 中序 遍历。

示例 1:

输入:root = [1,null,2,3]
输出:[1,3,2]
示例 2:

输入:root = []
输出:[]
示例 3:

输入:root = [1]
输出:[1]
示例 4:

输入:root = [1,2]
输出:[2,1]
示例 5:

输入:root = [1,null,2]
输出:[1,2]
/**

  • Definition for a binary tree node.
  • public class TreeNode {
  • int val;
    
  • TreeNode left;
    
  • TreeNode right;
    
  • TreeNode() {}
    
  • TreeNode(int val) { this.val = val; }
    
  • TreeNode(int val, TreeNode left, TreeNode right) {
    
  •     this.val = val;
    
  •     this.left = left;
    
  •     this.right = right;
    
  • }
    
  • }
    */
    class Solution {
    public List inorderTraversal(TreeNode root) {
    List list=new ArrayList();
    recur(list,root);
    return list;
    }
    public void recur(List res, TreeNode node){
    if(node==null) return;
    recur(res,node.left);
    res.add(node.val);
    recur(res,node.right);
    }
    }
  1. 不同的二叉搜索树

class Solution {
public int numTrees(int n) {
int[] G = new int[n + 1];
G[0] = 1;
G[1] = 1;

    for (int i = 2; i <= n; ++i) {
        for (int j = 1; j <= i; ++j) {
            G[i] += G[j - 1] * G[i - j];
        }
    }
    return G[n];
}

}

  1. 验证二叉搜索树(空值判断、越界判断、递归左&&右)
    给定一个二叉树,判断其是否是一个有效的二叉搜索树。

假设一个二叉搜索树具有如下特征:

节点的左子树只包含小于当前节点的数。
节点的右子树只包含大于当前节点的数。
所有左子树和右子树自身必须也是二叉搜索树。
示例 1:

输入:
2
/
1 3
输出: true
示例 2:

输入:
5
/
1 4
/
3 6
输出: false
解释: 输入为: [5,1,4,null,null,3,6]。
根节点的值为 5 ,但是其右子节点值为 4 。
/**

  • Definition for a binary tree node.
  • public class TreeNode {
  • int val;
    
  • TreeNode left;
    
  • TreeNode right;
    
  • TreeNode() {}
    
  • TreeNode(int val) { this.val = val; }
    
  • TreeNode(int val, TreeNode left, TreeNode right) {
    
  •     this.val = val;
    
  •     this.left = left;
    
  •     this.right = right;
    
  • }
    
  • }
    */
    class Solution {
    public boolean isValidBST(TreeNode root) {
    return isValidBST(root, Long.MIN_VALUE, Long.MAX_VALUE);//root节点的上边界和下边界
    }
    boolean isValidBST(TreeNode root, long min, long max) {
    if (root == null) //为空直接返回
    return true;
    if (root.val <= min || root.val >= max) //超出上下边界返回false
    return false;
    //递归左子树和右子树
    return isValidBST(root.left, min, root.val) && isValidBST(root.right, root.val, max);
    }
    }
    *104. 二叉树的最大深度(if-else(控制判断、递归左右、Math.max(左、右)+1))
    给定一个二叉树,找出其最大深度。

二叉树的深度为根节点到最远叶子节点的最长路径上的节点数。

说明: 叶子节点是指没有子节点的节点。

示例:
给定二叉树 [3,9,20,null,null,15,7],

3

/
9 20
/
15 7
返回它的最大深度 3 。
/**

  • Definition for a binary tree node.
  • public class TreeNode {
  • int val;
    
  • TreeNode left;
    
  • TreeNode right;
    
  • TreeNode() {}
    
  • TreeNode(int val) { this.val = val; }
    
  • TreeNode(int val, TreeNode left, TreeNode right) {
    
  •     this.val = val;
    
  •     this.left = left;
    
  •     this.right = right;
    
  • }
    
  • }
    */
    class Solution {
    public int maxDepth(TreeNode root) {
    if (root == null) {
    return 0;
    } else {
    return Math.max(maxDepth(root.left), maxDepth(root.right)) + 1;
    }
    }
    }
  1. 二叉树的最小深度
    给定一个二叉树,找出其最小深度。

最小深度是从根节点到最近叶子节点的最短路径上的节点数量。

说明:叶子节点是指没有子节点的节点。

示例 1:

输入:root = [3,9,20,null,null,15,7]
输出:2
示例 2:

输入:root = [2,null,3,null,4,null,5,null,6]
输出:5
/**

  • Definition for a binary tree node.

  • public class TreeNode {

  • int val;
    
  • TreeNode left;
    
  • TreeNode right;
    
  • TreeNode() {}
    
  • TreeNode(int val) { this.val = val; }
    
  • TreeNode(int val, TreeNode left, TreeNode right) {
    
  •     this.val = val;
    
  •     this.left = left;
    
  •     this.right = right;
    
  • }
    
  • }
    */
    class Solution {
    public int minDepth(TreeNode root) {
    if(root == null) return 0;
    //这道题递归条件里分为三种情况
    //1.左孩子和有孩子都为空的情况,说明到达了叶子节点,直接返回1即可
    if(root.left == null && root.right == null) return 1;
    //2.如果左孩子和由孩子其中一个为空,那么需要返回比较大的那个孩子的深度
    int m1 = minDepth(root.left);
    int m2 = minDepth(root.right);
    //这里其中一个节点为空,说明m1和m2有一个必然为0,所以可以返回m1 + m2 + 1;
    if(root.left == null || root.right == null) return m1 + m2 + 1;

     //3.最后一种情况,也就是左右孩子都不为空,返回最小深度+1即可
     return Math.min(m1,m2) + 1;
    

    }
    }

  1. 求根节点到叶节点数字之和(空值判断、求和公式、左&&右==null、递归左右)
    给你一个二叉树的根节点 root ,树中每个节点都存放有一个 0 到 9 之间的数字。
    每条从根节点到叶节点的路径都代表一个数字:

例如,从根节点到叶节点的路径 1 -> 2 -> 3 表示数字 123 。
计算从根节点到叶节点生成的 所有数字之和 。

叶节点 是指没有子节点的节点。

示例 1:

输入:root = [1,2,3]
输出:25
解释:
从根到叶子节点路径 1->2 代表数字 12
从根到叶子节点路径 1->3 代表数字 13
因此,数字总和 = 12 + 13 = 25
示例 2:

输入:root = [4,9,0,5,1]
输出:1026
解释:
从根到叶子节点路径 4->9->5 代表数字 495
从根到叶子节点路径 4->9->1 代表数字 491
从根到叶子节点路径 4->0 代表数字 40
因此,数字总和 = 495 + 491 + 40 = 1026
/**

  • Definition for a binary tree node.

  • public class TreeNode {

  • int val;
    
  • TreeNode left;
    
  • TreeNode right;
    
  • TreeNode() {}
    
  • TreeNode(int val) { this.val = val; }
    
  • TreeNode(int val, TreeNode left, TreeNode right) {
    
  •     this.val = val;
    
  •     this.left = left;
    
  •     this.right = right;
    
  • }
    
  • }
    */
    class Solution {
    public int sumNumbers(TreeNode root) {
    if(root==null) return 0;
    int sum = 0;
    return preOrder(root, sum);
    }

    public int preOrder(TreeNode root, int sum) {
    if (rootnull) return 0;
    sum = 10 * sum + root.val;
    if (root.left
    null && root.right==null) return sum;
    return preOrder(root.left, sum) + preOrder(root.right, sum);
    }
    }

*144. 二叉树的前序遍历(空值判断、list.add添加val、递归左、递归右)
给你二叉树的根节点 root ,返回它节点值的 前序 遍历。

示例 1:

输入:root = [1,null,2,3]
输出:[1,2,3]
示例 2:

输入:root = []
输出:[]
示例 3:

输入:root = [1]
输出:[1]
示例 4:

输入:root = [1,2]
输出:[1,2]
示例 5:

输入:root = [1,null,2]
输出:[1,2]
/**

  • Definition for a binary tree node.
  • public class TreeNode {
  • int val;
    
  • TreeNode left;
    
  • TreeNode right;
    
  • TreeNode() {}
    
  • TreeNode(int val) { this.val = val; }
    
  • TreeNode(int val, TreeNode left, TreeNode right) {
    
  •     this.val = val;
    
  •     this.left = left;
    
  •     this.right = right;
    
  • }
    
  • }
    */
    class Solution {
    public List preorderTraversal(TreeNode root) {
    List list=new ArrayList();
    recur(list,root);
    return list;
    }
    public void recur(List res, TreeNode node){
    if(node==null) return;
    res.add(node.val);
    recur(res,node.left);
    recur(res,node.right);
    }
    }
    *145. 二叉树的后序遍历(空值判断、递归左、递归右、list.add添加val)
    给定一个二叉树,返回它的 后序 遍历。

示例:

输入: [1,null,2,3]
1

2
/
3

输出: [3,2,1]
/**

  • Definition for a binary tree node.
  • public class TreeNode {
  • int val;
    
  • TreeNode left;
    
  • TreeNode right;
    
  • TreeNode() {}
    
  • TreeNode(int val) { this.val = val; }
    
  • TreeNode(int val, TreeNode left, TreeNode right) {
    
  •     this.val = val;
    
  •     this.left = left;
    
  •     this.right = right;
    
  • }
    
  • }
    */
    class Solution {
    public List postorderTraversal(TreeNode root) {
    List list=new ArrayList();
    recur(list,root);
    return list;
    }
    public void recur(List res, TreeNode node){
    if(node==null) return;
    recur(res,node.left);
    recur(res,node.right);
    res.add(node.val);
    }
    }
    *222. 完全二叉树的节点个数(空值判断、全局变量n、递归左右)
    给你一棵 完全二叉树 的根节点 root ,求出该树的节点个数。

完全二叉树 的定义如下:在完全二叉树中,除了最底层节点可能没填满外,其余每层节点数都达到最大值,并且最下面一层的节点都集中在该层最左边的若干位置。若最底层为第 h 层,则该层包含 1~ 2h 个节点。

示例 1:

输入:root = [1,2,3,4,5,6]
输出:6
示例 2:

输入:root = []
输出:0
示例 3:

输入:root = [1]
输出:1
/**

  • Definition for a binary tree node.
  • public class TreeNode {
  • int val;
    
  • TreeNode left;
    
  • TreeNode right;
    
  • TreeNode() {}
    
  • TreeNode(int val) { this.val = val; }
    
  • TreeNode(int val, TreeNode left, TreeNode right) {
    
  •     this.val = val;
    
  •     this.left = left;
    
  •     this.right = right;
    
  • }
    
  • }
    */
    class Solution {
    int n=0;
    public int countNodes(TreeNode root) {
    recur(root);
    return n;
    }
    public void recur(TreeNode node){
    if(node==null) return;
    n++;
    recur(node.left);
    recur(node.right);
    }

}
230. 二叉搜索树中第K小的元素

/**

  • Definition for a binary tree node.
  • public class TreeNode {
  • int val;
    
  • TreeNode left;
    
  • TreeNode right;
    
  • TreeNode() {}
    
  • TreeNode(int val) { this.val = val; }
    
  • TreeNode(int val, TreeNode left, TreeNode right) {
    
  •     this.val = val;
    
  •     this.left = left;
    
  •     this.right = right;
    
  • }
    
  • }
    */
    class Solution {
    int res, k;
    public int kthSmallest(TreeNode root, int k) {
    this.k = k;
    dfs(root);
    return res;
    }
    void dfs(TreeNode root) {
    if(root == null) return;
    dfs(root.left);
    if(k == 0) return;
    if(–k == 0) res = root.val;
    dfs(root.right);
    }
    }

1302.层数最深叶子节点的和
给你一棵二叉树的根节点 root ,请你返回 层数最深的叶子节点的和
输入:root = [1,2,3,4,5,null,6,7,null,null,null,null,8]
输出:15
public int deepestLeavesSum2(TreeNode root) {
if (root == null) {
return 0;
}
int maxDepth = getLevel(root);
return getSum(root, 1, maxDepth);//一定要注意,是从第一层往下的
}

public int getLevel(TreeNode root) {
	if (root == null) {
		return 0;
	}
	return Math.max(1 + getLevel(root.left), 1 + getLevel(root.right));
}

public int getSum(TreeNode root, int cur, int maxDepth) {
	if (root == null) {
		return 0;
	}
	if (cur == maxDepth) {
		return root.val;
	}
	return getSum(root.left, cur + 1, maxDepth) + getSum(root.right, cur + 1, maxDepth);
}
  1. 二叉树的层序遍历
    给你一个二叉树,请你返回其按 层序遍历 得到的节点值。 (即逐层地,从左到右访问所有节点)。
    示例:
    二叉树:[3,9,20,null,null,15,7],
3

/
9 20
/
15 7
返回其层序遍历结果:

[
[3],
[9,20],
[15,7]
]
/**

  • Definition for a binary tree node.

  • public class TreeNode {

  • int val;
    
  • TreeNode left;
    
  • TreeNode right;
    
  • TreeNode() {}
    
  • TreeNode(int val) { this.val = val; }
    
  • TreeNode(int val, TreeNode left, TreeNode right) {
    
  •     this.val = val;
    
  •     this.left = left;
    
  •     this.right = right;
    
  • }
    
  • }
    */
    class Solution {
    public List<List> levelOrder(TreeNode root) {
    List<List> ret = new ArrayList<List>();
    if (root == null) {
    return ret;
    }
    Queue queue = new LinkedList();
    queue.offer(root);
    while (!queue.isEmpty()) {
    List level = new ArrayList();
    int currentLevelSize = queue.size();
    for (int i = 1; i <= currentLevelSize; ++i) {
    TreeNode node = queue.poll();
    level.add(node.val);
    if (node.left != null) {
    queue.offer(node.left);
    }
    if (node.right != null) {
    queue.offer(node.right);
    }
    }
    ret.add(level);
    }

     return ret;
    

    }
    }

  1. 相同的树
    给你两棵二叉树的根节点 p 和 q ,编写一个函数来检验这两棵树是否相同。
    如果两个树在结构上相同,并且节点具有相同的值,则认为它们是相同的。
    示例 1:

输入:p = [1,2,3], q = [1,2,3]
输出:true
示例 2:

输入:p = [1,2], q = [1,null,2]
输出:false
示例 3:

输入:p = [1,2,1], q = [1,1,2]
输出:false
/**

  • Definition for a binary tree node.
  • public class TreeNode {
  • int val;
    
  • TreeNode left;
    
  • TreeNode right;
    
  • TreeNode() {}
    
  • TreeNode(int val) { this.val = val; }
    
  • TreeNode(int val, TreeNode left, TreeNode right) {
    
  •     this.val = val;
    
  •     this.left = left;
    
  •     this.right = right;
    
  • }
    
  • }
    */
    class Solution {
    public boolean isSameTree(TreeNode p, TreeNode q) {
    if (p == null && q == null) {
    return true;
    } else if (p == null || q == null) {
    return false;
    } else if (p.val != q.val) {
    return false;
    } else {
    return isSameTree(p.left, q.left) && isSameTree(p.right, q.right);
    }
    }
    }
  1. 将有序数组转换为二叉搜索树
    给你一个整数数组 nums ,其中元素已经按 升序 排列,请你将其转换为一棵 高度平衡 二叉搜索树。
    高度平衡 二叉树是一棵满足「每个节点的左右两个子树的高度差的绝对值不超过 1 」的二叉树。

/**

  • Definition for a binary tree node.

  • public class TreeNode {

  • int val;
    
  • TreeNode left;
    
  • TreeNode right;
    
  • TreeNode() {}
    
  • TreeNode(int val) { this.val = val; }
    
  • TreeNode(int val, TreeNode left, TreeNode right) {
    
  •     this.val = val;
    
  •     this.left = left;
    
  •     this.right = right;
    
  • }
    
  • }
    */
    class Solution {
    public TreeNode sortedArrayToBST(int[] nums) {
    return helper(nums, 0, nums.length - 1);
    }
    public TreeNode helper(int[] nums, int left, int right) {
    if (left > right) {
    return null;
    }
    // 总是选择中间位置左边的数字作为根节点
    int mid = (left + right) / 2;

     TreeNode root = new TreeNode(nums[mid]);
     root.left = helper(nums, left, mid - 1);
     root.right = helper(nums, mid + 1, right);
     return root;
    

    }
    }

  1. 二叉树的锯齿形层序遍历
    给定一个二叉树,返回其节点值的锯齿形层序遍历。(即先从左往右,再从右往左进行下一层遍历,以此类推,层与层之间交替进行)。

例如:
给定二叉树 [3,9,20,null,null,15,7],

/**

  • Definition for a binary tree node.

  • public class TreeNode {

  • int val;
    
  • TreeNode left;
    
  • TreeNode right;
    
  • TreeNode() {}
    
  • TreeNode(int val) { this.val = val; }
    
  • TreeNode(int val, TreeNode left, TreeNode right) {
    
  •     this.val = val;
    
  •     this.left = left;
    
  •     this.right = right;
    
  • }
    
  • }
    */
    class Solution {
    public List<List> zigzagLevelOrder(TreeNode root) {
    List<List> ans = new LinkedList<List>();
    if (root == null) {
    return ans;
    }

     Queue<TreeNode> nodeQueue = new LinkedList<TreeNode>();
     nodeQueue.offer(root);
     boolean isOrderLeft = true;
    
     while (!nodeQueue.isEmpty()) {
         Deque<Integer> levelList = new LinkedList<Integer>();
         int size = nodeQueue.size();
         for (int i = 0; i < size; ++i) {
             TreeNode curNode = nodeQueue.poll();
             if (isOrderLeft) {
                 levelList.offerLast(curNode.val);
             } else {
                 levelList.offerFirst(curNode.val);
             }
             if (curNode.left != null) {
                 nodeQueue.offer(curNode.left);
             }
             if (curNode.right != null) {
                 nodeQueue.offer(curNode.right);
             }
         }
         ans.add(new LinkedList<Integer>(levelList));
         isOrderLeft = !isOrderLeft;
     }
    
     return ans;
    

    }
    }

  1. 左叶子之和

/**

  • Definition for a binary tree node.

  • public class TreeNode {

  • int val;
    
  • TreeNode left;
    
  • TreeNode right;
    
  • TreeNode() {}
    
  • TreeNode(int val) { this.val = val; }
    
  • TreeNode(int val, TreeNode left, TreeNode right) {
    
  •     this.val = val;
    
  •     this.left = left;
    
  •     this.right = right;
    
  • }
    
  • }
    */
    class Solution {

    public int sumOfLeftLeaves(TreeNode root) {
    if(rootnull) return 0;
    return sumOfLeftLeaves(root.left)
    + sumOfLeftLeaves(root.right)
    + (root.left!=null && root.left.left
    null && root.left.right==null ? root.left.val : 0);
    }
    }

  1. 路径总和 III
    给定一个二叉树的根节点 root ,和一个整数 targetSum ,求该二叉树里节点值之和等于 targetSum 的 路径 的数目。

路径 不需要从根节点开始,也不需要在叶子节点结束,但是路径方向必须是向下的(只能从父节点到子节点)。

输入:root = [10,5,-3,3,2,null,11,3,-2,null,1], targetSum = 8
输出:3
解释:和等于 8 的路径有 3 条,如图所示。
示例 2:

输入:root = [5,4,8,11,null,13,4,7,2,null,null,5,1], targetSum = 22
输出:3
/**

  • Definition for a binary tree node.

  • public class TreeNode {

  • int val;
    
  • TreeNode left;
    
  • TreeNode right;
    
  • TreeNode() {}
    
  • TreeNode(int val) { this.val = val; }
    
  • TreeNode(int val, TreeNode left, TreeNode right) {
    
  •     this.val = val;
    
  •     this.left = left;
    
  •     this.right = right;
    
  • }
    
  • }
    */
    class Solution {
    int count=0;
    public int pathSum(TreeNode root, int sum) {
    Map<Integer,Integer> pre=new HashMap<>();
    pre.put(0,1);
    dfs(root,sum,pre,0);
    return count;
    }

    public void dfs(TreeNode root, int sum,Map<Integer,Integer> pre,int cur){

     if(root==null){
         return;
     }
    
     cur+=root.val;
     count+=pre.getOrDefault(cur-sum,0);
     pre.put(cur,pre.getOrDefault(cur,0)+1);
    
     dfs(root.left,sum,pre,cur);
     dfs(root.right,sum,pre,cur);
    
     pre.put(cur,pre.getOrDefault(cur,0)-1); 
    

    }
    }

数组:
*剑指 Offer 03. 数组中重复的数字
找出数组中重复的数字。

在一个长度为 n 的数组 nums 里的所有数字都在 0~n-1 的范围内。数组中某些数字是重复的,但不知道有几个数字重复了,也不知道每个数字重复了几次。请找出数组中任意一个重复的数字。

示例 1:

输入:
[2, 3, 1, 0, 2, 5, 3]
输出:2 或 3
class Solution {
public int findRepeatNumber(int[] nums) {
Set set=new HashSet();
for(int i=0;i<nums.length;i++){
if(set.contains(nums[i])){
return nums[i];
}else{
set.add(nums[i]);
}
}
// int temp;
// for(int i=0;i<nums.length;i++){
// while (nums[i]!=i){
// if(nums[i]==nums[nums[i]]){
// return nums[i];
// }
// temp=nums[i];
// nums[i]=nums[temp];
// nums[temp]=temp;
// }
// }
return -1;
}
}
*剑指 Offer 04. 二维数组中的查找
在一个 n * m 的二维数组中,每一行都按照从左到右递增的顺序排序,每一列都按照从上到下递增的顺序排序。请完成一个高效的函数,输入这样的一个二维数组和一个整数,判断数组中是否含有该整数。

示例:

现有矩阵 matrix 如下:

[
[1, 4, 7, 11, 15],
[2, 5, 8, 12, 19],
[3, 6, 9, 16, 22],
[10, 13, 14, 17, 24],
[18, 21, 23, 26, 30]
]
给定 target = 5,返回 true。

给定 target = 20,返回 false。
class Solution {
public boolean findNumberIn2DArray(int[][] matrix, int target) {
int i = matrix.length - 1, j = 0; //i表示行,j表示列
while(i >= 0 && j < matrix[0].length)
{
if(matrix[i][j] > target) i–;
else if(matrix[i][j] < target) j++;
else return true;
}
return false;
}
}
*剑指 Offer 11. 旋转数组的最小数字
把一个数组最开始的若干个元素搬到数组的末尾,我们称之为数组的旋转。输入一个递增排序的数组的一个旋转,输出旋转数组的最小元素。例如,数组 [3,4,5,1,2] 为 [1,2,3,4,5] 的一个旋转,该数组的最小值为1。

示例 1:

输入:[3,4,5,1,2]
输出:1
示例 2:

输入:[2,2,2,0,1]
输出:0
class Solution {
public int minArray(int[] numbers) {
int temp=0;
for(int i=1;i<numbers.length;i++){
if(numbers[i]<numbers[0]){
temp=numbers[i];
numbers[i]=numbers[0];
numbers[0]=temp;
}
}
return numbers[0];
}
}
*剑指 Offer 21. 调整数组顺序使奇数位于偶数前面
输入一个整数数组,实现一个函数来调整该数组中数字的顺序,使得所有奇数位于数组的前半部分,所有偶数位于数组的后半部分。

示例:

输入:nums = [1,2,3,4]
输出:[1,3,2,4]
注:[3,1,2,4] 也是正确的答案之一。
class Solution {
public int[] exchange(int[] nums) {
int i=0,j=0;
while(j<nums.length){
if((nums[j]&1)!=0){ // nums[j]&1== nums[j]%2
int tmp=nums[i];
nums[i]=nums[j];
nums[j]=tmp;
i++;
}
j++;
}
return nums;
}
}
栈:
*剑指 Offer 09. 用两个栈实现队列
用两个栈实现一个队列。队列的声明如下,请实现它的两个函数 appendTail 和 deleteHead ,分别完成在队列尾部插入整数和在队列头部删除整数的功能。(若队列中没有元素,deleteHead 操作返回 -1 )

示例 1:

输入:
[“CQueue”,“appendTail”,“deleteHead”,“deleteHead”]
[[],[3],[],[]]
输出:[null,null,3,-1]
示例 2:

输入:
[“CQueue”,“deleteHead”,“appendTail”,“appendTail”,“deleteHead”,“deleteHead”]
[[],[],[5],[2],[],[]]
输出:[null,-1,null,null,5,2]
class CQueue {
LinkedList stack1;
LinkedList stack2;

public CQueue() {
    stack1 = new LinkedList<Integer>();
    stack2 = new LinkedList<Integer>();
}

public void appendTail(int value) {
    stack1.push(value);
}

public int deleteHead() {
    // 如果第二个栈为空
    if (stack2.isEmpty()) {
        while (!stack1.isEmpty()) {
            stack2.push(stack1.pop());
        }
    } 
    if (stack2.isEmpty()) {
        return -1;
    } else {
        int deleteItem = stack2.pop();
        return deleteItem;
    }
}

}

/**

  • Your CQueue object will be instantiated and called as such:
  • CQueue obj = new CQueue();
  • obj.appendTail(value);
  • int param_2 = obj.deleteHead();
    */
    剑指 Offer 31. 栈的压入、弹出序列
    输入两个整数序列,第一个序列表示栈的压入顺序,请判断第二个序列是否为该栈的弹出顺序。假设压入栈的所有数字均不相等。例如,序列 {1,2,3,4,5} 是某栈的压栈序列,序列 {4,5,3,2,1} 是该压栈序列对应的一个弹出序列,但 {4,3,5,1,2} 就不可能是该压栈序列的弹出序列。

示例 1:

输入:pushed = [1,2,3,4,5], popped = [4,5,3,2,1]
输出:true
解释:我们可以按以下顺序执行:
push(1), push(2), push(3), push(4), pop() -> 4,
push(5), pop() -> 5, pop() -> 3, pop() -> 2, pop() -> 1
示例 2:

输入:pushed = [1,2,3,4,5], popped = [4,3,5,1,2]
输出:false
解释:1 不能在 2 之前弹出。
class Solution {
public boolean validateStackSequences(int[] pushed, int[] popped) {
Stack stack = new Stack<>();
int i = 0;
for(int num : pushed) {
stack.push(num); // num 入栈
while(!stack.isEmpty() && stack.peek() == popped[i]) { // 循环判断与出栈
stack.pop();
i++;
}
}
return stack.isEmpty();
}
}
*20. 有效的括号(栈)
给定一个只包括 ‘(’,‘)’,‘{’,‘}’,‘[’,‘]’ 的字符串 s ,判断字符串是否有效。

有效字符串需满足:

左括号必须用相同类型的右括号闭合。
左括号必须以正确的顺序闭合。

示例 1:

输入:s = “()”
输出:true
示例 2:

输入:s = “()[]{}”
输出:true
示例 3:

输入:s = “(]”
输出:false
示例 4:

输入:s = “([)]”
输出:false
示例 5:

输入:s = “{[]}”
输出:true
class Solution {
public boolean isValid(String s) {
Stack stack=new Stack();
char[] c=s.toCharArray();
for(int i=0;i<c.length;i++){
if(c[i]‘[’ || c[i]‘{’ || c[i]‘(’){
stack.push(c[i]);
}else{
if(stack.isEmpty()){
return false;
}else{
if(stack.peek()
‘(’ && c[i]‘)’ || stack.peek()‘{’ && c[i]‘}’ || stack.peek()‘[’ && c[i]==‘]’){
stack.pop();
}else{
return false;
}
}
}
}
if(stack.isEmpty()){
return true;
}else{
return false;
}
}
}
矩阵:
剑指 Offer 12. 矩阵中的路径
给定一个 m x n 二维字符网格 board 和一个字符串单词 word 。如果 word 存在于网格中,返回 true ;否则,返回 false 。

单词必须按照字母顺序,通过相邻的单元格内的字母构成,其中“相邻”单元格是那些水平相邻或垂直相邻的单元格。同一个单元格内的字母不允许被重复使用。

例如,在下面的 3×4 的矩阵中包含单词 “ABCCED”(单词中的字母已标出)。

示例 1:

输入:board = [[“A”,“B”,“C”,“E”],[“S”,“F”,“C”,“S”],[“A”,“D”,“E”,“E”]], word = “ABCCED”
输出:true
示例 2:

输入:board = [[“a”,“b”],[“c”,“d”]], word = “abcd”
输出:false
class Solution {
public boolean exist(char[][] board, String word) {
char[] words = word.toCharArray();
for(int i = 0; i < board.length; i++) {
for(int j = 0; j < board[0].length; j++) {
if(dfs(board, words, i, j, 0)) return true;
}
}
return false;
}
boolean dfs(char[][] board, char[] word, int i, int j, int k) {
if(i >= board.length || i < 0 || j >= board[0].length || j < 0 || board[i][j] != word[k]) return false;
if(k == word.length - 1) return true;
board[i][j] = ‘\0’;
boolean 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;
}
}
剑指 Offer 29. 顺时针打印矩阵

输入一个矩阵,按照从外向里以顺时针的顺序依次打印出每一个数字。

示例 1:
输入:matrix = [[1,2,3],[4,5,6],[7,8,9]]
输出:[1,2,3,6,9,8,7,4,5]
示例 2:
输入:matrix = [[1,2,3,4],[5,6,7,8],[9,10,11,12]]
输出:[1,2,3,4,8,12,11,10,9,5,6,7]
class Solution {
public int[] spiralOrder(int[][] matrix) {
if(matrix.length == 0) return new int[0];
int l = 0, r = matrix[0].length - 1, t = 0, b = matrix.length - 1, x = 0;
int[] res = new int[(r + 1) * (b + 1)];
while(true) {
for(int i = l; i <= r; i++) res[x++] = matrix[t][i]; // left to right.
if(++t > b) break;
for(int i = t; i <= b; i++) res[x++] = matrix[i][r]; // top to bottom.
if(l > --r) break;
for(int i = r; i >= l; i–) res[x++] = matrix[b][i]; //
right to left.
if(t > --b) break;
for(int i = b; i >= t; i–) res[x++] = matrix[i][l]; //
bottom to top.
if(++l > r) break;
}
return res;
}

}
54. 螺旋矩阵
给你一个 m 行 n 列的矩阵 matrix ,请按照 顺时针螺旋顺序 ,返回矩阵中的所有元素。

示例 1:

输入:matrix = [[1,2,3],[4,5,6],[7,8,9]]
输出:[1,2,3,6,9,8,7,4,5]
示例 2:

输入:matrix = [[1,2,3,4],[5,6,7,8],[9,10,11,12]]
输出:[1,2,3,4,8,12,11,10,9,5,6,7]
class Solution {
public List spiralOrder(int[][] matrix) {
List list = new ArrayList();
if(matrix.length == 0) return list;
int l = 0, r = matrix[0].length - 1, t = 0, b = matrix.length - 1, x = 0;
while(true) {
for(int i = l; i <= r; i++) list.add(matrix[t][i]); // left to right.
if(++t > b) break;
for(int i = t; i <= b; i++) list.add(matrix[i][r]); // top to bottom.
if(l > --r) break;
for(int i = r; i >= l; i–) list.add(matrix[b][i]);//right to
left.
if(t > --b) break;
for(int i = b; i >= t; i–) list.add(matrix[i][l]); // bottom
to top.
if(++l > r) break;
}
return list;
}
}
378. 有序矩阵中第 K 小的元素
给你一个 n x n 矩阵 matrix ,其中每行和每列元素均按升序排序,找到矩阵中第 k 小的元素。
请注意,它是 排序后 的第 k 小元素,而不是第 k 个 不同 的元素。

示例 1:

输入:matrix = [[1,5,9],[10,11,13],[12,13,15]], k = 8
输出:13
解释:矩阵中的元素为 [1,5,9,10,11,12,13,13,15],第 8 小元素是 13
示例 2:

输入:matrix = [[-5]], k = 1
输出:-5
class Solution {
public int kthSmallest(int[][] matrix, int k) {
int rows = matrix.length, columns = matrix[0].length;
int[] sorted = new int[rows * columns];
int index = 0;
for (int[] row : matrix) {
for (int num : row) {
sorted[index++] = num;
}
}
Arrays.sort(sorted);
return sorted[k - 1];
}
}

二分查找:
剑指 Offer 53 - I. 在排序数组中查找数字 I
统计一个数字在排序数组中出现的次数。

示例 1:

输入: nums = [5,7,7,8,8,10], target = 8
输出: 2
示例 2:

输入: nums = [5,7,7,8,8,10], target = 6
输出: 0
class Solution {
public int search(int[] nums, int target) {
// 搜索右边界 right
int i = 0, j = nums.length - 1;
while(i <= j) {
int m = (i + j) / 2;
if(nums[m] <= target) i = m + 1;
else j = m - 1;
}
int right = i;
// 若数组中无 target ,则提前返回
if(j >= 0 && nums[j] != target) return 0;
// 搜索左边界 right
i = 0; j = nums.length - 1;
while(i <= j) {
int m = (i + j) / 2;
if(nums[m] < target) i = m + 1;
else j = m - 1;
}
int left = j;
return right - left - 1;
}
}
*剑指 Offer 57. 和为s的两个数字(二分查找)
输入一个递增排序的数组和一个数字s,在数组中查找两个数,使得它们的和正好是s。如果有多对数字的和等于s,则输出任意一对即可。

示例 1:

输入:nums = [2,7,11,15], target = 9
输出:[2,7] 或者 [7,2]
示例 2:

输入:nums = [10,26,30,31,47,60], target = 40
输出:[10,30] 或者 [30,10]
class Solution {
public int[] twoSum(int[] nums, int target) {
int i=0,j=nums.length-1;
while(i<j){
int s=nums[i]+nums[j];
if(s>target){
j–;
}
else if(s<target){
i++;
}
else return new int[] { nums[i], nums[j] };
}
return new int[0];
}
}
704. 二分查找
给定一个 n 个元素有序的(升序)整型数组 nums 和一个目标值 target ,写一个函数搜索 nums 中的 target,如果目标值存在返回下标,否则返回 -1。

示例 1:

输入: nums = [-1,0,3,5,9,12], target = 9
输出: 4
解释: 9 出现在 nums 中并且下标为 4
示例 2:

输入: nums = [-1,0,3,5,9,12], target = 2
输出: -1
解释: 2 不存在 nums 中因此返回 -1
class Solution {
public int search(int[] nums, int target) {
int i=0,j=nums.length-1;
while(i<=j){
int mid=(i+j)/2;
if(nums[mid]>target)j=mid-1;
else if(nums[mid]<target)i=mid+1;
else return mid;
}
return -1;
}
}
35. 搜索插入位置(二分查找)
给定一个排序数组和一个目标值,在数组中找到目标值,并返回其索引。如果目标值不存在于数组中,返回它将会被按顺序插入的位置。

请必须使用时间复杂度为 O(log n) 的算法。

示例 1:

输入: nums = [1,3,5,6], target = 5
输出: 2
示例 2:

输入: nums = [1,3,5,6], target = 2
输出: 1
示例 3:

输入: nums = [1,3,5,6], target = 7
输出: 4
示例 4:

输入: nums = [1,3,5,6], target = 0
输出: 0
示例 5:

输入: nums = [1], target = 0
输出: 0
class Solution {
public int searchInsert(int[] nums, int target) {
int n = nums.length;
int left = 0, right = n - 1, ans = n;
while (left <= right) {
int mid = ((right - left) >> 1) + left;
if (target <= nums[mid]) {
ans = mid;
right = mid - 1;
} else {
left = mid + 1;
}
}
return ans;
}
}
贪心算法:
贪心算法(又称贪婪算法)是指,在对问题求解时,总是做出在当前看来是最好的选择,就能得到问题的答案。贪心算法需要充分挖掘题目中条件,没有固定的模式,解决有贪心算法需要一定的直觉和经验。
贪心算法不是对所有问题都能得到整体最优解。能使用贪心算法解决的问题具有「贪心选择性质」。「贪心选择性质」严格意义上需要数学证明。能使用贪心算法解决的问题必须具备「无后效性」,即某个状态以前的过程不会影响以后的状态,只与当前状态有关。

  1. 盛最多水的容器
    给你 n 个非负整数 a1,a2,…,an,每个数代表坐标中的一个点 (i, ai) 。在坐标内画 n 条垂直线,垂直线 i 的两个端点分别为 (i, ai) 和 (i, 0) 。找出其中的两条线,使得它们与 x 轴共同构成的容器可以容纳最多的水。

说明:你不能倾斜容器。

class Solution {
public int maxArea(int[] height) {
int l = 0, r = height.length - 1;
int ans = 0;
while (l < r) {
int area = Math.min(height[l], height[r]) * (r - l);
ans = Math.max(ans, area);
if (height[l] <= height[r]) {
++l;
}
else {
–r;
}
}
return ans;
}
}
134. 加油站
在一条环路上有 N 个加油站,其中第 i 个加油站有汽油 gas[i] 升。

你有一辆油箱容量无限的的汽车,从第 i 个加油站开往第 i+1 个加油站需要消耗汽油 cost[i] 升。你从其中的一个加油站出发,开始时油箱为空。

如果你可以绕环路行驶一周,则返回出发时加油站的编号,否则返回 -1。

说明:

如果题目有解,该答案即为唯一答案。
输入数组均为非空数组,且长度相同。
输入数组中的元素均为非负数。
示例 1:

输入:
gas = [1,2,3,4,5]
cost = [3,4,5,1,2]

输出: 3

解释:
从 3 号加油站(索引为 3 处)出发,可获得 4 升汽油。此时油箱有 = 0 + 4 = 4 升汽油
开往 4 号加油站,此时油箱有 4 - 1 + 5 = 8 升汽油
开往 0 号加油站,此时油箱有 8 - 2 + 1 = 7 升汽油
开往 1 号加油站,此时油箱有 7 - 3 + 2 = 6 升汽油
开往 2 号加油站,此时油箱有 6 - 4 + 3 = 5 升汽油
开往 3 号加油站,你需要消耗 5 升汽油,正好足够你返回到 3 号加油站。
因此,3 可为起始索引。
示例 2:

输入:
gas = [2,3,4]
cost = [3,4,3]

输出: -1

解释:
你不能从 0 号或 1 号加油站出发,因为没有足够的汽油可以让你行驶到下一个加油站。
我们从 2 号加油站出发,可以获得 4 升汽油。 此时油箱有 = 0 + 4 = 4 升汽油
开往 0 号加油站,此时油箱有 4 - 3 + 2 = 3 升汽油
开往 1 号加油站,此时油箱有 3 - 3 + 3 = 3 升汽油
你无法返回 2 号加油站,因为返程需要消耗 4 升汽油,但是你的油箱只有 3 升汽油。
因此,无论怎样,你都不可能绕环路行驶一周。

class Solution {
public int canCompleteCircuit(int[] gas, int[] cost) {
int n = gas.length;
int i = 0;
while (i < n) {
int sumOfGas = 0, sumOfCost = 0;
int cnt = 0;
while (cnt < n) {
int j = (i + cnt) % n;
sumOfGas += gas[j];
sumOfCost += cost[j];
if (sumOfCost > sumOfGas) {
break;
}
cnt++;
}
if (cnt == n) {
return i;
} else {
i = i + cnt + 1;
}
}
return -1;
}
}

  1. 分发糖果
    老师想给孩子们分发糖果,有 N 个孩子站成了一条直线,老师会根据每个孩子的表现,预先给他们评分。

你需要按照以下要求,帮助老师给这些孩子分发糖果:

每个孩子至少分配到 1 个糖果。
评分更高的孩子必须比他两侧的邻位孩子获得更多的糖果。
那么这样下来,老师至少需要准备多少颗糖果呢?

示例 1:

输入:[1,0,2]
输出:5
解释:你可以分别给这三个孩子分发 2、1、2 颗糖果。
示例 2:

输入:[1,2,2]
输出:4
解释:你可以分别给这三个孩子分发 1、2、1 颗糖果。
第三个孩子只得到 1 颗糖果,这已满足上述两个条件。
class Solution {
public int candy(int[] ratings) {
int size = ratings.length;
int[] c_d = new int[size];
for (int i = 0; i < size; i++) {
c_d[i] = 1;
}
int count = 0;
for (int i = 1; i < ratings.length; i++) {
if (ratings[i] > ratings[i - 1]) {
c_d[i] = c_d[i - 1] + 1;
}
}
for (int i = ratings.length - 1; i > 0; i–) {
if (ratings[i - 1] > ratings[i]) {
c_d[i - 1] = Math.max(c_d[i - 1], c_d[i] + 1);
}
}
for (int i = 0; i < c_d.length; i++) {
count += c_d[i];
}
return count;
}
}
*409. 最长回文串(hashset)
给定一个包含大写字母和小写字母的字符串,找到通过这些字母构造成的最长的回文串。

在构造过程中,请注意区分大小写。比如 “Aa” 不能当做一个回文字符串。

注意:
假设字符串的长度不会超过 1010。

示例 1:

输入:
“abccccdd”

输出:
7

解释:
我们可以构造的最长的回文串是"dccaccd", 它的长度是 7。
class Solution {
public int longestPalindrome(String s) {
if(s.length()==0)
return 0;
char[] c =s.toCharArray();
int count=0;
HashSet set=new HashSet();
for(int i=0;i<c.length;i++){
if(!set.contains(c[i])){
set.add(c[i]);
}else{
set.remove(c[i]);
count++;
}
}
return set.isEmpty()?count2:count2+1;
}
}
435. 无重叠区间
给定一个区间的集合,找到需要移除区间的最小数量,使剩余区间互不重叠。

注意:

可以认为区间的终点总是大于它的起点。
区间 [1,2] 和 [2,3] 的边界相互“接触”,但没有相互重叠。
示例 1:

输入: [ [1,2], [2,3], [3,4], [1,3] ]

输出: 1

解释: 移除 [1,3] 后,剩下的区间没有重叠。
示例 2:

输入: [ [1,2], [1,2], [1,2] ]

输出: 2

解释: 你需要移除两个 [1,2] 来使剩下的区间没有重叠。
示例 3:

输入: [ [1,2], [2,3] ]

输出: 0

解释: 你不需要移除任何区间,因为它们已经是无重叠的了。
class Solution {
public int eraseOverlapIntervals(int[][] intervals) {
if (intervals.length == 0) {
return 0;
}

    Arrays.sort(intervals, new Comparator<int[]>() {
        public int compare(int[] interval1, int[] interval2) {
            return interval1[1] - interval2[1];
        }
    });

    int n = intervals.length;
    int right = intervals[0][1];
    int ans = 1;
    for (int i = 1; i < n; ++i) {
        if (intervals[i][0] >= right) {
            ++ans;
            right = intervals[i][1];
        }
    }
    return n - ans;
}

}
581. 最短无序连续子数组
给你一个整数数组 nums ,你需要找出一个 连续子数组 ,如果对这个子数组进行升序排序,那么整个数组都会变为升序排序。

请你找出符合题意的 最短 子数组,并输出它的长度。

示例 1:

输入:nums = [2,6,4,8,10,9,15]
输出:5
解释:你只需要对 [6, 4, 8, 10, 9] 进行升序排序,那么整个表都会变为升序排序。
示例 2:

输入:nums = [1,2,3,4]
输出:0
示例 3:

输入:nums = [1]
输出:0
class Solution {
public int findUnsortedSubarray(int[] nums) {
int n = nums.length;
int maxn = Integer.MIN_VALUE, right = -1;
int minn = Integer.MAX_VALUE, left = -1;
for (int i = 0; i < n; i++) {
if (maxn > nums[i]) {
right = i;
} else {
maxn = nums[i];
}
if (minn < nums[n - i - 1]) {
left = n - i - 1;
} else {
minn = nums[n - i - 1];
}
}
return right == -1 ? 0 : right - left + 1;
}
}
其余未归类
*剑指 Offer 05. 替换空格
请实现一个函数,把字符串 s 中的每个空格替换成"%20"。

示例 1:

输入:s = “We are happy.”
输出:“We%20are%20happy.”
class Solution {
public String replaceSpace(String s) {
return s.replace(" ", “%20”);
}
}

*剑指 Offer 10- I. 斐波那契数列
写一个函数,输入 n ,求斐波那契(Fibonacci)数列的第 n 项(即 F(N))。斐波那契数列的定义如下:

F(0) = 0, F(1) = 1
F(N) = F(N - 1) + F(N - 2), 其中 N > 1.
斐波那契数列由 0 和 1 开始,之后的斐波那契数就是由之前的两数相加而得出。

答案需要取模 1e9+7(1000000007),如计算初始结果为:1000000008,请返回 1。

示例 1:

输入:n = 2
输出:1
示例 2:

输入:n = 5
输出:5
class Solution {
public int fib(int n) {
int a=0,b=1,sum=0;
for(int i=0;i<n;i++){
sum=(a+b)%1000000007;
a=b;
b=sum;
}
return a;
}
}
*剑指 Offer 10- II. 青蛙跳台阶问题
一只青蛙一次可以跳上1级台阶,也可以跳上2级台阶。求该青蛙跳上一个 n 级的台阶总共有多少种跳法。

答案需要取模 1e9+7(1000000007),如计算初始结果为:1000000008,请返回 1。

示例 1:

输入:n = 2
输出:2
示例 2:

输入:n = 7
输出:21
示例 3:

输入:n = 0
输出:1
class Solution {
public int numWays(int n) {
int a=1,b=1,sum=0;
for(int i=0;i<n;i++){
sum=(a+b)%1000000007;
a=b;
b=sum;
}
return a;
}
}

剑指 Offer 13. 机器人的运动范围
地上有一个m行n列的方格,从坐标 [0,0] 到坐标 [m-1,n-1] 。一个机器人从坐标 [0, 0] 的格子开始移动,它每次可以向左、右、上、下移动一格(不能移动到方格外),也不能进入行坐标和列坐标的数位之和大于k的格子。例如,当k为18时,机器人能够进入方格 [35, 37] ,因为3+5+3+7=18。但它不能进入方格 [35, 38],因为3+5+3+8=19。请问该机器人能够到达多少个格子?

示例 1:

输入:m = 2, n = 3, k = 1
输出:3
示例 2:

输入:m = 3, n = 1, k = 0
输出:1
class Solution {
int m, n, k;
boolean[][] visited;
public int movingCount(int m, int n, int k) {
this.m = m; this.n = n; this.k = k;
this.visited = new boolean[m][n];
return dfs(0, 0, 0, 0);
}
public int dfs(int i, int j, int si, int sj) {
if(i >= m || j >= n || k < si + sj || visited[i][j]) return 0;
visited[i][j] = true;
return 1 + dfs(i + 1, j, (i + 1) % 10 != 0 ? si + 1 : si - 8, sj) + dfs(i, j + 1, si, (j + 1) % 10 != 0 ? sj + 1 : sj - 8);
}
}
*剑指 Offer 15. 二进制中1的个数
请实现一个函数,输入一个整数(以二进制串形式),输出该数二进制表示中 1 的个数。例如,把 9 表示成二进制是 1001,有 2 位是 1。因此,如果输入 9,则该函数输出 2。

示例 1:

输入:00000000000000000000000000001011
输出:3
解释:输入的二进制串 00000000000000000000000000001011 中,共有三位为 ‘1’。
示例 2:

输入:00000000000000000000000010000000
输出:1
解释:输入的二进制串 00000000000000000000000010000000 中,共有一位为 ‘1’。
示例 3:

输入:11111111111111111111111111111101
输出:31
解释:输入的二进制串 11111111111111111111111111111101 中,共有 31 位为 ‘1’。

提示:

输入必须是长度为 32 的 二进制串 。
public class Solution {
// you need to treat n as an unsigned value
public int hammingWeight(int n) {
int res = 0;
while(n != 0) {
res++;
n &= n - 1; //n=n&(n-1);
}
return res;
}
}
*剑指 Offer 16. 数值的整数次方
实现 pow(x, n) ,即计算 x 的 n 次幂函数(即,xn)。不得使用库函数,同时不需要考虑大数问题。

示例 1:

输入:x = 2.00000, n = 10
输出:1024.00000
示例 2:

输入:x = 2.10000, n = 3
输出:9.26100
示例 3:

输入:x = 2.00000, n = -2
输出:0.25000
解释:2-2 = 1/22 = 1/4 = 0.25

提示:

-100.0 < x < 100.0
-231 <= n <= 231-1
-104 <= xn <= 104

class Solution {
public double myPow(double x, int n) {
if(x == 0) return 0;
long b = n;
double res = 1.0;
if(b < 0) {
x = 1 / x;
b = -b;
}
while(b > 0) {
if((b & 1) == 1) res *= x; //(b&1)<==>(b%2)
x *= x;
b >>= 1; //b=b>>1
}
return res;
}
}
算法流程:
1、当 x = 0 时:直接返回 0 (避免后续 x = 1 / x 操作报错)。
2、初始化 res = 1;
3、当 n < 0 时:把问题转化至 n≥0 的范围内,即执行 x = 1/x ,n = - n;
4、循环计算:当 n = 0 时跳出;
1、当 n & 1 = 1时:将当前 x 乘入 res (即 res *= x);
2、执行 x = x^2 (即 x *= x );
3、执行 n右移一位(即 n >>= 1)。
5返回 res 。

转化为位运算:
向下整除 n // 2等价于 右移一位 n >> 1 ;
取余数 n % 2 等价于 判断二进制最右一位值 n & 1 ;

*剑指 Offer 17. 打印从1到最大的n位数

输入数字 n,按顺序打印出从 1 到最大的 n 位十进制数。比如输入 3,则打印出 1、2、3 一直到最大的 3 位数 999。
示例 1:
输入: n = 1
输出: [1,2,3,4,5,6,7,8,9]

说明:
• 用返回一个整数列表来代替打印
• n 为正整数
• class Solution {
• public int[] printNumbers(int n) {
• int sum=1;
• for(int i=0;i<n;i++){
• sum*=10;
• }
• int[] a=new int[sum-1];
• for(int j=0;j<sum-1;j++){
• a[j]=j+1;
• }
• return a;
• }
• }

*剑指 Offer 39. 数组中出现次数超过一半的数字(难度:简单)(中位数)
数组中有一个数字出现的次数超过数组长度的一半,请找出这个数字。
你可以假设数组是非空的,并且给定的数组总是存在多数元素。
示例 1:
输入: [1, 2, 3, 2, 2, 2, 5, 4, 2]
输出: 2

限制:
1 <= 数组长度 <= 50000
class Solution {
public int majorityElement(int[] nums) {
// HashMap<Integer,Integer> map=new HashMap();
// int len = nums.length;
// for(int i = 0;i < len;i++){
// if(map.containsKey(nums[i])){
// map.put(nums[i],map.get(nums[i])+1);
// }
// else{
// map.put(nums[i],1);
// }
// }
// int num = 0;
// for (Integer key : map.keySet()){
// if(map.get(key) > len/2){
// num = key;
// break;
// }
// }
// return num;
Arrays.sort(nums);
int m=nums.length/2;
return nums[m];
}
}
*剑指 Offer 40. 最小的k个数(排序遍历前k)
输入整数数组 arr ,找出其中最小的 k 个数。例如,输入4、5、1、6、2、7、3、8这8个数字,则最小的4个数字是1、2、3、4。

示例 1:

输入:arr = [3,2,1], k = 2
输出:[1,2] 或者 [2,1]
示例 2:

输入:arr = [0,1,2,1], k = 1
输出:[0]
class Solution {
public int[] getLeastNumbers(int[] arr, int k) {
int[] res=new int[k];
Arrays.sort(arr);
for(int j=0;j<k;j++){
res[j]=arr[j];
}
return res;
}
}
剑指 Offer 42. 连续子数组的最大和(遍历、Math.max和Math.max)
输入一个整型数组,数组中的一个或连续多个整数组成一个子数组。求所有子数组的和的最大值。

要求时间复杂度为O(n)。

示例1:

输入: nums = [-2,1,-3,4,-1,2,1,-5,4]
输出: 6
解释: 连续子数组 [4,-1,2,1] 的和最大,为 6。
class Solution {
public int maxSubArray(int[] nums) {
int res = nums[0];
for(int i = 1; i < nums.length; i++) {
nums[i] += Math.max(nums[i - 1], 0);
res = Math.max(res, nums[i]);
}
return res;
}
}
*剑指 Offer 50. 第一个只出现一次的字符(hashmap)
在字符串 s 中找出第一个只出现一次的字符。如果没有,返回一个单空格。 s 只包含小写字母。

示例:

s = “abaccdeff”
返回 “b”

s = “”
返回 " "
class Solution {
public char firstUniqChar(String s) {
HashMap<Character,Boolean> dic=new HashMap();
char[] ch=s.toCharArray();
for(char c:ch)
dic.put(c,!dic.containsKey©);
for(char c:ch)
if(dic.get©)
return c;
return ’ ';

}

}

*剑指 Offer 53 - II. 0~n-1中缺失的数字(难度:简单)(循环遍历)
一个长度为n-1的递增排序数组中的所有数字都是唯一的,并且每个数字都在范围0~n-1之内。在范围0~n-1内的n个数字中有且只有一个数字不在该数组中,请找出这个数字。
示例 1:
输入: [0,1,3]
输出: 2
示例 2:
输入: [0,1,2,3,4,5,6,7,9]
输出: 8
代码:
class Solution {
public int missingNumber(int[] nums) {
int res=0;
for(int i=0;i<nums.length;i++){
if(nums[i]==res){ //相等res后移
res++; //
}
}
return res;
}
}

*剑指 Offer 56 - I. 数组中数字出现的次数(难度:中等)
一个整型数组 nums 里除两个数字之外,其他数字都出现了两次。请写程序找出这两个只出现一次的数字。要求时间复杂度是O(n),空间复杂度是O(1)。
示例 1:

输入:nums = [4,1,4,6]
输出:[1,6] 或 [6,1]
示例 2:

输入:nums = [1,2,10,4,1,4,3,3]
输出:[2,10] 或 [10,2]
代码:
class Solution {
public int[] singleNumbers(int[] nums) {
HashMap<Integer,Integer> map=new HashMap(); //定义一个HashMap
int m=0;
int[] result=new int[2]; //定义存储返回结果的数组
for(int i=0;i<nums.length;i++){
if(map.containsKey(nums[i])){ //判断map是否包含key,
map.put(nums[i],map.get(nums[i])+1); //存储每个key,以及对应的Value(元素的次数)
}else{
map.put(nums[i],1); //存储不包含的value的值为1
}
}
for(int res:map.keySet()){ //遍历key-value集合,根据key获取value=1的元素
if(map.get(res)==1){
result[m++]=res;
}
}
return result;
}
}
*剑指 Offer 56 - II. 数组中数字出现的次数 II

在一个数组 nums 中除一个数字只出现一次之外,其他数字都出现了三次。请找出那个只出现一次的数字。

示例 1:
输入:nums = [3,4,3,3]
输出:4
示例 2:
输入:nums = [9,1,7,9,7,9,7]
输出:1

• class Solution {
• public int singleNumber(int[] nums) {
• HashMap<Integer,Integer> map=new HashMap();
• int result=0;
• for(int i=0;i<nums.length;i++){
• if(map.containsKey(nums[i])){
• map.put(nums[i],map.get(nums[i])+1);
• }else{
• map.put(nums[i],1);
• }
• }
• for(int res:map.keySet()){
• if(map.get(res)==1){
• result=res;
• }
• }
• return result;
• }
• }

剑指 Offer 57 - II. 和为s的连续正数序列
输入一个正整数 target ,输出所有和为 target 的连续正整数序列(至少含有两个数)。

序列内的数字由小到大排列,不同序列按照首个数字从小到大排列。

示例 1:

输入:target = 9
输出:[[2,3,4],[4,5]]
示例 2:

输入:target = 15
输出:[[1,2,3,4,5],[4,5,6],[7,8]]
class Solution {
public int[][] findContinuousSequence(int target) {
List<int[]> vec = new ArrayList<int[]>();
for (int l = 1, r = 2; l < r;) {
int sum = (l + r) * (r - l + 1) / 2;
if (sum == target) {
int[] res = new int[r - l + 1];
for (int i = l; i <= r; ++i) {
res[i - l] = i;
}
vec.add(res);
l++;
} else if (sum < target) {
r++;
} else {
l++;
}
}
return vec.toArray(new int[vec.size()][]);
}
}

*剑指 Offer 58 - I. 翻转单词顺序
输入一个英文句子,翻转句子中单词的顺序,但单词内字符的顺序不变。为简单起见,标点符号和普通字母一样处理。例如输入字符串"I am a student. “,则输出"student. a am I”。

示例 1:

输入: “the sky is blue”
输出: “blue is sky the”
示例 2:

输入: " hello world! "
输出: “world! hello”
解释: 输入字符串可以在前面或者后面包含多余的空格,但是反转后的字符不能包括。
示例 3:

输入: “a good example”
输出: “example good a”
解释: 如果两个单词间有多余的空格,将反转后单词间的空格减少到只含一个。
class Solution {
public String reverseWords(String s) {
s = s.trim(); // 删除首尾空格
int j = s.length() - 1, i = j;
StringBuilder res = new StringBuilder();
while(i >= 0) {
while(i >= 0 && s.charAt(i) != ’ ') i–; // 搜索首个空格
res.append(s.substring(i + 1, j + 1) + " "); // 添加单词
while(i >= 0 && s.charAt(i) == ’ ') i–; // 跳过单词间空格
j = i; // j 指向下个单词的尾字符
}
return res.toString().trim(); // 转化为字符串并返回
}
}

*剑指 Offer 58 - II. 左旋转字符串
字符串的左旋转操作是把字符串前面的若干个字符转移到字符串的尾部。请定义一个函数实现字符串左旋转操作的功能。比如,输入字符串"abcdefg"和数字2,该函数将返回左旋转两位得到的结果"cdefgab"。

示例 1:

输入: s = “abcdefg”, k = 2
输出: “cdefgab”
示例 2:

输入: s = “lrloseumgh”, k = 6
输出: “umghlrlose”
class Solution {
public String reverseLeftWords(String s, int n) {
return s.substring(n, s.length()) + s.substring(0, n);
}
}

*剑指 Offer 59 - I. 滑动窗口的最大值(难度:困难)(动态规划)
给定一个数组 nums 和滑动窗口的大小 k,请找出所有滑动窗口里的最大值。
示例:
输入: nums = [1,3,-1,-3,5,3,6,7], 和 k = 3
输出: [3,3,5,5,6,7]
解释:
滑动窗口的位置 最大值


[1 3 -1] -3 5 3 6 7 3
1 [3 -1 -3] 5 3 6 7 3
1 3 [-1 -3 5] 3 6 7 5
1 3 -1 [-3 5 3] 6 7 5
1 3 -1 -3 [5 3 6] 7 6
1 3 -1 -3 5 [3 6 7] 7
代码:
class Solution {
public int[] maxSlidingWindow(int[] nums, int k) {
int[] array = {};
int[] res=new int[nums.length-k+1];
if(nums.length==0){
return array;
}
for(int i=0;i<nums.length-k+1;i++){
int max=nums[i];
for(int j=i+1;j<i+k;j++){
if(nums[j]>max){
max=nums[j];
}
}
res[i]=max;
}
return res;
}
}
剑指 Offer 61. 扑克牌中的顺子(hashset、遍历、Math.min、Math.max)
从扑克牌中随机抽5张牌,判断是不是一个顺子,即这5张牌是不是连续的。2~10为数字本身,A为1,J为11,Q为12,K为13,而大、小王为 0 ,可以看成任意数字。A 不能视为 14。

示例 1:

输入: [1,2,3,4,5]
输出: True

示例 2:

输入: [0,0,1,2,5]
输出: True
class Solution {
public boolean isStraight(int[] nums) {
HashSet repeat = new HashSet<>();
int max = 0, min = 14;
for(int num : nums) {
if(num == 0) continue; // 跳过大小王
max = Math.max(max, num); // 最大牌
min = Math.min(min, num); // 最小牌
if(repeat.contains(num)) return false; // 若有重复,提前返回 false
repeat.add(num); // 添加此牌至 Set
}
return max - min < 5; // 最大牌 - 最小牌 < 5 则可构成顺子
}
}

*剑指 Offer 62. 圆圈中最后剩下的数字(遍历取余)
0,1,•••,n-1这n个数字排成一个圆圈,从数字0开始,每次从这个圆圈里删除第m个数字(删除后从下一个数字开始计数)。求出这个圆圈里剩下的最后一个数字。

例如,0、1、2、3、4这5个数字组成一个圆圈,从数字0开始每次删除第3个数字,则删除的前4个数字依次是2、0、4、1,因此最后剩下的数字是3。

示例 1:

输入: n = 5, m = 3
输出: 3
示例 2:

输入: n = 10, m = 17
输出: 2
class Solution {
public int lastRemaining(int n, int m) {
int res = 0;
for (int i = 2; i <= n; i++) {
res = (res + m) % i;
}
return res;
}
}
剑指 Offer 63. 股票的最大利润(难度:中等)(使用Math.min和Math.max)
假设把某股票的价格按照时间先后顺序存储在数组中,请问买卖该股票一次可能获得的最大利润是多少?
示例 1:
输入: [7,1,5,3,6,4]
输出: 5
解释: 在第 2 天(股票价格 = 1)的时候买入,在第 5 天(股票价格 = 6)的时候卖出,最大利润 = 6-1 = 5 。
注意利润不能是 7-1 = 6, 因为卖出价格需要大于买入价格。
示例 2:

输入: [7,6,4,3,1]
输出: 0
解释: 在这种情况下, 没有交易完成, 所以最大利润为 0。

代码:
class Solution {
public int maxProfit(int[] prices) {
if (prices == null || prices.length == 0)
return 0;
int maxPro = 0;//记录最大利润
int min = prices[0];//记录数组中访问过的最小值
for (int i = 1; i < prices.length; i++) {
min = Math.min(min, prices[i]);
maxPro = Math.max(prices[i] - min, maxPro);
}
return maxPro;
}
}
剑指 Offer 65. 不用加减乘除做加法
写一个函数,求两个整数之和,要求在函数体内不得使用 “+”、“-”、“
”、“/” 四则运算符号。

示例:

输入: a = 1, b = 1
输出: 2
class Solution {
public int add(int a, int b) {
while(b != 0) { // 当进位为 0 时跳出
int c = (a & b) << 1; // c = 进位
a ^= b; // a = 非进位和
b = c; // b = 进位
}
return a;
}
}

剑指 Offer 66. 构建乘积数组(先左乘,再右乘)
给定一个数组 A[0,1,…,n-1],请构建一个数组 B[0,1,…,n-1],其中 B[i] 的值是数组 A 中除了下标 i 以外的元素的积, 即 B[i]=A[0]×A[1]×…×A[i-1]×A[i+1]×…×A[n-1]。不能使用除法。

示例:

输入: [1,2,3,4,5]
输出: [120,60,40,30,24]
class Solution {
public int[] constructArr(int[] a) {
if(a.length==0)return new int[0];
int n = a.length;
int []lsy = new int[n];
int rey = 1;
for(int i=0;i<n;i++){
lsy[i] = rey;
rey *= a[i]; //先乘左
}
rey = 1;
for(int i = n-1;i>=0;i–){//再乘右
lsy[i] *= rey;
rey *= a[i];
}
return lsy;
}
}

  1. 岛屿数量(递归)
    给你一个由 ‘1’(陆地)和 ‘0’(水)组成的的二维网格,请你计算网格中岛屿的数量。

岛屿总是被水包围,并且每座岛屿只能由水平方向和/或竖直方向上相邻的陆地连接形成。

此外,你可以假设该网格的四条边均被水包围。

示例 1:

输入:grid = [
[“1”,“1”,“1”,“1”,“0”],
[“1”,“1”,“0”,“1”,“0”],
[“1”,“1”,“0”,“0”,“0”],
[“0”,“0”,“0”,“0”,“0”]
]
输出:1
示例 2:

输入:grid = [
[“1”,“1”,“0”,“0”,“0”],
[“1”,“1”,“0”,“0”,“0”],
[“0”,“0”,“1”,“0”,“0”],
[“0”,“0”,“0”,“1”,“1”]
]
输出:3
class Solution {
public int numIslands(char[][] grid) {
if (grid == null || grid.length == 0) {
return 0;
}

    int nr = grid.length;
    int nc = grid[0].length;
    int num_islands = 0;
    for (int r = 0; r < nr; ++r) {
        for (int c = 0; c < nc; ++c) {
            if (grid[r][c] == '1') {
                ++num_islands;
                dfs(grid, r, c);
            }
        }
    }
    return num_islands;
}
void dfs(char[][] grid, int r, int c) {
    int nr = grid.length;
    int nc = grid[0].length;

    if (r < 0 || c < 0 || r >= nr || c >= nc || grid[r][c] == '0') {
        return;
    }

    grid[r][c] = '0';
    dfs(grid, r - 1, c);
    dfs(grid, r + 1, c);
    dfs(grid, r, c - 1);
    dfs(grid, r, c + 1);
} 

}

*217. 存在重复元素(hashset+遍历)
给定一个整数数组,判断是否存在重复元素。

如果存在一值在数组中出现至少两次,函数返回 true 。如果数组中每个元素都不相同,则返回 false 。

示例 1:

输入: [1,2,3,1]
输出: true
示例 2:

输入: [1,2,3,4]
输出: false
示例 3:

输入: [1,1,1,3,3,4,3,2,4,2]
输出: true
class Solution {
public boolean containsDuplicate(int[] nums) {
Set visited=new HashSet();
for(int n:nums){
if(visited.contains(n)){
return true;
}else{
visited.add(n);
}
}
return false;
}
}
*219. 存在重复元素 II(hashmap+遍历)
给定一个整数数组和一个整数 k,判断数组中是否存在两个不同的索引 i 和 j,使得 nums [i] = nums [j],并且 i 和 j 的差的 绝对值 至多为 k。

示例 1:

输入: nums = [1,2,3,1], k = 3
输出: true
示例 2:

输入: nums = [1,0,1,1], k = 1
输出: true
示例 3:

输入: nums = [1,2,3,1,2,3], k = 2
输出: false
class Solution {
public boolean containsNearbyDuplicate(int[] nums, int k) {
HashMap<Integer,Integer> map=new HashMap<Integer,Integer>();
for(int i=0;i<nums.length;i++){
if(map.containsKey(nums[i]) && map.get(nums[i])!=i && Math.abs(map.get(nums[i])-i)<=k) {
return true;
}else{
map.put(nums[i],i);
}
}
return false;
}
}

  1. 前 K 个高频元素
    给你一个整数数组 nums 和一个整数 k ,请你返回其中出现频率前 k 高的元素。你可以按 任意顺序 返回答案。

示例 1:

输入: nums = [1,1,1,2,2,3], k = 2
输出: [1,2]
示例 2:

输入: nums = [1], k = 1
输出: [1]
class Solution {
public static int[] topKFrequent(int[] nums, int k) {
int[] res = new int[k]; // 结果数组
Map<Integer, Integer> map = new HashMap();
// 统计数组中各元素出现的次数
for(int num : nums){
if(map.containsKey(num)){
map.put(num, map.get(num) + 1);
}else{
map.put(num, 1);
}
}

    int maxTimes = 0;    // 出现最多的元素的出现次数
    // 找出出现次数最多的元素出现的次数
    for(Map.Entry<Integer, Integer> entry : map.entrySet()){
        if(entry.getValue() > maxTimes){
            maxTimes = entry.getValue();
        }
    }

    // 按出现次数从大到小添加到结果数组
    while(k > 0){
        for(Map.Entry<Integer, Integer> entry : map.entrySet()){
            if(entry.getValue() == maxTimes){
                res[k - 1] = entry.getKey();
                k--;
            }
        }
        maxTimes--;
    }

    return res;
}

}
695. 岛屿的最大面积(递归+num+=)
给定一个包含了一些 0 和 1 的非空二维数组 grid 。

一个 岛屿 是由一些相邻的 1 (代表土地) 构成的组合,这里的「相邻」要求两个 1 必须在水平或者竖直方向上相邻。你可以假设 grid 的四个边缘都被 0(代表水)包围着。

找到给定的二维数组中最大的岛屿面积。(如果没有岛屿,则返回面积为 0 。)

示例 1:

[[0,0,1,0,0,0,0,1,0,0,0,0,0],
[0,0,0,0,0,0,0,1,1,1,0,0,0],
[0,1,1,0,1,0,0,0,0,0,0,0,0],
[0,1,0,0,1,1,0,0,1,0,1,0,0],
[0,1,0,0,1,1,0,0,1,1,1,0,0],
[0,0,0,0,0,0,0,0,0,0,1,0,0],
[0,0,0,0,0,0,0,1,1,1,0,0,0],
[0,0,0,0,0,0,0,1,1,0,0,0,0]]
class Solution {
public int maxAreaOfIsland(int[][] grid) {
if (grid == null || grid.length == 0) {
return 0;
}

    int nr = grid.length;
    int nc = grid[0].length;
    int num_islands = 0;
    for (int r = 0; r < nr; ++r) {
        for (int c = 0; c < nc; ++c) {
            if (grid[r][c] == 1) {
                num_islands=Math.max(num_islands,dfs(grid, r, c));
            }
        }
    }
    return num_islands;
}
int dfs(int[][] grid, int r, int c) {
    int nr = grid.length;
    int nc = grid[0].length;

    if (r < 0 || c < 0 || r >= nr || c >= nc || grid[r][c] == 0) {
        return 0;
    }
    int num=1;
    grid[r][c] = 0;
    num+=dfs(grid, r - 1, c);
    num+=dfs(grid, r + 1, c);
    num+=dfs(grid, r, c - 1);
    num+=dfs(grid, r, c + 1);
    return num;
} 

}

*面试题 10.01. 合并排序的数组(index–,I,j,i–,j–;j>0;index–,j–)
给定两个排序后的数组 A 和 B,其中 A 的末端有足够的缓冲空间容纳 B。 编写一个方法,将 B 合并入 A 并排序。

初始化 A 和 B 的元素数量分别为 m 和 n。

示例:

输入:
A = [1,2,3,0,0,0], m = 3
B = [2,5,6], n = 3

输出: [1,2,2,3,5,6]
说明:

A.length == n + m
class Solution {
public void merge(int[] A, int m, int[] B, int n) {
int i=m-1;
int j=n-1;
int index=m+n-1;
while(i>=0 && j>=0){
A[index–]=A[i]>B[j]?A[i–]:B[j–];
}
while(j>=0){
A[index–]=B[j–];
}
}
}

*442. 数组中重复的数据(set,list)
给定一个整数数组 a,其中1 ≤ a[i] ≤ n (n为数组长度), 其中有些元素出现两次而其他元素出现一次。

找到所有出现两次的元素。

你可以不用到任何额外空间并在O(n)时间复杂度内解决这个问题吗?

示例:

输入:
[4,3,2,7,8,2,3,1]

输出:
[2,3]
class Solution {
public List findDuplicates(int[] nums) {
Set map=new HashSet();
List res=new ArrayList();
for(int i=0;i<nums.length;i++){
if(!map.contains(nums[i])){
map.add(nums[i]);
}else{
res.add(nums[i]);
}
}
return res;
}
}
*1137. 第 N 个泰波那契数
泰波那契序列 Tn 定义如下:

T0 = 0, T1 = 1, T2 = 1, 且在 n >= 0 的条件下 Tn+3 = Tn + Tn+1 + Tn+2

给你整数 n,请返回第 n 个泰波那契数 Tn 的值。

示例 1:

输入:n = 4
输出:4
解释:
T_3 = 0 + 1 + 1 = 2
T_4 = 1 + 1 + 2 = 4
示例 2:

输入:n = 25
输出:1389537
代码:
class Solution {
public int tribonacci(int n) {
int a=0,b=1,c=1,sum=0;
for(int i=0;i<n;i++){
sum=a+b+c;
a=b;
b=c;
c=sum;
}
return a;
}
}

  1. 有序数组的平方
    给你一个按 非递减顺序 排序的整数数组 nums,返回 每个数字的平方 组成的新数组,要求也按 非递减顺序 排序。

示例 1:

输入:nums = [-4,-1,0,3,10]
输出:[0,1,9,16,100]
解释:平方后,数组变为 [16,1,0,9,100]
排序后,数组变为 [0,1,9,16,100]
示例 2:

输入:nums = [-7,-3,2,3,11]
输出:[4,9,9,49,121]
class Solution {
public int[] sortedSquares(int[] nums) {
int[] res=new int[nums.length];
int sum=0;
for(int i=0;i<nums.length;i++){
res[i]=nums[i]*nums[i];
}
Arrays.sort(res);
return res;
}
}
189. 旋转数组(遍历取余)
给定一个数组,将数组中的元素向右移动 k 个位置,其中 k 是非负数。

进阶:

尽可能想出更多的解决方案,至少有三种不同的方法可以解决这个问题。
你可以使用空间复杂度为 O(1) 的 原地 算法解决这个问题吗?

示例 1:

输入: nums = [1,2,3,4,5,6,7], k = 3
输出: [5,6,7,1,2,3,4]
解释:
向右旋转 1 步: [7,1,2,3,4,5,6]
向右旋转 2 步: [6,7,1,2,3,4,5]
向右旋转 3 步: [5,6,7,1,2,3,4]
示例 2:

输入:nums = [-1,-100,3,99], k = 2
输出:[3,99,-1,-100]
解释:
向右旋转 1 步: [99,-1,-100,3]
向右旋转 2 步: [3,99,-1,-100]
class Solution {
public void rotate(int[] nums, int k) {
int n = nums.length;
int[] newArr = new int[n];
for (int i = 0; i < n; ++i) {
newArr[(i + k) % n] = nums[i];
}
System.arraycopy(newArr, 0, nums, 0, n);
}
}
NC129 阶乘末尾为0的数量(5的倍数增长)
输入:
5
复制
返回值:
1
复制
说明:
5!=120 示
输入
1000000000
复制
返回值:
249999998
import java.util.*;

public class Solution {
/**
* the number of 0
* @param n long长整型 the number
* @return long长整型
/
public long thenumberof0 (long n) {
// write code here
long ans = 0;
long d = 5;
while(n>=d)
{
ans+=n/d;
d = d
5;
}
return ans;
}
}
415. 字符串相加(StringBuffer)
给定两个字符串形式的非负整数 num1 和num2 ,计算它们的和。

提示:

num1 和num2 的长度都小于 5100
num1 和num2 都只包含数字 0-9
num1 和num2 都不包含任何前导零
你不能使用任何內建 BigInteger 库, 也不能直接将输入的字符串转换为整数形式
class Solution {
public String addStrings(String num1, String num2) {
int i = num1.length() - 1, j = num2.length() - 1, add = 0;
StringBuffer ans = new StringBuffer();
while (i >= 0 || j >= 0 || add != 0) {
int x = i >= 0 ? num1.charAt(i) - ‘0’ : 0;
int y = j >= 0 ? num2.charAt(j) - ‘0’ : 0;
int result = x + y + add;
ans.append(result % 10);
add = result / 10;
i–;
j–;
}
// 计算完以后的答案需要翻转过来
ans.reverse();
return ans.toString();
}
}
求double的开方(牛顿迭代法)
public static double sqrt(double c) {

if (c < 0) {

return Double.NaN;
}

double e = 1e-15;
double x = c;
double y = (x + c / x) / 2;
while (Math.abs(x - y) > e) {
    x = y;
    y = (x + c / x) / 2;
}
return x;

}
计算1到n的和
public static int sum(int n ){
if(n==1){
return 1;
}
return n+sum(n-1);

}
public static long pathFinding (int m, int n, int x, int y) {
// write code here
int[][] grid = new int[m+1][n+1];
grid[x][y] = -1;
for(int i=0;i<=m;i++){
for(int j=0;j<=n;j++){
if(grid[i][j]-1){
grid[i][j]=0;
}else if(i
0 || j==0){
grid[i][j]=1;
}else{
grid[i][j]=grid[i-1][j] + grid[i][j-1];
}
}
}
return grid[m][n];
}

动态规划:
动态规划(英语:Dynamic programming,简称 DP)是一种在数学、管理科学、计算机科学、经济学和生物信息学中使用的,通过把原问题分解为相对简单的子问题的方式求解复杂问题的方法。
动态规划常常适用于有重叠子问题和最优子结构性质的问题,并且记录所有子问题的结果,因此动态规划方法所耗时间往往远少于朴素解法。
动态规划有自底向上和自顶向下两种解决问题的方式。自顶向下即记忆化递归,自底向上就是递推。
使用动态规划解决的问题有个明显的特点,一旦一个子问题的求解得到结果,以后的计算过程就不会修改它,这样的特点叫做无后效性,求解问题的过程形成了一张有向无环图。动态规划只解决每个子问题一次,具有天然剪枝的功能,从而减少计算量。
32. 最长有效括号
给你一个只包含 ‘(’ 和 ‘)’ 的字符串,找出最长有效(格式正确且连续)括号子串的长度。

示例 1:

输入:s = “(()”
输出:2
解释:最长有效括号子串是 “()”
示例 2:

输入:s = “)()())”
输出:4
解释:最长有效括号子串是 “()()”
示例 3:

输入:s = “”
输出:0
class Solution {
public int longestValidParentheses(String s) {
if (s == null || s.length() == 0) return 0;
Deque stack = new ArrayDeque<>();
stack.push(-1);
int res = 0;
for (int i = 0; i < s.length(); i++) {
if (s.charAt(i) == ‘(’) stack.push(i);
else {
stack.pop();
if (stack.isEmpty()) stack.push(i);
else {
res = Math.max(res, i - stack.peek());
}
}
}
return res;
}
}
42. 接雨水
给定 n 个非负整数表示每个宽度为 1 的柱子的高度图,计算按此排列的柱子,下雨之后能接多少雨水。

class Solution {
public int trap(int[] height) {
int ans = 0;
int left = 0, right = height.length - 1;
int leftMax = 0, rightMax = 0;
while (left < right) {
leftMax = Math.max(leftMax, height[left]);
rightMax = Math.max(rightMax, height[right]);
if (height[left] < height[right]) {
ans += leftMax - height[left];
++left;
} else {
ans += rightMax - height[right];
–right;
}
}
return ans;
}
}
55. 跳跃游戏
给定一个非负整数数组 nums ,你最初位于数组的 第一个下标 。

数组中的每个元素代表你在该位置可以跳跃的最大长度。

判断你是否能够到达最后一个下标。

示例 1:

输入:nums = [2,3,1,1,4]
输出:true
解释:可以先跳 1 步,从下标 0 到达下标 1, 然后再从下标 1 跳 3 步到达最后一个下标。
示例 2:

输入:nums = [3,2,1,0,4]
输出:false
解释:无论怎样,总会到达下标为 3 的位置。但该下标的最大跳跃长度是 0 , 所以永远不可能到达最后一个下标。
class Solution {
public boolean canJump(int[] nums) {
int n = nums.length;
int rightmost = 0;
for (int i = 0; i < n; ++i) {
if (i <= rightmost) {
rightmost = Math.max(rightmost, i + nums[i]);
if (rightmost >= n - 1) {
return true;
}
}
}
return false;
}
}
45. 跳跃游戏 II
给你一个非负整数数组 nums ,你最初位于数组的第一个位置。

数组中的每个元素代表你在该位置可以跳跃的最大长度。

你的目标是使用最少的跳跃次数到达数组的最后一个位置。

假设你总是可以到达数组的最后一个位置。

示例 1:

输入: nums = [2,3,1,1,4]
输出: 2
解释: 跳到最后一个位置的最小跳跃数是 2。
从下标为 0 跳到下标为 1 的位置,跳 1 步,然后跳 3 步到达数组的最后一个位置。
示例 2:

输入: nums = [2,3,0,1,4]
输出: 2
class Solution {
public int jump(int[] nums) {
int length = nums.length;
int end = 0;
int maxPosition = 0;
int steps = 0;
for (int i = 0; i < length - 1; i++) {
maxPosition = Math.max(maxPosition, i + nums[i]);
if (i == end) {
end = maxPosition;
steps++;
}
}
return steps;
}
}
62. 不同路径
一个机器人位于一个 m x n 网格的左上角 (起始点在下图中标记为 “Start” )。

机器人每次只能向下或者向右移动一步。机器人试图达到网格的右下角(在下图中标记为 “Finish” )。

问总共有多少条不同的路径?

class Solution {
public int uniquePaths(int m, int n) {
int[][] f = new int[m][n];
for (int i = 0; i < m; ++i) {
f[i][0] = 1;
}
for (int j = 0; j < n; ++j) {
f[0][j] = 1;
}
for (int i = 1; i < m; ++i) {
for (int j = 1; j < n; ++j) {
f[i][j] = f[i - 1][j] + f[i][j - 1];
}
}
return f[m - 1][n - 1];
}
}
63. 不同路径 II
一个机器人位于一个 m x n 网格的左上角 (起始点在下图中标记为“Start” )。

机器人每次只能向下或者向右移动一步。机器人试图达到网格的右下角(在下图中标记为“Finish”)。

现在考虑网格中有障碍物。那么从左上角到右下角将会有多少条不同的路径?

class Solution {
public int uniquePathsWithObstacles(int[][] obstacleGrid) {
int n = obstacleGrid.length, m = obstacleGrid[0].length;
int[] f = new int[m];
f[0] = obstacleGrid[0][0] == 0 ? 1 : 0;
for (int i = 0; i < n; ++i) {
for (int j = 0; j < m; ++j) {
if (obstacleGrid[i][j] == 1) {
f[j] = 0;
continue;
}
if (j - 1 >= 0 && obstacleGrid[i][j - 1] == 0) {
f[j] += f[j - 1];
}
}
}

    return f[m - 1];
}

}
64. 最小路径和
给定一个包含非负整数的 m x n 网格 grid ,请找出一条从左上角到右下角的路径,使得路径上的数字总和为最小。
说明:每次只能向下或者向右移动一步。

输入:grid = [[1,2,3],[4,5,6]]
输出:12
class Solution {
public int minPathSum(int[][] grid) {
int len = grid[0].length;
int[] dp = new int[len];
dp[0] = grid[0][0];
for (int i = 1; i < len; i++)
dp[i]=dp[i-1]+grid[0][i];
for (int i = 1; i < grid.length; i++) {
dp[0] = dp[0] + grid[i][0];
for (int j = 1; j < len; j++)
dp[j] = Math.min(dp[j-1]+grid[i][j], dp[j]+grid[i][j]);
}
return dp[len-1];
}
}
113. 路径总和 II
给你二叉树的根节点 root 和一个整数目标和 targetSum ,找出所有 从根节点到叶子节点 路径总和等于给定目标和的路径。

叶子节点 是指没有子节点的节点。

/**

  • Definition for a binary tree node.

  • public class TreeNode {

  • int val;
    
  • TreeNode left;
    
  • TreeNode right;
    
  • TreeNode() {}
    
  • TreeNode(int val) { this.val = val; }
    
  • TreeNode(int val, TreeNode left, TreeNode right) {
    
  •     this.val = val;
    
  •     this.left = left;
    
  •     this.right = right;
    
  • }
    
  • }
    */
    class Solution {
    List<List> ret = new LinkedList<List>();
    Deque path = new LinkedList();

    public List<List> pathSum(TreeNode root, int targetSum) {
    dfs(root, targetSum);
    return ret;
    }

    public void dfs(TreeNode root, int targetSum) {
    if (root == null) {
    return;
    }
    path.offerLast(root.val);
    targetSum -= root.val;
    if (root.left == null && root.right == null && targetSum == 0) {
    ret.add(new LinkedList(path));
    }
    dfs(root.left, targetSum);
    dfs(root.right, targetSum);
    path.pollLast();
    }
    }
    注意:offerLast(element)指定元素添加在列表的末尾,pollLast()返回列表的最后一个元素并从列表中移除

  1. 三角形最小路径和
    给定一个三角形 triangle ,找出自顶向下的最小路径和。

每一步只能移动到下一行中相邻的结点上。相邻的结点 在这里指的是 下标 与 上一层结点下标 相同或者等于 上一层结点下标 + 1 的两个结点。也就是说,如果正位于当前行的下标 i ,那么下一步可以移动到下一行的下标 i 或 i + 1 。

示例 1:

输入:triangle = [[2],[3,4],[6,5,7],[4,1,8,3]]
输出:11
解释:如下面简图所示:
2
3 4
6 5 7
4 1 8 3
自顶向下的最小路径和为 11(即,2 + 3 + 5 + 1 = 11)。
示例 2:

输入:triangle = [[-10]]
输出:-10
class Solution {
public int minimumTotal(List<List> triangle) {
int n = triangle.size();
// dp[i][j] 表示从点 (i, j) 到底边的最小路径和。
int[][] dp = new int[n + 1][n + 1];
// 从三角形的最后一行开始递推。
for (int i = n - 1; i >= 0; i–) {
for (int j = 0; j <= i; j++) {
dp[i][j] = Math.min(dp[i + 1][j], dp[i + 1][j + 1]) + triangle.get(i).get(j);
}
}
return dp[0][0];
}
}

  1. 最大正方形

class Solution {
public int maximalSquare(char[][] matrix) {
int maxSide = 0;
if (matrix == null || matrix.length == 0 || matrix[0].length == 0) {
return maxSide;
}
int rows = matrix.length, columns = matrix[0].length;
int[][] dp = new int[rows][columns];
for (int i = 0; i < rows; i++) {
for (int j = 0; j < columns; j++) {
if (matrix[i][j] == ‘1’) {
if (i == 0 || j == 0) {
dp[i][j] = 1;
} else {
dp[i][j] = Math.min(Math.min(dp[i - 1][j], dp[i][j - 1]), dp[i - 1][j - 1]) + 1;
}
maxSide = Math.max(maxSide, dp[i][j]);
}
}
}
int maxSquare = maxSide * maxSide;
return maxSquare;
}
}

  1. 最长递增子序列
    给你一个整数数组 nums ,找到其中最长严格递增子序列的长度。

子序列是由数组派生而来的序列,删除(或不删除)数组中的元素而不改变其余元素的顺序。例如,[3,6,2,7] 是数组 [0,3,1,6,2,2,7] 的子序列。
示例 1:

输入:nums = [10,9,2,5,3,7,101,18]
输出:4
解释:最长递增子序列是 [2,3,7,101],因此长度为 4 。
示例 2:

输入:nums = [0,1,0,3,2,3]
输出:4
示例 3:

输入:nums = [7,7,7,7,7,7,7]
输出:1
class Solution {
public int lengthOfLIS(int[] nums) {
if (nums.length == 0) {
return 0;
}
int[] dp = new int[nums.length];
dp[0] = 1;
int maxans = 1;
for (int i = 1; i < nums.length; i++) {
dp[i] = 1;
for (int j = 0; j < i; j++) {
if (nums[i] > nums[j]) {
dp[i] = Math.max(dp[i], dp[j] + 1);
}
}
maxans = Math.max(maxans, dp[i]);
}
return maxans;
}
}
回溯法:
39. 组合总和
给定一个无重复元素的正整数数组 candidates 和一个正整数 target ,找出 candidates 中所有可以使数字和为目标数 target 的唯一组合。

candidates 中的数字可以无限制重复被选取。如果至少一个所选数字数量不同,则两种组合是唯一的。

对于给定的输入,保证和为 target 的唯一组合数少于 150 个。

示例 1:

输入: candidates = [2,3,6,7], target = 7
输出: [[7],[2,2,3]]
示例 2:

输入: candidates = [2,3,5], target = 8
输出: [[2,2,2,2],[2,3,3],[3,5]]
示例 3:

输入: candidates = [2], target = 1
输出: []
示例 4:

输入: candidates = [1], target = 1
输出: [[1]]
示例 5:

输入: candidates = [1], target = 2
输出: [[1,1]]
class Solution {
public List<List> combinationSum(int[] candidates, int target) {
List<List> ans = new ArrayList<List>();
List combine = new ArrayList();
dfs(candidates, target, ans, combine, 0);
return ans;
}

public void dfs(int[] candidates, int target, List<List<Integer>> ans, List<Integer> combine, int idx) {
    if (idx == candidates.length) {
        return;
    }
    if (target == 0) {
        ans.add(new ArrayList<Integer>(combine));
        return;
    }
    // 直接跳过
    dfs(candidates, target, ans, combine, idx + 1);
    // 选择当前数
    if (target - candidates[idx] >= 0) {
        combine.add(candidates[idx]);
        dfs(candidates, target - candidates[idx], ans, combine, idx);
        combine.remove(combine.size() - 1);
    }
}

}
40. 组合总和 II
给定一个数组 candidates 和一个目标数 target ,找出 candidates 中所有可以使数字和为 target 的组合。

candidates 中的每个数字在每个组合中只能使用一次。

注意:解集不能包含重复的组合。

示例 1:

输入: candidates = [10,1,2,7,6,1,5], target = 8,
输出:
[
[1,1,6],
[1,2,5],
[1,7],
[2,6]
]
示例 2:

输入: candidates = [2,5,2,1,2], target = 5,
输出:
[
[1,2,2],
[5]
]
class Solution {

private List<List<Integer>> res = new ArrayList<>();
public List<List<Integer>> combinationSum2(int[] candidates, int target) {
    Arrays.sort(candidates);
    List<Integer> path = new ArrayList<>();
    backtrack(path,candidates,target,0,0);
    return res;
}

private void backtrack(List<Integer> path,int[] candidates,int target,int sum,int begin) {
    if(sum == target) {
        res.add(new ArrayList<>(path));
        return;
    }
    for(int i = begin;i < candidates.length;i++) {
		//避免出现重复情况
        if(i > begin && candidates[i] == candidates[i-1]) continue;
        int rs = candidates[i] + sum;
        if(rs <= target) {
            path.add(candidates[i]);
            backtrack(path,candidates,target,rs,i+1);
            path.remove(path.size()-1);
        } else {
            break;
        }
    }
}

}

  1. 全排列
    给定一个不含重复数字的数组 nums ,返回其 所有可能的全排列 。你可以 按任意顺序 返回答案。

示例 1:

输入:nums = [1,2,3]
输出:[[1,2,3],[1,3,2],[2,1,3],[2,3,1],[3,1,2],[3,2,1]]
示例 2:

输入:nums = [0,1]
输出:[[0,1],[1,0]]
示例 3:

输入:nums = [1]
输出:[[1]]
class Solution {
public List<List> permute(int[] nums) {
List<List> res = new ArrayList<List>();

    List<Integer> output = new ArrayList<Integer>();
    for (int num : nums) {
        output.add(num);
    }

    int n = nums.length;
    backtrack(n, output, res, 0);
    return res;
}

public void backtrack(int n, List<Integer> output, List<List<Integer>> res, int first) {
    // 所有数都填完了
    if (first == n) {
        res.add(new ArrayList<Integer>(output));
    }
    for (int i = first; i < n; i++) {
        // 动态维护数组
        Collections.swap(output, first, i);
        // 继续递归填下一个数
        backtrack(n, output, res, first + 1);
        // 撤销操作
        Collections.swap(output, first, i);
    }
}

}
47. 全排列 II
给定一个可包含重复数字的序列 nums ,按任意顺序 返回所有不重复的全排列。

示例 1:

输入:nums = [1,1,2]
输出:
[[1,1,2],
[1,2,1],
[2,1,1]]
示例 2:

输入:nums = [1,2,3]
输出:[[1,2,3],[1,3,2],[2,1,3],[2,3,1],[3,1,2],[3,2,1]]
class Solution {
Set<List> set = new HashSet<List>();
public List<List> permuteUnique(int[] nums) {
List<List> res = new ArrayList<List>();

    List<Integer> output = new ArrayList<Integer>();
    for (int num : nums) {
        output.add(num);
    }

    int n = nums.length;
    backtrack(n, output, res, 0);
    return res;
}

public void backtrack(int n, List<Integer> output, List<List<Integer>> res, int first) {
    // 所有数都填完了  
    if (first == n && !set.contains(output)) {
        res.add(new ArrayList<Integer>(output));
        set.add(output);
    }
    for (int i = first; i < n; i++) {
        // 动态维护数组
        Collections.swap(output, first, i);
        // 继续递归填下一个数
        backtrack(n, output, res, first + 1);
        // 撤销操作
        Collections.swap(output, first, i);
    }
}

}
78. 子集
给你一个整数数组 nums ,数组中的元素 互不相同 。返回该数组所有可能的子集(幂集)。

解集 不能 包含重复的子集。你可以按 任意顺序 返回解集。

示例 1:

输入:nums = [1,2,3]
输出:[[],[1],[2],[1,2],[3],[1,3],[2,3],[1,2,3]]
示例 2:

输入:nums = [0]
输出:[[],[0]]
class Solution {
List t = new ArrayList();
List<List> ans = new ArrayList<List>();

public List<List<Integer>> subsets(int[] nums) {
    dfs(0, nums);
    return ans;
}

public void dfs(int cur, int[] nums) {
    if (cur == nums.length) {
        ans.add(new ArrayList<Integer>(t));
        return;
    }
    t.add(nums[cur]);
    dfs(cur + 1, nums);
    t.remove(t.size() - 1);
    dfs(cur + 1, nums);
}

}
79. 单词搜索
给定一个 m x n 二维字符网格 board 和一个字符串单词 word 。如果 word 存在于网格中,返回 true ;否则,返回 false 。

单词必须按照字母顺序,通过相邻的单元格内的字母构成,其中“相邻”单元格是那些水平相邻或垂直相邻的单元格。同一个单元格内的字母不允许被重复使用。

class Solution {
public boolean exist(char[][] board, String word) {
for (int i = 0; i < board.length; ++i) {
for (int j = 0; j < board[i].length; ++j) {
if (board[i][j] == word.charAt(0)) {
if (dfs(board, word, i, j, 0)) return true;
}
}
}
return false;
}
public boolean dfs(char[][] board, String word, int i, int j, int k) {
if (k == word.length()) {
return true;
}
if (i < 0 || j < 0 || i >= board.length || j >= board[i].length) {
return false;
}

    if (word.charAt(k) != board[i][j]) {
        return false;
    }
    char t = board[i][j];
    board[i][j] = '0';
    boolean 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] = t;
    return res;
}

}

  1. 子集 II
    给你一个整数数组 nums ,其中可能包含重复元素,请你返回该数组所有可能的子集(幂集)。

解集 不能 包含重复的子集。返回的解集中,子集可以按 任意顺序 排列。

示例 1:

输入:nums = [1,2,2]
输出:[[],[1],[1,2],[1,2,2],[2],[2,2]]
示例 2:

输入:nums = [0]
输出:[[],[0]]
class Solution {
List t = new ArrayList();
List<List> ans = new ArrayList<List>();

public List<List<Integer>> subsetsWithDup(int[] nums) {
    Arrays.sort(nums);
    dfs(false, 0, nums);
    return ans;
}

public void dfs(boolean choosePre, int cur, int[] nums) {
    if (cur == nums.length) {
        ans.add(new ArrayList<Integer>(t));
        return;
    }
    dfs(false, cur + 1, nums);
    if (!choosePre && cur > 0 && nums[cur - 1] == nums[cur]) {
        return;
    }
    t.add(nums[cur]);
    dfs(true, cur + 1, nums);
    t.remove(t.size() - 1);
}

}

  1. 分割回文串
    给你一个字符串 s,请你将 s 分割成一些子串,使每个子串都是 回文串 。返回 s 所有可能的分割方案。

回文串 是正着读和反着读都一样的字符串。

示例 1:

输入:s = “aab”
输出:[[“a”,“a”,“b”],[“aa”,“b”]]
示例 2:

输入:s = “a”
输出:[[“a”]]
class Solution {
boolean[][] f;
List<List> ret = new ArrayList<List>();
List ans = new ArrayList();
int n;
public List<List> partition(String s) {
n = s.length();
f = new boolean[n][n];
for (int i = 0; i < n; ++i) {
Arrays.fill(f[i], true);
}
for (int i = n - 1; i >= 0; --i) {
for (int j = i + 1; j < n; ++j) {
f[i][j] = (s.charAt(i) == s.charAt(j)) && f[i + 1][j - 1];
}
}
dfs(s, 0);
return ret;
}
public void dfs(String s, int i) {
if (i == n) {
ret.add(new ArrayList(ans));
return;
}
for (int j = i; j < n; ++j) {
if (f[i][j]) {
ans.add(s.substring(i, j + 1));
dfs(s, j + 1);
ans.remove(ans.size() - 1);
}
}
}
}
字符串:
3. 无重复字符的最长子串
给定一个字符串 s ,请你找出其中不含有重复字符的 最长子串 的长度。

示例 1:

输入: s = “abcabcbb”
输出: 3
解释: 因为无重复字符的最长子串是 “abc”,所以其长度为 3。
示例 2:

输入: s = “bbbbb”
输出: 1
解释: 因为无重复字符的最长子串是 “b”,所以其长度为 1。
示例 3:

输入: s = “pwwkew”
输出: 3
解释: 因为无重复字符的最长子串是 “wke”,所以其长度为 3。
请注意,你的答案必须是 子串 的长度,“pwke” 是一个子序列,不是子串。
示例 4:

输入: s = “”
输出: 0
每次左指针右移一位,移除set的一个字符,这一步会导致很多无用的循环。while循环发现的重复字符不一定就是Set最早添加那个,还要好多次循环才能到达,这些都是无效循环,不如直接用map记下每个字符的索引,直接进行跳转
class Solution {
public int lengthOfLongestSubstring(String s) {
HashMap<Character, Integer> map = new HashMap<>();
int max = 0, start = 0;
for (int end = 0; end < s.length(); end++) {
char ch = s.charAt(end);
if (map.containsKey(ch)){
start = Math.max(map.get(ch)+1,start);
}
max = Math.max(max,end - start + 1);
map.put(ch,end);
}
return max;
}
}

  1. 最长回文子串(中心扩展法)
    给你一个字符串 s,找到 s 中最长的回文子串。

示例 1:

输入:s = “babad”
输出:“bab”
解释:“aba” 同样是符合题意的答案。
示例 2:

输入:s = “cbbd”
输出:“bb”
示例 3:

输入:s = “a”
输出:“a”
示例 4:

输入:s = “ac”
输出:“a”

class Solution {
public String longestPalindrome(String s) {
if (s == null || s.length() < 1) {
return “”;
}
int start = 0, end = 0;
for (int i = 0; i < s.length(); i++) {
int len1 = expandAroundCenter(s, i, i);
int len2 = expandAroundCenter(s, i, i + 1);
int len = Math.max(len1, len2);
if (len > end - start) {
start = i - (len - 1) / 2;
end = i + len / 2;
}
}
return s.substring(start, end + 1);
}
public int expandAroundCenter(String s, int left, int right) {
while (left >= 0 && right < s.length() && s.charAt(left) == s.charAt(right)) {
–left;
++right;
}
return right - left - 1;
}
}

  1. 字符串转换整数 (atoi)
    请你来实现一个 myAtoi(string s) 函数,使其能将字符串转换成一个 32 位有符号整数(类似 C/C++ 中的 atoi 函数)。

函数 myAtoi(string s) 的算法如下:

读入字符串并丢弃无用的前导空格
检查下一个字符(假设还未到字符末尾)为正还是负号,读取该字符(如果有)。 确定最终结果是负数还是正数。 如果两者都不存在,则假定结果为正。
读入下一个字符,直到到达下一个非数字字符或到达输入的结尾。字符串的其余部分将被忽略。
将前面步骤读入的这些数字转换为整数(即,“123” -> 123, “0032” -> 32)。如果没有读入数字,则整数为 0 。必要时更改符号(从步骤 2 开始)。
如果整数数超过 32 位有符号整数范围 [−231, 231 − 1] ,需要截断这个整数,使其保持在这个范围内。具体来说,小于 −231 的整数应该被固定为 −231 ,大于 231 − 1 的整数应该被固定为 231 − 1 。
返回整数作为最终结果。
注意:

本题中的空白字符只包括空格字符 ’ ’ 。
除前导空格或数字后的其余字符串外,请勿忽略 任何其他字符。
示例 1:

输入:s = “42”
输出:42
解释:加粗的字符串为已经读入的字符,插入符号是当前读取的字符。
第 1 步:“42”(当前没有读入字符,因为没有前导空格)
^
第 2 步:“42”(当前没有读入字符,因为这里不存在 ‘-’ 或者 ‘+’)
^
第 3 步:“42”(读入 “42”)
^
解析得到整数 42 。
由于 “42” 在范围 [-231, 231 - 1] 内,最终结果为 42 。
示例 2:

输入:s = " -42"
输出:-42
解释:
第 1 步:" -42"(读入前导空格,但忽视掉)
^
第 2 步:" -42"(读入 ‘-’ 字符,所以结果应该是负数)
^
第 3 步:" -42"(读入 “42”)
^
解析得到整数 -42 。
由于 “-42” 在范围 [-231, 231 - 1] 内,最终结果为 -42 。
示例 3:

输入:s = “4193 with words”
输出:4193
解释:
第 1 步:“4193 with words”(当前没有读入字符,因为没有前导空格)
^
第 2 步:“4193 with words”(当前没有读入字符,因为这里不存在 ‘-’ 或者 ‘+’)
^
第 3 步:“4193 with words”(读入 “4193”;由于下一个字符不是一个数字,所以读入停止)
^
解析得到整数 4193 。
由于 “4193” 在范围 [-231, 231 - 1] 内,最终结果为 4193 。
示例 4:

输入:s = “words and 987”
输出:0
解释:
第 1 步:“words and 987”(当前没有读入字符,因为没有前导空格)
^
第 2 步:“words and 987”(当前没有读入字符,因为这里不存在 ‘-’ 或者 ‘+’)
^
第 3 步:“words and 987”(由于当前字符 ‘w’ 不是一个数字,所以读入停止)
^
解析得到整数 0 ,因为没有读入任何数字。
由于 0 在范围 [-231, 231 - 1] 内,最终结果为 0 。
示例 5:

输入:s = “-91283472332”
输出:-2147483648
解释:
第 1 步:“-91283472332”(当前没有读入字符,因为没有前导空格)
^
第 2 步:“-91283472332”(读入 ‘-’ 字符,所以结果应该是负数)
^
第 3 步:“-91283472332”(读入 “91283472332”)
^
解析得到整数 -91283472332 。
由于 -91283472332 小于范围 [-231, 231 - 1] 的下界,最终结果被截断为 -231 = -2147483648 。

class Solution {
public int myAtoi(String s) {
s = s.trim();
if (s.length() == 0) return 0;
if (!Character.isDigit(s.charAt(0))
&& s.charAt(0) != ‘-’ && s.charAt(0) != ‘+’)
return 0;
int ans = 0;
boolean neg = s.charAt(0) == ‘-’;
int i = !Character.isDigit(s.charAt(0)) ? 1 : 0;
while (i < s.length() && Character.isDigit(s.charAt(i))) {
int tmp = ((neg ? Integer.MIN_VALUE : Integer.MIN_VALUE + 1) + (s.charAt(i) - ‘0’)) / 10;
if (tmp > ans) {
return neg ? Integer.MIN_VALUE : Integer.MAX_VALUE;
}
ans = ans * 10 - (s.charAt(i++) - ‘0’);
}
return neg ? ans : -ans;
}
}
28. 实现 strStr()
实现 strStr() 函数。

给你两个字符串 haystack 和 needle ,请你在 haystack 字符串中找出 needle 字符串出现的第一个位置(下标从 0 开始)。如果不存在,则返回 -1 。

说明:

当 needle 是空字符串时,我们应当返回什么值呢?这是一个在面试中很好的问题。

对于本题而言,当 needle 是空字符串时我们应当返回 0 。这与 C 语言的 strstr() 以及 Java 的 indexOf() 定义相符。

示例 1:

输入:haystack = “hello”, needle = “ll”
输出:2
示例 2:

输入:haystack = “aaaaa”, needle = “bba”
输出:-1
示例 3:

输入:haystack = “”, needle = “”
输出:0
class Solution {
public int strStr(String haystack, String needle) {
int n = haystack.length(), m = needle.length();
char[] s = haystack.toCharArray(), p = needle.toCharArray();
// 枚举原串的「发起点」
for (int i = 0; i <= n - m; i++) {
// 从原串的「发起点」和匹配串的「首位」开始,尝试匹配
int a = i, b = 0;
while (b < m && s[a] == p[b]) {
a++;
b++;
}
// 如果能够完全匹配,返回原串的「发起点」下标
if (b == m) return i;
}
return -1;
}
}

  1. 字符串相乘
    给定两个以字符串形式表示的非负整数 num1 和 num2,返回 num1 和 num2 的乘积,它们的乘积也表示为字符串形式。

示例 1:

输入: num1 = “2”, num2 = “3”
输出: “6”
示例 2:

输入: num1 = “123”, num2 = “456”
输出: “56088”
class Solution {
public String multiply(String num1, String num2) {
if (num1.equals(“0”) || num2.equals(“0”)) {
return “0”;
}
int m = num1.length(), n = num2.length();
int[] ansArr = new int[m + n];
for (int i = m - 1; i >= 0; i–) {
int x = num1.charAt(i) - ‘0’;
for (int j = n - 1; j >= 0; j–) {
int y = num2.charAt(j) - ‘0’;
ansArr[i + j + 1] += x * y;
}
}
for (int i = m + n - 1; i > 0; i–) {
ansArr[i - 1] += ansArr[i] / 10;
ansArr[i] %= 10;
}
int index = ansArr[0] == 0 ? 1 : 0;
StringBuffer ans = new StringBuffer();
while (index < m + n) {
ans.append(ansArr[index]);
index++;
}
return ans.toString();
}
}
151. 翻转字符串里的单词
给你一个字符串 s ,逐个翻转字符串中的所有 单词 。

单词 是由非空格字符组成的字符串。s 中使用至少一个空格将字符串中的 单词 分隔开。

请你返回一个翻转 s 中单词顺序并用单个空格相连的字符串。

说明:

输入字符串 s 可以在前面、后面或者单词间包含多余的空格。
翻转后单词间应当仅用一个空格分隔。
翻转后的字符串中不应包含额外的空格。

示例 1:

输入:s = “the sky is blue”
输出:“blue is sky the”
示例 2:

输入:s = " hello world "
输出:“world hello”
解释:输入字符串可以在前面或者后面包含多余的空格,但是翻转后的字符不能包括。
示例 3:

输入:s = “a good example”
输出:“example good a”
解释:如果两个单词间有多余的空格,将翻转后单词间的空格减少到只含一个。
示例 4:

输入:s = " Bob Loves Alice "
输出:“Alice Loves Bob”
示例 5:

输入:s = “Alice does not even like bob”
输出:“bob like even not does Alice”
class Solution {
public String reverseWords(String s) {
// 除去开头和末尾的空白字符
s = s.trim();
// 正则匹配连续的空白字符作为分隔符分割
List wordList = Arrays.asList(s.split(“\s+”));
Collections.reverse(wordList);
return String.join(" ", wordList);
}
}
205. 同构字符串
给定两个字符串 s 和 t,判断它们是否是同构的。

如果 s 中的字符可以按某种映射关系替换得到 t ,那么这两个字符串是同构的。

每个出现的字符都应当映射到另一个字符,同时不改变字符的顺序。不同字符不能映射到同一个字符上,相同字符只能映射到同一个字符上,字符可以映射到自己本身。

示例 1:

输入:s = “egg”, t = “add”
输出:true
示例 2:

输入:s = “foo”, t = “bar”
输出:false
示例 3:

输入:s = “paper”, t = “title”
输出:true
class Solution {
public boolean isIsomorphic(String s, String t) {
int m = s.length(), n = t.length();
if(m != n) return false;
Map<Character, Character> map = new HashMap<>();
for(int i = 0; i < m; i++){
if(!map.containsKey(s.charAt(i))){
if(map.containsValue(t.charAt(i))) return false;
map.put(s.charAt(i), t.charAt(i));
}else{
if(map.get(s.charAt(i)) != t.charAt(i)){
return false;
}
}
}
return true;
}
}
318. 最大单词长度乘积
给定一个字符串数组 words,找到 length(word[i]) * length(word[j]) 的最大值,并且这两个单词不含有公共字母。你可以认为每个单词只包含小写字母。如果不存在这样的两个单词,返回 0。

示例 1:

输入: [“abcw”,“baz”,“foo”,“bar”,“xtfn”,“abcdef”]
输出: 16
解释: 这两个单词为 “abcw”, “xtfn”。
示例 2:

输入: [“a”,“ab”,“abc”,“d”,“cd”,“bcd”,“abcd”]
输出: 4
解释: 这两个单词为 “ab”, “cd”。
示例 3:

输入: [“a”,“aa”,“aaa”,“aaaa”]
输出: 0
解释: 不存在这样的两个单词。
class Solution {
public boolean noCommonLetters(String s1, String s2){
for (char ch : s1.toCharArray())
if (s2.indexOf(ch) != -1) return false;
return true;
}
public int maxProduct(String[] words) {
int n = words.length;
int maxProd = 0;
for (int i = 0; i < n; ++i)
for (int j = i + 1; j < n; ++j)
if (noCommonLetters(words[i], words[j]))
maxProd = Math.max(maxProd, words[i].length() * words[j].length());
return maxProd;
}
}

  1. 最长回文子序列
    给你一个字符串 s ,找出其中最长的回文子序列,并返回该序列的长度。
    子序列定义为:不改变剩余字符顺序的情况下,删除某些字符或者不删除任何字符形成的一个序列。
    示例 1:

输入:s = “bbbab”
输出:4
解释:一个可能的最长回文子序列为 “bbbb” 。
示例 2:

输入:s = “cbbd”
输出:2
解释:一个可能的最长回文子序列为 “bb” 。
class Solution {
public int longestPalindromeSubseq(String s) {
int n = s.length();
int[][] dp = new int[n][n];
for (int i = n - 1; i >= 0; i–) {
dp[i][i] = 1;
char c1 = s.charAt(i);
for (int j = i + 1; j < n; j++) {
char c2 = s.charAt(j);
if (c1 == c2) {
dp[i][j] = dp[i + 1][j - 1] + 2;
} else {
dp[i][j] = Math.max(dp[i + 1][j], dp[i][j - 1]);
}
}
}
return dp[0][n - 1];
}
}
541. 反转字符串 II
给定一个字符串 s 和一个整数 k,从字符串开头算起,每计数至 2k 个字符,就反转这 2k 字符中的前 k 个字符。

如果剩余字符少于 k 个,则将剩余字符全部反转。
如果剩余字符小于 2k 但大于或等于 k 个,则反转前 k 个字符,其余字符保持原样。

示例 1:

输入:s = “abcdefg”, k = 2
输出:“bacdfeg”
示例 2:

输入:s = “abcd”, k = 2
输出:“bacd”
class Solution {
public String reverseStr(String s, int k) {
int n = s.length();
char[] arr = s.toCharArray();
for (int i = 0; i < n; i += 2 * k) {
reverse(arr, i, Math.min(i + k, n) - 1);
}
return new String(arr);
}
public void reverse(char[] arr, int left, int right) {
while (left < right) {
char temp = arr[left];
arr[left] = arr[right];
arr[right] = temp;
left++;
right–;
}
}
}

  1. 回文子串
    给你一个字符串 s ,请你统计并返回这个字符串中 回文子串 的数目。

回文字符串 是正着读和倒过来读一样的字符串。

子字符串 是字符串中的由连续字符组成的一个序列。

具有不同开始位置或结束位置的子串,即使是由相同的字符组成,也会被视作不同的子串。

示例 1:

输入:s = “abc”
输出:3
解释:三个回文子串: “a”, “b”, “c”
示例 2:

输入:s = “aaa”
输出:6
解释:6个回文子串: “a”, “a”, “a”, “aa”, “aa”, “aaa”

class Solution {
public int countSubstrings(String s) {
boolean[][] dp = new boolean[s.length()][s.length()];
int ans = 0;
for (int j = 0; j < s.length(); j++) {
for (int i = 0; i <= j; i++) {
if (s.charAt(i) == s.charAt(j) && (j - i < 2 || dp[i + 1][j - 1])) {
dp[i][j] = true;
ans++;
}
}
}
return ans;
}
}
848. 字母移位
有一个由小写字母组成的字符串 S,和一个整数数组 shifts。

我们将字母表中的下一个字母称为原字母的 移位(由于字母表是环绕的, ‘z’ 将会变成 ‘a’)。

例如•,shift(‘a’) = ‘b’, shift(‘t’) = ‘u’,, 以及 shift(‘z’) = ‘a’。

对于每个 shifts[i] = x , 我们会将 S 中的前 i+1 个字母移位 x 次。

返回将所有这些移位都应用到 S 后最终得到的字符串。

示例:

输入:S = “abc”, shifts = [3,5,9]
输出:“rpl”
解释:
我们以 “abc” 开始。
将 S 中的第 1 个字母移位 3 次后,我们得到 “dbc”。
再将 S 中的前 2 个字母移位 5 次后,我们得到 “igc”。
最后将 S 中的这 3 个字母移位 9 次后,我们得到答案 “rpl”。
class Solution {
public String shiftingLetters(String s, int[] shifts) {
for(int i=shifts.length-2;i>=0;i–){
shifts[i]=(shifts[i+1]+shifts[i])%26;
}
StringBuilder ans=new StringBuilder();
for(int i=0;i<s.length();i++){
ans.append((char)((s.charAt(i)-‘a’+shifts[i])%26+‘a’));
}
return ans.toString();
}
}

  1. IP 地址无效化
    给你一个有效的 IPv4 地址 address,返回这个 IP 地址的无效化版本。

所谓无效化 IP 地址,其实就是用 “[.]” 代替了每个 “.”。

示例 1:

输入:address = “1.1.1.1”
输出:“1[.]1[.]1[.]1”
示例 2:

输入:address = “255.100.50.0”
输出:“255[.]100[.]50[.]0”
class Solution {
public String defangIPaddr(String address) {
String str = address.replace(“.”, “[.]”);
return str;
}
}
二、Sql语句部分
175. 组合两个表(简单)
表1: Person

±------------±--------+
| 列名 | 类型 |
±------------±--------+
| PersonId | int |
| FirstName | varchar |
| LastName | varchar |
±------------±--------+
PersonId 是上表主键
表2: Address

±------------±--------+
| 列名 | 类型 |
±------------±--------+
| AddressId | int |
| PersonId | int |
| City | varchar |
| State | varchar |
±------------±--------+
AddressId 是上表主键
编写一个 SQL 查询,满足条件:无论 person 是否有地址信息,都需要基于上述两表提供 person 的以下信息:

FirstName, LastName, City, State

代码:
SELECT
P.FirstName,
P.LastName,
A.City,
A.State
FROM
Person P
LEFT JOIN Address A ON P.PersonId = A.PersonId;
知识点:左连接
176. 第二高的薪水(简单)
编写一个 SQL 查询,获取 Employee 表中第二高的薪水(Salary) 。

±—±-------+
| Id | Salary |
±—±-------+
| 1 | 100 |
| 2 | 200 |
| 3 | 300 |
±—±-------+
例如上述 Employee 表,SQL查询应该返回 200 作为第二高的薪水。如果不存在第二高的薪水,那么查询应返回 null。

±--------------------+
| SecondHighestSalary |
±--------------------+
| 200 |
±--------------------+
代码:
SELECT
Max(Salary) AS SecondHighestSalary
FROM
Employee
WHERE
Salary < (
SELECT
Max(Salary)
FROM Employee)
知识点:子查询
177. 第N高的薪水(中等)
编写一个 SQL 查询,获取 Employee 表中第 n 高的薪水(Salary)。

±—±-------+
| Id | Salary |
±—±-------+
| 1 | 100 |
| 2 | 200 |
| 3 | 300 |
±—±-------+
例如上述 Employee 表,n = 2 时,应返回第二高的薪水 200。如果不存在第 n 高的薪水,那么查询应返回 null。

±-----------------------+
| getNthHighestSalary(2) |
±-----------------------+
| 200 |
±-----------------------+
代码:
CREATE FUNCTION getNthHighestSalary(N INT) RETURNS INT
BEGIN
SET N := N-1;
RETURN (
# Write your MySQL query statement below.
SELECT
salary
FROM
employee
GROUP BY
salary
ORDER BY
salary desc
LIMIT N, 1
);
END
知识点:GROUP BY、ORDER BY 、LIMIT
178. 分数排名(中等)
编写一个 SQL 查询来实现分数排名。

如果两个分数相同,则两个分数排名(Rank)相同。请注意,平分后的下一个名次应该是下一个连续的整数值。换句话说,名次之间不应该有“间隔”。

±—±------+
| Id | Score |
±—±------+
| 1 | 3.50 |
| 2 | 3.65 |
| 3 | 4.00 |
| 4 | 3.85 |
| 5 | 4.00 |
| 6 | 3.65 |
±—±------+
例如,根据上述给定的 Scores 表,你的查询应该返回(按分数从高到低排列):

±------±-----+
| Score | Rank |
±------±-----+
| 4.00 | 1 |
| 4.00 | 1 |
| 3.85 | 2 |
| 3.65 | 3 |
| 3.65 | 3 |
| 3.50 | 4 |
±------±-----+
重要提示:对于 MySQL 解决方案,如果要转义用作列名的保留字,可以在关键字之前和之后使用撇号。例如 Rank
代码:
SELECT
Score,
DENSE_RANK () over (ORDER BY score DESC) ‘Rank’
FROM
Scores
知识点:DENSE_RANK () over函数作用:查出指定条件后的进行排名,条件相同排名相同,排名间断不连续。

说明:和rank() over 的作用相同,区别在于dense_rank() over 排名是密集连续的。例如学生排名,使用这个函数,成绩相同的两名是并列,下一位同学接着下一个名次。即:1 1 2 3 4 5 5 6
原文链接:https://blog.csdn.net/qq_41057885/article/details/109176014
180. 连续出现的数字(中等)
表:Logs

±------------±--------+
| Column Name | Type |
±------------±--------+
| id | int |
| num | varchar |
±------------±--------+
id 是这个表的主键。
编写一个 SQL 查询,查找所有至少连续出现三次的数字。
返回的结果表中的数据可以按 任意顺序 排列。
代码:
SELECT DISTINCT
l1.Num AS ConsecutiveNums
FROM
Logs l1,
Logs l2,
Logs l3
WHERE
l1.Id = l2.Id - 1
AND l2.Id = l3.Id - 1
AND l1.Num = l2.Num
AND l2.Num = l3.Num
知识点:DISTINCT、本表自身关联查询
181. 超过经理收入的员工(简单)
Employee 表包含所有员工,他们的经理也属于员工。每个员工都有一个 Id,此外还有一列对应员工的经理的 Id。

±—±------±-------±----------+
| Id | Name | Salary | ManagerId |
±—±------±-------±----------+
| 1 | Joe | 70000 | 3 |
| 2 | Henry | 80000 | 4 |
| 3 | Sam | 60000 | NULL |
| 4 | Max | 90000 | NULL |
±—±------±-------±----------+
给定 Employee 表,编写一个 SQL 查询,该查询可以获取收入超过他们经理的员工的姓名。在上面的表格中,Joe 是唯一一个收入超过他的经理的员工。

±---------+
| Employee |
±---------+
| Joe |
±---------+
代码:
SELECT
a. NAME AS Employee
FROM
(
SELECT
*
FROM
Employee
WHERE
ManagerId IN (
SELECT
ManagerId
FROM
Employee
)
) AS a
JOIN (
SELECT
*
FROM
Employee
WHERE
Id IN (
SELECT
ManagerId
FROM
Employee
)
) AS b ON a.ManagerId = b.Id
AND a.Salary > b.Salary
知识点:join连接、子查询
182. 查找重复的电子邮箱(简单)
编写一个 SQL 查询,查找 Person 表中所有重复的电子邮箱。

示例:

±—±--------+
| Id | Email |
±—±--------+
| 1 | a@b.com |
| 2 | c@d.com |
| 3 | a@b.com |
±—±--------+
根据以上输入,你的查询应返回以下结果:

±--------+
| Email |
±--------+
| a@b.com |
±--------+
说明:所有电子邮箱都是小写字母。
代码:
SELECT
Email
FROM
Person
GROUP BY
Email
HAVING
count(Email) >= 2
知识点:GROUP BY、HAVING
183. 从不订购的客户(简单)
某网站包含两个表,Customers 表和 Orders 表。编写一个 SQL 查询,找出所有从不订购任何东西的客户。

Customers 表:

±—±------+
| Id | Name |
±—±------+
| 1 | Joe |
| 2 | Henry |
| 3 | Sam |
| 4 | Max |
±—±------+
Orders 表:

±—±-----------+
| Id | CustomerId |
±—±-----------+
| 1 | 3 |
| 2 | 1 |
±—±-----------+
例如给定上述表格,你的查询应返回:

±----------+
| Customers |
±----------+
| Henry |
| Max |
±----------+
代码:
SELECT
NAME AS Customers
FROM
Customers
WHERE
Id NOT IN (SELECT CustomerId FROM Orders)
知识点:NOT IN
184. 部门工资最高的员工(中等)
Employee 表包含所有员工信息,每个员工有其对应的 Id, salary 和 department Id。

±—±------±-------±-------------+
| Id | Name | Salary | DepartmentId |
±—±------±-------±-------------+
| 1 | Joe | 70000 | 1 |
| 2 | Jim | 90000 | 1 |
| 3 | Henry | 80000 | 2 |
| 4 | Sam | 60000 | 2 |
| 5 | Max | 90000 | 1 |
±—±------±-------±-------------+
Department 表包含公司所有部门的信息。

±—±---------+
| Id | Name |
±—±---------+
| 1 | IT |
| 2 | Sales |
±—±---------+
编写一个 SQL 查询,找出每个部门工资最高的员工。对于上述表,您的 SQL 查询应返回以下行(行的顺序无关紧要)。

±-----------±---------±-------+
| Department | Employee | Salary |
±-----------±---------±-------+
| IT | Max | 90000 |
| IT | Jim | 90000 |
| Sales | Henry | 80000 |
±-----------±---------±-------+
解释:
Max 和 Jim 在 IT 部门的工资都是最高的,Henry 在销售部的工资最高。
代码:
SELECT
Department.name AS ‘Department’,
Employee.name AS ‘Employee’,
Salary
FROM
Employee
JOIN
Department ON Employee.DepartmentId = Department.Id
WHERE
(Employee.DepartmentId , Salary) IN
( SELECT
DepartmentId, MAX(Salary)
FROM
Employee
GROUP BY DepartmentId
)
知识点:join连接、子查询、Group By
185. 部门工资前三高的所有员工(困难)
Employee 表包含所有员工信息,每个员工有其对应的工号 Id,姓名 Name,工资 Salary 和部门编号 DepartmentId 。

±—±------±-------±-------------+
| Id | Name | Salary | DepartmentId |
±—±------±-------±-------------+
| 1 | Joe | 85000 | 1 |
| 2 | Henry | 80000 | 2 |
| 3 | Sam | 60000 | 2 |
| 4 | Max | 90000 | 1 |
| 5 | Janet | 69000 | 1 |
| 6 | Randy | 85000 | 1 |
| 7 | Will | 70000 | 1 |
±—±------±-------±-------------+
Department 表包含公司所有部门的信息。

±—±---------+
| Id | Name |
±—±---------+
| 1 | IT |
| 2 | Sales |
±—±---------+
编写一个 SQL 查询,找出每个部门获得前三高工资的所有员工。例如,根据上述给定的表,查询结果应返回:

±-----------±---------±-------+
| Department | Employee | Salary |
±-----------±---------±-------+
| IT | Max | 90000 |
| IT | Randy | 85000 |
| IT | Joe | 85000 |
| IT | Will | 70000 |
| Sales | Henry | 80000 |
| Sales | Sam | 60000 |
±-----------±---------±-------+
解释:

IT 部门中,Max 获得了最高的工资,Randy 和 Joe 都拿到了第二高的工资,Will 的工资排第三。销售部门(Sales)只有两名员工,Henry 的工资最高,Sam 的工资排第二。
代码:
SELECT
d.Name AS ‘Department’, e1.Name AS ‘Employee’, e1.Salary
FROM
Employee e1
JOIN
Department d ON e1.DepartmentId = d.Id
WHERE
3 > (SELECT
COUNT(DISTINCT e2.Salary)
FROM
Employee e2
WHERE
e2.Salary > e1.Salary
AND e1.DepartmentId = e2.DepartmentId
)
196. 删除重复的电子邮箱(简单)
编写一个 SQL 查询,来删除 Person 表中所有重复的电子邮箱,重复的邮箱里只保留 Id 最小 的那个。

±—±-----------------+
| Id | Email |
±—±-----------------+
| 1 | john@example.com |
| 2 | bob@example.com |
| 3 | john@example.com |
±—±-----------------+
Id 是这个表的主键。
例如,在运行你的查询语句之后,上面的 Person 表应返回以下几行:

±—±-----------------+
| Id | Email |
±—±-----------------+
| 1 | john@example.com |
| 2 | bob@example.com |
±—±-----------------+

提示:

执行 SQL 之后,输出是整个 Person 表。
使用 delete 语句。
代码:
DELETE
FROM
Person
WHERE
Id NOT IN (
SELECT
a.Id
FROM
(
SELECT
min(Id) AS Id
FROM
Person
GROUP BY
Email
) AS a
)
197. 上升的温度(简单)
表 Weather

±--------------±--------+
| Column Name | Type |
±--------------±--------+
| id | int |
| recordDate | date |
| temperature | int |
±--------------±--------+
id 是这个表的主键
该表包含特定日期的温度信息

编写一个 SQL 查询,来查找与之前(昨天的)日期相比温度更高的所有日期的 id 。

返回结果 不要求顺序 。

查询结果格式如下例:

Weather
±—±-----------±------------+
| id | recordDate | Temperature |
±—±-----------±------------+
| 1 | 2015-01-01 | 10 |
| 2 | 2015-01-02 | 25 |
| 3 | 2015-01-03 | 20 |
| 4 | 2015-01-04 | 30 |
±—±-----------±------------+

Result table:
±—+
| id |
±—+
| 2 |
| 4 |
±—+
2015-01-02 的温度比前一天高(10 -> 25)
2015-01-04 的温度比前一天高(20 -> 30)
代码:
SELECT
a.id
FROM
weather a
JOIN weather b ON (
a.recorddate = adddate(b.recorddate, 1)
)
WHERE
a.temperature > b.temperature
595. 大的国家(简单)
这里有张 World 表

±----------------±-----------±-----------±-------------±--------------+
| name | continent | area | population | gdp |
±----------------±-----------±-----------±-------------±--------------+
| Afghanistan | Asia | 652230 | 25500100 | 20343000 |
| Albania | Europe | 28748 | 2831741 | 12960000 |
| Algeria | Africa | 2381741 | 37100000 | 188681000 |
| Andorra | Europe | 468 | 78115 | 3712000 |
| Angola | Africa | 1246700 | 20609294 | 100990000 |
±----------------±-----------±-----------±-------------±--------------+
如果一个国家的面积超过 300 万平方公里,或者人口超过 2500 万,那么这个国家就是大国家。

编写一个 SQL 查询,输出表中所有大国家的名称、人口和面积。

例如,根据上表,我们应该输出:

±-------------±------------±-------------+
| name | population | area |
±-------------±------------±-------------+
| Afghanistan | 25500100 | 652230 |
| Algeria | 37100000 | 2381741 |
±-------------±------------±-------------+
代码:
SELECT
NAME,
population,
area
FROM
World
WHERE
area > 3000000
UNION
SELECT
NAME,
population,
area
FROM
World
WHERE
population > 25000000
596. 超过5名学生的课(简单)
有一个courses 表 ,有: student (学生) 和 class (课程)。

请列出所有超过或等于5名学生的课。

例如,表:

±--------±-----------+
| student | class |
±--------±-----------+
| A | Math |
| B | English |
| C | Math |
| D | Biology |
| E | Math |
| F | Computer |
| G | Math |
| H | Math |
| I | Math |
±--------±-----------+
应该输出:

±--------+
| class |
±--------+
| Math |
±--------+

提示:

学生在每个课中不应被重复计算。
代码:
SELECT
a.class
FROM
(
SELECT
count(DISTINCT(student)) AS cc,
class
FROM
courses
GROUP BY
class
) AS a
WHERE
a.cc >= 5
620. 有趣的电影(简单)
某城市开了一家新的电影院,吸引了很多人过来看电影。该电影院特别注意用户体验,专门有个 LED显示板做电影推荐,上面公布着影评和相关电影描述。

作为该电影院的信息部主管,您需要编写一个 SQL查询,找出所有影片描述为非 boring (不无聊) 的并且 id 为奇数 的影片,结果请按等级 rating 排列。

例如,下表 cinema:

±--------±----------±-------------±----------+
| id | movie | description | rating |
±--------±----------±-------------±----------+
| 1 | War | great 3D | 8.9 |
| 2 | Science | fiction | 8.5 |
| 3 | irish | boring | 6.2 |
| 4 | Ice song | Fantacy | 8.6 |
| 5 | House card| Interesting| 9.1 |
±--------±----------±-------------±----------+
对于上面的例子,则正确的输出是为:

±--------±----------±-------------±----------+
| id | movie | description | rating |
±--------±----------±-------------±----------+
| 5 | House card| Interesting| 9.1 |
| 1 | War | great 3D | 8.9 |
±--------±----------±-------------±----------+
代码:
SELECT
*
FROM
cinema
WHERE
description <> ‘boring’
AND id % 2 = 1
ORDER BY
rating DESC
626. 换座位(中等)
小美是一所中学的信息科技老师,她有一张 seat 座位表,平时用来储存学生名字和与他们相对应的座位 id。

其中纵列的 id 是连续递增的

小美想改变相邻俩学生的座位。

你能不能帮她写一个 SQL query 来输出小美想要的结果呢?

示例:

±--------±--------+
| id | student |
±--------±--------+
| 1 | Abbot |
| 2 | Doris |
| 3 | Emerson |
| 4 | Green |
| 5 | Jeames |
±--------±--------+
假如数据输入的是上表,则输出结果如下:

±--------±--------+
| id | student |
±--------±--------+
| 1 | Doris |
| 2 | Abbot |
| 3 | Green |
| 4 | Emerson |
| 5 | Jeames |
±--------±--------+
代码:
SELECT
*
FROM
(
SELECT
(
CASE
WHEN id % 2 = 1
AND id + 1 > (SELECT count(*) FROM seat) THEN
id
WHEN id % 2 = 0 THEN
id - 1
ELSE
id + 1
END
) AS id,
student
FROM
seat
) s
ORDER BY
Id
627. 变更性别(简单)
给定一个 salary 表,如下所示,有 m = 男性 和 f = 女性 的值。交换所有的 f 和 m 值(例如,将所有 f 值更改为 m,反之亦然)。要求只使用一个更新(Update)语句,并且没有中间的临时表。

注意,您必只能写一个 Update 语句,请不要编写任何 Select 语句。

例如:

idnamesexsalary
1Am2500
2Bf1500
3Cm5500
4Df500
运行你所编写的更新语句之后,将会得到以下表:
idnamesexsalary
1Af2500
2Bm1500
3Cf5500
4Dm500
代码:
UPDATE salary

SET sex = CASE
WHEN sex = ‘m’ THEN
‘f’
WHEN sex = ‘f’ THEN
‘m’
END
262. 行程和用户(困难)
表:Trips
±------------±---------+
| Column Name | Type |
±------------±---------+
| Id | int |
| Client_Id | int |
| Driver_Id | int |
| City_Id | int |
| Status | enum |
| Request_at | date |
±------------±---------+
Id 是这张表的主键。
这张表中存所有出租车的行程信息。每段行程有唯一 Id ,其中 Client_Id 和 Driver_Id 是 Users 表中 Users_Id 的外键。
Status 是一个表示行程状态的枚举类型,枚举成员为(‘completed’‘cancelled_by_driver’, ‘cancelled_by_client’) 。
表:Users

±------------±---------+
| Column Name | Type |
±------------±---------+
| Users_Id | int |
| Banned | enum |
| Role | enum |
±------------±---------+
Users_Id 是这张表的主键。
这张表中存所有用户,每个用户都有一个唯一的 Users_Id ,Role 是一个表示用户身份的枚举类型,枚举成员为 (‘client’, ‘driver’, ‘partner’) 。
Banned 是一个表示用户是否被禁止的枚举类型,枚举成员为 (‘Yes’, ‘No’) 。

写一段 SQL 语句查出 “2013-10-01” 至 “2013-10-03” 期间非禁止用户(乘客和司机都必须未被禁止)的取消率。非禁止用户即 Banned 为 No 的用户,禁止用户即 Banned 为 Yes 的用户。

取消率 的计算方式如下:(被司机或乘客取消的非禁止用户生成的订单数量) / (非禁止用户生成的订单总数)。

返回结果表中的数据可以按任意顺序组织。其中取消率 Cancellation Rate 需要四舍五入保留 两位小数 。

查询结果格式如下例所示:

Trips 表:
±—±----------±----------±--------±--------------------±-----------+
| Id | Client_Id | Driver_Id | City_Id | Status | Request_at |
±—±----------±----------±--------±--------------------±-----------+
| 1 | 1 | 10 | 1 | completed | 2013-10-01 |
| 2 | 2 | 11 | 1 | cancelled_by_driver | 2013-10-01 |
| 3 | 3 | 12 | 6 | completed | 2013-10-01 |
| 4 | 4 | 13 | 6 | cancelled_by_client | 2013-10-01 |
| 5 | 1 | 10 | 1 | completed | 2013-10-02 |
| 6 | 2 | 11 | 6 | completed | 2013-10-02 |
| 7 | 3 | 12 | 6 | completed | 2013-10-02 |
| 8 | 2 | 12 | 12 | completed | 2013-10-03 |
| 9 | 3 | 10 | 12 | completed | 2013-10-03 |
| 10 | 4 | 13 | 12 | cancelled_by_driver | 2013-10-03 |
±—±----------±----------±--------±--------------------±-----------+

Users 表:
±---------±-------±-------+
| Users_Id | Banned | Role |
±---------±-------±-------+
| 1 | No | client |
| 2 | Yes | client |
| 3 | No | client |
| 4 | No | client |
| 10 | No | driver |
| 11 | No | driver |
| 12 | No | driver |
| 13 | No | driver |
±---------±-------±-------+

Result 表:
±-----------±------------------+
| Day | Cancellation Rate |
±-----------±------------------+
| 2013-10-01 | 0.33 |
| 2013-10-02 | 0.00 |
| 2013-10-03 | 0.50 |
±-----------±------------------+

2013-10-01:

  • 共有 4 条请求,其中 2 条取消。
  • 然而,Id=2 的请求是由禁止用户(User_Id=2)发出的,所以计算时应当忽略它。
  • 因此,总共有 3 条非禁止请求参与计算,其中 1 条取消。
  • 取消率为 (1 / 3) = 0.33
    2013-10-02:
  • 共有 3 条请求,其中 0 条取消。
  • 然而,Id=6 的请求是由禁止用户发出的,所以计算时应当忽略它。
  • 因此,总共有 2 条非禁止请求参与计算,其中 0 条取消。
  • 取消率为 (0 / 2) = 0.00
    2013-10-03:
  • 共有 3 条请求,其中 1 条取消。
  • 然而,Id=8 的请求是由禁止用户发出的,所以计算时应当忽略它。
  • 因此,总共有 2 条非禁止请求参与计算,其中 1 条取消。
  • 取消率为 (1 / 2) = 0.50
    代码:
    SELECT T.request_at AS Day,
    ROUND(
    SUM(
    IF(T.STATUS = ‘completed’,0,1)
    )
    /
    COUNT(T.STATUS),
    2
    ) AS Cancellation Rate
    FROM Trips AS T
    JOIN Users AS U1 ON (T.client_id = U1.users_id AND U1.banned =‘No’)
    JOIN Users AS U2 ON (T.driver_id = U2.users_id AND U2.banned =‘No’)
    WHERE T.request_at BETWEEN ‘2013-10-01’ AND ‘2013-10-03’
    GROUP BY T.request_at
  1. 体育馆的人流量(困难)
    表:Stadium
    ±--------------±--------+
    | Column Name | Type |
    ±--------------±--------+
    | id | int |
    | visit_date | date |
    | people | int |
    ±--------------±--------+
    visit_date 是表的主键
    每日人流量信息被记录在这三列信息中:序号 (id)、日期 (visit_date)、 人流量 (people)
    每天只有一行记录,日期随着 id 的增加而增加

编写一个 SQL 查询以找出每行的人数大于或等于 100 且 id 连续的三行或更多行记录。

返回按 visit_date 升序排列的结果表。

查询结果格式如下所示。

Stadium table:
±-----±-----------±----------+
| id | visit_date | people |
±-----±-----------±----------+
| 1 | 2017-01-01 | 10 |
| 2 | 2017-01-02 | 109 |
| 3 | 2017-01-03 | 150 |
| 4 | 2017-01-04 | 99 |
| 5 | 2017-01-05 | 145 |
| 6 | 2017-01-06 | 1455 |
| 7 | 2017-01-07 | 199 |
| 8 | 2017-01-09 | 188 |
±-----±-----------±----------+

Result table:
±-----±-----------±----------+
| id | visit_date | people |
±-----±-----------±----------+
| 5 | 2017-01-05 | 145 |
| 6 | 2017-01-06 | 1455 |
| 7 | 2017-01-07 | 199 |
| 8 | 2017-01-09 | 188 |
±-----±-----------±----------+
id 为 5、6、7、8 的四行 id 连续,并且每行都有 >= 100 的人数记录。
请注意,即使第 7 行和第 8 行的 visit_date 不是连续的,输出也应当包含第 8 行,因为我们只需要考虑 id 连续的记录。
不输出 id 为 2 和 3 的行,因为至少需要三条 id 连续的记录。
代码:
select distinct t1.*
from stadium t1, stadium t2, stadium t3
where t1.people >= 100 and t2.people >= 100 and t3.people >= 100
and
(
(t1.id - t2.id = 1 and t1.id - t3.id = 2 and t2.id - t3.id =1) – t1, t2, t3
or
(t2.id - t1.id = 1 and t2.id - t3.id = 2 and t1.id - t3.id =1) – t2, t1, t3
or
(t3.id - t2.id = 1 and t2.id - t1.id =1 and t3.id - t1.id = 2) – t3, t2, t1
)
order by t1.id
1179. 重新格式化部门表(简单)
部门表 Department:

±--------------±--------+
| Column Name | Type |
±--------------±--------+
| id | int |
| revenue | int |
| month | varchar |
±--------------±--------+
(id, month) 是表的联合主键。
这个表格有关于每个部门每月收入的信息。
月份(month)可以取下列值 [“Jan”,“Feb”,“Mar”,“Apr”,“May”,“Jun”,“Jul”,“Aug”,“Sep”,“Oct”,“Nov”,“Dec”]。

编写一个 SQL 查询来重新格式化表,使得新的表中有一个部门 id 列和一些对应 每个月 的收入(revenue)列。

查询结果格式如下面的示例所示:

Department 表:
±-----±--------±------+
| id | revenue | month |
±-----±--------±------+
| 1 | 8000 | Jan |
| 2 | 9000 | Jan |
| 3 | 10000 | Feb |
| 1 | 7000 | Feb |
| 1 | 6000 | Mar |
±-----±--------±------+

查询得到的结果表:
±-----±------------±------------±------------±----±------------+
| id | Jan_Revenue | Feb_Revenue | Mar_Revenue | … | Dec_Revenue |
±-----±------------±------------±------------±----±------------+
| 1 | 8000 | 7000 | 6000 | … | null |
| 2 | 9000 | null | null | … | null |
| 3 | null | 10000 | null | … | null |
±-----±------------±------------±------------±----±------------+

注意,结果表有 13 列 (1个部门 id 列 + 12个月份的收入列)。
代码:
SELECT id,
SUM(CASE WHEN month=‘Jan’ THEN revenue END) AS Jan_Revenue,
SUM(CASE WHEN month=‘Feb’ THEN revenue END) AS Feb_Revenue,
SUM(CASE WHEN month=‘Mar’ THEN revenue END) AS Mar_Revenue,
SUM(CASE WHEN month=‘Apr’ THEN revenue END) AS Apr_Revenue,
SUM(CASE WHEN month=‘May’ THEN revenue END) AS May_Revenue,
SUM(CASE WHEN month=‘Jun’ THEN revenue END) AS Jun_Revenue,
SUM(CASE WHEN month=‘Jul’ THEN revenue END) AS Jul_Revenue,
SUM(CASE WHEN month=‘Aug’ THEN revenue END) AS Aug_Revenue,
SUM(CASE WHEN month=‘Sep’ THEN revenue END) AS Sep_Revenue,
SUM(CASE WHEN month=‘Oct’ THEN revenue END) AS Oct_Revenue,
SUM(CASE WHEN month=‘Nov’ THEN revenue END) AS Nov_Revenue,
SUM(CASE WHEN month=‘Dec’ THEN revenue END) AS Dec_Revenue
FROM department
GROUP BY id
ORDER BY id;
排序算法
1、 冒泡排序(稳定)交换
冒泡排序是一种简单的排序算法。它重复地走访过要排序的数列,一次比较两个元素,如果
它们的顺序错误就把它们交换过来。 走访数列的工作是重复地进行直到没有再需要交换, 也就是说该数列已经排序完成。 这个算法的名字由来是因为越小的元素会经由交换慢慢“浮”到数列的顶端。 (array.length - 1 – i、j和j+1)
public static int[] bubbleSort(int[] array) {
if (array.length == 0)
return array;
for (int i = 0; i < array.length; i++)
for (int j = 0; j < array.length - 1 - i; j++)
if (array[j + 1] < array[j]) {
int temp = array[j + 1];
array[j + 1] = array[j];
array[j] = temp;
}
return array;
}
2、 选择排序(稳定)
表现 最稳定的排序算法之一,因为 无论什么数据进去都是 O(n2) 的时间复杂度,所以用到它的时候,数据规模越小越好。唯一的好处可能就是不占用额外的内存空间了吧。理论上讲,选择排序可能也是平时排序一般人想到的最多的排序方法了吧。选择排序(Selection-sort)是一种简单直观的排序算法。它的工作原理:首先在未排序序列中找到最小(大)元素,存放到排序序列的起始位置,然后,再从剩余未排序元素中继续寻找最小(大)元素,然后放到已排序序列的末尾。以此类推,直到所有元素均排序完毕。
I,j,minIndex
5 7 2 4 9 8 1 6 3 0

I,j minIndex
0 7 2 4 9 8 1 6 3 5
I,j minIndex
0 7 2 4 9 8 1 6 3 5

public static int[] selectionSort(int[] array) {
if (array.length == 0)
return array;
for (int i = 0; i < array.length; i++) {
int minIndex = i;
for (int j = i; j < array.length; j++) {
if (array[j] < array[minIndex]) //找到最小的数
minIndex = j; //将最小数的索引保存
}
Swap(array[minIndex],array[i]);
}
return array;
}
选择排序: 就是从第一个到最后 1 个数, 每次都找最小的数放
在第 i 个位置上
3、 插入排序(不稳定)
插入排序(Insertion-Sort)的算法描述是一种简单直观的排序算法。它的工作原理是通过构建有序序列,对于未排序数据,在已排序序列中从后向前扫描,找到相应位置并插入。插入排序在实现上,通常采用 in-place 排序(即只需用到 O(1)的额外空间的排序) ,因而在从后向前扫描过程中,需要反复把已排序元素逐步向后挪位,为最新元素提供插入空间。
public static int[] insertionSort(int[] array) {
if (array.length == 0)
return array;
int current;
for (int i = 0; i < array.length - 1; i++) {
current = array[i + 1];
int preIndex = i;
while (preIndex >= 0 && current < array[preIndex]) {
array[preIndex + 1] = array[preIndex];
preIndex–;
}
array[preIndex + 1] = current;
}
return array;
}
插入排序: 就是从第一个到最后 1 个数, 之后的第一个数都向
前遍历最多 i 次,当满足大于前一个小于最后一个就插进去;
4、 希尔排序(不稳定)
我们来看下希尔排序的基本步骤,在此我们选择增量 gap=length/2,缩小增量继续以gap = gap/2的方式, 这种增量选择我们可以用一个序列来表示, {n/2,(n/2)/2…1},称为增量序列。 希尔排序的增量序列的选择与证明是个数学难题, 我们选择的这个增量序列是比较常用的,也是希尔建议的增量,称为希尔增量,但其实这个增量序列不是最优的。此处我们做示例使用希尔增量。
public static int[] ShellSort(int[] array) {
int len = array.length;
int temp, gap = len / 2;
while (gap > 0) {
for (int i = gap; i < len; i++) {
temp = array[i];
int preIndex = i - gap;
while (preIndex >= 0 && array[preIndex] > temp) {
array[preIndex + gap] = array[preIndex];
preIndex -= gap;
}
array[preIndex + gap] = temp;
}
gap /= 2;
}
return array;
}
希尔排序:先把数组分成 length/2 组,然后排序,再重复
分组再排序……
5、 归并排序(稳定)分治(先分再合)
和选择排序一样,归并排序的性能不受输入数据的影响,但表现比选择排序好的多,因为始终都是 O(n log n)的时间复杂度。代价是需要额外的内存空间。 归并排序是建立在归并操作上的一种有效的排序算法。该算法是采用分治法(Divide and Conquer)的一个非常典型的应用。归并排序是一种稳定的排序方法。将已有序的子序列合并,得到完全有序的序列;即先使每个子序列有序,再使子序列段间有序。若将两个有序表合并成一个有序表,称为 2-路归并。
public class MergeSort {
public static void main(String []args){
int []arr = {9,8,7,6,5,4,3,2,1};
sort(arr);
System.out.println(Arrays.toString(arr));
}
public static void sort(int []arr){
int []temp = new int[arr.length];//在排序前,先建好一个长度等于原数组长度的临时数组,避免递归中频繁开辟空间
sort(arr,0,arr.length-1,temp);
}
private static void sort(int[] arr,int left,int right,int []temp){
if(left<right){
int mid = (left+right)/2;
sort(arr,left,mid,temp);//左边归并排序,使得左子序列有序
sort(arr,mid+1,right,temp);//右边归并排序,使得右子序列有序
merge(arr,left,mid,right,temp);//将两个有序子数组合并操作
}
}
private static void merge(int[] arr,int left,int mid,int right,int[] temp){
int i = left;//左序列指针
int j = mid+1;//右序列指针
int t = 0;//临时数组指针
while (i<=mid && j<=right){
if(arr[i]<=arr[j]){
temp[t++] = arr[i++];
}else {
temp[t++] = arr[j++];
}
}
while(i<=mid){//将左边剩余元素填充进temp中
temp[t++] = arr[i++];
}
while(j<=right){//将右序列剩余元素填充进temp中
temp[t++] = arr[j++];
}
t = 0;
//将temp中的元素全部拷贝到原数组中
while(left <= right){
arr[left++] = temp[t++];
}
}
}
6、 快速排序(不稳定)分治思想(递归)
快速排序的基本思想: 通过一趟排序将待排记录分隔成独立的两部分,其中一部分记录的关键字均比另一部分的关键字小,则可分别对这两部分记录继续进行排序,以达到整个序列有序。
class Solution {
public int[] sortArray(int[] nums) {
int left = 0 ;
int right = nums.length-1;
abc(nums,left,right);
return nums;
}
public void abc(int[] nums,int left,int right){
if(left > right) {
return;
}
int a = left ;
int b = right;
int temp = nums[left];

    while(a!=b){ 
        while( nums[b]>=temp&&b > a ) b--; 
        while( nums[a]<=temp&&b > a ) a++; 
        if(a<b){ 
            int thd = nums[a]; 
            nums[a] = nums[b]; 
            nums[b] = thd; 
        } 

    } 
    nums[left] = nums[a]; 
    nums[a] = temp; 
    abc(nums,left,a-1); 
    abc(nums,a+1,right); 
} 

}
void quick_sort(int q[],int l,int r){

if(l >= r) return;
int x = q[(l + r) >> 1], i = l - 1, j = r + 1;
while (i < j){
    do i++; while (q[i] < x);
    do j--; while (q[j] > x);
    if (i < j) swap(q[i], q[j]);
}

quick_sort(q, l, j);
quick_sort(q, j + 1, r);

}
7、 堆排序(不稳定)
堆排序(Heapsort)是指利用堆这种数据结构所设计的一种排序算法。堆
积是一个近似完全二叉树的结构,并同时满足堆积的性质:即子结点的键值或索
引总是小于(或者大于)它的父节点。
class Solution {
public int[] sortArray(int[] nums) {
//1.构建大顶堆
for(int i=nums.length/2-1;i>=0;i–){
//从第一个非叶子结点从下至上,从右至左调整结构
adjustHeap(nums,i,nums.length);
}
for(int j=nums.length-1;j>0;j–){
swap(nums,0,j);//将堆顶元素与末尾元素进行交换
adjustHeap(nums,0,j);//重新对堆进行调整
}
return nums;
}
private void adjustHeap(int[] arr, int parent, int length) {
//将 temp 作为父节点
int temp = arr[parent];
//左孩子
int lChild = 2 * parent + 1;
while (lChild < length) {
//右孩子
int rChild = lChild + 1;
// 如果有右孩子结点,并且右孩子结点的值大于左孩子结点,则选取右孩子结

if (rChild < length && arr[lChild] < arr[rChild]) {
lChild++;
}
// 如果父结点的值已经大于孩子结点的值,则直接结束
if (temp >= arr[lChild]) {
break;
}
// 把孩子结点的值赋给父结点
arr[parent] = arr[lChild];
//选取孩子结点的左孩子结点,继续向下筛选
parent = lChild;
lChild = 2 * lChild + 1;
}
arr[parent] = temp;
}
public void swap(int []arr,int a ,int b){
int temp=arr[a];
arr[a] = arr[b];
arr[b] = temp;
}
}

1、算法复杂度与初始状态无关的有:选择排序、堆排序、归并排序、基数排序。
2、元素总比较次数与初始状态无关的有:选择排序、基数排序。
3、元素总移动次数与初始状态无关的有:归并排序、基数排序。
关于时间复杂度:

  1. 平方阶 (O(n2)) 排序 各类简单排序:直接插入、直接选择和冒泡排序。
  2. 线性对数阶 (O(nlog2n)) 排序 快速排序、堆排序和归并排序;
  3. O(n1+§)) 排序,§ 是介于 0 和 1 之间的常数。 希尔排序
  4. 线性阶 (O(n)) 排序 基数排序,此外还有桶、箱排序。
    关于稳定性:
  5. 稳定的排序算法:冒泡排序、插入排序、归并排序和基数排序。
  6. 不是稳定的排序算法:选择排序、快速排序、希尔排序、堆排序。

作者:程序员吴师兄
链接:https://www.zhihu.com/question/51337272/answer/572455307
来源:知乎
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

查找算法
1、 顺序查找
public static int search(int[] a, int key) {
for (int i = 0, length = a.length; i < length; i++) {
if (a[i] == key)
return i;
}
return -1;
}
2、 二分查找
public static int binarySearch(int[] array, int value) {
int low = 0;
int high = array.length - 1;
while (low <= high) {
int middle = (low + high) >> 1;
if (value == array[middle]) {
return middle;
}
if (value > array[middle]) {
low = middle + 1;
}
if (value < array[middle]) {
high = middle - 1;
}
}
return -1;
}
位运算加速技巧

  1. 如果乘上一个2的倍数数值,可以改用左移运算(Left Shift) 加速 300%
    x = x * 2;
    x = x * 64;
    改为:
    x = x << 1; // 2 = 21
    x = x << 6; // 64 = 26
  2. 如果除上一个 2 的倍数数值,可以改用右移运算加速 350%
    x = x / 2;
    x = x / 64;
    改为:
    x = x >> 1;// 2 = 21
    x = x >> 6;// 64 = 26
  3. 数值转整数加速 10%
    x = int(1.232)
    改为:
    x = 1.232 >> 0;
  4. 交换两个数值(swap),使用 XOR 可以加速20%
    var t:int = a;
    a = b;
    b = t;
    改为:
    a = a^b;
    b = a^b;
    a = a^b;
  5. 正负号转换,可以加入 300%
    i = -i;
    改为
    i = ~i + 1; // NOT 写法
    //或
    i = (i ^ -1) + 1; // XOR 写法
  6. 取余数,如果除数为 2 的倍数,可利用 AND 运算加速 600%
    x = 131 % 4;
    改为:
    x = 131 & (4 - 1);
  7. 利用 AND 运算检查整数是否为 2 的倍数,可以加速 600%
    isEven = (i % 2) == 0;
    改为:
    isEven = (i & 1) == 0;
  8. 加速 Math.abs 600% 的写法1,写法2 又比写法1加速 20%
    //写法1
    i = x < 0 ? -x : x;
    //写法2
    i = (x ^ (x >> 31)) - (x >> 31);
    //写法3
    i=x^(~(x>>31)+1)+(x>>31);
  9. 比较两数值相乘之后是否拥有相同的符号,加速 35%
    eqSign = a * b > 0;
    改为:
    eqSign = a ^ b > 0;
    图文详解两种算法:深度优先遍历(DFS)和广度优先遍历(BFS)
    1、 深度优先遍历
    2、 递归实现
    3、 public class Solution {
    4、 private static class Node {
    5、 /**
    6、 * 节点值
    7、 /
    8、 public int value;
    9、 /
    *
    10、 * 左节点
    11、 /
    12、 public Node left;
    13、 /
    *
    14、 * 右节点
    15、 /
    16、 public Node right;
    17、
    18、 public Node(int value, Node left, Node right) {
    19、 this.value = value;
    20、 this.left = left;
    21、 this.right = right;
    22、 }
    23、 }
    24、
    25、 public static void dfs(Node treeNode) {
    26、 if (treeNode == null) {
    27、 return;
    28、 }
    29、 // 遍历节点
    30、 process(treeNode)
    31、 // 遍历左节点
    32、 dfs(treeNode.left);
    33、 // 遍历右节点
    34、 dfs(treeNode.right);
    35、 }
    36、 }
    2、 非递归实现
    3、 /
    *
    4、 * 使用栈来实现 dfs
    5、 * @param root
    6、 /
    7、 public static void dfsWithStack(Node root) {
    8、 if (root == null) {
    9、 return;
    10、 }
    11、
    12、 Stack stack = new Stack<>();
    13、 // 先把根节点压栈
    14、 stack.push(root);
    15、 while (!stack.isEmpty()) {
    16、 Node treeNode = stack.pop();
    17、 // 遍历节点
    18、 process(treeNode)
    19、
    20、 // 先压右节点
    21、 if (treeNode.right != null) {
    22、 stack.push(treeNode.right);
    23、 }
    24、
    25、 // 再压左节点
    26、 if (treeNode.left != null) {
    27、 stack.push(treeNode.left);
    28、 }
    29、 }
    30、 }
    2、 广度优先遍历
    1、 非递归实现
    2、 /
    *
    3、 * 使用队列实现 bfs
    4、 * @param root
    5、 */
    6、 private static void bfs(Node root) {
    7、 if (root == null) {
    8、 return;
    9、 }
    10、 Queue stack = new LinkedList<>();
    11、 stack.add(root);
    12、
    13、 while (!stack.isEmpty()) {
    14、 Node node = stack.poll();
    15、 System.out.println("value = " + node.value);
    16、 Node left = node.left;
    17、 if (left != null) {
    18、 stack.add(left);
    19、 }
    20、 Node right = node.right;
    21、 if (right != null) {
    22、 stack.add(right);
    23、 }
    24、 }
    25、 }
    矩阵旋转:
    //原图像
    14 for(int i=0;i<10;i++){
    15 for(int j=0;j<10;j++){
    16 a[i][j] = k++;
    17 }
    18 }
    19 //顺时针旋转90
    20 int shun90[10][10];
    21 for(int i=0;i<10;i++){
    22 for(int j=0;j<10;j++){
    23 shun90[j][9-i] = a[i][j];
    24 }
    25 }
    26 //逆时针旋转90
    27 int ni90[10][10];
    28 for(int i=0;i<10;i++){
    29 for(int j=0;j<10;j++){
    30 ni90[9-j][i] = a[i][j];
    31 }
    32 }
    33 //旋转180
    34 int zhuan180[10][10];
    35 for(int i=0;i<10;i++){
    36 for(int j=0;j<10;j++){
    37 zhuan180[9-i][9-j] = a[i][j];
    38 }
    39 }
    求二叉树中的最大二叉搜索树
    3 2
    2 1 3
    1 0 0
    3 0 0
    3
    #include
    #include

using namespace std;

    const int N=1000010;
    const int INF=0x3f3f3f3f;
    struct BST{
    int val;
    int lch,rch;
    }bst[N];
    int n,rt;
    int fa,lch,rch;
    int ret;
    int postorder(int root,int*ans){
    if(!root){
    ans[0]=0,ans[1]=INF,ans[2]=-INF;
    return 0;
    }
    int val = bst[root].val;
    int _lch = bst[root].lch,_rch = bst[root].rch;
    int L = postorder(_lch,ans);
    int lsize = ans[0],lmin = ans[1],lmax = ans[2];
    int R = postorder(_rch,ans);
    int rsize = ans[0],rmin = ans[1],rmax = ans[2];
    int max_size = max(lsize,rsize);
    int head = lsize > rsize ? L : R;
    ans[1] = min(lmin,val);
    ans[2] = max(rmax,val);
    if(L ==_lch && R ==_rch && lmax < val && rmin > val){
    max_size = lsize + rsize + 1;
    head = root;
    }
    ans[0] = max_size;
    ret = max(ret, max_size);
    return head;
    }
    int main(void){
    scanf("%d%d",&n,&rt);
    while(n--){
    scanf("%d%d%d",&fa,&lch,&rch);
    bst[fa].val=fa;
    bst[fa].lch=lch;
    bst[fa].rch=rch;
    }
    int ans[3];
    postorder(rt, ans);
    printf("%d\n",ret);
    return 0;

    }
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

流金de岁月

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值