面试常考简单操作
参考文章
面试常考简单操作
- 快速排序
- 归并排序
- Dijkstra
- 自定义排序
- 交替打印奇偶数
- 冒泡排序
- 插入排序
- 堆排序
- 欧几里得算法求最大公约数
- 单例模式的双重校验
- LRU
快速排序
public class Solution {
private static int partition(int[] arr, int left, int right) {
int temp = arr[left]; // 1
while(left < right) {
while(left < right && temp <= arr[right]) right--;
arr[left] = arr[right]; // 2
while(left < right && temp >= arr[left]) left++;
arr[right] = arr[left]; // 3
}
arr[left] = temp; // 4
return left;
}
private static void quicSort(int[] arr, int left, int right) {
if(left<right) {
int mid = partition(arr, left, right);
quicSort(arr,left, mid-1);
quicSort(arr,mid+1, right);
}
}
public static void main(String[] args) {
int[] arr = {3,5,1,32,8,5,3,0};
quicSort(arr, 0, arr.length-1);
Arrays.stream(arr).forEach((n)->{
System.out.print(n + " ");
});
}
}
- 时间复杂度O( n l o g n nlogn nlogn):快排使用的是递归,需要做logn次分割,而每次划分需要找对应的基准元素,需要O(n)时间。
- 空间复杂度O( l o g n logn logn):平均情况下,递归树的高度为logn。
归并排序
public class Solution {
// 主要是这里,对于两个有序数组合并,是挺简单的。
// 如何把它拆分出来是一个问题。
private static int[] mergeSort(int[] arr, int left, int right) {
if(left == right) { //长度为1,直接放回
return new int[]{arr[left]};
}
int mid = ((right - left) >> 1 ) + left;
int[] l = mergeSort(arr, left, mid);
int[] r = mergeSort(arr, mid+1, right);
return mergeTwoArray(l,r);
}
// 归并思想挺简单的,如下
private static int[] mergeTwoArray(int[] l, int[] r) {
int[] res = new int[l.length+r.length];
int left = 0, right = 0, index = 0;
while(left < l.length && right < r.length) {
if(l[left] < r[right]) {
res[index++] = l[left++];
}else {
res[index++] = r[right++];
}
}
while(left < l.length) {
res[index++] = l[left++];
}
while(right < r.length) {
res[index++] = r[right++];
}
return res;
}
public static void main(String[] args) {
int[] arr = {3,5,1,32,8,5,3,0};
int[] res = mergeSort(arr, 0, arr.length-1); // 注意这里不是原地排序的,是新开辟一个空间
Arrays.stream(res).forEach((n)->{
System.out.print(n + " ");
});
}
}
-
时间复杂度O( n l o g n nlogn nlogn):无论原始数组的顺序如何,归并排序都需要将数组不断地分成两半,然后再合并,总共需要进行logn
层的划分和合并操作,每层操作都需要遍历 O(n)个元素。 -
空间复杂度O( n n n):需要额外的辅助数组。
Dijkstra
package com.zy;
import java.util.Arrays;
public class Solution {
private static final int INF = Integer.MAX_VALUE;
public static int[] dijkstra(int[][] graph, int src) {
int n = graph.length;
int[] dist = new int[n];
boolean[] sptSet = new boolean[n];
//初始化距离数组和最短路径集合
Arrays.fill(dist, INF);
dist[src] = 0;
// 每次循环找到距离src最近的一个节点,总共循环n-1次
for (int count = 0; count < n - 1; count++) {
//找到最近节点u
int u = minDistance(dist, sptSet);
sptSet[u] = true;
// 加入u后,各个节点的变化
for (int v = 0; v < n; v++) {
// 要是还没处理,且u和v是可达的,且刚刚更新的最近节点u不是不可达的,且更新后的距离更短
if (!sptSet[v] && graph[u][v] != 0 && dist[u] != INF && dist[u] + graph[u][v] < dist[v]) {
dist[v] = dist[u] + graph[u][v];
}
}
}
return dist;
}
private static int minDistance(int[] dist, boolean[] sptSet) {
int min = INF, minIndex = -1;
for (int v = 0; v < dist.length; v++) {
if (!sptSet[v] && dist[v] <= min) {
min = dist[v];
minIndex = v;
}
}
return minIndex;
}
public static void main(String[] args) {
int[][] graph = {
{0, 4, 0, 0, 0, 0, 0, 8, 0},
{4, 0, 8, 0, 0, 0, 0, 11, 0},
{0, 8, 0, 7, 0, 4, 0, 0, 2},
{0, 0, 7, 0, 9, 14, 0, 0, 0},
{0, 0, 0, 9, 0, 10, 0, 0, 0},
{0, 0, 4, 14, 10, 0, 2, 0, 0},
{0, 0, 0, 0, 0, 2, 0, 1, 6},
{8, 11, 0, 0, 0, 0, 1, 0, 7},
{0, 0, 2, 0, 0, 0, 6, 7, 0}
};
int src = 0;
int[] dist = dijkstra(graph, src);
System.out.println("从顶点 " + src + " 到各顶点的最短距离为:");
for (int i = 0; i < dist.length; i++) {
System.out.println("到顶点 " + i + " 的最短距离是:" + dist[i]);
}
}
}
- 时间复杂度O( n 2 n^2 n2):由于使用了邻接矩阵存储图,每次查找距离源点最近的顶点需要遍历所有顶点,更新距离也需要遍历所有顶点。
- 空间复杂度O( n n n):主要使用了 dist 数组和 sptSet 数组来存储距离和标记顶点是否已处理
自定义排序
思考Comparator和Comparable的区别
实现Comparator接口实现。
package com.zy;
import java.util.Arrays;
import java.util.Comparator;
class Student implements Comparable<Student>{
int age;
int score;
public Student(int age, int score) {
this.age = age;
this.score = score;
}
@Override
public int compareTo(Student o) {
if(this.age != o.age) {
return this.age - o.age;
}
return o.score - this.score;
}
}
public class Solution {
public static void main(String[] args) {
Student[] stu = new Student[3];
stu[0] = new Student(10,13);
stu[1] = new Student(5, 4);
stu[2] = new Student(5,33);
// 法1
Arrays.sort(stu, new Comparator<Student>() {
@Override
public int compare(Student o1, Student o2) {
if(o1.age != o2.age) {
return o1.age - o2.age;
}
return o2.score - o1.score;
}
});
// 法2
Arrays.sort(stu, (o1,o2)-> {
if(o1.age != o2.age) {
return o1.age - o2.age;
}
return o2.score - o1.score;
});
// 法3(用Comparable实现默认排序)
Arrays.sort(stu);
Arrays.stream(stu).forEach(student -> {
System.out.println(student.age + " " + student.score);
});
}
}
交替打印奇偶数
package com.zy;
class AlterPrint {
private static final Object lock = new Object();
private static final int MAX = 200;
private static int count = 0;
// 打印,唤醒,看有没有终止,有就结束,没有就等待
void printNumber() {
synchronized (lock) {
while(true) {
System.out.println(Thread.currentThread().getName() + "打印" + count++);
lock.notify();
if(count>=MAX) {
lock.notifyAll(); //在彻底退出之前,最好都唤醒,不然某些线程会一直等待。
break;
}
try {
lock.wait();
}catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
}
public class Solution {
public static void main(String[] args) {
// 实现两个线程交替打印奇偶
AlterPrint alterPrint = new AlterPrint();
Thread thread0 = new Thread(alterPrint::printNumber, "线程1");
Thread thread1 = new Thread(alterPrint::printNumber, "线程2");
thread0.start();
thread1.start();
}
}
冒泡排序
public class Solution {
private static void swap(int[] arr, int i, int j) {
int temp = arr[i];
arr[i] = arr[j];
arr[j] = temp;
}
private static void bubbleSort(int[] arr) {
for(int i=0;i<arr.length;i++) {
boolean flag = true;
for(int j=arr.length-1;j>i;j--) {
if(arr[j-1] > arr[j]) {
swap(arr, j-1, j);
flag = false;
}
}
if(flag) break;
}
}
public static void main(String[] args) {
int[] arr = {3,4,6,8,3,1,0,22,10};
bubbleSort(arr);
for(int i : arr) {
System.out.println(i);
}
}
}
插入排序
public class Solution {
private static void InsertSort(int[] arr) {
for(int i=1;i<arr.length;i++) {
int key = arr[i]; // 要排序的数字
int j = i-1; // 从有序数组的尾部开始比较
while(j >= 0 && arr[j] > key) {
arr[j + 1] = arr[j];
j = j - 1;
}
arr[j + 1] = key;
}
}
public static void main(String[] args) {
int[] arr = {3,4,6,8,3,1,0,22,10};
InsertSort(arr);
for(int i : arr) {
System.out.println(i);
}
}
}
堆排序
package com.zy;
import java.util.Scanner;
public class Main {
// 调整有效大小为n的堆中的i节点
private static void adjust(int[] nums, int n, int i) {
int largest = i;
int left = 2 * i + 1;
int right = 2 * i + 2;
// 找左右节点跟节点i中最大的节点索引 : largest
if(left < n && nums[left] > nums[largest]) {
largest = left;
}
if(right < n && nums[right] > nums[largest]) {
largest = right;
}
if(largest != i) { // 如果节点i需要往下调整
// 跟最大的节点替换后
int tmp = nums[i];
nums[i] = nums[largest];
nums[largest] = tmp;
// 接着继续向下调整
adjust(nums,n,largest);
}
}
private static void heapSort(int[] arr) {
int n = arr.length;
// 调整数组,使得父节点>子节点,从最后一个非叶子节点开始
for(int i=n/2-1;i>=0;i--) { // 自己小推一下就知道了
adjust(arr, n, i);
}
// i*2-1
for(int i=n-1;i>0;i--) {
// 当前父节点移动到末尾(这里他就是排到正确位置了)
int temp = arr[0];
arr[0] = arr[i];
arr[i] = temp;
// 剩下部分继续调整
adjust(arr, i, 0);
}
}
public static void main(String[] args) {
int[] arr = {3,1,2,5,7,4};
heapSort(arr);
for(int num : arr) {
System.out.print(num + " ");
}
}
}
欧几里得算法求最大公约数
static int gcb(int a, int b) {
if(b==0) return a;
return gcb(b, a%b);
}
单例模式的双重校验
package com.zy;
public class Singleton {
private volatile static Singleton singletonInstance;
private Singleton(){}
public static Singleton getSingleton(){
if(singletonInstance == null) {
synchronized (Singleton.class) {
if(singletonInstance == null) {
singletonInstance = new Singleton();
}
}
}
return singletonInstance;
}
}
LRU