目录node
生产者消费者模式 算法
wait/notify 数组
await/signal ide
blockQueue 函数
map按照value排序(比较器) post
二叉树: ui
前序遍历-递归,使用list spa
中序遍历-递归,使用list 线程
后序遍历-递归,使用list 排序
前序遍历--非递归
中序遍历--非递归
后序遍历--非递归
广度优先(层次遍历)用队列
二叉树深度
排序算法
快速排序
堆排序
归并排序
回溯算法
机器人的运动范围
滑动窗口问题
滑动窗口的最大值
动态规划
放苹果问题
汉诺塔问题
生产者-消费者模型
1.wait()/notify()
public class Storage{
public static final int MAX_COUNT = 10;
private LinkedList<Object> list = new LinkedList<Object>();
public void productor(){
synchronized(list){
while(list.size() == MAX_COUNT){
list.wait();
}
list.add(new Object());
list.notifyAll();
}
}
public void consumer(){
synchronized(list){
while(list.size()==0){
list.wait();
}
list.remove();
list.notifyAll();
}
}
}
2.await()/signal()
public class storage{
public static final int MAX_COUNT = 10;
private LinkedList<Object> list = new LinkedList<Object>();
private final Lock lock = new ReentrantLock();
private final Condition fullCon = lock.newCondition();
//此处有两个condition条件,可让生产者线程和消费者线程同时执行,相对于synchronized
private final Condition emptyCon = lock.newCondition();
public void producer(){
lock.lock();
while(list.size() == MAX_COUNT){
fullCon.await();
}
list.add(new Object());
emptyCon.signalAll();
lock.unlock();
}
public void consumer(){
lock.lock();
while(list.size() == 0){
emptyCon.await();
}
list.remove();
fullCon.signalAll();
lock.unlock();
}
}
3.blockQueue(实现原理是lock,condition的await/signal)
public class storage{
public static final int MAX_COUNT = 10;
private LinkedBlockingList<Object> list = new LinkedBlockingList<Object>(MAX_COUNT);
public void producer(){
list.put(new Object());
}
public void consumer(){
list.take();
}
}
单例模式
public class Singleton{
public Singleton(){}
public Singleton getsingleton(){
if(singleton == null){
synchronized(Singleton.class){
if(singleton == null){
singleton = new Singleton();
}
}
}
return singleton;
}
]
map按值排序(比较器)
public static Map<String, String> sortMapByValue(Map<String, String> oriMap) {
if (oriMap == null || oriMap.isEmpty()) {
return null;
}
//必定是LinkedHashMap,由于LinkedHashMap保证put顺序和输出顺序一致!
Map<String, String> sortedMap = new LinkedHashMap<>();
//map.entry把map的<key,value>当节点装进list,对list排序
List<Map.Entry<String, String>> entryList = new ArrayList<>(oriMap.entrySet());
Collections.sort(entryList, new MapValueComparator());
Iterator<Map.Entry<String, String>> iter = entryList.iterator();
Map.Entry<String, String> tmpEntry = null;
while (iter.hasNext()) {
tmpEntry = iter.next();
sortedMap.put(tmpEntry.getKey(), tmpEntry.getValue());
}
return sortedMap;
}
}
class MapKeyComparator2 implements Comparator<Map.Entry<String, String>> {
@Override
public int compare(Map.Entry<String, String> me1, Map.Entry<String, String> me2) {
return me1.getKey().compareTo(me2.getKey());
}
}
二叉树:
//二叉树的遍历分为前序后序中序的递归与非递归,和层序遍历
//先后中-递归:list(深度优先)
//先后中-非递归:stack(深度优先)
//层序遍历:queue(广度优先)
前序遍历-递归,使用list
public static void preOrder(BinaryTree node)
{
list.add(node); //先将根节点存入list
//若是左子树不为空继续往左找,在递归调用方法的时候一直会将子树的根存入list,这就作到了先遍历根节点
if(node.hasLeftNode())
{
preOrder(node.getLeftNode());
}
//不管走到哪一层,只要当前节点左子树为空,那么就能够在右子树上遍历,保证了根左右的遍历顺序
if(node.hasRightNode())
{
preOrder(node.getRightNode());
}
}
中序遍历-递归,使用list
public static void inOrder(BinaryTree node)
{
if(node.hasLeftNode())
{
preOrder(node.getLeftNode());
}
list.add(node);
if(node.hasRightNode())
{
preOrder(node.getRightNode());
}
}
后序遍历-递归,使用list
public static void postOrder(BinaryTree node)
{
if(node.hasLeftNode())
{
preOrder(node.getLeftNode());
}
if(node.hasRightNode())
{
preOrder(node.getRightNode());
}
list.add(node);
}
前序遍历--非递归
public static void preOrderNonRecursive(BinaryTree p){
Stack<BinaryTree> stack=new Stack<BinaryTree>();
while(true){
while(p!=null){
System.out.print(p.getValue()+" ");//先打印,再压栈
stack.push(p);
p=p.getLeftNode();//压栈,直到一个节点p没有左子树
}
if(stack.isEmpty())
break;
p=stack.pop(); //当前p没有左子树,出栈,
p=p.getRightNode(); //p设为当前p的右孩子
}
}
中序遍历--非递归
public static void inOrderNonRecursive(BinaryTree p){
Stack<BinaryTree> stack=new Stack<BinaryTree>();
while(true){
while(p!=null){
stack.push(p);
p=p.getLeftNode();//压栈,直到一个节点p没有左子树
}
if(stack.isEmpty())
break;
p=stack.pop(); //当前p没有左子树,出栈
System.out.print(p.getValue()+" "); //先出栈,再打印
p=p.getRightNode(); //p设为当前p的右孩子
}
}
后序遍历--非递归
public static void postOrderNonRecursive(BinaryTree p){
Stack<BinaryTree> stack = new Stack<BinaryTree>();
if(p == null)
return;
BinaryTree cur,pre = null; //pre:当前节点的以前访问的节点
stack.push(p);
while(!stack.empty()){
cur = stack.peek();
if((cur.getLeftNode() == null && cur.getRightNode() == null)
//当前节点是叶子节点,能够直接访问该节点
||
(pre != null && (cur.getLeftNode() == pre || cur.getRightNode() == pre)))
//以前一个访问的节点不为空 而且是当前节点的左孩子或者右孩子,
//(当是左孩子时说明当前节点右孩子为空,当是右孩子时,说明左右孩子都访问过了,且都不为空 )
{
BinaryTree temp = stack.pop();
System.out.print(temp.getValue()+" ");
pre = temp;
}
else{ //先压栈右节点再压栈左节点 这样出栈时是先左后右
if(cur.getRightNode() != null)
stack.push(cur.getRightNode());
if(cur.getLeftNode() != null)
stack.push(cur.getLeftNode());
}
}
}
广度优先(层次遍历)用队列
public static void print(BinaryTree root) {
Queue<BinaryTree> tree = new LinkedList<BinaryTree>();
tree.add(root);
// 当前行的最右节点
BinaryTree currlast = root;
// 下一行的最右节点
BinaryTree nextlast = null;
// 当前打印的节点
BinaryTree nownode = root;
while(!tree.isEmpty()) {
nownode = tree.poll();
// 若是当前节点有左节点,将左节点压入队列中
if(nownode.hasLeftNode()) {
tree.add(nownode.getLeftNode());
nextlast = nownode.getLeftNode();
}
// 若是当前节点有右节点,将左节点压入队列中
if(nownode.hasRightNode()) {
tree.add(nownode.getRightNode());
nextlast = nownode.getRightNode();
}
System.out.print(nownode.getValue()+" ");
// 当当前打印节点为当前行最右节点时换行
if(nownode.equals(currlast)) {
System.out.println();
currlast = nextlast;
}
}
}
二叉树深度
public int TreeDepth(TreeNode root) {
if(root == null)
return 0;
int leftlen = TreeDepth(root.left);
int rightlen = TreeDepth(root.right);
return leftlen>rightlen?leftlen+1:rightlen+1;
}
排序算法
快速排序
把整个序列看作一个数组,把第零个位置看作中轴,和最后一个比,若是比它小交换,比它大不作任何处理;交换了之后再和小的那端比,比它小不交换,比他大交换。这样循环往复,一趟排序完成,左边就是比中轴小的,右边就是比中轴大的,而后再用分治法,分别对这两个独立的数组进行排序
public static void quick(int[] numbers,int low, int high) {
if(low < high) {
int middle = getMiddle(numbers,low,high);
quick(numbers , low , middle-1);
quick(numbers , middle+1 , high);
}
}
public static int getMiddle(int[] numbers , int low, int high) {
int pre = numbers[low];
int tmp = numbers[low];
while( low < high) {
while(numbers[high] > pre)
high--;
numbers[low] = numbers[high];
while(low < high && numbers[low] < pre)
low++;
numbers[high] = numbers[low];
}
numbers[low] = tmp;
return low;
}
public static void printArr(int[] numbers){
for(int i = 0 ; i < numbers.length ; i ++ )
System.out.print(numbers[i] + ",");
System.out.println("");
}
堆排序
public static void heapSort(int[] arr) {
buildMaxHeap(arr);//构建最大堆
for(int i = arr.length-1 ; i>0 ; i--) {
exchangeElements(arr,i,0);//交换堆顶和第0位置元素
maxHeap(arr, i, 0);//由于交换元素后,有可能违反堆的性质,因此沉降元素
}
}
public static void buildMaxHeap(int[] arr) {
for(int i = arr.length/2 ; i>0 ; i--) {// 从n/2个节点开始(即最后一个节点)为根的子树,使之成为堆
maxHeap(arr, arr.length, i); //向前依次对各节点为根的子树筛选,直到根节点
}
}
private static void maxHeap(int[] arr, int heapSize, int index) {
int left = index * 2 + 1; //左子树上的元素
int right = index * 2 + 2; //右子树上的元素
int largest = index; //初始化最大元素
if (left < heapSize && arr[left] > arr[index]) {
largest = left;
}
if (right < heapSize && arr[right] > arr[largest]) {
largest = right;
}
if (index != largest) { //判断根元素是否为最大元素
exchangeElements(arr, index, largest);
maxHeap(arr, heapSize, largest);
}
}
public static void exchangeElements(int[] arr, int index1, int index2) { //交换元素
int temp = arr[index1];
arr[index1] = arr[index2];
arr[index2] = temp;
}
归并排序
public static void sort(int[] arr) {
mergeSort(arr,0,arr.length-1);
}
public static void mergeSort(int[] arr,int left,int right) {
int middle = (left+right)/2;
if(left < right) {
mergeSort(arr,left,middle);
mergeSort(arr,middle+1,right);
merge(arr,left,middle,right);
}
}
public static void merge(int[] arr,int left,int middle,int right) {
int[] tmplist = new int[right-left+1];
int lindex = left;
int rindex = middle+1;
int tmpindex = 0;
while(lindex<=middle && rindex<=right) {
if(arr[lindex]<arr[rindex]) // 把较小的数先移到新数组中
tmplist[tmpindex++] = arr[lindex++];
else
tmplist[tmpindex++] = arr[rindex++];
}
while(lindex<=middle) // 把左边剩余的数移入数组
tmplist[tmpindex++] = arr[lindex++];
while(rindex<=right) // 把右边边剩余的数移入数组
tmplist[tmpindex++] = arr[rindex++];
for(int i=0;i<tmplist.length;i++) // 把新数组中的数覆盖nums数组
arr[i+left] = tmplist[i];
}
回溯算法
机器人的运动范围
地上有一个m行和n列的方格。一个机器人从坐标0,0的格子开始移动,每一次只能向左,右,上,下四个方向移动一格,可是不能进入行坐标和列坐标的数位之和大于k的格子。 例如,当k为18时,机器人可以进入方格(35,37),由于3+5+3+7 = 18。可是,它不能进入方格(35,38),由于3+5+3+8 = 19。请问该机器人可以达到多少个格子?
核心思路:
1.从(0,0)开始走,每成功走一步标记当前位置为true,而后从当前位置往四个方向探索,
返回1 + 4 个方向的探索值之和。
2.探索时,判断当前节点是否可达的标准为:
当前节点在矩阵内;
当前节点未被访问过;
当前节点知足limit限制。
public int movingCount(int threshold, int rows, int cols)
{
boolean[][] visited = new boolean[rows][cols];
int lr = 0;
int ud = 0;
return count(threshold, rows,cols,visited,lr,ud);
}
public int count(int threshold, int rows, int cols,boolean[][] visited,int lr,int ud) {
if(lr<0||lr>=rows
||ud<0||ud>=cols
||visited[lr][ud]
||bitSum(lr)+bitSum(ud)>threshold)
return 0;
visited[lr][ud] = true;
return count(threshold, rows,cols,visited,lr,ud-1)
+count(threshold, rows,cols,visited,lr,ud+1)
+count(threshold, rows,cols,visited,lr-1,ud)
+count(threshold, rows,cols,visited,lr+1,ud)
+1;
}
public int bitSum(int t){
int count = 0;
while (t != 0){
count += t % 10;
t /= 10;
}
return count;
}
滑动窗口问题
滑动窗口的最大值
给定一个数组和滑动窗口的大小,找出全部滑动窗口里数值的最大值。例如,若是输入数组{2,3,4,2,6,2,5,1}及滑动窗口的大小3,那么一共存在6个滑动窗口,他们的最大值分别为{4,4,6,6,6,5}; 针对数组{2,3,4,2,6,2,5,1}的滑动窗口有如下6个: {[2,3,4],2,6,2,5,1}, {2,[3,4,2],6,2,5,1}, {2,3,[4,2,6],2,5,1}, {2,3,4,[2,6,2],5,1}, {2,3,4,2,[6,2,5],1}, {2,3,4,2,6,[2,5,1]}。
public static ArrayList<Integer> maxInWindows(int [] num, int size){
Queue<Integer> queue = new LinkedList<Integer>();
ArrayList<Integer> list = new ArrayList<Integer>();
if(size == 0 || size > num.length)
return list;
for(int i=0;i<size;i++)
queue.add(num[i]);
list.add(maxint(queue));
for(int z=size;z<num.length;z++) {
queue.poll();
queue.add(num[z]);
list.add(maxint(queue));
}
return list;
}
public static int maxint(Queue<Integer> queue) {
Integer res = queue.peek();
Iterator<Integer> it = queue.iterator();
while(it.hasNext()) {
int i = it.next();
if(i > res)
res = i;
}
return res;
}
动态规划
放苹果问题
把 M 个一样的苹果放在 N 个一样的盘子里,容许有的盘子空着不放,问共有多少种不一样的分法?
注意:五、一、1 和 一、五、1 是同一种分法,即顺序无关。
解题分析:
设f(m,n) 为m个苹果,n个盘子的放法数目,则先对n做讨论,
当n>m:一定有n-m个盘子永远空着,去掉它们对摆放苹果方法数目不产生影响。即if(n>m) f(m,n) = f(m,m)
当n<=m:不一样的放法能够分红两类:
一、有至少一个盘子空着,即至关于f(m,n) = f(m,n-1);
二、全部盘子都有苹果,至关于能够从每一个盘子中拿掉一个苹果,不影响不一样放法的数目,
即f(m,n) = f(m-n,n).而总的放苹果的放法数目等于二者的和,
即 f(m,n) =f(m,n-1)+f(m-n,n)
递归出口条件说明:
当n=1只有一个盘子时,全部苹果都必须放在一个盘子里,因此返回1;
当没有苹果可放时m=0,定义为1种放法;
递归的两条路,第一条n会逐渐减小,终会到达出口n==1;
第二条m会逐渐减小,由于n>m时,咱们会return f(m,m) 因此终会到达出口m==0.
public static int put(int m,int n){
//出口条件,当没有苹果可放的时候,只存在一种状况,那就是盘子全为空
//当只剩下一个盘子的时候,也只有一种状况,就是全部的果子都放在这个盘子里
if(m == 0||n==1)
return 1;
//当盘子的数量比苹果多的时候
if(n>m)
return put(m,m);
//第一种状况,至少存在一个空盘子,因此拿去那个空盘子
//第二种状况,每一个盘子里都有苹果,那么每一个盘子里拿掉一个苹果
else
return put(m,n-1) + put(m-n,n);
}
汉诺塔问题
给定一个整数n,表明汉诺塔游戏中从小到大放置的n个圆盘,假设开始时全部的圆盘都放在左边的柱子上,想按照汉诺塔游戏的要求把全部的圆盘都移到右边的柱子上,实现函数打印最优移动轨迹。
【举例】
n = 2时,打印:
move from left to mid
move from left to right
move from mid to right
【基本思路】
假设有left柱子,mid柱子和right柱子,都在left柱子的圆盘1~i彻底移动到right,最优的过程为:
递归条件出口:
若是盘子只有一个,直接从left移动到right便可
将圆盘1~i-1从left移动到mid
将圆盘i从left移动到right
将圆盘1~i-1从mid移动到right
即:hanno(n,left,mid,right)=hanno(n-1,left,right,mid)+hanno(1,left,mid,right)+hanno(n-1,mid,left,right);
public ArrayList<String> getSolution(int n) {
ArrayList<String> list = new ArrayList<String>();
hanno(list, n,"left","mid","right");
return list;
}
public static void hanno(ArrayList<String> list, int n, String left, String mid, String right){
if(n == 1) {
list.add("move from "+ left +" to " + right);
return ;
}
hanno(list, n-1,left,right,mid);
hanno(list, 1,left,mid,right);
hanno(list, n-1,mid,left,right);
}