目錄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);
}