剑指Offer算法笔记(一)

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 首元素;
  • 入队 push_back() :

    • 将元素 value 入队 queue ;
      将双向队列中队尾 所有 小于 value 的元素弹出(以保持 deque 非单调递减),并将元素 value 入队 deque ;
  • 出队 pop_front() :

    • 若队列 queue 为空,则直接返回 -1−1 ;
      否则,将 queue 首元素出队;
      若 deque 首元素和 queue 首元素 相等 ,则将 deque 首元素出队(以保持两队列 元素一致 ) ;

设计双向队列为 非单调递减 的原因:若队列 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。

当需要替换内容时候,链表的最后位置就是最少被命中的位置,我们只需要淘汰链表最后的部分即可。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

猿小许

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

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

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

打赏作者

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

抵扣说明:

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

余额充值