面试中可能常见的编码问题

面试中可能常见的编码问题

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

 

posted @ 2019-08-05 14:42  动态土拨鼠  阅读(330)  评论(0)    收藏  举报