手寫常見算法

目錄

生產者消費者模式 

    wait/notify 

    await/signal 

    blockQueue 

map按照value排序(比較器) 

二叉樹: 

    前序遍歷-遞歸,使用list 

    中序遍歷-遞歸,使用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;

   }

排序算法

快速排序

image.png

把整個序列看做一個數組,把第零個位置看做中軸,和最後一個比,如果比它小交換,比它大不做任何處理;交換了以後再和小的那端比,比它小不交換,比他大交換。這樣循環往復,一趟排序完成,左邊就是比中軸小的,右邊就是比中軸大的,然後再用分治法,分別對這兩個獨立的數組進行排序

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("");

}

堆排序

image.png

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;

 }

歸併排序

image.png

image.png

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 個同樣的盤子裏,允許有的盤子空着不放,問共有多少種不同的分法?

注意:5、1、1 和 1、5、1 是同一種分法,即順序無關。

解題分析:

設f(m,n) 爲m個蘋果,n個盤子的放法數目,則先對n作討論,

當n>m:必定有n-m個盤子永遠空着,去掉它們對擺放蘋果方法數目不產生影響。即if(n>m) f(m,n) = f(m,m)  

當n<=m:不同的放法可以分成兩類:

1、有至少一個盤子空着,即相當於f(m,n) = f(m,n-1);

2、所有盤子都有蘋果,相當於可以從每個盤子中拿掉一個蘋果,不影響不同放法的數目,

即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);

}

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章