力扣刷题历程

最近一段时间找实习惨败,心情也一直因此不太好。想沉淀自己,真正蜕变!

当时有点幸运地考上了研究生,方向是计算机视觉,两年制意味着研一下学期开始要找实习。算法肯定找不到,开始转后端,在学校里的鄙视链,总是算法>后端>前端>客户端>测试。我一向是好强的人,但是要补的东西实在太多了,从头开始学的java语法,不能光看网课不思考吧。八股文背的时候也不能光背啊,得理解吧。项目得有吧,不会自己弄,看网课学着别人弄,但是最近忽视了最重要的力扣题,最有希望拿的offer却因为一道简单的题没了,荒谬且心酸。

那就干脆刷题提升自己吧,加油,调整心态。

1. leetcode45:跳跃游戏 Ⅱ

  • 题目链接:leetcode45:跳跃游戏 Ⅱ
  • 题目描述:输入: nums = [2,3,1,1,4],输出: 2
  • 解释: 跳到最后一个位置的最小跳跃数是 2。从下标为 0 跳到下标为 1 的位置,跳 1 步,然后跳 3 步到达数组的最后一个位置。
class Solution {
    public int jump(int[] nums) {
        int maxPosition = 0;
        int end = 0;
        int steps = 0;
        if (nums.length == 0)
            return 0;
        for (int i = 0; i < nums.length; i++) {
            maxPosition = Math.max(maxPosition, i + nums[i]);
            if ( maxPosition >= nums.length - 1) 
                return (steps + 1);
            if (i == end) {
                end = maxPosition;
                steps++;
            }
        }
        return steps;
    }
}
  • 总结:代码还是比较冗余,可能能再优雅一些,二刷回来再看吧。还是实现了时间复杂度变为了O(1)。

2. leetcode 02.05. 链表求和

  • 题目链接:leetcode 02.05. 链表求和

  •  题目描述:给定两个用链表表示的整数,每个节点包含一个数位。这些数位是反向存放的,也就是个位排在链表首部。编写函数对这两个整数求和,并用链表形式返回结果。
  • 输入:(7 -> 1 -> 6) + (5 -> 9 -> 2),即617 + 295
    输出:2 -> 1 -> 9,即912
/**
 * Definition for singly-linked list.
 * public class ListNode {
 *     int val;
 *     ListNode next;
 *     ListNode(int x) { val = x; }
 * }
 */
class Solution {
    public ListNode addTwoNumbers(ListNode l1, ListNode l2) {
        int carry = 0;
        ListNode res = new ListNode(-1);
        ListNode cur = res;
        while ( l1 != null || l2 != null) {
            int num1 = l1 == null ? 0 : l1.val;
            int num2 = l2 == null ? 0 : l2.val;
            
            int sum = num1 + num2 + carry;
            carry = sum / 10;
            cur.next = new ListNode(sum%10);
            cur = cur.next;
            l1 = l1 == null ? null : l1.next;
            l2 = l2 == null ? null : l2.next;

        }

        if (carry > 0) {
            cur.next = new ListNode(carry);
        }
        return res.next;

    }
}

    /**
    进阶:正向相加,即个位排在链表尾部
    思想:先用两个栈分别把两个链表的元素装进去,新建一个链表需要用头插法插入元素
    */

    /** public ListNode addTwoNumbers(ListNode l1, ListNode l2) {
       Deque stack1 = new ArrayDeque();
       Deque stack2 = new ArrayDeque();
       ListNode res = null;
       while (l1 != null) {
           stack1.addFirst(l1.val);
           l1 = l1.next;
       }
       while (l2 != null) {
           stack2.addFirst(l2.val);
           l2 = l2.next;
       }
        int carry = 0;
        while (!stack1.isEmpty() || !stack2.isEmpty()) {
            int num1 = stack1.isEmpty() ? 0 : stack1.pollFirst();
            int num2 = stack2.isEmpty() ? 0 : stack2.pollFirst();

            int sum = num1 + num2 + carry;
            ListNode cur = new ListNode(sum % 10);
            carry = sum / 10;
            cur.next = res;
            res = cur;
        }
        if (carry > 0) {
            ListNode cur = new ListNode(carry);
            cur.next = res;
            res = cur;
        }
        return res;
    }
    
}
*/
  • 总结:反向相加的话,比较简单,不能忘记两个链表加完了,但是carry有余位,那么加到末尾就行了。正向相加,要想加,就得先放到栈里面,再出栈这样就能从后往前加。但是因为是从后往前,所以要用头插法把链表连接起来。

3. 牛客BM1 反转链表

  • 题目链接:牛客BM1 反转链表
  • 题目描述:当输入链表{1,2,3}时,经反转后,对应的输出为{3,2,1}。
import java.util.*;

/*
public class ListNode {
    int val;
    ListNode next = null;

    ListNode(int val) {
        this.val = val;
    }
}*/

public class Solution {
    public ListNode ReverseList(ListNode head) {
        if (head == null) 
            return null;
        ListNode res = new ListNode(-1);
        res.next = head;
        ListNode cur = head;
        while (cur.next != null) {
            
            ListNode temp = cur.next;
            cur.next = temp.next;
            temp.next = res.next;
            res.next = temp;
            
        }
        return res.next;
        
    }
}
  • 总结:要注意while的边界条件,因为出现了cur.next.next,所以需要保证cur.next不为空。其实还是新建了一个链表,对内存有浪费。

4. BM2 链表内指定区间反转

  • 题目链接:BM2 链表内指定区间反转
  • 题目描述:将一个节点数为 size 链表 m 位置到 n 位置之间的区间反转,要求时间复杂度 O(n),空间复杂度 O(1)。
  • 输入:{1,2,3,4,5},2,4。
  • 输出:{1,4,3,2,5}。
import java.util.*;

/*
 * public class ListNode {
 *   int val;
 *   ListNode next = null;
 * }
 */

public class Solution {
    
    /**
     * 
     * @param head ListNode类 
     * @param m int整型 
     * @param n int整型 
     * @return ListNode类 
     */
    
    
    public ListNode reverseBetween (ListNode head, int m, int n) {
        // write code here
        if (head == null) {
            return null;
        }
        
        ListNode res = new ListNode(-1);
        res.next = head;
        ListNode pre = res;
        ListNode cur = head;
        
        for (int i = 0; i < m-1; i++ ) {
            pre = cur;
            cur = cur.next;
        }
        
        for (int j = m; j < n; j++ ) {
            ListNode temp = cur.next;
            cur.next = temp.next;
            temp.next = pre.next;
            pre.next = temp;
        }
        return res.next;
        
        
    }
    
}
  • 总结:反转链表一定要会用头插法,题目又要求在指定区间内反转链表,所以得遍历找到这段链表在哪,找到在哪需要用头插法反转,而头插法反转需要知道头也就是pre在哪,确定了pre的位置就是一道普通的反转链表的题目了。

5. BM3 链表中的节点每k个一组翻转

  • 题目链接:BM3 链表中的节点每k个一组翻转
  • 题目描述:将给出的链表中的节点每 k 个一组翻转,返回翻转后的链表。如果链表中的节点数不是 k 的倍数,将最后剩下的节点保持原样。不能更改节点中的值,只能更改节点本身。
  • 输入:{1,2,3,4,5},2
  • 返回值:{2,1,4,3,5}
import java.util.*;

/*
 * public class ListNode {
 *   int val;
 *   ListNode next = null;
 * }
 */

public class Solution {
    /**
     * 
     * @param head ListNode类 
     * @param k int整型 
     * @return ListNode类
     */
    public ListNode reverseKGroup (ListNode head, int k) {
        
        ListNode pre = null;
        ListNode cur = head;
        ListNode tail = head;
        
        for (int i = 0; i < k; i++) {
            if (tail == null)
                return head;
            tail = tail.next;
        }
        
        while (cur != tail) {
            ListNode temp = cur.next;
            cur.next = pre;
            pre = cur;
            cur = temp;
        }
        
        head.next = reverseKGroup(cur, k);
        return pre;
        
        
    }
}
  •  总结:我感觉这道题还是挺难的,看到题,我也想到了递归,但是递归的入口我确实没想到,以及链表反转的边界条件,这种题就是见过一次,就能做出来类似的,但是没见过,还是很难想出来做法。

6. BM4 合并两个排序的链表

  • 题目链接:BM4 合并两个排序的链表
  • 题目描述:输入两个递增的链表,单个链表的长度为n,合并这两个链表并使新链表中的节点仍然是递增排序的。
  • 输入{1,3,5},{2,4,6}时,合并后的链表为{1,2,3,4,5,6},所以对应的输出为{1,2,3,4,5,6}
import java.util.*;
/*
public class ListNode {
    int val;
    ListNode next = null;

    ListNode(int val) {
        this.val = val;
    }
}*/
public class Solution {
    public ListNode Merge(ListNode list1,ListNode list2) {
        ListNode res = new ListNode(-1);
        ListNode cur = res;
        while (list1 != null && list2 != null) {
            if (list1.val < list2.val) {
                cur.next = list1;
                list1 = list1.next;
                cur = cur.next;
            } else {
                cur.next = list2;
                list2 = list2.next;
                cur = cur.next;
            }
        }
        if (list1 != null) 
            cur.next = list1;
        if (list2 != null)
            cur.next = list2;
        return res.next;
    }
}
  • 总结:这道题也很简单,我就比较直观的写出来了,其实感觉有很多冗余代码,二刷的时候再看看怎么优化吧。

7. BM5 合并k个已排序的链表

  • 题目链接:BM5 合并k个已排序的链表
  • 题目描述:合并 k 个升序的链表并将结果作为一个升序的链表返回其头节点。
  • 输入:[{1,2},{1,4,5},{6}]
  • 输出:{1,1,2,4,5,6}
import java.util.*;
/**
 * Definition for singly-linked list.
 * public class ListNode {
 *     int val;
 *     ListNode next;
 *     ListNode(int x) {
 *         val = x;
 *         next = null;
 *     }
 * }
 */
public class Solution {
    public ListNode mergeKLists(ArrayList lists) {
        return merge(lists, 0, lists.size()-1);
    }
    public ListNode merge(ArrayList lists, int l, int r) {
        int mid = l + (r - l)/2;
        if (l > r)
            return null;
        else if (l == r)
            return lists.get(l);
        else
            return mergeTwoList(merge(lists, l, mid), merge(lists, mid+1, r));
    }
    public ListNode mergeTwoList(ListNode node1, ListNode node2) {
        ListNode res = new ListNode(-1);
        ListNode cur = res;
        while (node1 != null && node2 != null) {
            if (node1.val <= node2.val) {
                cur.next = node1;
                node1 = node1.next;
            }else {
                cur.next = node2;
                node2 = node2.next;
            }
            cur = cur.next;
    }
        cur.next = node1 != null ? node1 : node2;
        return res.next;
    }
}
  • 总结:用到了归并思想,这个我每次能看懂别人写的,但是自己总也想不到,可能还是这方面的题目做的少。时间复杂度:O(nlogk),空间复杂度:O(logk),k是链表的数量。

8. BM6 判断链表中是否有环

  • 题目链接:BM6 判断链表中是否有环
  • 题目描述:判断给定的链表中是否有环。如果有环则返回true,否则返回false。
  • 输入:{3,2,0,-4},1,返回值:true
  • 第一部分{3,2,0,-4}代表一个链表,第二部分的1表示,-4到位置1(注:头结点为位置0),即-4->2存在一个链接,组成传入的head为一个带环的链表,返回true
import java.util.*;
/**
 * 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) {
        ListNode fast = head;
        ListNode slow = head;
        while (fast != null && fast.next != null) {
            fast = fast.next.next;
            slow = slow.next;
            if (fast == slow)
                return true;
        }
        return false;
    }
}
  • 总结:快慢双指针的应用,只要有环,走两步的快指针始终能追上走一步的慢指针,就是感觉不太高效,但是也没想到更好的,时间复杂度:O(n),空间复杂度:O(1)

9. BM7 链表中环的入口结点 

  • 题目链接:BM7 链表中环的入口结点 
  • 题目描述:给一个长度为n链表,若其中包含环,请找出该链表的环的入口结点,否则,返回null。
  • 输入:{1,2},{3,4,5},返回值:3
  • 说明:返回环形链表入口结点,我们后台程序会打印该环形链表入口结点对应的结点值,即3 
import java.util.*;
/*
 public class ListNode {
    int val;
    ListNode next = null;

    ListNode(int val) {
        this.val = val;
    }
}
*/
public class Solution {

    public ListNode EntryNodeOfLoop(ListNode pHead) {
        ListNode slow = hasFloop(pHead);
        ListNode fast = pHead;
        if (slow == null)
            return null;
        while (fast != slow) {
            fast = fast.next;
            slow = slow.next;
        }
        return slow;
    }
    public ListNode hasFloop(ListNode pHead) {
        ListNode fast = pHead;
        ListNode slow = pHead;
        while (fast != null && fast.next != null) {
            fast = fast.next.next;
            slow = slow.next;
            if (fast == slow)
                return slow;
        }
        return null;
    }
}
  •  总结:那道是否有环问题的升级版,有个数学问题,我开始没反应过来,就是确定有环了以后,快指针从表头出发,慢指针从相遇点出发,那么再次相遇的时候就是环的入口,这个是用走的路程计算一下相减就能看出来,但是光凭想,我真的是想不出来。双指针就满足了题目要求的空间复杂度是O(1),而最容易想到的是哈希表,把遇到的结点放进去,如果发现哈希表已经存在了,那么说明那个点就是环的入口,但是这样的话空间复杂度是O(n)。

10. BM8 链表中倒数最后k个结点 

  • 题目链接:BM8 链表中倒数最后k个结点
  • 题目描述:输入一个长度为 n 的链表,设链表中的元素的值为 ai ,返回该链表中倒数第k个节点。如果该链表长度小于k,请返回一个长度为 0 的链表。
  • 输入:{1,2,3,4,5},2。返回值:{4,5}
  • 说明:返回倒数第2个节点4,系统会打印后面所有的节点来比较。 
import java.util.*;

/*
 * public class ListNode {
 *   int val;
 *   ListNode next = null;
 *   public ListNode(int val) {
 *     this.val = val;
 *   }
 * }
 */

public class Solution {
    /**
     * 代码中的类名、方法名、参数名已经指定,请勿修改,直接返回方法规定的值即可
     *
     * 
     * @param pHead ListNode类 
     * @param k int整型 
     * @return ListNode类
     */
    public ListNode FindKthToTail (ListNode pHead, int k) {
        // write code here
        if (k == 0)
            return null;
        ListNode fast = pHead;
        ListNode slow = pHead;
        for (int i=0; i < k; i++)
            // 一定要注意边界条件,否则容易出现超出边界非法访问的问题
            if (fast != null)
                fast = fast.next;
            else 
                slow = null;
        while (fast != null) {
            fast = fast.next;
            slow = slow.next;
        }
        return slow;
    }
}
  • 总结:双指针问题,先让快的走k步,慢的再开始走,这样快的到末尾,慢的指向的就是我们要寻找的,我做的时候思路没问题,还是边界条件那儿,没注意fast为null的时候,所以第一次提交还出现的空链表报错的问题。 

11. leetcode20. 有效的括号 

  • 题目链接:leetcode20. 有效的括号 
  • 题目描述:给定一个只包括 '(',')','{','}','[',']' 的字符串 s ,判断字符串是否有效。有效字符串需满足:左括号必须用相同类型的右括号闭合。左括号必须以正确的顺序闭合。
  • 输入:s = "()"
    输出:true
  • 输入:s = "()[]{}"
    输出:true
class Solution {
    public boolean isValid(String s) {
        Deque stack = new ArrayDeque();
        for (char c : s.toCharArray()) {
            if (c == '(' || c == '[' || c == '{')
                stack.push(c);
            else if (c == ')' && !stack.isEmpty() && stack.peek() == '(')
                stack.pop();
            else if (c == ']' && !stack.isEmpty() && stack.peek() == '[')
                stack.pop();
            else if (c == '}' && !stack.isEmpty() && stack.peek() == '{')
                stack.pop();
                
            // 如果不满足上述我们对于括号匹配的正确筛选,就返回false
            else
                return false;
        }
        return stack.isEmpty();
    }
}
  • 总结:用到了辅助的数据结构,我没直接用stack,发现好像官方更推荐用Deque,因为stack继承了很多类,比如Vector就不太符合人们对于stack的定义

12. BM9 删除链表的倒数第n个节点

  • 题目链接:BM9 删除链表的倒数第n个节点
  • 题目描述:给定一个链表,删除链表的倒数第 n 个节点并返回链表的头指针
  • 输入:{1,2},2 。返回值:{2} 

import java.util.*;

/*
 * public class ListNode {
 *   int val;
 *   ListNode next = null;
 * }
 */

public class Solution {
    /**
     * 
     * @param head ListNode类 
     * @param n int整型 
     * @return ListNode类
     */
    public ListNode removeNthFromEnd (ListNode head, int n) {
        // write code here
        ListNode res = new ListNode(-1);
        res.next = head;
        ListNode slowPre = res;
        ListNode slow = head;
        ListNode fast = head;
        for (int i = 0; i < n; i++) {
            if (fast != null)
                fast = fast.next;
            else
                return res.next;
        }
        // 找到倒数第n个结点slow    
        while (fast != null) {
            slowPre = slowPre.next;
            fast = fast.next;
            slow = slow.next;
        }
        
        // 删除倒数第n个结点
        slowPre.next = slow.next;
        return res.next;
        
    }
}
  • 总结:这道题就是那道找倒数第n个结点的升级版,只需要再定义一个pre指针指向slow指针的前一个位置就可以了,这里还设置了一个虚拟结点导致占用的内存增加了,但是除了内存多一点,是真的好用,我理解因为要设置pre指针,那么pre开始初始得指向一个结点吧,那就得需要head前面有这么一个结点。

13. BM10 两个链表的第一个公共结点

  • 题目链接 :BM10 两个链表的第一个公共结点
  • 题目描述:输入两个无环的单向链表,找出它们的第一个公共结点,如果没有公共节点则返回空。(注意因为传入数据是链表,所以错误测试数据的提示是用其他方式显示的,保证传入数据是正确的)。要求:空间复杂度 O(1),时间复杂度 O(n)
  • 输入:{1,2,3},{4,5},{6,7}。返回值:{6,7}

  • 说明:第一个参数{1,2,3}代表是第一个链表非公共部分,第二个参数{4,5}代表是第二个链表非公共部分,最后的{6,7}表示的是2个链表的公共部分 这3个参数最后在后台会组装成为2个两个无环的单链表,且是有公共节点的。

import java.util.*;
/*
public class ListNode {
    int val;
    ListNode next = null;

    ListNode(int val) {
        this.val = val;
    }
}*/
public class Solution {
    public ListNode FindFirstCommonNode(ListNode pHead1, ListNode pHead2) {
        ListNode longer = pHead1, shorter = pHead2;
        int len1 = 0, len2 = 0;
        // 分别计算两个链表的长度
        while (longer != null) {
            longer = longer.next;
            len1++;
        }
        while (shorter != null) {
            shorter = shorter.next;
            len2++;
        }
        
        // 找到两个链表的长度差值
        int diff = Math.abs(len1 - len2);
        // 重新更正两个指针的位置,长指针指向更长的那一个链表
        if (len1 < len2) {
            longer = pHead2;
            shorter = pHead1;
        } else {
            longer = pHead1;
            shorter = pHead2;
        }
        
        // 使两个指针处于同一个起点
        while (diff > 0) {
            longer = longer.next;
            diff--;
        }
        
        // 同时向后走,找公共结点
        while (longer != shorter && longer != null) {
            longer = longer.next;
            shorter = shorter.next;
        }
        return longer;
    }
}
  • 总结:先找两个链表各自的长度,然后定义两个指针从两个链表的对应位置出发(可以想象成同时同地出发,若是相遇,就是那个公共结点)。这道题要求了空间复杂度是O(1),所以无法使用哈希表,我看了一圈评论基本是这样实现的。

14.  BM12 单链表的排序

  • 题目链接:BM12 单链表的排序
  • 题目描述:给定一个节点数为n的无序单链表,对其按升序排序。要求:时间复杂度 O(nlogn)
  • 输入:{1,3,2,4,5},返回值:{1,2,3,4,5}。
import java.util.*;

/*
 * public class ListNode {
 *   int val;
 *   ListNode next = null;
 * }
 */

public class Solution {
    
    /**
     * 
     * @param head ListNode类 the head node
     * @return ListNode类
     */
    
    public ListNode mergeSort (ListNode h1, ListNode h2) {
        if (h1 == null)
            return h1;
        if (h2 == null)
            return h2;
        ListNode res = new ListNode(0);
        ListNode cur = res;
        
        while (h1 != null && h2 != null) {
            if (h1.val <= h2.val) {
                cur.next = h1;
                h1 = h1.next;
            } else {
                cur.next = h2;
                h2 = h2.next;
            }
            cur = cur.next;
        }
        if (h1 != null)
            cur.next = h1;
        if (h2 != null)
            cur.next = h2;
//         cur.next = h1 == null ? null : h1;
//         cur.next = h2 == null ? null : h2;
        return res.next;
    }
    
    public ListNode sortInList (ListNode head) {
        if (head == null || head.next == null) 
            return head;
        // write code here
        ListNode mid = head;
        ListNode fast = head;
        ListNode pre = new ListNode(-1);
        pre.next = mid;
        
        
        while (fast != null && fast.next != null) {
            fast = fast.next.next;
            mid = mid.next;
            pre = pre.next;
        }
        // 将链表断开
        pre.next = null;
        
        return mergeSort(sortInList(head), sortInList(mid));
            
    }
}
  • 总结: 那几个边界条件我还是很疑惑的,比如为什么都判断fast.next是否为null了,还需要判断fast是否为null,还有那个我注释掉的h1是否为空那里,有的测试用例就会报错,还是自己刷题少,以后有时间回来要搞懂。

15. leetcode103. 二叉树的锯齿形层序遍历

  • 题目链接:leetcode103. 二叉树的锯齿形层序遍历
  • 题目描述: 给你二叉树的根节点 root ,返回其节点值的 锯齿形层序遍历 。(即先从左往右,再从右往左进行下一层遍历,以此类推,层与层之间交替进行)。
  • 输入:root = [3,9,20,null,null,15,7]
    输出:[[3],[20,9],[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> zigzagLevelOrder(TreeNode root) {
        List > res = new LinkedList<> ();
        if (root == null)
            return res;
        Queue que = new LinkedList();
        que.offer(root);
        boolean flag = false;
        while (!que.isEmpty()) {
            int size = que.size();
            LinkedList row = new LinkedList();
            
            for (int i = 0; i < size; i++) {
                TreeNode temp = que.poll();
                row.add(temp.val);
                if (temp.left != null)
                    que.offer(temp.left);
                if (temp.right != null)
                    que.offer(temp.right);
            }
            if (flag)
                Collections.reverse(row);
            flag = !flag;
            res.add(row);
        }
        return res;
    }
}
  • 总结:用的是最容易想到的层序遍历加偶数层反转做的,期间还因为没注意边界条件导致出错,我看评论很多是用DFS做的,以后回来再刷的时候尝试一下。而且需要注意数据结构list,queue,这些是接口,不能被实例化,实例化需要具体的实现类。

16. BM13 判断一个链表是否为回文结构

  • 题目链接:BM13 判断一个链表是否为回文结构
  • 题目描述:给定一个链表,请判断该链表是否为回文结构。回文是指该字符串正序逆序完全一致。
  • 输入:{1,2,2,1}。返回值:true
import java.util.*;

/*
 * public class ListNode {
 *   int val;
 *   ListNode next = null;
 * }
 */

public class Solution {
    /**
     * 
     * @param head ListNode类 the head
     * @return bool布尔型
     */
    
    public ListNode reverse (ListNode head) {
        ListNode pre = null;
        ListNode cur = head;
        
        while (cur != null) {
            ListNode temp = cur.next;
            cur.next = pre;
            pre = cur;
            cur = temp;
        }
        return pre; 
    }
    
    public boolean isPail (ListNode head) {
        // write code here
        if (head == null)
            return true;
        ListNode fast = head;
        ListNode slow = head;
        // 找链表的中点
        while (fast != null && fast.next != null) {
            fast = fast.next.next;
            slow = slow.next;
        }
        
        // 相当于将链表断开为两部分
        fast = head;
        // 将从中点处反转
        slow = reverse(slow);
        
        while (slow != null) {
            if (fast.val != slow.val)
                return false;
            fast = fast.next;
            slow = slow.next;
        }
        return true;
    }
}
  • 总结:现时用两个指针找到中点,然后反转后半段,挨个比较判断是否相等,感觉很巧妙。

17.  BM14 链表的奇偶重排

  • 题目链接:BM14 链表的奇偶重排
  • 题目描述:给定一个单链表,请设定一个函数,将链表的奇数位节点和偶数位节点分别放在一起,重排后输出。注意是节点的编号而非节点的数值。
  • 要求:空间复杂度 O(n),时间复杂度 O(n)。
  • 输入:{1,2,3,4,5,6}。返回值:{1,3,5,2,4,6}
import java.util.*;

/*
 * public class ListNode {
 *   int val;
 *   ListNode next = null;
 *   public ListNode(int val) {
 *     this.val = val;
 *   }
 * }
 */

public class Solution {
    /**
     * 代码中的类名、方法名、参数名已经指定,请勿修改,直接返回方法规定的值即可
     * 
     * @param head ListNode类 
     * @return ListNode类
     */
    public ListNode oddEvenList (ListNode head) {
        // write code here
        if (head == null || head.next == null)
            return head;
        
        ListNode odd = head;
        ListNode even = head.next;
        // 记录偶数头
        ListNode evenHead = even;
        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;
    }
}
  • 总结:这道题还是思想比较重要,定义奇数和偶数的指针,奇数指针指向偶数的后面,偶数指针指向奇数的后面,最后把偶数头接到奇数的后面。

18. BM15 删除有序链表中重复的元素-I

  • 题目链接:BM15 删除有序链表中重复的元素-I
  • 题目描述:删除给出链表中的重复元素(链表中元素从小到大有序),使链表中的所有元素都只出现一次。
  • 输入:{1,1,2}。返回值:{1,2}
import java.util.*;

/*
 * public class ListNode {
 *   int val;
 *   ListNode next = null;
 * }
 */

public class Solution {
    /**
     * 
     * @param head ListNode类 
     * @return ListNode类
     */
    public ListNode deleteDuplicates (ListNode head) {
        // write code here
        if (head == null || head.next == null)
            return head;
        ListNode cur = head;
        while (cur != null && cur.next != null) {
            if (cur.val == cur.next.val) 
                cur.next = cur.next.next;
            else 
                cur = cur.next;
        }
        return head;
    }
}
  • 总结:这道题比较简单,只要记住如何实现删除后面重复的元素就行。

19. BM16 删除有序链表中重复的元素-II

  • 题目链接:BM16 删除有序链表中重复的元素-II
  • 题目描述:给出一个升序排序的链表,删除链表中的所有重复出现的元素,只保留原链表中只出现一次的元素。
  • 输入:{1,2,2}。输出:{1}
import java.util.*;

/*
 * public class ListNode {
 *   int val;
 *   ListNode next = null;
 * }
 */

public class Solution {
    /**
     * 
     * @param head ListNode类 
     * @return ListNode类
     */
    public ListNode deleteDuplicates (ListNode head) {
        // write code here
        
        if (head == null || head.next == null)
            return head;
        ListNode res = new ListNode(-1);
        res.next  = head;
        ListNode pre = res;
        ListNode cur = head;
        while (cur != null && cur.next != null) {
            if (cur.val == cur.next.val){
                int temp = cur.val;
                while (cur != null && cur.val == temp)
                    cur = cur.next;
                pre.next = cur;
            }
            else {
                pre = cur;
                cur = cur.next;
            }
                
        }
        return res.next;
    }
}
  • 总结:感觉总体思想不难,是上面那道删除重复元素的升级版,但是在边界条件判断那里还是得注意一下,先if再while的结合看起来有点奇怪,但是逻辑还是没问题的。

20.  leetcode695. 岛屿的最大面积

  • 题目链接:leetcode695. 岛屿的最大面积
  • 题目描述:给你一个大小为 m x n 的二进制矩阵 grid 。岛屿是由一些相邻的 1 (代表土地) 构成的组合,这里的「相邻」要求两个 1 必须在 水平或者竖直的四个方向上相邻。你可以假设 grid 的四个边缘都被 0(代表水)包围着。岛屿的面积是岛上值为 1 的单元格的数目。计算并返回 grid 中最大的岛屿面积。如果没有岛屿,则返回面积为 0 。
  • 力扣刷题历程_第1张图片

  • 输出:6
class Solution {
    public int maxAreaOfIsland(int[][] grid) {
        int res = 0;
        for (int i = 0; i < grid.length; i++) {
            for (int j = 0; j < grid[0].length; j++){
                if (grid[i][j] == 1) {
                    int a = landArea(grid, i, j);
                    res = Math.max(res, a);
                }
            }
        }
        return res;
    }
    public int landArea (int[][] grid, int r, int c) {
        if (!isLand(grid, r, c))
            return 0;
        if (grid[r][c] != 1)
            return 0;
        grid[r][c] = 2;

        return 1 
            + landArea(grid, r - 1, c) 
            + landArea(grid, r + 1, c) 
            + landArea(grid, r, c - 1) 
            + landArea(grid, r, c + 1);
    }

    public boolean isLand (int[][] grid, int r, int c) {
        return 0 <= r && r < grid.length && 0 <= c && c < grid[0].length; 
    }

}
  • 总结:还是要记住这种岛屿的DFS思想吧 

21. leetcode827. 最大人工岛

leetcode827. 最大人工岛https://leetcode.cn/problems/making-a-large-island/

  • 题目描述:给你一个大小为 n x n 二进制矩阵 grid 。最多 只能将一格 0 变成 1 。返回执行此操作后,grid 中最大的岛屿面积是多少?岛屿 由一组上、下、左、右四个方向相连的 1 形成。
  • 输入:grid = [[1, 0], [0, 1]]。输出:3。
class Solution {
    public int largestIsland(int[][] grid) {
        if (grid==null || grid.length == 0){
            return 1;
        }

        int res = 0;
        int index = 2;
        HashMap indexAndAreas = new HashMap<>();//岛屿编号:岛屿面积

        /**
         * 计算每个岛屿的面积,并标记是第几个岛屿
         */
        for (int r=0;r hashSet = findNeighbour(grid,r,c);//把上下左右邻居放入set去重
                    if (hashSet.size() < 1)continue;//如果海洋格子周围没有格子不必计算
                    int twoIsland = 1;//填充这个格子,初始为1,这个变量记录合并岛屿后的面积
                    for (Integer i: hashSet){
                        twoIsland += indexAndAreas.get(i);//该格子填充,则上下左右的陆地的都连接了,通过序号获得面积,加上面积
                    }
                    res = Math.max(res,twoIsland);//比较得到最大的面积
                }
            }
        }
        return res;
    }


    /**
     * 对于海洋格子,找到上下左右
     * 每个方向,都要确保有效inArea以及是陆地格子,则表示是该海洋格子的陆地邻居
     * @param grid
     * @param r
     * @param c
     * @return
     */
    private HashSet findNeighbour(int[][] grid,int r,int c){
        HashSet hashSet = new HashSet<>();
        if (inArea(grid,r-1,c)&&grid[r-1][c] != 0){
            hashSet.add(grid[r-1][c]);
        }
        if (inArea(grid,r+1,c) && grid[r+1][c] != 0){
            hashSet.add(grid[r+1][c]);
        }
        if (inArea(grid,r,c-1) && grid[r][c-1] != 0){
            hashSet.add(grid[r][c-1]);
        }
        if (inArea(grid,r,c+1) && grid[r][c+1] != 0){
            hashSet.add(grid[r][c+1]);
        }
        return hashSet;
    }

    /**
     * dfs方法,将格子填充为index,即表示这个格子属于哪个岛的
     * 计算岛屿面积,上下左右,当然这个可以优化的,因为不需要计算上面的,会有重复
     * @param grid
     * @param r
     * @param c
     * @param index
     * @return
     */
    private int area(int[][] grid, int r, int c,int index){
        if (!inArea(grid,r,c)){
            return 0;
        }
        //不为1,表示为海洋格子或者已经遍历过了
        if (grid[r][c] != 1){
            return 0;
        }
        grid[r][c] = index;//设置当前格子为某个岛屿编号
        return 1 + area(grid,r-1,c,index) + area(grid,r+1,c,index) + area(grid,r,c-1,index) + area(grid,r,c+1,index);
    }

    /**
     * 判断grid[r][c]是否大小合适
     * @param grid
     * @param r
     * @param c
     * @return
     */
    private boolean inArea(int[][] grid,int r,int c){
        return r>=0 && r=0 && c
  • 总结:第一次尝试hard难度,果然很受打击,其实我都是上面那道最大岛屿的升级版,但是依旧没有思路,先按着评论里的题解走了一遍,以后回过头再刷吧。 

22. BM17 二分查找-I

BM17 二分查找-Ihttps://www.nowcoder.com/practice/d3df40bd23594118b57554129cadf47b

  • 题目描述:请实现无重复数字的升序数组的二分查找,给定一个 元素升序的、无重复数字的整型数组 nums 和一个目标值 target ,写一个函数搜索 nums 中的 target,如果目标值存在返回下标(下标从 0 开始),否则返回 -1
  • 时间复杂度 O(logn) ,空间复杂度 O(1)
import java.util.*;


public class Solution {
    /**
     * 代码中的类名、方法名、参数名已经指定,请勿修改,直接返回方法规定的值即可
     *
     * 
     * @param nums int整型一维数组 
     * @param target int整型 
     * @return int整型
     */
    public int search (int[] nums, int target) {
        // write code here
        if (nums.length < 1 )
            return -1;
        int left = 0;
        int right = nums.length - 1;
        while (left <= right) {
            int mid = left + (right - left) / 2;
            if (nums[mid] == target)
                return mid;
            else if (nums[mid] < target)
                left = mid + 1;
            else
                right = mid - 1;
        }
        return -1;
        
        
    }
}
  • 总结:注意left<=right的边界条件就行

23.  BM18 二维数组中的查找

BM18 二维数组中的查找https://www.nowcoder.com/practice/abc3fe2ce8e146608e868a70efebf62e

  • 题目描述:在一个二维数组array中(每个一维数组的长度相同),每一行都按照从左到右递增的顺序排序,每一列都按照从上到下递增的顺序排序。请完成一个函数,输入这样的一个二维数组和一个整数,判断数组中是否含有该整数。
  • 输入:7,[[1,2,8,9],[2,4,9,12],[4,7,10,13],[6,8,11,15]]。返回值:true
  • 说明:存在7,返回true
  • 空间复杂度 O(1) ,时间复杂度 O(n+m)
public class Solution {
    public boolean Find(int target, int [][] array) {
        if (array.length == 0 || array[0].length == 0)
            return false;
        for (int i = array.length - 1, j = 0; i >= 0 && j < array[0].length;) {
            if (array[i][j] == target)
                return true;
            // 元素较大往右走
            else if (array[i][j] < target)
                j++;
            // 元素较小往上走
            else
                i--;
        }
        return false;
    }
}
  • 总结:与一维数组相比,需要记住的是,二维数组的起点应该从左下角开始,目标值小往上走,目标值大一点往右走。

24. BM19 寻找峰值

BM19 寻找峰值https://www.nowcoder.com/practice/fcf87540c4f347bcb4cf720b5b350c76

  • 题目描述:给定一个长度为n的数组nums,请你找到峰值并返回其索引。数组可能包含多个峰值,在这种情况下,返回任何一个所在位置即可。
  • 输入:[2,4,1,2,7,8,4]。返回值:1.
  • 说明:4和8都是峰值元素,返回4的索引1或者8的索引5都可以
import java.util.*;


public class Solution {
    /**
     * 代码中的类名、方法名、参数名已经指定,请勿修改,直接返回方法规定的值即可
     *
     * 
     * @param nums int整型一维数组 
     * @return int整型
     */
    public int findPeakElement (int[] nums) {
        // write code here
        int left = 0;
        int right = nums.length - 1;
        
        while (left < right) {
            int mid  = left + (right - left) / 2;
            if (nums[mid] > nums[mid + 1])
                right = mid;
            else
                left = mid + 1;
        }
        
        return left;
    }
}
  • 总结:需要注意的是这道题是寻找其中一个波峰,所以要找那个相对大的,注意边界条件,以及right和left的赋值,是因为要找那个相对大的。

25.  BM20 数组中的逆序对

BM20 数组中的逆序对https://www.nowcoder.com/practice/96bd6684e04a44eb80e6a68efc0ec6c5

  • 题目描述:在数组中的两个数字,如果前面一个数字大于后面的数字,则这两个数字组成一个逆序对。输入一个数组,求出这个数组中的逆序对的总数P。并将P对1000000007取模的结果输出。
  • 要求:空间复杂度 O(n),时间复杂度 O(nlogn)。
  • 输入:[1,2,3,4,5,6,7,0]。输出:7
/*归并排序的改进,把数据分成前后两个数组(递归分到每个数组仅有一个数据项),
合并数组,合并时,出现前面的数组值array[i]大于后面数组值array[j]时;则前面
数组array[i]~array[mid]都是大于array[j]的,count += mid+1 - i
参考剑指Offer,但是感觉剑指Offer归并过程少了一步拷贝过程。
还有就是测试用例输出结果比较大,对每次返回的count mod(1000000007)求余
*/

public class Solution {
    public int InversePairs(int [] array) {
        int size = array.length;
        int[] copy = new int[size];
        return InversePairsCore(array, copy, 0, size - 1);
    }
    private int InversePairsCore(int[] array,int[] copy,int low,int high) {
       if (low >= high)
           return 0;
        int mid = low + (high - low) / 2;
        int i = low, j = mid + 1;
        int leftCount = InversePairsCore(array, copy, 0, mid) % 1000000007;
        int rightCount = InversePairsCore(array, copy, mid + 1, high) % 1000000007;
        int count = leftCount + rightCount;
        for (int m = low; m <= high; m++)
            copy[m] = array[m];
        for (int m = low; m <= high; m++) {
            // 如果左面的全找完了
            if (i == mid + 1)
                array[m] = copy[j++];
            // 如果右面的全找完了或者是左面小于右面的
            else if (j == high + 1 || copy[i] <= copy[j])
                array[m] = copy[i++];
            // 只剩下左面的大于右面的这种情况,这就属于逆序对
            else {
                // 统计逆序对,就是看左面还剩几个。这里是计算跨左右的逆序对
                count = count + mid - i + 1; 
                // 相当于是归并排序顺便计算逆序对,就是放较小的进去就行
                array[m] = copy[j++];
            }
        }
         return count % 1000000007;   
        
    }
}
  • 总结:这道题花了好长时间才搞懂,最后按照官方题解的思路写下来的。这里开始对时间复杂度有要求嘛,所以会考虑到归并排序,当然这有点牵强。主要是这里的思路,归并排序的时候计算逆序对是十分方便的,以及要弄清楚使用归并排序的时候,逆序对其实是分为三种类型的,一种是左面的内部逆序对数量,一种是右边的内部逆序对数量,一种是横跨左右的逆序对数量。哎,这道题还是得多看看。 

26. BM21 旋转数组的最小数字

BM21 旋转数组的最小数字https://www.nowcoder.com/practice/9f3231a991af4f55b95579b44b7a01ba

  • 题目描述:有一个长度为 n 的非降序数组,比如[1,2,3,4,5],将它进行旋转,即把一个数组最开始的若干个元素搬到数组的末尾,变成一个旋转数组,比如变成了[3,4,5,1,2],或者[4,5,1,2,3]这样的。请问,给定这样一个旋转数组,求数组中的最小值。
  • 要求:空间复杂度:O(1) ,时间复杂度:O(logn)。
  • 输入:[3,4,5,1,2]。返回值:1
import java.util.*;
import java.util.ArrayList;
public class Solution {
    public int minNumberInRotateArray(int [] array) {
        int left = 0;
        int right = array.length - 1;
        while (left < right) {
            
            int mid = left + (right - left) / 2;
            // 无法判断的时候,挨个试
            if (array[mid] == array[right])
                right--;
            else if (array[mid] > array[right])
                left = mid + 1;
            else 
                right = mid;
        }
        return array[left];
    }
}
  • 总结:这道题主要是要想清楚数组发生了旋转以后的分布是什么样子的,感觉自己一开始想还是挺难的,做过一次后就能记住了。

27. BM22 比较版本号

BM22 比较版本号https://www.nowcoder.com/practice/2b317e02f14247a49ffdbdba315459e7

  • 题目描述:牛客项目发布项目版本时会有版本号,比如1.02.11,2.14.4等等。现在给你2个版本号version1和version2,请你比较他们的大小版本号是由修订号组成,修订号与修订号之间由一个"."连接。1个修订号可能有多位数字组成,修订号可能包含前导0,且是合法的。例如,1.02.11,0.1,0.2都是合法的版本号每个版本号至少包含1个修订号。修订号从左到右编号,下标从0开始,最左边的修订号下标为0,下一个修订号下标为1,以此类推。
  • 输入:"1.1","1.01"。输出:0。version2忽略前导0,为"1.1",和version相同,返回0 
  • 输入:"1.1","1.1.1"。输出:-1。"1.1"的版本号小于"1.1.1"。因为"1.1"的版本号相当于"1.1.0",第3位修订号的下标为0,小于1,所以version1 < version2,返回-1 。
import java.util.*;


public class Solution {
    /**
     * 代码中的类名、方法名、参数名已经指定,请勿修改,直接返回方法规定的值即可
     *
     * 比较版本号
     * @param version1 string字符串 
     * @param version2 string字符串 
     * @return int整型
     */
    public int compare (String version1, String version2) {
        // write code here
        int l1 = version1.length();
        int l2 = version2.length();
        int i = 0, j = 0;
        while (i < l1 || j < l2) {
            long num1 = 0, num2 = 0;
            // 将.前面的加起来进行比较
            while (i < l1 && version1.charAt(i) != '.') 
                // 注意charAt取出来的是char,所以减去的应该是‘0’
                num1 = num1 * 10 + (version1.charAt(i++) - '0');
            // 为了跳过‘.’,方便下一轮比较
            i++;
            // 这里强调j < l2是怕j++的过程中越界
            while (j < l2 && version2.charAt(j) != '.')
                num2 = num2 * 10 + (version2.charAt(j++) - '0');
            // 为了跳过‘.’,方便下一轮比较
            j++;
            
            if (num1 > num2)
                return 1;
            else if (num1 < num2)
                return -1;
            
        }
        return 0;
    }
}
  • 总结: 每个'.'之前进行比较一次,注意边界条件和'0'就行。

28. leetcode15. 三数之和 

leetcode15. 三数之和 https://leetcode.cn/problems/3sum/

  • 题目描述:给你一个包含 n 个整数的数组 nums,判断 nums 中是否存在三个元素 a,b,c ,使得 a + b + c = 0 ?请你找出所有和为 0 且不重复的三元组。
  • 输入:nums = [-1,0,1,2,-1,-4]。输出:[[-1,-1,2],[-1,0,1]]
  • 输入: nums = []。输出:[]。
class Solution {
    public List> threeSum(int[] nums) {
        List> res = new ArrayList();
        if (nums == null || nums.length < 3)
            return res;
        // 排序后将三数之和问题转换为两数之和
        Arrays.sort(nums);
        // 开始遍历
        for (int i = 0; i < nums.length; i++) {
            // 因为是排序后的序列,如果最小的都大于0,说明不可能满足三数之和等于0了
            if (nums[i] > 0)
                break;
            // 对i去重
            if (i >= 1 && nums[i] == nums[i - 1])
                continue;
            int left = i + 1;
            int right = nums.length - 1;
            while (left < right) {
                int sum = nums[i] + nums[left] + nums[right];
                // 三数之和为0
                if (sum == 0) {
                    // 对left去重
                    while (left < right && nums[left] == nums[left + 1])
                        left++;
                    // 对right去重
                    while (left < right && nums[right] == nums[right - 1])
                        right--;
                    res.add(Arrays.asList(nums[i], nums[left], nums[right]));
                    left++;
                    right--;
                }
                else if (sum > 0)
                    right--;
                else 
                    left++;
            }
            
        }
        return res;
    }
}
  • 总结:这道题是两数之和的升级版,三数之和这次不是给定的target,而是0,还有一个特别重要的是不允许重复的三数,那么需要去重。去重的时候边界条件要注意,比如left和right加减的时候,还要保证left和right的相对位置,我忘记添加left

29. leetcode1353. 最多可以参加的会议数目

leetcode1353. 最多可以参加的会议数目https://leetcode.cn/problems/maximum-number-of-events-that-can-be-attended/

  • 题目描述:给你一个数组 events,其中 events[i] = [startDayi, endDayi] ,表示会议 i 开始于 startDayi ,结束于 endDayi 。你可以在满足 startDayi <= d <= endDayi 中的任意一天 d 参加会议 i 。注意,一天只能参加一个会议。请你返回你可以参加的 最大 会议数目。
  • 输入:events = [[1,2],[2,3],[3,4]]。输出:3
  • 解释:第 1 天参加第一个会议。第 2 天参加第二个会议。第 3 天参加第三个会议。
class Solution {
    public int maxEvents(int[][] events) {
        // 我这里使用了lambda表达式,这里a[0] - b[0]就相当于是升序了
        Arrays.sort(events, (a,b)->a[0]-b[0]);
        PriorityQueue q = new PriorityQueue ((a,b)->(a[1]-b[1]));
        int size = events.length;
        int i = 0;
        int res = 0;
        int curDay = 1;
        while (i < size || !q.isEmpty()) {
            // 将开始时间相同的放入堆中
            while (i < size && events[i][0] == curDay ) {
                int[] event = events[i];
                q.add(event);
                i++;
            }

            // 队列中没有元素的话,说明开始时间不合适,将开始时间向后移动一天
            if (q.isEmpty()) {
                curDay++;
                continue;
            }

            // 如果结束时间比开始时间都晚,说明已经错过了
            while (!q.isEmpty() && q.peek()[1] < curDay) 
                q.poll();
            

            // 这里剩下的终于是我们能参加的会议了,贪心得到满足了
            if (!q.isEmpty()) {
                q.poll();
                res++;
            }
            curDay++;
        }
        return res;
    }
}
  • 总结:这道题涉及到的知识点还是比较多的,容易想到的是多次遍历+set,但是这样会超时。优先队列我也是第一次用,通过堆实现,一般每次取出堆顶也就是最小的,当然堆顶也可以是最大的。还涉及到了lambda表达式,这里需要明白怎么写是升序,怎么写是降序。我是按照评论里的题解来解决的,先按开始时间排序,开始时间相同的话,按照结束时间排序,通过开始时间和结束时间的对比来判断是否能去参加会议。

30. 二叉树的前、中、后序遍历

  • 前序遍历
/**
 * 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();
        preorder(list, root);
        return list;
    }
    public void preorder(List list, TreeNode root) {
        if (root == null)
            return;
        list.add(root.val);
        preorder(list, root.left);
        preorder(list, root.right);

    }
    
}
  • 中序遍历
/**
 * 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();
        inorder(list, root);
        return list;
    }
    public void inorder(List list, TreeNode root) {
        if (root == null)
            return ;
        inorder(list, root.left);
        list.add(root.val);
        inorder(list, root.right);
    }
}
  • 后序遍历
/**
 * 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();
        postorder(list, root);
        return list;
    }

    public void postorder(List list, TreeNode root) {
        if (root == null)
            return;
        postorder(list, root.left);
        postorder(list, root.right);
        list.add(root.val);
    }
}

31. 二叉树的层序遍历

/**
 * 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> levelOrder(TreeNode root) {
        List> res = new ArrayList();
        if (root == null)
            return res;
        Queue q = new ArrayDeque();
        q.add(root);
        while (!q.isEmpty()) {
            List row  = new ArrayList();
            int len = q.size();
            for (int i = 0; i < len; i++) {
                TreeNode temp = q.poll();
                row.add(temp.val);
                if (temp.left != null)
                    q.add(temp.left);
                if (temp.right != null)
                    q.add(temp.right);
            }
            res.add(row);
        }
        
        return res;
    }
}

32. 按“之”字形顺序打印二叉树

  • 牛客网链接:按之字形顺序打印二叉树
import java.util.*;

/*
public class TreeNode {
    int val = 0;
    TreeNode left = null;
    TreeNode right = null;

    public TreeNode(int val) {
        this.val = val;

    }

}
*/
public class Solution {
    public ArrayList > Print(TreeNode pRoot) {
        ArrayList > res = new ArrayList >();
        
        if (pRoot == null) {
            return res;
        }
        
        // BFS需要先存储入口的根结点
        Queue temp = new LinkedList();
        temp.offer(pRoot);
        
        // 需要标识符来看是否翻转
        boolean flag = false;
        while (!temp.isEmpty()) {
            // 需要将每一行的元素记录下来
            ArrayList row = new ArrayList();
            int size = temp.size();
            for (int i = 0; i < size; i++) {
                TreeNode t = temp.poll();
                row.add(t.val);
                if (t.left != null) 
                    temp.offer(t.left);
                    
                if (t.right != null) 
                    temp.offer(t.right);
    
            }
            if (flag)
                Collections.reverse(row);
            flag = !flag;
            res.add(row);
        }
        return res;
    }
    

}
  • 总结:在层序遍历的基础上添加了一个标志位flag,通过判断flag来决定是否翻转 

33.  BM28 二叉树的最大深度

  • 链接:BM28 二叉树的最大深度
/**
 * 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;
        int leftDepth = maxDepth(root.left);
        int rightDepth  = maxDepth(root.right);
        return Math.max(leftDepth, rightDepth) + 1;
        
    }
}
  •  这道题很简单,但是不清楚递归的话,也很难办,递归类似于数学的归纳法,得有个初始值和递推公式,挨个写上去就行。

34.  剑指offer 34. 二叉树中和为某一值的路径(一)

BM29 二叉树中和为某一值的路径(一)https://leetcode.cn/problems/er-cha-shu-zhong-he-wei-mou-yi-zhi-de-lu-jing-lcof/

/**
 * 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 {
    LinkedList> res = new LinkedList<>();
    LinkedList path = new LinkedList<>(); 
    public List> pathSum(TreeNode root, int sum) {
        recur(root, sum);
        return res;
    }
    void recur(TreeNode root, int tar) {
        if(root == null) return;
        path.add(root.val);
        tar -= root.val;
        if(tar == 0 && root.left == null && root.right == null)
            res.add(new LinkedList(path));
        
        recur(root.left, tar);
        recur(root.right, tar);
        path.removeLast();
        
    }
}

35. leetcode134. 加油站

leetcode134. 加油站https://leetcode.cn/problems/gas-station/

class Solution {
    public int canCompleteCircuit(int[] gas, int[] cost) {
        int gasSum = 0, costSum = 0;
        for (int i = 0; i < gas.length; i++) {
            gasSum += gas[i];
            costSum += cost[i];
        }
        // 先整体判断整条环路是否满足总油耗小于总加油量
        if (gasSum < costSum)
            return -1;
        
        int curGas = 0;
        int start = 0;
        for (int i = 0; i < gas.length; i++) {
            curGas = curGas + gas[i] - cost[i];
            // 如果小于0,说明起始点不满足条件,就得从起始点的下一个结点开始
            if (curGas < 0) {
                curGas = 0;
                start = i + 1;
            }
                
        }
        return start;

    }
}

36. BM55 没有重复项数字的全排列 

  • 题目链接:BM55 没有重复项数字的全排列
  • 题目描述:给出一组数字,返回该组数字的所有排列
  • 输入:[1,2,3]。输出:[[1,2,3],[1,3,2],[2,1,3],[2,3,1],[3,1,2],[3,2,1]]。
class Solution {
    public List> permute(int[] nums) {
        List> res = new ArrayList> ();
        int len = nums.length;
        int depth = 0;
        Deque path = new ArrayDeque();
        boolean[] used = new boolean[len];
        if (len < 1)
            return res;
        dfs(nums, len, depth, path, used, res);
        return res;
    }

    public void dfs (int[] nums, int len, int depth, Deque path, boolean[] used, List> res) {
        if (len == depth) {
            res.add(new ArrayList<>(path));
            return;
        }
            
        
        for (int i = 0; i < len; i++) {
            if (used[i])
                continue;
            else {
                path.push(nums[i]);
                used[i] = true;
                dfs(nums, len, depth + 1, path, used, res);
                // 回溯
                used[i] = false;
                path.pop();
            }
        }

    } 
}
  • 总结:递归回溯的思想,主要是按照模板来的,使用了depth,终止条件是:depth == len。 

 37. BM56 有重复项数字的全排列

  • 题目链接:BM56 有重复项数字的全排列
  • 题目描述:给出一组可能包含重复项的数字,返回该组数字的所有排列。结果以字典序升序排列。
  • 输入:[1,1,2]。输出:[[1,1,2],[1,2,1],[2,1,1]]。
import java.util.*;

public class Solution {
    public ArrayList> permuteUnique(int[] num) {
        Arrays.sort(num);
        int len = num.length;
        boolean[] used = new boolean[len];
        int depth = 0;
        ArrayList> res = new ArrayList>();
        ArrayList path = new ArrayList();
        if (len < 1)
            return res;
        dfs(num, len, used, depth, path, res);
        return res;
    }
    public void dfs (int[] num, int len, boolean[] used, int depth, ArrayList path, ArrayList> res) {
        if (depth == len) {
            res.add(new ArrayList(path));
            return;
        }
        for (int i = 0; i < len; i++) {
            if (used[i])
                continue;
            if (i > 0 && num[i] == num[i - 1] && used[i - 1] == true)
                    continue;
            else {
                path.add(num[i]);
                used[i] = true;
                dfs(num, len, used, depth + 1, path, res);
                path.remove(path.size() - 1);
                used[i] = false;
            }
        }
    }
}
  • 总结: 

38. leetcode739. 每日温度

  • 题目链接:leetcode739. 每日温度
  • 题目描述:给定一个整数数组 temperatures ,表示每天的温度,返回一个数组 answer ,其中 answer[i] 是指对于第 i 天,下一个更高温度出现在几天后。如果气温在这之后都不会升高,请在该位置用 0 来代替。
  • 输入:temperatures = [73,74,75,71,69,72,76,73]。输出:[1,1,4,2,1,1,0,0]
class Solution {
    public int[] dailyTemperatures(int[] temperatures) {
        /** 
        int[] res = new int[temperatures.length];

        //双层循环遍历-暴力:时间复杂度O(n^2),空间复杂度O(n)
        for (int i = 0; i < temperatures.length - 1; i++) {
            for (int j = i + 1; j < temperatures.length; j++) {
                if (temperatures[j] > temperatures[i]) {
                    res[i] = j - i;
                    break;
                }
                    
            }
        }
        return res;
        */

        /**
        // 时间复杂度O(n),空间复杂度O(n)
        int len = temperatures.length;
        int[] res = new int[len];
        // res[len - 1] = 0;
        // 从后往前遍历
        // 因为对于最后一天,肯定没有比它温度再高的,所以直接从倒数第二天开始
        for (int i = len - 2; i >= 0; i--) {
            for (int j = i + 1; j < len; j += res[j]) {
                if (temperatures[i] < temperatures[j]) {
                    res[i] = j - i;
                    break;
                } else if (res[j] == 0) {
                    res[i] = 0;
                    break;
                }
            }
        }
        return res;
    }
     */

    // 栈,时间复杂度O(n),空间复杂度O(n)
    // 如果比栈顶元素大,就出栈并计算下标差值,否则入栈。
    int len = temperatures.length;
    int[] res = new int[len];

    Deque stack = new LinkedList();

    for (int i = 0; i < len; i++) {
        while (!stack.isEmpty() && temperatures[i] > temperatures[stack.peek()]) {
            int preIndex = stack.pop();
            res[preIndex] = i - preIndex;
        }
        stack.push(i);
    }

    return res;
    }
}
  • 总结:最容易想到的两层遍历,开始试了一下力扣竟然没显示超时可以通过,虽然运行时间不忍直视,但是不看题解就自己做出题的感觉真爽哈哈哈。第二种方法我是看评论区题解写的,好像和我曾经学过的KMP算法很像,不过没再看过,不清楚是否是记错了。第三种是官方题解,就是创建一个栈,还是比较容易懂得,这种比较典型的栈问题希望我下次能马上想出来。 

39. 二叉树的直径

  • 题目链接:二叉树的直径
  • 题目描述:给定一棵二叉树,你需要计算它的直径长度。一棵二叉树的直径长度是任意两个结点路径长度中的最大值。这条路径可能穿过也可能不穿过根结点。
/**
 * 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 res;
    public int diameterOfBinaryTree(TreeNode root) {
        res = 0;
        if (root == null)
            return res;
        dfs(root);
        return res;
    }

    private int dfs (TreeNode root) {
        // 最底层递归出口
        if (root == null)
            return 0;
        
        // 中间层递归形式
        int L = dfs(root.left);
        int R = dfs(root.right);

        //返回所需的结果,求的是直径即边长,所以是L+R,而不是L+R-1
         res = Math.max(res, L + R);

        // 最上层的公式求深度
        return Math.max(L, R) + 1;
    }
}
  • 总结:我感觉这道题不该是道简单题啊哈哈哈(咸鱼的辩护!),还是属于深度遍历系列的吧,需要注意这道题的两大坑,第一不是必须要经过根结点,所以需要动态取最大值,第二直径是边长,通过观察可以得知是L+R,而官方题解选择先L+R+1,再res-1更具有逻辑,反正能解题出来自圆其说就可以了吧。 

40.  

你可能感兴趣的:(leetcode)