1、滑动窗口求最大值
给定一个数组 nums 和滑动窗口的大小 k,请找出所有滑动窗口里的最大值。
示例:
给定一个数组 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) {
if(nums==null||nums.length<=0)return new int[0];
int t;
初始化一个新的数组用于返回结果
int[] a=new int[nums.length-k+1];
for (int i = 0; i < nums.length-k+1; i++) {
//每次将窗口的第一个值作为最大值
t=nums[i];
for (int j =i; j <k+i-1; j++) {
if(t<nums[j+1]){
//如果小于下一个,则讲下一个的值赋予t
t=nums[j+1];
}
}
//将最大的值赋予新数组
a[i]=t;
}
return a;
}
}
效果:
2、队列的最大值
请定义一个队列并实现函数 max_value 得到队列里的最大值,要求函数max_value、push_back 和 pop_front 的均摊时间复杂度都是O(1)。
若队列为空,pop_front 和 max_value 需要返回 -1
示例 1:
输入:
[“MaxQueue”,“push_back”,“push_back”,“max_value”,“pop_front”,“max_value”]
[[],[1],[2],[],[],[]] 输出: [null,null,null,2,1,2]
示例 2:输入: [“MaxQueue”,“pop_front”,“max_value”] [[],[],[]] 输出: [null,-1,-1]
限制:
1 <= push_back,pop_front,max_value的总操作数 <= 10000
1 <= value <= 10^5
题解
class MaxQueue {
Queue<Integer> queue;
Deque <Integer>deque;
public MaxQueue() {
//初始化队列
queue=new LinkedList<>();
deque=new LinkedList<>();
}
public int max_value() {
if(deque.isEmpty()) return -1;
return deque.getFirst();
}
public void push_back(int value) {
queue.offer(value);
while(!deque.isEmpty()&&deque.peekLast()<value){
deque.pollLast();
}
deque.offerLast(value);
}
public int pop_front() {
if(queue.isEmpty()) return -1;
if(queue.peek().equals(deque.getFirst())){
deque.pollFirst();
}
return queue.poll();
}
}
函数设计:
-
初始化队列 queue ,双向队列 deque ;
-
最大值 max_value() :
- 当双向队列 deque 为空,则返回 -1−1 ;
否则,返回 deque 首元素;
- 当双向队列 deque 为空,则返回 -1−1 ;
-
入队 push_back() :
- 将元素 value 入队 queue ;
将双向队列中队尾 所有 小于 value 的元素弹出(以保持 deque 非单调递减),并将元素 value 入队 deque ;
- 将元素 value 入队 queue ;
-
出队 pop_front() :
- 若队列 queue 为空,则直接返回 -1−1 ;
否则,将 queue 首元素出队;
若 deque 首元素和 queue 首元素 相等 ,则将 deque 首元素出队(以保持两队列 元素一致 ) ;
- 若队列 queue 为空,则直接返回 -1−1 ;
设计双向队列为 非单调递减 的原因:若队列 queue 中存在两个 值相同的最大元素 ,此时 queue 和 deque
同时弹出一个最大元素,而 queue 中还有一个此最大元素;即采用单调递减将导致两队列中的元素不一致。
3、递归求和
输入一个数求它的和
示例:
输入:3
输出:6=3+2+1
输入:5
输出:15=5+4+3+2+1
题解
public class Solution {
static int sum = 0;
public static int fib(int n) {
if(n!=0){
return sum =n+ fib(n - 1);
}
return 0;
}
public static void main(String[] args) {
System.out.println(fib(5));
}
}
4、斐波那契数列 &青蛙跳台阶
写一个函数输入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;
for(int i = 0; i < n; i++){
sum = (a + b) % 1000000007;
a = b;
b = sum;
}
return a;
}
}
思路
不断交换a ,b值来求和
5、反转链表
给你单链表的头节点 head ,请你反转链表,并返回反转后的链表。
示例 1:
输入:head = [1,2,3,4,5]
输出:[5,4,3,2,1]
题解:
ListNode curr=head;
ListNode prev=null;
ListNode next=null;
while(curr!=null){
// 先记录当前节点的下一个节点
next=curr.next;
// 降当前节点的next指向prev,也就是上一个节点
curr.next=prev;
// 将当前节点标记为prev,备下个节点使用
prev=curr;
// 将记录的下一个节点指向curr
curr=next;
}
return prev;
结果:
6、TOP_N问题 在10亿条数据中求前1000
参考链接:原文链接
动手操作:
package com.demo01.pojo;
import io.swagger.models.auth.In;
import java.util.Random;
public class Heap {
// 父节点
private int parent(int n) {
return (n - 1) / 2;
}
/**
* 打印最小堆
* @param data
*/
public void printHeap(int[] data){
for (int i = 0; i < data.length; i++) {
System.out.print(data[i]+" ");
}
}
// 构建堆
private int[] buildHeap(int n, int[] data) {
for(int i = 0; i < n; i++) {
int t = i;
// 调整堆
while(t != 0 && data[parent(t)] > data[t]) {
int temp = data[t];
data[t] = data[parent(t)];
data[parent(t)] = temp;
t = parent(t);
}
}
int a[]=new int[n];
for (int i = 0; i < n; i++) {
a[i]=data[i];
}
return a;
}
// 调整堆
private int[] reHeap(int[] data) {
for(int i = 0; i < data.length; i++) {
int t = i;
// 调整堆
while(t != 0 && data[parent(t)] > data[t]) {
int temp = data[t];
data[t] = data[parent(t)];
data[parent(t)] = temp;
t = parent(t);
}
}
return data;
}
/**
* 创建一个数组
* @return
*/
public int [] randomArray(){
int[]a=new int[10];
for (int i = 0; i < 10; i++) {
int j=(int)(1+Math.random()*20);
a[i]=j;
}
return a;
}
/**
* 进行比较
* @param a
* @param b
* @param n
* @return
*/
public int[] compare(int[] a,int[] b,int n){
for (int i = n-1; i <b.length ; i++) {
if(b[i]>a[0]){
a[0]=b[i];
reHeap(a);
}
}
return a;
}
public static void main(String[] args) {
Heap heap = new Heap();
int[] ints1 = heap.randomArray();
System.out.print("元素组:");
heap.printHeap(ints1);
System.out.println();
int[] ints = heap.buildHeap(5, ints1);
System.out.print("初始化前N个的数的数组:");
heap.printHeap(ints);
int[] compare = heap.compare(ints, ints1, 5);
System.out.println();
System.out.print("求前N个数的结果数组:");
heap.printHeap(compare);
}
}
结果:
7、二叉树实现
参考博客:链接
参考版:
package com.demo01.pojo;
class Node {
int data;
Node left;
Node right;
public Node(int data) {
this.data = data;
this.left = null;
this.right = null;
}
}
public class BinaryTree {
Node root;
private Node addNode(Node current, int value) {
if (current == null) {
return new Node(value);
}
if (value < current.data) {
current.left = addNode(current.left, value);
} else if (value > current.data) {
current.right = addNode(current.right, value);
} else {
return current;
}
return current;
}
public void addNode(int value) {
root = addNode(root, value);
}
public BinaryTree createBinaryTree() {
BinaryTree bt = new BinaryTree();
bt.addNode(6);
bt.addNode(4);
bt.addNode(8);
bt.addNode(10);
return bt;
}
private boolean containNode(Node current, int value) {
if (current == null) {
return false;
}
if (value == current.data) {
return true;
}
return value < current.data ? containNode(current.left, value) : containNode(current.right, value);
}
public boolean containNode(int value) {
return containNode(root, value);
}
private Node deleteNode(Node current, int value) {
if (current == null) {
return null;
}
if (value == current.data) {
if (current.left == null && current.right == null) {
return null;
}
if (current.left == null) {
return current.right;
}
if (current.right == null) {
return current.left;
}
int smallestValue = findSmallestValue(current.right);
current.data = smallestValue;
current.right = deleteNode(current.right, smallestValue);
return current;
}
if (value < current.data) {
current.left = deleteNode(current.left, value);
return current;
}
current.right = deleteNode(current.right, value);
return current;
}
private int findSmallestValue(Node root) {
return root.left == null ? root.data : findSmallestValue(root.right);
}
public void traverseInOrder(Node root) {
if (root != null) {
traverseInOrder(root.left);
System.out.println(root.data);
traverseInOrder(root.right);
}
}
public static void main(String[] args) {
BinaryTree binaryTree = new BinaryTree();
BinaryTree binaryTree1 = binaryTree.createBinaryTree();
binaryTree.traverseInOrder(binaryTree1.root);
}
}
自己版:
package com.demo01.pojo;
import io.swagger.models.auth.In;
public class BinTree {
class Node{
Node left;
Node right;
int v;
public Node() {
}
public Node(Node left, Node right, int v) {
this.left = left;
this.right = right;
this.v = v;
}
}
Node root;
int size=0;
//初始化一个空的二叉树
public BinTree BinTree(){
BinTree binTree = new BinTree();
return binTree;
}
//添加新节点的方法
public void addNode(Integer v){
//判断当前树是否存在root节点?创建一个:将root节点作为参数传递调用insertNode方法
if (size == 0) {
root=new Node(null,null,v);
size++;
}else {
insertNode(root,v);
}
}
//加入新节点的方法
private void insertNode(Node cur, Integer v) {
boolean zy=true; //记录在当前节点的左子树添加还是右子树添加
while(cur!=null){
// 判断新节点的值是否小于当前节点的值?cur=cur.left:cur=cur.right
if(v<cur.v){
//判断当前节点的是否是子节点?cur=cur.left:zy=true,break
if(cur.left!=null){
cur=cur.left;
}else{
zy=true;
break;
}
}else{
//判断当前节点的是否是子节点?cur=cur.right:zy=false,break
if(cur.right!=null){
cur=cur.right;
}else {
zy=false;
break;
}
}
}
//判断在当前节点的左子树还是右子树 上添加新的节点
if(zy){
cur.left=new Node(null,null,v);
}else{
cur.right=new Node(null,null,v);
}
size++;
}
//中序遍历二叉树
public void traverseInOrder(Node root) {
if (root != null) {
traverseInOrder(root.left);
System.out.print(root.v+" ");
traverseInOrder(root.right);
}
}
//先序遍历二叉树
public void traversePreOrder(Node root) {
if (root != null) {
System.out.println(root.v);
traversePreOrder(root.left);
traversePreOrder(root.right);
}
}
//后序遍历二叉树
public void traversePostOrder(Node root) {
if (root != null) {
traversePostOrder(root.left);
traversePostOrder(root.right);
System.out.println(root.v);
}
}
//删除一个节点
private void deleteNode(int value){
deleteNode(root,value);
}
private Node deleteNode(Node current, int value) {
if (current == null) {
return null;
}
if (value == current.v) {
if (current.left == null && current.right == null) {
return null;
}
if (current.left == null) {
return current.right;
}
if (current.right == null) {
return current.left;
}
int smallestValue = findSmallestValue(current.right);
current.v = smallestValue;
current.right = deleteNode(current.right, smallestValue);
return current;
}
if (value < current.v) {
current.left = deleteNode(current.left, value);
return current;
}
current.right = deleteNode(current.right, value);
return current;
}
private int findSmallestValue(Node root) {
return root.left == null ? root.v : findSmallestValue(root.right);
}
//查找是否包含一个节点
private boolean containNode(Node current, int value) {
if (current == null) {
return false;
}
if (value == current.v) {
return true;
}
return value < current.v ? containNode(current.left, value) : containNode(current.right, value);
}
public boolean containNode(int value) {
return containNode(root, value);
}
public static void main(String[] args) {
BinTree binTree = new BinTree();
binTree.addNode(6);
binTree.addNode(4);
binTree.addNode(8);
binTree.addNode(10);
binTree.addNode(3);
binTree.addNode(5);
binTree.addNode(7);
binTree.traverseInOrder(binTree.root);
System.out.println();
boolean b1 = binTree.containNode(3);
System.out.println("判断是否存在值为3得的节点: "+b1);
boolean b2= binTree.containNode(15);
System.out.println("判断是否存在值为15得的节点: "+b2);
binTree.deleteNode(10);
binTree.traverseInOrder(binTree.root);
}
}
8、LRU缓存淘汰算法
缓存是一种提高数据读取性能的技术,在硬件设计、软件开发中都有着非常广泛的应用,比如常见的 CPU 缓存、数据库缓存、浏览器缓存等等。
缓存的大小有限,当缓存被用满时,哪些数据应该被清理出去,哪些数据应该被保留?这就需要缓存淘汰策略来决定。常见的策略有三种:先进先出策略 FIFO(First In,First Out)、最少使用策略 LFU(Least Frequently Used)、最近最少使用策略 LRU(Least Recently Used)。
在各个语言的第三方框架中都大量使用到了 LRU 缓存策略。程序员小吴接触到的有Java中的 「 Mybatis 」,iOS中的 「YYCache」与「Lottie」等。
LRU缓存淘汰算法
LRU是最近最少使用策略的缩写,是根据数据的历史访问记录来进行淘汰数据,其核心思想是“如果数据最近被访问过,那么将来被访问的几率也更高”。
链表实现LRU
将Cache的所有位置都用双链表连接起来,当一个位置被命中之后,通过调整链表的指向,将该位置调整到链表头的位置,新加入的Cache直接加到链表头中。
这样,在多次进行Cache操作后,最近被命中的,就会被向链表头方向移动,而没有命中的,而想链表后面移动,链表尾则表示最近最少使用的Cache。
当需要替换内容时候,链表的最后位置就是最少被命中的位置,我们只需要淘汰链表最后的部分即可。