文章目录

  • 排序算法复杂度
  • 数组中出现次数超过一半的数字
  • 最小覆盖子串
  • 三数和
  • 接雨水
  • 链表环
  • 代理模式
  • 工厂模式
  • 观察者模式
  • 生产者消费者
  • 单例模式
  • 选择排序
  • 快排
  • 快排实现TopK
  • 归并排序
  • 死锁
  • 双线程轮流打印
  • 自定义线程池
  • 数组实现优先队列
  • LinkedHashMap实现LRU


排序算法复杂度

什么叫手撕深度学习代码 手撕代码是啥意思_java

数组中出现次数超过一半的数字

摩尔投票法:每次从序列里选择两个不相同的数字删除掉(或称为“抵消”),最后剩下一个数字或几个相同的数字,就是出现次数大于总数一半的那个。
题目链接

public int majorityElement(int[] nums) {
        int res = 0;
        int vote = 0;
        for (int i : nums) {
            if (vote == 0) {
                res = i;
            }
            vote += (res == i) ? +1 : -1;
        }
        return res;
    }

最小覆盖子串

题解

public String minWindow(String s, String t) {
        Map<Character,Integer> tMap=new HashMap<>();
        Map<Character,Integer> sMap=new HashMap<>();
        for(int i=0;i<t.length();i++){
            tMap.put(t.charAt(i),tMap.getOrDefault(t.charAt(i),0)+1);
        }
        int i,j,cnt=0,len=s.length()+1;
        String res="";
        for(i=0,j=0;i<s.length();i++){
            sMap.put(s.charAt(i),sMap.getOrDefault(s.charAt(i),0)+1);
            if(tMap.containsKey(s.charAt(i))&&sMap.get(s.charAt(i))<=tMap.get(s.charAt(i)))cnt++;
            while(j<i&&(!tMap.containsKey(s.charAt(j))||sMap.get(s.charAt(j))>tMap.get(s.charAt(j)))){
                sMap.put(s.charAt(j),sMap.get(s.charAt(j))-1);
                j++;
            }
            if(cnt==t.length()&&i-j+1<len){
                len=i-j+1;
                res=s.substring(j,i+1);
            }
        }
        return res;
    }

三数和

public List<List<Integer>> threeSum(int[] nums) {
        List<List<Integer>> result=new ArrayList<>();
        int len=nums.length;
        if(len<3)return result;
        Arrays.sort(nums);
        int left,right;
        for(int i=0;i<len;i++){
            if(nums[i]>0)break;
            if(i>0&&nums[i]==nums[i-1])continue;
            left=i+1;
            right=len-1;
            while(left<right){
                int sum=nums[i]+nums[left]+nums[right];
                if(sum==0){
                    result.add(Arrays.asList(nums[i],nums[left],nums[right]));
                    while(left<right&&nums[left]==nums[left+1])left++;
                    while(left<right&&nums[right]==nums[right-1])right--;
                    left++;
                    right--;
                }else if(sum<0)
                    left++;
                else if(sum>0)
                    right--;
            }
        }
        return result;
    }

接雨水

public int trap(int[] height) {
        int res=0;
        if(height.length==0)return res;
        int left=0,right=0;
        for(int i=1;i<height.length-1;i++){
            left=height[i];
            right=height[i];
            for(int j=0;j<i;j++)
                left=Math.max(left,height[j]);
            for(int j=i;j<height.length;j++)
                right=Math.max(right,height[j]);
            res+=Math.min(left,right)-height[i];
        }
        return res;
    }

复杂度O(n*n)
优化后O(n)

class Solution {
    public int trap(int[] height) {
        int n = height.length;
        if (n == 0) {
            return 0;
        }
		//eftMax[i] 表示下标 ii 及其左边的位置中,height 的最大高度
        int[] leftMax = new int[n];
        leftMax[0] = height[0];
        for (int i = 1; i < n; ++i) {
            leftMax[i] = Math.max(leftMax[i - 1], height[i]);
        }
		//rightMax[i] 表示下标 ii 及其右边的位置中,height 的最大高度
        int[] rightMax = new int[n];
        rightMax[n - 1] = height[n - 1];
        for (int i = n - 2; i >= 0; --i) {
            rightMax[i] = Math.max(rightMax[i + 1], height[i]);
        }

        int ans = 0;
        //位置i左边和右边最大高度的最小值减去height[i]就是该位置能接的雨水
        for (int i = 0; i < n; ++i) {
            ans += Math.min(leftMax[i], rightMax[i]) - height[i];
        }
        return ans;
    }
}

链表环

**
 1. 关于链表环的两个结论:
 2. 1.设置快慢指针,假如有环,他们最后一定相遇。(快指针一次两步,慢指针一次一步)
 3. 2.两个指针分别从链表头和相遇点继续出发,每次走一步,最后一定相遇在环入口。
 */
public class EntryNodeOfLoop {
    public ListNode entryNodeOfLoop(ListNode pNode) {
        ListNode low = pNode;
        ListNode fast = pNode;
        while (fast != null && fast.next != null) {
            low = low.next;
            fast = fast.next.next;
            if (low == fast)
                break;//链表存在环
        }
        if (fast == null || fast.next == null)//链表没有环
            return null;
        low = pNode;
        while (low != fast) {
            low = low.next;
            fast = fast.next;
        }
        return low;
    }
}

代理模式

代理模式使得客户端通过代理对象间接使用目标对象,
代理对象是对目标对象功能的增强。
代理类主要负责为目标对象预处理消息、过滤消息、把消息转发给委托类,以及事后处理消息等,委托类负责具体的实现

public class Daili {

    public static void main(String[] args) {
        RealMovie realMovie=new RealMovie();
        Cinema cinema=new Cinema(realMovie);
        cinema.play();
    }
}
//抽象主题
interface Movie {
    void play();
}
//真实电影类,被代理对象
class RealMovie implements Movie {
    @Override
    public void play() {
        System.out.println("播放电影<让子弹飞>");
    }
}
//代理对象,对play()进行增强
class Cinema implements Movie {
    RealMovie realMovie;

    Cinema(RealMovie realMovie) {
        this.realMovie = realMovie;
    }

    @Override
    public void play() {
        adPlay(true);
        realMovie.play();
        adPlay(false);
    }

    public void adPlay(boolean flag) {
        if (flag) {
            System.out.println("电影开始,播放开场广告");
        } else {
            System.out.println("电影结束,播放结束广告");
        }
    }
}

工厂模式

通过工厂模式,将创建产品实例的权利移交工厂,我们不再通过new来创建我们所需的对象,而是通过工厂来获取我们需要的产品。降低了产品使用者与使用者之间的耦合关系

package shejimoshi;

public class Factory {
    public static void main(String[] args) {
        //工厂模式
        carFactory bwm = new bmwFactory();
        Car car = bwm.create();
        car.run();

        carFactory benz=new benzFactory();
        Car car1 = benz.create();
        car1.run();
    }
}

//定义车的接口
interface Car {
    void run();
}

class Bmw implements Car {
    @Override
    public void run() {
        System.out.println("bmw---run");
    }
}

class Benz implements Car {
    @Override
    public void run() {
        System.out.println("Benz--run");
    }
}

//生产车的工厂抽象类
abstract class carFactory {
    public abstract Car create();
}

class bmwFactory extends carFactory {
    @Override
    public Car create() {
        return new Bmw();
    }
}
class benzFactory extends carFactory{
    @Override
    public Car create() {
        return new Benz();
    }
}

观察者模式

  • 适用场景:当一个对象的状态改变需要通知多个对象知道,并且做出相应操作的时候(主题1——————n观察者,即一对多情形)
  • 观察者–微信用户
  • 被观察者–公众号,公众号发布更新,用户应该接收到
  • 在被观察者类中应该实现如下几个方法:
  • 添加观察者add()
  • 删除观察者remove()
  • 发布通知notify()(在此方法中,让每个观察者都能接收到通知的数据)
public class Client {
    public static void main(String[] args) {
        Gongzhonghao gz=new Gongzhonghao();

        WeixinUser user1=new WeixinUser("小明");
        WeixinUser user2=new WeixinUser("小方");
        WeixinUser user3=new WeixinUser("小红");
        gz.attach(user1);
        gz.attach(user2);
        gz.attach(user3);
        //发布消息
        gz.notify("明天暴雨");
    }
}
//抽象观察者
interface Observe {
    public void update(String message);
}
//实例观察者,微信用户
class WeixinUser implements Observe {
    private String name;

    WeixinUser(String name) {
        this.name = name;
    }

    @Override
    public void update(String message) {
        System.out.println(name + "收到订阅消息:" + message);
    }
}
//抽象被观察者,即被订阅的公众号
interface Subject {
    void attach(Observe observe);

    void detach(Observe observe);

    void notify(String message);
}
//实例被观察者,公众号
class Gongzhonghao implements Subject {
    List<Observe> list = new ArrayList<>();

    @Override
    public void attach(Observe observe) {
        list.add(observe);
    }

    @Override
    public void detach(Observe observe) {
        list.remove(observe);
    }

    @Override
    public void notify(String message) {
        for (Observe observe : list) {
            observe.update(message);
        }
    }
}

生产者消费者

什么叫手撕深度学习代码 手撕代码是啥意思_什么叫手撕深度学习代码_02

package shejimoshi;

public class Test {
    private static Integer count = 0;
    private static final Integer  FULL = 3;
    private static String Lock = "lock";

    public static void main(String[] args) {
        Test test = new Test();
        new Thread(test.new Producer()).start();
        new Thread(test.new Consumer()).start();
        new Thread(test.new Producer()).start();
        new Thread(test.new Consumer()).start();
        new Thread(test.new Producer()).start();
        new Thread(test.new Consumer()).start();

    }

    class Producer implements Runnable {
        @Override
        public void run() {
            for (int i = 0; i < 3; i++) {
                try {
                    Thread.sleep(3000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                synchronized (Lock) {
                    while (count == FULL) {
                        try {
                            Lock.wait();
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                    }
                    count++;
                    System.out.println(Thread.currentThread().getName() + ":生产者生产,库存为" + count);
                    Lock.notifyAll();
                }
            }
        }
    }

    class Consumer implements Runnable {
        @Override
        public void run() {
            for (int i = 0; i < 3; i++) {
                try {
                    Thread.sleep(3000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                synchronized (Lock) {
                    while (count == 0) {
                        try {
                            Lock.wait();
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                    }
                    count--;
                    System.out.println(Thread.currentThread().getName() + ":消费者消费,库存为" + count);
                    Lock.notifyAll();
                }
            }
        }
    }
}

单例模式

什么叫手撕深度学习代码 手撕代码是啥意思_什么叫手撕深度学习代码_03

package shejimoshi;

//懒汉式---用到实例再创建
public class Singleton {
    private static Singleton instance;

    private Singleton() {
    }

    private static synchronized Singleton getInstance() {
        if (instance == null) {
            instance = new Singleton();
        }
        return instance;
    }
}

//饿汉式---初始化即创建
public class Singleton1 {
    private static Singleton1 instance = new Singleton1();

    private Singleton1() {
    }

    private static Singleton1 getInstance() {
        return instance;
    }
}

//双重校验锁
public class Singleton2 {
    private static Singleton2 instance;

    private Singleton2() {
    }

    private static Singleton2 getInstance() {
        if (instance == null) {
            synchronized (Singleton2.class) {
                if (instance == null) {
                    instance = new Singleton2();
                }
            }
        }
        return instance;
    }
}

选择排序

/*
        时间复杂度:最好=最坏=平均O(n2)
     */
  public static void main(String[] args) {
        // TODO 自动生成的方法存根
        //定义一个测试的排序数组
        int[] arr = {3, 9, -1, 10, -2};
        int min = 0;  //存储最小值的下标
        int temp = 0; //存储中间变量
        int j = 0;
        for (int i = 0; i < arr.length - 1; i++) {
            min = i;  //假设该下标为最小值的下标
            j = i;
            for (; j < arr.length; j++) {
                if (arr[min] > arr[j]) {  //选取最小值,得到最小值得下标
                    min = j;
                }
            }
            //进行交换
            temp = arr[i];
            arr[i] = arr[min];
            arr[min] = temp;
            System.out.printf("第%d次排序的结果是: \n", i + 1);
            System.out.println(Arrays.toString(arr));
        }

    }

快排

题目链接

package sort;

/**
 * 最佳情况:T(n) = O(nlogn)
 * 最差情况:T(n) = O(n2)
 * 平均情况:T(n) = O(nlogn)
 */
public static void quickSort(int[] arr, int low, int high) {
        int l, r, temp, t;
        if (low > high) {
            return;
        }
        l = low;
        r = high;
        //temp就是基准位
        temp = arr[low];

        while (l < r) {
            //从右边遍历找到第一个小于temp数的位置
            while (temp <= arr[r] && l < r) {
                r--;
            }
            //从左边遍历找到第一个大于temp数的位置
            while (temp >= arr[l] && l < r) {
                l++;
            }
            //如果满足条件则交换
            if (l < r) {
                t = arr[r];
                arr[r] = arr[l];
                arr[l] = t;
            }

        }
        //最后将基准为与i和j相等位置的数字交换
        arr[low] = arr[l];
        arr[l] = temp;
        //递归调用左半数组
        quickSort(arr, low, r - 1);
        //递归调用右半数组
        quickSort(arr, r + 1, high);
    }

快排实现TopK

优化为平均O(n)

什么叫手撕深度学习代码 手撕代码是啥意思_什么叫手撕深度学习代码_04

public static void topK(int[] arr, int low, int high, int k) {
        int l, r, temp, t;
        if (low > high) {
            return;
        }
        l = low;
        r = high;
        //temp就是基准位
        temp = arr[low];

        while (l < r) {
            //从右边遍历找到第一个小于temp数的位置
            while (temp <= arr[r] && l < r) {
                r--;
            }
            //从左边遍历找到第一个大于temp数的位置
            while (temp >= arr[l] && l < r) {
                l++;
            }
            //如果满足条件则交换
            if (l < r) {
                t = arr[r];
                arr[r] = arr[l];
                arr[l] = t;
            }

        }
        //最后将基准为与i和j相等位置的数字交换
        arr[low] = arr[l];
        arr[l] = temp;
        if (l == k) return;
            //递归调用左半数组
        else if (l < k) topK(arr, l + 1, high, k);
            //递归调用右半数组
        else topK(arr, low, l - 1, k);
    }

归并排序

package sort;

/**
 * 最佳情况:T(n) = O(n)
 * 最差情况:T(n) = O(nlogn)
 * 平均情况:T(n) = O(nlogn)
 */
public class MergeSort {
    public static void mergeSort(int[] arr, int l, int r) {
        //结束递归条件
        if (l >= r) return;

        int mid = (l + r) / 2;
        //归并左右两半部分
        mergeSort(arr, l, mid);
        mergeSort(arr, mid + 1, r);
        //临时数组,存放arr[l-r]部分
        int[] tmp = new int[r - l + 1];
        int i = l, j = mid + 1, k = 0;
        //归并
        while (i <= mid && j <= r) {
            if (arr[i] < arr[j])
                tmp[k++] = arr[i++];
            else
                tmp[k++] = arr[j++];
        }

        while (i <= mid) tmp[k++] = arr[i++];
        while (j <= r) tmp[k++] = arr[j++];
        //进行赋值
        for (i = l, k = 0; i <= r; i++, k++) {
            arr[i] = tmp[k];
        }
    }
}

死锁

两个线程互相请求对方的锁

ppackage threadAndLock;

public class  DeadLock {
    static Object lock1=new Object();
    static Object lock2=new Object();
    public static void main(String[] args) {
        Thread thread1=new Thread(){
            @Override
            public void run() {
                try{
                    while (true){
                        synchronized (lock1){
                            System.out.println(Thread.currentThread().getName()+"锁住了lock1");
                            Thread.sleep(1000);
                            synchronized (lock2){
                                System.out.println(Thread.currentThread().getName()+"锁住了lock2");
                            }
                        }
                    }
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        };
        Thread thread2=new Thread(){
            @Override
            public void run() {
                try{
                    while (true){
                        synchronized (lock2){
                            System.out.println(Thread.currentThread().getName()+"锁住了lock2");
                            Thread.sleep(1000);
                            synchronized (lock1){
                                System.out.println(Thread.currentThread().getName()+"锁住了lock1");
                            }
                        }
                    }
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        };
        thread1.start();
        thread2.start();
    }


}

双线程轮流打印

通过lock的signal精准唤醒,实现轮流打印

package threadAndLock;

import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

//用lock锁实现
class ThreadDemo2 {
    private int number = 1;//标志位,1代表A,2代表B,3代表C
    private Lock lock = new ReentrantLock();
    //设置三把锁
    private Condition condition1 = lock.newCondition();
    private Condition condition2 = lock.newCondition();
    private Condition condition3 = lock.newCondition();

    public void printA() {
        lock.lock();//加锁
        try {
            while (number != 1) {
                condition1.await();//对标wait()
            }
            for (int i = 0; i < 5; i++) {
                System.out.println(Thread.currentThread().getName() + "\t" + i);

            }
            number = 2;
            condition2.signal();//可以精准唤醒某个线程
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            lock.unlock();//解锁,否则会陷入死锁
        }
    }

    public void printB() {
        lock.lock();//加锁
        try {
            while (number != 2) {
                condition2.await();//对标wait()
            }
            for (int i = 0; i < 10; i++) {
                System.out.println(Thread.currentThread().getName() + "\t" + i);

            }
            number = 3;
            condition3.signal();//可以精准唤醒某个线程()
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            lock.unlock();//解锁,否则会陷入死锁
        }
    }

    public void printC() {
        lock.lock();//加锁
        try {
            while (number != 3) {
                condition3.await();//对标wait()
            }
            for (int i = 0; i < 15; i++) {
                System.out.println(Thread.currentThread().getName() + "\t" + i);
            }
            number = 1;
            condition1.signal();//可以精准唤醒某个线程()
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            lock.unlock();//解锁,否则会陷入死锁
        }
    }
}

/**
 * 三个线程按照顺序调用 A->B->C
 * A打印5次,B打印10次,C打印15次
 * 循环10次
 * 注意:
 * 1.一般多线程方法流程为:判断/干活/通知
 * 2.多线程交互中,必须要防止多线程的虚假唤醒,也即(多线程的程序中不可以用if,必须用while)
 * 3.注意标志位的修改和定位
 */
public class LockTest {
    public static void main(String[] args) {
        ThreadDemo2 threadDemo2 = new ThreadDemo2();
        new Thread("A") {
            @Override
            public void run() {
                for(int i=0;i<9;i++) {
                    threadDemo2.printA();
                }
            }
        }.start();
        new Thread("B") {
            @Override
            public void run() {
                for(int i=0;i<9;i++) {
                    threadDemo2.printB();
                }
            }
        }.start();
        new Thread("C") {
            @Override
            public void run() {
                for(int i=0;i<9;i++) {
                    threadDemo2.printC();
                }
            }
        }.start();
    }
}

自定义线程池

package threadAndLock;

import java.util.concurrent.*;

public class ThreadPool {
    public static void main(String[] args) {
        ExecutorService threadPool = Executors.newCachedThreadPool();//可扩展线程池
        //自定义线程池
        ExecutorService threadPool1 = new ThreadPoolExecutor(
                2,
                5,
                2,
                TimeUnit.SECONDS,
                new LinkedBlockingQueue<>(3),
                Executors.defaultThreadFactory(),
                new ThreadPoolExecutor.AbortPolicy());
        for (int i = 0; i < 10; i++) {
            threadPool.execute(() -> {
                System.out.println(Thread.currentThread().getName() + "办理业务");
            });
        }
    }
}

数组实现优先队列

public class PriorityQueue {
    //data[]数组尾为队列头
    //队列头为最大数
    private int[] data;
    private int size;

    PriorityQueue(int size) {
        data = new int[size];
        this.size = 0;
    }

    public void push(int insertNum) throws Exception {
        if (size == data.length)
            throw new Exception("queue is full");
        if (size == 0)
            data[0] = insertNum;
        else {
            int i = size - 1;
            for (; i >= 0; i--) {
                if (insertNum < data[i]) {
                    data[i + 1] = data[i];
                } else
                    break;
            }
            data[i + 1] = insertNum;
        }
        size++;
    }

    public int pop() throws Exception {
        if (size == 0)
            throw new Exception("queue is null");
        return data[--size];
    }

    public int peek() throws Exception {
        if (size == 0)
            throw new Exception("queue is null");
        return data[size - 1];
    }
}

LinkedHashMap实现LRU

import java.util.LinkedHashMap;
import java.util.Map;

public class LRU {
    int size;
    Map<Integer, Integer> map;

    LRU(int size) {
        map = new LinkedHashMap<>();
        this.size = size;
    }
    public int get(int num){
        if(!map.containsKey(num))
            return -1;
        //先删除旧的,再放入新的
        int value=map.get(num);
        map.remove(num);
        map.put(num,value);
        return value;
    }
    public void put(int key,int value){
        //新添加元素注意超出size
        if(!map.containsKey(key)){
            map.put(key,value);
            if(map.size()>size){
                map.remove(map.entrySet().iterator().next().getKey());
            }
        }else{
            map.remove(key);
            map.put(key,value);
        }

    }
}