文章目录
- 排序算法复杂度
- 数组中出现次数超过一半的数字
- 最小覆盖子串
- 三数和
- 接雨水
- 链表环
- 代理模式
- 工厂模式
- 观察者模式
- 生产者消费者
- 单例模式
- 选择排序
- 快排
- 快排实现TopK
- 归并排序
- 死锁
- 双线程轮流打印
- 自定义线程池
- 数组实现优先队列
- LinkedHashMap实现LRU
排序算法复杂度
数组中出现次数超过一半的数字
摩尔投票法:每次从序列里选择两个不相同的数字删除掉(或称为“抵消”),最后剩下一个数字或几个相同的数字,就是出现次数大于总数一半的那个。
题目链接
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);
}
}
}
生产者消费者
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();
}
}
}
}
}
单例模式
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)
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);
}
}
}