前言: \textcolor{Green}{前言:} 前言:
本专栏用于本人刷算法的过程。主要包含刷题中的感受以及知识点缺陷。对于学习者来说可以作为参考。
目前更新的算法内容会比较多,很多都是通过刷题来进行知识点的总结,其中部分来源于网络总结,如有侵权请联系。
文章存在时候,会随着博主的学习过程进行不间断更新,只增不减,请放心使用
题目来源:牛客网
等级: E a s y \textcolor{OrangeRed}{等级:Easy} 等级:Easy
题目描述
用两个栈来实现一个队列,使用n个元素来完成 n 次在队列尾部插入整数(push)和n次在队列头部删除整数(pop)的功能。 队列中的元素为int类型。保证操作合法,即保证pop操作时队列内已有元素。
数据范围: n ≤ 1000 n ≤ 1000 数据范围: n\le1000n≤1000 数据范围:n≤1000n≤1000
要求:存储n个元素的空间复杂度为 O(n) ,插入与删除的时间复杂度都是 O(1)
代码编写
方法1
import java.util.Stack;
public class Solution {
Stack<Integer> stack1 = new Stack<Integer>();
Stack<Integer> stack2 = new Stack<Integer>();
public void push(int node) {
stack1.push(node);
}
public int pop() {
if (stack1.isEmpty() && stack2.isEmpty()) {
return -1;
}
if (!stack2.isEmpty()) {
return stack2.pop();
}
while (!stack1.isEmpty()) {
stack2.push(stack1.pop());
}
return stack2.pop();
}
}
题目来源:牛客网
等级: E a s y \textcolor{OrangeRed}{等级:Easy} 等级:Easy
题目描述
定义栈的数据结构,请在该类型中实现一个能够得到栈中所含最小元素的 min 函数,输入操作时保证 pop、top 和 min 函数操作时,栈中一定有元素。
此栈包含的方法有:
push(value):将value压入栈中
pop():弹出栈顶元素
top():获取栈顶元素
min():获取栈中最小元素
数据范围:操作数量满足 0 ≤ n ≤ 300 0≤n≤300 0≤n≤300 ,输入的元素满足 ∣ v a l ∣ ≤ 10000 ∣val∣≤10000 ∣val∣≤10000
进阶:栈的各个操作的时间复杂度是 O(1) ,空间复杂度是 O(n)
示例:
输入: ["PSH-1","PSH2","MIN","TOP","POP","PSH1","TOP","MIN"]
输出: -1,2,1,-1
解析:
"PSH-1"表示将-1压入栈中,栈中元素为-1
"PSH2"表示将2压入栈中,栈中元素为2,-1
“MIN”表示获取此时栈中最小元素==>返回-1
"TOP"表示获取栈顶元素==>返回2
"POP"表示弹出栈顶元素,弹出2,栈中元素为-1
"PSH1"表示将1压入栈中,栈中元素为1,-1
"TOP"表示获取栈顶元素==>返回1
“MIN”表示获取此时栈中最小元素==>返回-1
示例1
输入:
["PSH-1","PSH2","MIN","TOP","POP","PSH1","TOP","MIN"]
复制
返回值:
-1,2,1,-1
代码编写
方法1
import java.util.Stack;
public class Solution {
private Stack<Integer> stack = new Stack<>();
private Stack<Integer> minStack = new Stack<>();
public void push(int node) {
stack.push(node);
if (minStack.isEmpty()){
minStack.push(node);
} else {
int min = minStack.peek();
if (min > node) {
minStack.push(node);
} else {
minStack.push(min);
}
}
}
public void pop() {
stack.pop();
minStack.pop();
}
public int top() {
if (stack.isEmpty()) {
return -1;
}
return stack.peek();
}
public int min() {
return minStack.peek();
}
}
可以提炼的知识
堆栈类peek()方法peek()方法在java.util包中使用。
peek()方法用于从此Stack中返回顶部元素,并且它不删除检索元素
。
peek()方法是一种非静态方法,只能通过类对象访问,如果尝试使用类名访问该方法,则会收到错误消息。
peek()方法在返回top元素时不会引发异常。
题目来源:牛客网
等级:简单 \textcolor{OrangeRed}{等级:简单} 等级:简单
题目描述
给出一个仅包含字符’(‘,’)‘,’{‘,’}‘,’[‘和’]',的字符串,判断给出的字符串是否是合法的括号序列
括号必须以正确的顺序关闭,"()“和”()[]{}“都是合法的括号序列,但”(]“和”([)]"不合法。
数据范围:字符串长度 0 ≤ n ≤ 100000 ≤ n ≤ 10000 0\le n \le 100000≤n≤10000 0≤n≤100000≤n≤10000
要求:空间复杂度 O(n),时间复杂度 O(n)
示例1
输入:"["
返回值:false
示例2
输入:"[]"
返回值:true
代码编写
方法1
import java.util.*;
public class Solution {
/**
*
* @param s string字符串
* @return bool布尔型
*/
public boolean isValid (String s) {
// write code here
int len = s.length();
if (len % 2 == 1) {
return false;
}
Stack<Character> stack = new Stack<>();
for (int i = 0; i < len; ++i) {
char c = s.charAt(i);
if (c == '(') {
stack.push(')');
} else if (c == '{') {
stack.push('}');
} else if (c == '[') {
stack.push(']');
} else if (stack.isEmpty() || stack.peek() != c) {
return false;
} else {
stack.pop();
}
}
if (stack.isEmpty()) {
return true;
}
return false;
}
}
题目来源:牛客网
等级: M e d i u m \textcolor{OrangeRed}{等级:Medium} 等级:Medium
使用方法:大根堆
题目描述
给定一个长度为 n 的可能有重复值的数组,找出其中不去重的最小的 k 个数。例如数组元素是4,5,1,6,2,7,3,8这8个数字,则最小的4个数字是1,2,3,4(任意顺序皆可)。
数据范围: ≤ k , n ≤ 100000 ≤ k , n ≤ 10000 ,数组中每个数的大小 0 ≤ v a l ≤ 10000 ≤ v a l ≤ 1000 \le k,n \le 100000≤k,n≤10000,数组中每个数的大小0 \le val \le 10000≤val≤1000 ≤k,n≤100000≤k,n≤10000,数组中每个数的大小0≤val≤10000≤val≤1000
要求:空间复杂度 O(n) ,时间复杂度 O(nlogk)
示例1
输入:
[4,5,1,6,2,7,3,8],4
返回值:[1,2,3,4]
说明:
返回最小的4个数即可,返回[1,3,2,4]也可以
示例2
输入:[1],0
返回值:[]
示例3
输入:[0,1,2,1,2],3
返回值:[0,1,1]
代码编写
方法1
import java.util.*;
public class Solution {
public ArrayList<Integer> GetLeastNumbers_Solution(int [] input, int k) {
ArrayList<Integer> res = new ArrayList<>();
if (k == 0 || input.length == 0) {
return res;
}
// large root heap
Queue<Integer> maxQueue= new PriorityQueue<>((v1, v2) -> v2 - v1);
// Build large root heap
for (int i = 0; i < k; ++i) {
maxQueue.offer(input[i]);
}
for (int i = k; i < input.length; ++i) {
// Small elements are piled
if (maxQueue.peek() > input[i]) {
maxQueue.poll();
maxQueue.offer(input[i]);
}
}
// Extract elements from the heap
for (int i = 0; i < k; ++i) {
res.add(maxQueue.poll());
}
return res;
}
}
注意点
Queue maxQueue= new PriorityQueue<>((v1, v2) -> v2 - v1);
可以提炼的知识
(v1, v2) -> v2 - v1
。offer()
方法,弹出是 poll()
方法。Queue<Integer> maxQueue= new PriorityQueue<>((v1, v2) -> v2 - v1);
maxQueue.offer(1);
maxQueue.poll();
maxQueue.size();
题目来源:牛客网
等级: M e d i u m \textcolor{OrangeRed}{等级:Medium} 等级:Medium
使用方法:小根堆
题目描述
有一个整数数组,请你根据快速排序的思路,找出数组中第 k 大的数。
给定一个整数数组 a ,同时给定它的大小n和要找的 k ,请返回第 k 大的数(包括重复的元素,不用去重),保证答案存在。
要求:时间复杂度 O(nlogn)O(nlogn),空间复杂度 O(1)O(1)
数据范围: 0 ≤ n ≤ 10000 ≤ n ≤ 10001 ≤ K ≤ n 1 ≤ K ≤ n ,数组中每个元素满足 0 ≤ v a l ≤ 100000000 ≤ v a l ≤ 10000000 0\le n \le 10000≤n≤10001 \le K \le n1≤K≤n,数组中每个元素满足 0 \le val \le 100000000≤val≤10000000 0≤n≤10000≤n≤10001≤K≤n1≤K≤n,数组中每个元素满足0≤val≤100000000≤val≤10000000
示例1
输入:[1,3,5,2,2],5,3
返回值:2
示例2
输入:
[10,10,9,9,8,7,5,6,4,3,4,2],12,3
返回值:9
说明:
去重后的第3大是8,但本题要求包含重复的元素,不用去重,所以输出9 \
代码编写
方法1
import java.util.*;
public class Solution {
public int findKth(int[] a, int n, int K) {
// write code here
if (K == 0 || n == 0) {
return -1;
}
// Build small root heap with a sizeof K
PriorityQueue<Integer> smallQueue = new PriorityQueue<>(K);
for (int i = 0; i < K; ++i) {
if (i > n) {
break;
}
smallQueue.offer(a[i]);
}
for (int i = K; i < n; ++i) {
if (smallQueue.peek() < a[i]) {
smallQueue.poll();
smallQueue.offer(a[i]);
}
}
return smallQueue.peek();
}
}
方法2
注意点
// Build small root heap with a sizeof K
PriorityQueue<Integer> smallQueue = new PriorityQueue<>(K);
可以提炼的知识
题目来源:牛客网
等级: M e d i u m \textcolor{OrangeRed}{等级:Medium} 等级:Medium
使用方法:小根堆+大根堆
题目描述
如何得到一个数据流中的中位数?如果从数据流中读出奇数个数值,那么中位数就是所有数值排序之后位于中间的数值。如果从数据流中读出偶数个数值,那么中位数就是所有数值排序之后中间两个数的平均值。我们使用Insert()方法读取数据流,使用GetMedian()方法获取当前读取数据的中位数。
数据范围:$数据流中数个数满足 1 \le n \le 1000 \1≤n≤1000 ,大小满足 1 \le val \le 1000 \1≤val≤1000 $
进阶: 空间复杂度 O(n) , 时间复杂度 O(nlogn)
示例1
输入:[5,2,3,4,1,6,7,0,8]
返回值:"5.00 3.50 3.00 3.50 3.00 3.50 4.00 3.50 4.00 "
说明:
数据流里面不断吐出的是5,2,3...,则得到的平均数分别为5,(5+2)/2,3...
示例2
输入:[1,1,1]
返回值:"1.00 1.00 1.00 "
代码编写
方法1
参考大佬写法
[最大堆左边最大 leftMax] | [右边最小rightMin 最小堆] |
---|
直接插入到对面的堆中,然后再转移到自己的堆中但是它的 时间复杂度提高, 每次都插入时间复杂度O(log n)
import java.util.*;
public class Solution {
// Build large root heap,
private PriorityQueue<Integer> leftHeap = new PriorityQueue<>((v1, v2) -> v2 - v1);
// Build small root heap
private PriorityQueue<Integer> rightHeap = new PriorityQueue<>();
// Judge whether it is odd
private boolean isOdd = true;
public void Insert(Integer num) {
if (isOdd) {
rightHeap.offer(num);
leftHeap.offer(rightHeap.poll());
} else {
leftHeap.offer(num);
rightHeap.offer(leftHeap.poll());
}
isOdd = !isOdd;
}
public Double GetMedian() {
if (leftHeap.isEmpty()) {
return 0.0;
}
if (!isOdd) {
return leftHeap.peek() / 1.0;
} else {
return (leftHeap.peek() + rightHeap.peek()) / 2.0;
}
}
}
方法2
注意点
一点要保证两个堆中的数据数目差不能超过1,这样可以使中位数只会出现在两个堆的交接处;
保证大根堆所有数据都小于小根堆
可以提炼的知识
对于海量数据和流的数据,用最大堆和最小堆来管理
堆通常是一个可以被看做一棵树的数组对象。堆是非线性数据结构,相当于一维数组,有两个直接后继。堆总是满足下列性质:
求第k大,就要用小根堆,每一次更新后,顶点时刻都是整个树中数值最小的节点,它的孩子都比他大,而我们求第k大,就构建一个只有 k 个节点的堆,最后返回堆顶。
PriorityQueue<Integer> smallQueue = new PriorityQueue<>(K);
求第K小,就要用大根堆,每一次更新后,顶点都是整棵树中数值最大的节点,它的孩子都比他小,那么就第K小,就构建一个只有k个节点的堆,最后返回堆顶。
Queue<Integer> maxQueue= new PriorityQueue<>((v1, v2) -> v2 - v1);
加入:offer()
弹出:poll()
大小:size()
peek() :返回栈顶元素,不在堆栈中删除它。
pop() :返回栈顶元素,并在进程中删除它。
push():在栈顶增加元素