前言
最近开始总结算法题,加油,不定期更新总结。
力扣124题:关于二叉树: 二叉树中的最大路径和
路径 被定义为一条从树中任意节点出发,沿父节点-子节点连接,达到任意节点的序列。同一个节点在一条路径序列中 至多出现一次 。该路径 至少包含一个 节点,且不一定经过根节点。
路径和 是路径中各节点值的总和。
给你一个二叉树的根节点 root ,返回其 最大路径和 。
示例 1:
输入:root = [1,2,3]
输出:6
解释:最优路径是 2 -> 1 -> 3 ,路径和为 2 + 1 + 3 = 6
示例 2:
输入:root = [-10,9,20,null,null,15,7]
输出:42
解释:最优路径是 15 -> 20 -> 7 ,路径和为 15 + 20 + 7 = 42
答:尽可能使用一个简单易懂的语言描述!
首先关注几个点:不管什么数,我们在脑子里面有一个概念,就把它想象成为一个二叉树,只有一个根节点和一个左孩子和一个右孩子。
图很简陋,这是一个简单的二叉树,如果求他的最短路径,有几个,第一:A点谁也不连,就自己单独成为一个点(就是左右孩子是负数),第二:AB,第三AC,第四:BAC。
注意第四点:为什么是BAC,因为AB无法到-c,并且同一个节点只能路过一次。
那我们可以想象,任意一个二叉树计算的时候都有这四种情况。那我们求取最大路径的时候就会发现。
我们要一直进行比较然后得到最大值,每一种情况都有可能发生。
看代码,分析:
class Solution {
int res = Integer.MIN_VALUE;
public int maxPathSum(TreeNode root) {
if(root==null){
return 0;
}
dfs(root);
return res;
}
public int dfs(TreeNode root){
if(root==null){
return 0;
}
int left=Math.max(0,dfs(root.left));
int right=Math.max(0,dfs(root.right));
res=Math.max(res,root.val+left+right);
return Math.max(left,right)+root.val;
}
}
首先:我们发现定义了:res,Integer.MIN_VALUE这是什么意思呢?就是理解成为int可以取到的最小值,也是无穷小,它的具体数值为(-2147483648)。
然后我们进入maxPathSum这个方法获取最大路径和。
这里调用了一个递归函数dfs。
left和right的求取充分展示了什么我们在文章开篇所说的,以大化小原则,把一个大二叉树想象成一个小的二叉树。然后把这个二叉树当做一个整体。递归求取left或者right的最大路径。最后得到最大值赋给res。
27 移除元素
给你一个数组 nums 和一个值 val,你需要 原地 移除所有数值等于 val 的元素,并返回移除后数组的新长度。
不要使用额外的数组空间,你必须仅使用 O(1) 额外空间并 原地 修改输入数组。
元素的顺序可以改变。你不需要考虑数组中超出新长度后面的元素。
说明:
为什么返回数值是整数,但输出的答案是数组呢?
请注意,输入数组是以「引用」方式传递的,这意味着在函数里修改输入数组对于调用者是可见的。
你可以想象内部操作如下:
// nums 是以“引用”方式传递的。也就是说,不对实参作任何拷贝
int len = removeElement(nums, val);
// 在函数里修改输入数组对于调用者是可见的。
// 根据你的函数返回的长度, 它会打印出数组中 该长度范围内 的所有元素。
for (int i = 0; i < len; i++) {
print(nums[i]);
}
示例 1:
输入:nums = [3,2,2,3], val = 3
输出:2, nums = [2,2]
解释:函数应该返回新的长度 2, 并且 nums 中的前两个元素均为 2。你不需要考虑数组中超出新长度后面的元素。例如,函数返回的新长度为 2 ,而 nums = [2,2,3,3] 或 nums = [2,2,0,0],也会被视作正确答案。
示例 2:
输入:nums = [0,1,2,2,3,0,4,2], val = 2
输出:5, nums = [0,1,4,0,3]
解释:函数应该返回新的长度 5, 并且 nums 中的前五个元素为 0, 1, 3, 0, 4。注意这五个元素可为任意顺序。你不需要考虑数组中超出新长度后面的元素。
提示:
0 <= nums.length <= 100
0 <= nums[i] <= 50
0 <= val <= 100
class Solution {
public int removeElement(int[] nums, int val) {
int j=0;
if(nums.length==0){
return 0;
}
for(int i=0;i<nums.length;i++){
if(nums[i]!=val){
nums[j]=nums[i];
j++;
}
}
return j;
}
}
206 反转链表
反转一个链表
示例:
输入: 1->2->3->4->5->NULL
输出: 5->4->3->2->1->NULL
class Solution {
public ListNode reverseList(ListNode head) {
if(head ==null || head.next==null ){
return head;
}
ListNode prev=null;
ListNode next=null;
while(head!=null){
next = head.next;
head.next = prev;
prev = head;
head = next;
}
return prev;
}
}
155 最小栈问题(简单)
设计一个支持 push ,pop ,top 操作,并能在常数时间内检索到最小元素的栈。
- push(x) —— 将元素 x 推入栈中。
- pop() —— 删除栈顶的元素。
- top() —— 获取栈顶元素。
- getMin() —— 检索栈中的最小元素。
示例:
输入:
["MinStack","push","push","push","getMin","pop","top","getMin"]
[[],[-2],[0],[-3],[],[],[],[]]
输出:
[null,null,null,null,-3,null,0,-2]
解释:
MinStack minStack = new MinStack();
minStack.push(-2);
minStack.push(0);
minStack.push(-3);
minStack.getMin(); --> 返回 -3.
minStack.pop();
minStack.top(); --> 返回 0.
minStack.getMin(); --> 返回 -2.
提示:
pop、top 和 getMin 操作总是在 非空栈 上调用。
class MinStack {
private Stack<Integer> stack;
private Stack<Integer> min_stack;
public MinStack() {
stack = new Stack<>();
min_stack = new Stack<>();
}
public void push(int x) {
stack.push(x);
if(min_stack.isEmpty() || x <= min_stack.peek())
min_stack.push(x);
}
public void pop() {
if(stack.pop().equals(min_stack.peek()))
min_stack.pop();
}
public int top() {
return stack.peek();
}
public int getMin() {
return min_stack.peek();
}
}
- 解释:相当于数据栈和辅助栈的原理,首先,观察题目,我们需要写出push();pop();top();getMin();四个方法的实体,然后我们发现,push()和pop(),可以自己直接运行,top()也可以直接得出问题答案,问题的关键出在getMin()上面,我们需要任何时候都能取出最小的栈元素,因此,我们需要思考,此时我们是否还用一个东西来一直储存最小值的存在,并且能够实时更新,所以,我们能看见我们又建立了一个辅助栈,辅助栈一直保存的都是当前位置下的最小栈元素,并且当新的最小栈元素出现是,此辅助站还会实时更新数据,关键点是,当我们数据栈进行的是pop()操作的时候,如果此课pop出去的是原先栈中的最小元素,那么,此时栈中的最小元素是什么?思考得知,如果发生此种情况,辅助栈应该同时pop出当前最小栈元素,得出当前数据栈中的最小栈元素。
203 移除链表元素
给你一个链表的头节点 head 和一个整数 val ,请你删除链表中所有满足 Node.val == val 的节点,并返回 新的头节点 。
示例 1:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-sfpa6ARE-1644476543059)(D:\File\typoraImg\image-20210316185106659.png)]
输入:head = [1,2,6,3,4,5,6], val = 6
输出:[1,2,3,4,5]
示例 2:
输入:head = [], val = 1
输出:[]
示例 3:
输入:head = [7,7,7,7], val = 7
输出:[]
提示:
列表中的节点在范围 [0, 104] 内
1 <= Node.val <= 50
0 <= k <= 50
class Solution {
public ListNode removeElements(ListNode head, int val) {
ListNode dummyHead = new ListNode(-1);
dummyHead.next = head;
ListNode prev = dummyHead;
while(prev.next != null){
if(prev.next.val == val)
prev.next = prev.next.next;
else
prev = prev.next;
}
return dummyHead.next;
}
}
206 反转链表
反转一个单链表。
示例:
输入: 1->2->3->4->5->NULL
输出: 5->4->3->2->1->NULL
class Solution {
public ListNode reverseList(ListNode head) {
if(head ==null || head.next==null ){
return head;
}
ListNode prev=null;
ListNode next=null;
while(head!=null){
next = head.next;
head.next = prev;
prev = head;
head = next;
}
return prev;
}
}
225 用队列实现栈(简单)
请你仅使用两个队列实现一个后入先出(LIFO)的栈,并支持普通队列的全部四种操作(push、top、pop 和 empty)。
实现 MyStack 类:
- void push(int x) 将元素 x 压入栈顶。
- int pop() 移除并返回栈顶元素。
- int top() 返回栈顶元素。
- boolean empty() 如果栈是空的,返回 true ;否则,返回 false 。
注意:
- 你只能使用队列的基本操作 —— 也就是 push to back、peek/pop from front、size 和 is empty 这些操作。
- 你所使用的语言也许不支持队列。 你可以使用 list (列表)或者 deque(双端队列)来模拟一个队列 , 只要是标准的队列操作即可。
示例:
输入:
["MyStack", "push", "push", "top", "pop", "empty"]
[[], [1], [2], [], [], []]
输出:
[null, null, null, 2, 2, false]
解释:
MyStack myStack = new MyStack();
myStack.push(1);
myStack.push(2);
myStack.top(); // 返回 2
myStack.pop(); // 返回 2
myStack.empty(); // 返回 False
提示:
1 <= x <= 9
最多调用100 次 push、pop、top 和 empty
每次调用 pop 和 top 都保证栈不为空
class MyStack {
//初始化队列
Queue<Integer> queue;
public MyStack() {
queue = new LinkedList<>();
}
//模拟入栈操作
public void push(int x) {
queue.offer(x);
//将之前的全部都出队,然后再入队
for(int i = 1;i<queue.size();i++){
queue.offer(queue.poll());
}
}
//模拟出栈
public int pop() {
return queue.poll();
}
//返回栈顶元素
public int top() {
return queue.peek();
}
//判断是否为空
public boolean empty() {
return queue.isEmpty();
}
}
- 解释:栈是先进后出的结构,而队列是先进先出的结构,但是有一个队列就可以模拟一个栈的运行方式,而需要两个栈才能模拟一个队列,这一题的难点在于在进行push()操作的时候,我们就应该做好讲队列先进先出的结构特性改变成为栈的先进后出的特性,,所以我们需要进行一个模拟入栈的操作,假设此时栈中元素为321,此时入栈4,入栈之后,现存元素应该是3214,但是由于此时这是一个队列,如果,这时候进行的是出栈操作,那么出去的应该是3,但是实际上的栈出去的应该是4,所以,我们需要改变队列内元素的位置,将321号元素往后移,移动到4的后面,此时栈内元素应该是4321,若此时进行出栈操作,按照栈的push()操作,出栈的应该是4,而实际上按照队列的出队操作,我们出去的也是4,因此,我们从某种程度上就是完全模拟出了栈的操作。
485.最大连续为1的个数
给定一个二进制数组, 计算其中最大连续 1 的个数。
示例:
输入:[1,1,0,1,1,1]
输出:3
解释:开头的两位和最后的三位都是连续 1 ,所以最大连续 1 的个数是 3.
提示:
输入的数组只包含 0 和 1 。
输入数组的长度是正整数,且不超过 10,000。
class Solution {
public int findMaxConsecutiveOnes(int[] nums) {
int maxCount = 0, count = 0;
int n = nums.length;
for (int i = 0; i < n; i++) {
if (nums[i] == 1) {
count++;
} else {
maxCount = Math.max(maxCount, count);
count = 0;
}
}
maxCount = Math.max(maxCount, count); //当循环结束之后再次比较大小
return maxCount;
}
}
class Solution {
public void moveZeroes(int[] nums) {
if(nums==null) {
return;
}
//第一次遍历的时候,j指针记录非0的个数,只要是非0的统统都赋给nums[j]
int j = 0;
for(int i=0;i<nums.length;++i) {
if(nums[i]!=0) {
nums[j++] = nums[i];
}
}
//非0元素统计完了,剩下的都是0了
//所以第二次遍历把末尾的元素都赋为0即可
for(int i=j;i<nums.length;++i) {
nums[i] = 0;
}
}
}
class MyCircularQueue {
private int [] queue;//以数组为底层原理,构建队列
private int headIndex;//头节点
private int count;//当前大小
private int capacity;//总容量
public MyCircularQueue(int k) {
this.capacity=k;
this.queue=new int[k];
this.headIndex=0;
this.count=0;
}
public boolean enQueue(int value) {
if(this.count==this.capacity)
return false;//当容量==大小的时候,返回false,此时无法插入数据
this.queue[(this.headIndex+this.count)%this.capacity]=value;
this.count+=1;
return true;
}
public boolean deQueue() {
if(this.count==0){
return false;//此时队列为空
}
this.headIndex=(this.headIndex+1)%this.capacity;
this.count-=1;
}
/** Get the front item from the queue. */
public int Front() {
if (this.count == 0)
return -1;
return this.queue[this.headIndex];
}
/** Get the last item from the queue. */
public int Rear() {
if (this.count == 0)
return -1;
int tailIndex = (this.headIndex + this.count - 1) % this.capacity;
return this.queue[tailIndex];
}
/** Checks whether the circular queue is empty or not. */
public boolean isEmpty() {
return (this.count == 0);
}
/** Checks whether the circular queue is full or not. */
public boolean isFull() {
return (this.count == this.capacity);
}
}
933 最近的请求次数
写一个 RecentCounter 类来计算特定时间范围内最近的请求。
请你实现 RecentCounter 类:
RecentCounter() 初始化计数器,请求数为 0 。
int ping(int t) 在时间 t 添加一个新请求,其中 t 表示以毫秒为单位的某个时间,并返回过去 3000 毫秒内发生的所有请求数(包括新请求)。确切地说,返回在 [t-3000, t] 内发生的请求数。
保证 每次对 ping 的调用都使用比之前更大的 t 值。
示例:
输入:
["RecentCounter", "ping", "ping", "ping", "ping"]
[[], [1], [100], [3001], [3002]]
输出:
[null, 1, 2, 3, 3]
解释:
RecentCounter recentCounter = new RecentCounter();
recentCounter.ping(1); // requests = [1],范围是 [-2999,1],返回 1
recentCounter.ping(100); // requests = [1, 100],范围是 [-2900,100],返回 2
recentCounter.ping(3001); // requests = [1, 100, 3001],范围是 [1,3001],返回 3
recentCounter.ping(3002); // requests = [1, 100, 3001, 3002],范围是 [2,3002],返回 3
class RecentCounter {
Queue<Integer> q;
public RecentCounter() {
q = new LinkedList<>();
}
public int ping(int t) {
q.add(t);
while(q.peek()<(t-3000)){
q.poll();
}
return q.size();
}
}
链表的反转
public class Solution {
public ListNode ReverseList(ListNode head) {
//判断链表为空或者为1的状态
if(head == null || head.next == null)
return head;
ListNode pre = null ;
ListNode next = null;
while(head!=null){
next = head.next;
head.next = pre;
pre = head;
head = next;
}
return pre;
}
}
详解:
在此记录一个哭笑不得的错误点:if()中的括号错误的使用的中文,导致编译时一直编译不出来。后来发现if后面于括号的间距不一样,所以修改后完成,得以提交。