手写常见算法

目录

生产者消费者模式 

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

}

猜你喜欢

转载自blog.51cto.com/13580976/2160479