手撕java算法-面试中常出现的代码

1.快速排序:
详情介绍快排https://blog.csdn.net/qq_34134299/article/details/112368477

public static void que(int array[], int left, int righ) {
    
    
		if (left > righ) {
    
    
			return;
		}
		// 定义一个基本数
		int base = array[left];
		int i = left;
		int j = righ;
		// i和j移动,
		// i向右移动找出数组元素大于基本数base,j向左边移动数组元素小于基本数base,直到相等循环停止
		while (i != j) {
    
    
			// j向左边移动,只有当元素大于等于基本数,j才左移
			while (array[j] >= base && i < j) {
    
    
				j--;
			}
			// i向右移,只有当元素小于等于基本数,i才右移
			while (array[i] <= base && i < j) {
    
    
				i++;
			}
			// 当移动停止说明找到了该元素,就交换位置
			int temp = 0;
			temp = array[i];
			array[i] = array[j];
			array[j] = temp;
		}
		// 当i和j第一次相遇就说明快速排序第一次完成
		// 就将最左边的基本数和当前ij相遇位置的元素交换

		array[left] = array[i];
		array[i] = base;
		// 到这里后base左边元素全部小于base,右边全部大于base;
		// 所有左边再次排序,递归
		for (int k : array) {
    
    
			System.out.print(k + "-");
		}
		System.out.println();
		que(array, left, i - 1);
		// 右边再次排序,递归
		que(array, i + 1, righ);
	}

2.实现链表删除元素:

public class ListNode {
    
    
	// 当前节点的值
	int val;
	// 指向的下一个节点
	ListNode next;
	// 有参构造函数,初始化就直接给val赋值
	ListNode(int x) {
    
    
		val = x;
	}
	public static void main(String[] args) {
    
    
		// 定义五个节点
		ListNode a = new ListNode(1), b = new ListNode(2), c = new ListNode(3), d = new ListNode(4),
				e = new ListNode(5);
		// 再让五个节点分别指向下一个
		// 如a->b->c->d->e
		a.next = b;
		b.next = c;
		c.next = d;
		d.next = e;
		deleteNode(d);
		System.out.println(c.next.val);
	}
	 a------------------>b-----------------> c ----------------->d
// [head][val][next]--->[head][val][next]--->[head][val][next]--->[head][val][next]
	private static void deleteNode(ListNode d) {
    
    
		// 删除: 将当前节点的下一个节点的值赋值给当前节点的值
		// 第一步:就是将e的值复制给d 相当于(d.val==e.val)
		// 也就是让c.next=5
		d.val = d.next.val;
		// 第二步:再让d直接等于e,应为d.next是e
		// 最终目的就是让d前面的节点c跳过d直接指向e即可
		d.next = d.next.next;
	}
}

3.手写简单实现ArrayLIst的源码

public class MyArrayList {
    
    
	private final static int default_capacity = 10;
	private int size = 0;
	public int[] elements;

	// 构造函数
	public MyArrayList() {
    
    
		this(default_capacity);// 常量
	}

	// 有参数构造函数
	public MyArrayList(int capacity) {
    
    
		elements = new int[capacity];
	}

	public void add(int element) {
    
    
		// this.elements[size] = element;
		// size++;
		// 默认size等于0
		add(size, element);
	}

	public void add(int index, int element) {
    
    
		if (index < 0 || index > elements.length) {
    
    
			throw new IndexOutOfBoundsException();
		}
		// 达到一定数量就需要扩容
		capacityCheck();

		// 将要插入位置的前一个全部依次向后移一位
		for (int i = size; i > index; i--) {
    
    
			elements[i] = elements[i - 1];
		}
		// 再将元素给到要插入的位置
		elements[index] = element;
		size++;
	}

	/**
	 * 数组扩容
	 */
	private void capacityCheck() {
    
    
		if (size < elements.length) {
    
    
			return;
		}
		// << >>移位符号,1<<n 左乘以2,n>>1 右除以2,,新容量是原来的1.5倍,也就是10+10/2
		int newcapacity = elements.length + elements.length >> 1;
		// 扩容就是新建一个大容量数组,
		int[] newElement = new int[newcapacity];
		// 再将原来的数组赋值给新数组
		for (int i = 0; i < size; i++) {
    
    
			newElement[i] = elements[i];
		}
		// 改变一个指向
		elements = newElement;
	}

	public int remove(int index) {
    
    
		if (index < 0 || index >= size) {
    
    
			throw new IndexOutOfBoundsException();
		}

		int old = elements[index];
		for (int i = index; i < size; i++) {
    
    
			elements[i] = elements[i + 1];
		}
		size--;
		return old;
	}

	// 修改值
	public int set(int index, int element) {
    
    
		if (index < 0 || index > elements.length) {
    
    
			throw new IndexOutOfBoundsException();
		}
		elements[index] = element;
		return 0;

	}

	public int get(int index) {
    
    
		return elements[index];
	}

	public int size() {
    
    
		return size;
	}

	public void clear() {
    
    
		size = 0;
	}

	public String toStringg() {
    
    
		StringBuffer sBuffer = new StringBuffer("size=").append(size).append("[");
		for (int i = 0; i < size; i++) {
    
    
			// 看是否是最后一个
			if (i == size - 1) {
    
    
				sBuffer.append(elements[i]);
			} else {
    
    
				sBuffer.append(elements[i]).append(",");
			}
		}
		sBuffer.append("]");
		return sBuffer.toString();

	}

}

4.判断回文字符串:(给定一个字符串,编写一个函数判定其是否为某个回文串的排列之一)

public class palindrome {
    
    
	public static void main(String[] args) {
    
    
		System.err.println(canPermutePalindrome("aabbccccc"));
		// System.err.println(canPermutePalindrome("tactcoa"));
		System.err.println(canPermutePalindrome(""));
	}

	public static boolean canPermutePalindrome(String s) {
    
    
		if (s == null || s == "")
			return false;
		// 做题思路是利用set集合的不可重复性,和包含性
		// 第一步先将字符串转换成字符数组
		char[] c1 = s.toCharArray();
		// 第二步再新建一个set集合,集合里面放char类型的数据,放String就不行。
		Set<Character> set = new HashSet<>();

		// 第三步循环便利字符数组
		for (char c : c1) {
    
    
			// 第四步 确定set集合里面是否已经添加重复字符,如果重复添加,也就是集合已经包含
			// 需要添加的字符就将该字符删除
			if (set.contains(c)) {
    
    
				set.remove(c);
			} else {
    
    
				// 否则就重新添加
				set.add(c);
			}
		}
		// 第五步 返回一个值,最后结果是set集合之剩下一个字符或者空
		// 回文字符串基本是以一个字符为中心前后对应的,只要有对应的都被删除了
		// 如回文ababcabab a和b字符重复了就会被删除,只剩下c一个字符
		// 如非回文aeabcabd a和b字符重复了就会被删除,还剩下e、c和d三个字符
		return set.size() <= 1;

	}
}

5.反转链表(注意特别重要)

public class reverseListNode {
    
    
	class Node {
    
    
		// 定义下一个节点
		private Node next;
		// 定义节点当前的数据
		private Object data;
		public Node(Object data) {
    
    
			this.data = data;
		}
		public Node(Object data, Node next) {
    
    
			this.data = data;
			this.next = next;
		}
		public Node getNext() {
    
    
			return next;
		}
		public void setNext(Node next) {
    
    
			this.next = next;
		}
		public Object getData() {
    
    
			return data;
		}
		public void setData(Object data) {
    
    
			this.data = data;
		}
	}
//链表的表现形式:
//[prenode]-[curnode]-[nextnode]------------>[prenode2]-[curnode2]-[nextnode2]------------>[prenode3]-[curnode3]-[nextnode3]
//大概意思就是将指向调转,将值也调转赋值,但不是简单的转指向还涉及内存改变
//[prenode]-[curnode]-[nextnode]<------------[prenode2]-[curnode2]-[nextnode2]<------------[prenode3]-[curnode3]-[nextnode3]
	public static Node get(Node head) {
    
    
		// 最先判断节点是否为空,空就返回该节点
		if (head == null || head.getNext() == null) {
    
    
			return head;
		}
		// 首先定义三个节点
		//前一个节点为空
		Node preNode = null;
		//将head赋值给当前节点
		Node curNode = head;
		//设置下一个节点为空
		Node nextNode = null;
		// 当当前节点不为空就循环
		while (curNode != null) {
    
    
			nextNode = curNode.getNext();
			curNode.setNext(preNode);
			// 把当前给前一个
			preNode = curNode;
			// 把下一个给到当前
			curNode = nextNode;
		}
		return preNode;
	}

5.堆排序:

public class stackSort {
    
    
	public static void main(String[] args) {
    
    
		int a[] = {
    
     1, 5, 9, 6, 3, 2, 4, 7, 8, 10 };
		// 第一步 首先构建大顶堆,也就是完全二叉树,每个节点的父节点大于左右孩子节点
		// 第二步再排序
		stacksort(a, 9);
		for (int i : a) {
    
    
			System.out.print(i + " ");
		}
	}
	// 构建大顶堆
	// -------------1
	// -----2--------------3
	// --4------5-------6-----7
	// 8--9---10--11--12-13-14--15
	//
	private static void bigStack(int[] k, int s, int n) {
    
    
		int i = 0, temp = 0;
		temp = k[s];
		for (i = 2 * s; i <= n; i *= 2) {
    
    
			if (i < n && k[i] < k[i + 1]) {
    
    
				i++;
			}
			if (temp >= k[i]) {
    
    
				break;
			}
			k[s] = k[i];
			s = i;
		}
		k[s] = temp;
	}
	/**
	 * 堆排序
	 */
	private static void stacksort(int[] k, int n) {
    
    
		// 找最下方有右孩子的父节点,依次从下往上找
		int i;
		for (i = n / 2; i > 0; i--) {
    
    
			// k数组,i表示双亲节点,总共的长度
			bigStack(k, i, n);
		}
		// 调整
		for (i = n; i > 1; i--) {
    
    
		// 交换,将每个父节点和最后一个元素交换,也就是将最大的放到最后。
			swap(k, 1, i);
		// 重新构建堆的序列
			bigStack(k, 1, i - 1);
		}
	}
	//这里是两个值互交换的方法
	private static void swap(int[] k, int a, int b) {
    
    
		int temp = 0;
		temp = k[a];
		k[a] = k[b];
		k[b] = temp;
	}

6.二分查找:

public class TwoSearch {
    
    
	public static void main(String[] args) {
    
    
		int a[] = {
    
     1, 5, 8, 9, 15, 16, 19, 25, 27, 28, 26 };
		System.out.println(binary(a, 0, 10, 25));
	}
	/**
	 * 递归二分查找,前提是数组已经排好序列,否则二分查找没用
	 */
	static int binary(int array[], int left, int righ, int target) {
    
    
		if (left > righ) {
    
    
			return -1;
		}
		int mid = left + (righ - left) / 2;
		// 如果中间元素比查找的元素大,那么砍半,从左边再查找
		if (array[mid] > target) {
    
    
			return binary(array, left, mid - 1, target);
		}
		// 如果中间元素比查找的元素小,那么砍半,从右边再查找
		if (array[mid] < target) {
    
    
			return binary(array, mid + 1, righ, target);
		}
		return mid;
	}
}

7.多线程的使用:(龟兔赛跑问题)

public class Multithreading implements Runnable {
    
    
	String winner = null;
	@Override
	public void run() {
    
    
		for (int i = 0; i <= 100; i++) {
    
    
		//这里表示现场名为兔子就让其每10步睡1毫秒
			if (Thread.currentThread().getName().equals("兔子") && i % 10 == 0) {
    
    
				try {
    
    
					Thread.sleep(1);
				} catch (InterruptedException e) {
    
    
					e.printStackTrace();
				}
			}
			//如果步数大于100则比赛结束也就是线程结束
			boolean flag = way(i);
			if (flag) {
    
    
				break;
			}
			System.out.println(Thread.currentThread().getName() + "跑了" + i + "步");
		}
	}
	public boolean way(int temp) {
    
    
	//判断谁赢了,没赢就继续比赛。线程继续运行
		if (winner != null) {
    
    
			return true;
		}
		//判断步数是否大于100,大于100就输出获胜者
		if (temp >= 100) {
    
    
		//获得当前线程的名字
			winner = Thread.currentThread().getName();
			System.out.println("winnner is" + winner);
			return true;
		}
		return false;
	}
	public static void main(String[] args) {
    
    
		Multithreading m = new Multithreading();
		//设置同一个线程的不同名字
		new Thread(m, "兔子").start();
		new Thread(m, "乌龟").start();
	}
}

8.生产者消费者模式:

 //测试:生产者消费者模型-->利用缓冲区解决:管程法
//生产者,消费者,产品,缓冲区
public class testPC {
    
    
	public static void main(String[] args) {
    
    
		// 新建一个空的容器
		SynContainer container = new SynContainer();
		// 开启生产者线程,并将其放入容器中
		new Productor(container).start();
		// 开启消费者线程,并将其放入容器中
		new Comsumer(container).start();
	}
}
//生产者
class Productor extends Thread {
    
    
	// 定义一个容器
	SynContainer container;
	// 定义一个构造方法
	public Productor(SynContainer container) {
    
    
		this.container = container;
	}
	// 生产产品的线程重写方法
	public void run() {
    
    
		// 循环生产鸡
		for (int i = 0; i < 100; i++) {
    
    
			// 将生产的每一个带有id的鸡放入到容器中container中
			// 理论是可以生产一百至鸡但是生产到10只时就会通知消费者消费
			// 因为下面定义的容器的大小是10
			container.push(new Chicken(i));
			System.out.println("生产了" + i + "只鸡");
		}
	}
}
//消费者
class Comsumer extends Thread {
    
    
	// 定义一个容器
	SynContainer container;

	// 消费者的构造方法
	public Comsumer(SynContainer container) {
    
    
		this.container = container;
	}
	// 消费产品
	public void run() {
    
    
		// 循环消费100只鸡
		for (int i = 0; i < 100; i++) {
    
    
			// 调用pop方法返回每一只被消费的鸡
			Chicken chicken = container.pop();
			// 输出每一只鸡的id,调用方法 container.pop()表示返回一个鸡对象
			System.out.println("消费了----->" + chicken.id + "只鸡");
		}
	}
}
//产品
class Chicken {
    
    
	int id;// 产品编号

	public Chicken(int id) {
    
    
		this.id = id;
	}
}
//缓冲区
class SynContainer {
    
    
	// 创建一个产品对象数组,也就是一个容器
	Chicken[] chickens = new Chicken[10];
	// 容器计数器
	int count = 0;
	// 生产者放入产品
	public synchronized void push(Chicken chicken) {
    
    
		// 如果容器满了,就需要等待消费者消费
		if (count == chickens.length) {
    
    
			// 通知消费者消费,生产等待
			try {
    
    
				this.wait();
			} catch (InterruptedException e) {
    
    
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
		}
		// 如果没有满,我们就需要丢入产品
		chickens[count] = chicken;
		// 使得计数变量增加一个
		count++;
		// 可以通知消费者消费了
		this.notifyAll();// 该方法是唤醒等待池中的所有线程,
	}
	// 消费者消费产品
	public synchronized Chicken pop() {
    
    
		// 判断能否消费
		if (count == 0) {
    
    
			// 等待生产者生产,消费者等待
			try {
    
    
				this.wait();// 该方法是让当前线程等待
			} catch (InterruptedException e) {
    
    
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
		}
		// 如果可以消费, 使得计数变量减少一个
		count--;
		// 取出来保存着
		Chicken chicken = chickens[count];
		// 吃完了, 通知生产者生产
		this.notifyAll();// 该方法是唤醒等待池中的所有线程,
		return chicken;
	}
}

9.字符串计数问题(深信服面试)

/*你的实现应该支持如下操作: WordsFrequency(book)构造函数,参数为字符串数组构成的一本书 get(word)查询指定单词在书中出现的频率
 */
public class WordsFrequency {
    
    
	// 新建一个字符串数组
	static String[] wordsF = {
    
     "i", "have", "an", "apple", "he", "have", "a", "pen" };
	// 构造函数初始化字符串
	static WordsFrequency w = new WordsFrequency(wordsF);
	// 新建一个map集合,map集合
	Map<String, Integer> map = new HashMap<>();
	public static void main(String[] args) {
    
    
		System.out.println(w.get("have"));
	}
	public WordsFrequency(String[] book) {
    
    
		// 循环遍历字符串数组
		for (String string : book) {
    
    
			// 利用map集合的键值不能重复性
			// 查看map集合是否包含传入单词
			if (!map.containsKey(string)) {
    
    
				// 判断map的键值是否包含,不包含value设置成1;
				map.put(string, 1);
			} else {
    
    
				// 包含value值加一,
				map.put(string, map.get(string) + 1);
			}
		}
	}
	public int get(String word) {
    
    
		/*这里主要是利用map.getOrDefault(word, 0);方法代替了对map进行的遍历否则时间会限制,超出时间限制
		 */
		// getOrDefault()方法意思就是当Map集合中有这个key时,就使用这个key对应的value值,如果没有就使用默认值defaultValue
		return map.getOrDefault(word, 0);
	}
}

10.工厂模式:

//简单工厂模式
class SimpleFactory2{
    
    
public static Car getCar(String type){
    
    
if("BMW".equals(type)){
    
    
     return new BMW();
  }else if("BZ".equals(type)){
    
    
     return new Benz();
    }
     return null;
  }
}
//示例:工厂方法模式
interface Factory{
    
    
VehiCle getVehiCle ();
}
class BMWFactory implements Factory{
    
    
@Override
public Che getChe() {
    
    
return new BaoMa();
   }
}
class BZFactory implements Factory{
    
    
@Override
public Che getChe() {
    
    
return new BZ();
  }
}
//示例:使用反射,结合工厂方法模式与简单工厂模式
class SimpleFactory{
    
    
public static Vehicle getVehicle(String className)throws Exception{
    
    
Class clazz = Class.forName(className);
Object obj = clazz.newInstance();
if(obj instance of Vehicle){
    
    
    return (Vehicle) obj;
    }
    return null;
   }
}

11.单例模式:

//懒汉式
public class Singleton {
    
    
    private static Singleton instance = null;
    private Singleton() {
    
     }
    public static Synchronized Singleton getInstance() {
    
    
          if (instance == null) {
    
    
                instance = new Singleton();
          }
        return instance;
    }
}
//饿汉式
public class Singleton {
    
    
    private static Singleton instance = null;
    private Singleton() {
    
     }
    public static Singleton getInstance() {
    
    
          if (instance == null) {
    
    
                instance = new Singleton();
          }
        return instance;
    }
}
//单例模式加强版-----双重校验锁
//手写双重校验单例,为什么这么写。
	private static volatile Singleton uniqueSingle=null;
	    private Singleton() {
    
    
	    }
	    public static Singleton getUinqueSingle(){
    
    
	        if (uniqueSingle==null){
    
    //不判断uniqueSingle每次都会竞争锁大大提高性能
            synchronized (Singleton.class){
    
    //和懒汉模式没啥区别
	                if (uniqueSingle==null){
    
    //再次判断是因为两个线程同时进行空值判断成功后都有可能创建对象,容易导致重复创建多个对象。
	                    uniqueSingle=new Singleton();
	                }
	            }
	        }
	        return uniqueSingle;
	    }

猜你喜欢

转载自blog.csdn.net/qq_34134299/article/details/115261342