面试中可能常见的编码问题
面试中可能常见的编码问题
1.生产者消费者问题
方法一:利用sycronyzed,wait,notify
import org.junit.Test;
class Resource {
//当前资源的数量
int num = 0;
//当前资源的上限
int size = 10;
//消费资源
public synchronized void remove() {
//如果num为0,没有资源了,需要等待
while (num == 0) {//这里jdk源码里推荐用while,因为有可能出现虚假唤醒,所以要再次确认
try {
System.out.println("消费者进入等待");
this.wait();//线程等待,并释放锁
} catch (InterruptedException e) {
e.printStackTrace();
}
}
//如果线程可以执行到这里,说明资源里有资源可以消费
num--;
System.out.println("消费者线程为:" + Thread.currentThread().getName() + "--资源数量:" + num);
this.notify();//唤醒其他正在等待的线程
}
//生产资源
public synchronized void put() {
//如果资源满了,就进入阻塞状态
while (num == size) {//这里jdk源码里推荐用while,因为有可能出现虚假唤醒,所以要再次确认
try {
System.out.println("生产者进入等待");
this.wait();//线程进入阻塞状态,并释放锁
} catch (InterruptedException e) {
e.printStackTrace();
}
}
num++;
System.out.println("生产者线程为:" + Thread.currentThread().getName() + "--资源数量:" + num);
this.notify();//唤醒其他正在等待的线程
}
}
class Consumer implements Runnable {
private Resource resource;
public Consumer(Resource resource) {
this.resource = resource;
}
@Override
public void run() {
while (true){
resource.remove();
}
}
}
class Producer implements Runnable {
private Resource resource;
public Producer(Resource resource){
this.resource=resource;
}
@Override
public void run() {
while (true){
resource.put();
}
}
}
class TestConsumerAndProducer {
@Test
public static void main(String[] args) {
Resource resource = new Resource();
//生产线程
Producer p1 = new Producer(resource);
//消费线程
Consumer c1 = new Consumer(resource);
new Thread(p1).start();
new Thread(c1).start();
}
}
方法二:利用lock,condition,await,signal
import org.junit.Test;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
class Resource1 {
//当前资源的数量
private int num = 0;
//当前资源的上限
private int size = 10;
private Lock lock = new ReentrantLock();//创建锁对象
private Condition condition = lock.newCondition();//创建锁的条件,情况
//消费资源
public void remove() {
try {
lock.lock();//开启锁
//如果num为0,没有资源了,需要等待
while (num == 0) {//这里jdk源码里推荐用while,因为有可能出现虚假唤醒,所以要再次确认
try {
System.out.println("消费者进入等待");
condition.await();//线程等待,并释放锁
} catch (InterruptedException e) {
e.printStackTrace();
}
}
//如果线程可以执行到这里,说明资源里有资源可以消费
num--;
System.out.println("消费者线程为:" + Thread.currentThread().getName() + "--资源数量:" + num);
condition.signal();//唤醒其他等待的线程
} finally {
lock.unlock();//释放锁
}
}
//生产资源
public void put() {
try {
lock.lock();//开启锁
//如果资源满了,就进入阻塞状态
while (num == size) {//这里jdk源码里推荐用while,因为有可能出现虚假唤醒,所以要再次确认
try {
System.out.println("生产者进入等待");
condition.await();//线程等待,并释放锁
} catch (InterruptedException e) {
e.printStackTrace();
}
}
num++;//如果线程执行到这里,说明资源未满,可以开始生产
System.out.println("生产者线程为:" + Thread.currentThread().getName() + "--资源数量:" + num);
condition.signal();//唤醒其他等待的线程
} finally {
lock.unlock();//释放锁
}
}
}
class Consumer1 implements Runnable {
private Resource1 resource;
public Consumer1(Resource1 resource) {
this.resource = resource;
}
@Override
public void run() {
while (true){
resource.remove();
}
}
}
class Producer1 implements Runnable {
private Resource1 resource;
public Producer1(Resource1 resource) {
this.resource = resource;
}
@Override
public void run() {
while (true) {
resource.put();
}
}
}
class TestForComPro {
@Test
public static void main(String[] args) {
Resource1 resource = new Resource1();
//生产线程
Producer1 p1 = new Producer1(resource);
//消费线程
Consumer1 c1 = new Consumer1(resource);
new Thread(p1).start();
new Thread(c1).start();
}
}
友情链接:https://blog.csdn.net/u010452388/article/details/82624599
2.单例模式
1.饿汉式(立即加载):
// 饿汉式单例 public class Singleton1 { // 指向自己实例的私有静态引用,主动创建 private static Singleton1 singleton1 = new Singleton1(); // 私有的构造方法 private Singleton1(){} // 以自己实例为返回值的静态的公有方法,静态工厂方法 public static Singleton1 getSingleton1(){ return singleton1; } }
2.懒汉式(延迟加载):
// 懒汉式单例 public class Singleton2 { // 指向自己实例的私有静态引用 private static Singleton2 singleton2; // 私有的构造方法 private Singleton2(){} // 以自己实例为返回值的静态的公有方法,静态工厂方法 public static Singleton2 getSingleton2(){ // 被动创建,在真正需要使用时才去创建 if (singleton2 == null) { singleton2 = new Singleton2(); } return singleton2; } }
饿汉式与懒汉式的区别在于,饿汉式会直接创建对象,然后返回对象,而懒汉式在获取对象的方法中会做一个判断,如果对象为空,则创建对象并返回。
饿汉式是线程安全的,懒汉式需要加sycronyzed关键字等方式使其线程安全。
// 线程安全的懒汉式单例 public class Singleton2 { private static Singleton2 singleton2; private Singleton2(){} public static Singleton2 getSingleton2(){ synchronized(Singleton2.class){ // 使用 synchronized 块,临界资源的同步互斥访问 if (singleton2 == null) { singleton2 = new Singleton2(); } } return singleton2; } } // 线程安全的懒汉式单例 public class Singleton2 { private static Singleton2 singleton2; private Singleton2(){} // 使用 synchronized 修饰,临界资源的同步互斥访问 public static synchronized Singleton2 getSingleton2(){ if (singleton2 == null) { singleton2 = new Singleton2(); } return singleton2; } }
友情链接:https://blog.csdn.net/gan785160627/article/details/81946242
3.关于排序
1.冒泡排序
public static void BubbleSort(int [] arr){ int temp;//临时变量 for(int i=0; i<arr.length-1; i++){ //表示趟数,一共arr.length-1次。 for(int j=arr.length-1; j>i; j--){ if(arr[j] < arr[j-1]){ temp = arr[j]; arr[j] = arr[j-1]; arr[j-1] = temp; } } } }
优化方案:
-
针对问题:
数据的顺序排好之后,冒泡算法仍然会继续进行下一轮的比较,直到arr.length-1次,后面的比较没有意义的。 -
方案:
设置标志位flag,如果发生了交换flag设置为true;如果没有交换就设置为false。
这样当一轮比较结束后如果flag仍为false,即:这一轮没有发生交换,说明数据的顺序已经排好,没有必要继续进行下去。
public static void BubbleSort1(int [] arr){ int temp;//临时变量 boolean flag;//是否交换的标志 for(int i=0; i<arr.length-1; i++){ //表示趟数,一共arr.length-1次。 flag = false; for(int j=arr.length-1; j>i; j--){ if(arr[j] < arr[j-1]){ temp = arr[j]; arr[j] = arr[j-1]; arr[j-1] = temp; flag = true; } } if(!flag) break; } }
2.选择排序
public static void select_sort(int array[],int lenth){ for(int i=0;i<lenth-1;i++){ int minIndex = i; for(int j=i+1;j<lenth;j++){ if(array[j]<array[minIndex]){ minIndex = j; } } if(minIndex != i){ int temp = array[i]; array[i] = array[minIndex]; array[minIndex] = temp; } } }
3.插入排序
public static void insert_sort(int array[],int lenth){ int temp; for(int i=0;i<lenth-1;i++){ for(int j=i+1;j>0;j--){ if(array[j] < array[j-1]){ temp = array[j-1]; array[j-1] = array[j]; array[j] = temp; }else{ //不需要交换 break; } } } }
4.希尔排序
public static void shell_sort(int array[],int lenth){ int temp = 0; int incre = lenth; while(true){ incre = incre/2; for(int k = 0;k<incre;k++){ //根据增量分为若干子序列 for(int i=k+incre;i<lenth;i+=incre){ for(int j=i;j>k;j-=incre){ if(array[j]<array[j-incre]){ temp = array[j-incre]; array[j-incre] = array[j]; array[j] = temp; }else{ break; } } } } if(incre == 1){ break; } } }
5.快速排序
public static void quickSort(int a[],int l,int r){ if(l>=r) return; int i = l; int j = r; int key = a[l];//选择第一个数为key while(i<j){ while(i<j && a[j]>=key)//从右向左找第一个小于key的值 j--; if(i<j){ a[i] = a[j]; i++; } while(i<j && a[i]<key)//从左向右找第一个大于key的值 i++; if(i<j){ a[j] = a[i]; j--; } } //i == j a[i] = key; quickSort(a, l, i-1);//递归调用 quickSort(a, i+1, r);//递归调用 }
6.归并排序
//将有序数组a[]和b[]合并到c[]中 void MemeryArray(int a[], int n, int b[], int m, int c[]) { int i, j, k; i = j = k = 0; while (i < n && j < m) { if (a[i] < b[j]) c[k++] = a[i++]; else c[k++] = b[j++]; } while (i < n) c[k++] = a[i++]; while (j < m) c[k++] = b[j++]; }
public static void merge_sort(int a[],int first,int last,int temp[]){ if(first < last){ int middle = (first + last)/2; merge_sort(a,first,middle,temp);//左半部分排好序 merge_sort(a,middle+1,last,temp);//右半部分排好序 mergeArray(a,first,middle,last,temp); //合并左右部分 } } //合并 :将两个序列a[first-middle],a[middle+1-end]合并 public static void mergeArray(int a[],int first,int middle,int end,int temp[]){ int i = first; int m = middle; int j = middle+1; int n = end; int k = 0; while(i<=m && j<=n){ if(a[i] <= a[j]){ temp[k] = a[i]; k++; i++; }else{ temp[k] = a[j]; k++; j++; } } while(i<=m){ temp[k] = a[i]; k++; i++; } while(j<=n){ temp[k] = a[j]; k++; j++; } for(int ii=0;ii<k;ii++){ a[first + ii] = temp[ii]; } }
7.堆排序
//构建最小堆 public static void MakeMinHeap(int a[], int n){ for(int i=(n-1)/2 ; i>=0 ; i--){ MinHeapFixdown(a,i,n); } } //从i节点开始调整,n为节点总数 从0开始计算 i节点的子节点为 2*i+1, 2*i+2 public static void MinHeapFixdown(int a[],int i,int n){ int j = 2*i+1; //子节点 int temp = 0; while(j<n){ //在左右子节点中寻找最小的 if(j+1<n && a[j+1]<a[j]){ j++; } if(a[i] <= a[j]) break; //较大节点下移 temp = a[i]; a[i] = a[j]; a[j] = temp; i = j; j = 2*i+1; } } public static void MinHeap_Sort(int a[],int n){ int temp = 0; MakeMinHeap(a,n); for(int i=n-1;i>0;i--){ temp = a[0]; a[0] = a[i]; a[i] = temp; MinHeapFixdown(a,0,i); } }
8.基数排序
public static void RadixSort(int A[],int temp[],int n,int k,int r,int cnt[]){ //A:原数组 //temp:临时数组 //n:序列的数字个数 //k:最大的位数2 //r:基数10 //cnt:存储bin[i]的个数 for(int i=0 , rtok=1; i<k ; i++ ,rtok = rtok*r){ //初始化 for(int j=0;j<r;j++){ cnt[j] = 0; } //计算每个箱子的数字个数 for(int j=0;j<n;j++){ cnt[(A[j]/rtok)%r]++; } //cnt[j]的个数修改为前j个箱子一共有几个数字 for(int j=1;j<r;j++){ cnt[j] = cnt[j-1] + cnt[j]; } for(int j = n-1;j>=0;j--){ //重点理解 cnt[(A[j]/rtok)%r]--; temp[cnt[(A[j]/rtok)%r]] = A[j]; } for(int j=0;j<n;j++){ A[j] = temp[j]; } } }
友情链接:https://www.cnblogs.com/Jason-Xiang/p/8567751.html

浙公网安备 33010602011771号