zoukankan      html  css  js  c++  java
  • 算法随笔

    1.输入接受一个数字 n ,求一个队列,该队列由数字[1...n]组成, 且满足不断将队头元素移到队尾,然后输出队头,如此循环,最后的输出是有序的。

    思路:用有序的队列存放下标 [0...n-1] ,用下标进行移到队尾,出队的操作,出队的值对应的是 [1....n] ,这就构建了一个下标到数字的映射 map,存放到一个长度为 n 的数组即可。

      public static void getQueue(int n){
            int[] map = new int[n];
            LinkedList<Integer> queue = new LinkedList<Integer>();
            for(int i = 0 ; i < n; i ++) queue.add(i);
            int pos = 1;
            while(!queue.isEmpty()){
                queue.addLast(queue.removeFirst());
                map[queue.removeFirst()] = pos ++;
            }
            for(int i = 0 ; i < map.length ; i ++) {System.out.print(map[i] +" ");}
            System.out.println();
    }
    View Code

     2. LRU cache leetcode

    思路:hashmap + 双链表 ,但是 TLE 了==!

        private Map<Integer,Integer> map;
        private int capacity; // 容量
        
        private ListNode fake ;
        private ListNode tail ;
        
        public LRUCache(int capacity) {
            fake = new ListNode(-1); //虚拟头结点
            tail = fake; // 尾部节点,便于删除
            map = new HashMap<Integer,Integer>(capacity);
            this.capacity = capacity;
        }
        //将 key 对应的节点,移到最前边
        public void rm2front(int key){
            ListNode cur = fake.next;
            //找到 key 对应的节点
            while(cur.val != key) cur = cur.next;
            if(cur.prev == fake) return; //已经是第一个节点
            
            //取出当前节点
            if(cur.next != null){ 
                cur.next.prev = cur.prev;
                cur.prev.next = cur.next;
            }else{ // cur 为当前最后一个节点
                tail = cur.prev; //先改变尾节点的位置
                cur.prev.next = null;
            }
            //将该节点插到fake之后
            insert2head(cur);
        }
        public void insert2head(ListNode cur){
            //插入到  fake 之后
            // 1) fake 之后为空
            if(fake.next == null){
                fake.next = cur;
                cur.prev = fake;
                tail = cur;
                return;
            }
            // 2)正常插入
            cur.next = fake.next;
            cur.prev = fake.next.prev;
            fake.next.prev = cur;
            fake.next = cur; 
        }
        public int get(int key) {
            //包含该 key,代表使用了当前 key,将其放到最前边 
            if(map.containsKey(key)){ 
                rm2front(key);
                return map.get(key);
            } 
            return -1;
        }
        
        public void set(int key, int val) {
            if(map.containsKey(key)){
                map.put(key, val);
                rm2front(key);
            }else{
                map.put(key,val);
                insert2head(new ListNode(key));
                if(map.size() > capacity){ //超过容量,尾指针前移
                    
                    map.remove(tail.val); // 先移除map里的元素
                    
                    tail = tail.prev; 
                    tail.next.prev = null;
                    tail.next = null;
                }
            }
        }
    
    }
    class ListNode{
        ListNode prev;
        ListNode next;
        int val;
        ListNode (int val){
            this.val = val;
        }
    View Code

     3. 生成长度为 n 的格雷码

      思路: 后面的格雷码等于其相邻的前面的格雷码按顺序书写,加前缀0,再按逆序书写,加前缀1。

    //生成长度为 n 的格雷码
        public static String[] graycode(int n){
            if(n == 1){
                String[] str = {"0", "1"}; 
                return str;
            }
            // 长度  n 的格雷码有 2^n 个
            String[] code = new String[(int)Math.pow(2,n)];
            int len = code.length ;
            
            String[] last = graycode(n-1);
            
            for(int i = 0 ; i < last.length ; i ++){
                code[i] = "0" + last[i];
                code[len-1-i] = "1" + last[i];
            }
            return code;
        }
    View Code

    4. 生成 4 7 幸运数字

    4和7是两个幸运数字,我们定义,十进制表示中,每一位只有4和7两个数的正整数都是幸运数字,前几个幸运数字为:4,7,44,47,74,77,444,447······

      输入: 第一行一个数字T(T <= 1000)表示测试数据的组数,对于每组测试数据,输出一个数K(1 <= K <= 10(18)  10的18次幂)

      注意:不同于格雷码,这个没有位置对称这一说

      分析:幸运数字的集合4,7,44,47,74
           可以看做为0,1,00,01,10,但是这样不能转成对应的十进制表示,我们在转换后的每个二进制数前面加一个1,
           变成10,11,100,101,110,这样转换成十进制之后就为2,3,4,5,6,
           这就相当于原来的幸运数字顺序从1,2,3,4,5变成了2,3,4,5,6
      思路:当求第N个幸运数字时,例如 N=100,我们先让 N+1=101,
           对应的二进制数为1100101,然后去掉二进制的第一个数1,
           则变成了100101,然后将0和1分别用4和7替换,则得到了最终的幸运数字。

    public static void main(String[] args){
            System.out.println(getKth(1000000000));
        }
        
        public static String getKth(long K){
            //先 + 1 得到其2进制
            String binstr = Long.toBinaryString(++ K);
            // 替换二进制中的 0 1为 4 7
            binstr = binstr.replaceAll("0", "4");
            binstr = binstr.replaceAll("1", "7");
            // 去掉最高位并返回
            return binstr.substring(1);
            
        }
    View Code

     5. 打印对角线矩阵

      有一个二维数组(n*n),写程序实现从右上角到左下角沿主对角线方向打印。

      给定一个二位数组arr及题目中的参数n,请返回结果数组。

      测试样例:
      [[1,2,3,4],[5,6,7,8],[9,10,11,12],[13,14,15,16]],4
      返回:[4,3,8,2,7,12,1,6,11,16,5,10,15,9,14,13]
    //     *       | 1  2  3  4 |               4         
    //     *       | 5  6  7  8 |     ----->    3 8 
    //     *       | 9 10  11 12|               2 7 12  
    //     *       |13 14  15 16|               1 6 11 16 
    //     *                                    5 10 15 
    //     *                                    9 14  
    //     *                                    13  
          public int[] arrayPrint(int[][] arr, int n) {
              int[] ret = new int[n * n];
              int pos = 0, bound = 1;
               // 打印上三角与主对角线
              for(int col = n-1 ; col >= 0 ; col -- ){
                  int tmp_col = col;
                  for(int row = 0 ; row < bound ; row ++){
                      ret[pos ++] = arr[row][tmp_col ++];
                  }
                  if( col !=0 ) ++ bound;
              }
              
              -- bound ;
              //打印下三角
              for(int row = 1 ; row < n ; row ++){
                  int tmp_row = row;
                  for(int col = 0 ; col < bound ; col ++ ){
                      ret[pos ++] = arr[tmp_row ++][col];
                  }
                  -- bound;
              }
              return ret;
            }
    View Code

     6. 给一个串,打印 huffman 树编码的长度

      思路:这个其实很简单,每次通过 PriorityQueue 找出两个最小元素,然后求和,路径长 + 2 即可,一直进行下去

      

    //用 优先
        public static int huffman(String str){
            char[] chs = str.toCharArray();
            int[] nums = new int[256];
            for(int i = 0 ; i < chs.length ; i ++){
                ++ nums[chs[i]];
            }
            PriorityQueue<Integer> pq = new PriorityQueue<>(); 
            int ret = 0;
            for(int n : nums){
                if(n != 0) pq.add(n);
            }
            while (pq.size() > 1){
                int x = pq.poll();
                int y = pq.poll();
                pq.add(x + y);
                ret += (x + y);
            }
            return ret;
        }
    View Code

    5. 从一个长度为 n 的数组里找出 m 个随机数

      从长度为n的非重复数组里随机选择 m个数
      首先在下标 [0...n] 之间随机生成一个下标,将这个下标对应的数和数组的第一个数交换位置;
      然后从小标 [1...n] 之间随机生成一个数,将下标对应的数和数组的第二个数交换位置。
      依次这样下去,知道找出m个随机数。

    public int[] getNumbers(int[] nums,int n, int m){
            
            Random r = new Random(); 
            
            int[] ret = new int[m];
            int pos = 0;
            
            if(m > n) System.exit(0); 
            if(m == n) return nums;
            
            for(int i = 0 ; i < m ; i++){
                
                //随机产生 [i...n] 的随机数
                int idx = i + r.nextInt( n - i);
                ret[pos ++] = nums[idx];
                
                //和元素 0 交换位置,
                int temp = nums[i];
                nums[i] = nums[idx];
                nums[idx] = temp;
            }
            System.out.println(Arrays.toString(ret));
            return ret;
        }
    View Code

     6. 去掉字符串中的多于空格,首先去掉前后的空格,然后将内部的多个空格变为一个

    //将字符串中的多个空格 替换为 1个空格。且去掉前后的空格
        public String removeSpace(String str)
        {
            char[] seq = str.toCharArray();
            int lo = 0, hi = 0;
            while( hi < seq.length)
            {
                while(hi < seq.length && seq[hi] == ' ') ++ hi;
                while(hi < seq.length && seq[hi] != ' ') 
                    seq[lo ++] = seq[hi ++];
                while(hi < seq.length && seq[hi] == ' ') ++ hi;
                if(hi < seq.length) seq[lo ++] = ' ';
            }
            
            return new String(seq, 0, lo);
        }
    View Code

     7. 给定一个数组,该数组进栈,打印该数组所有合法的出栈序列

    import java.util.*;
    
    public class Main 
    {
        static List<Integer> list = new ArrayList<>(); //该数据集为待打印的序列
        static Stack<Integer> stk = new Stack<>(); // 用一个栈来模拟这个过程
        public static void main(String args[]) 
        {
            Scanner sc = new Scanner(System.in) ;
            Main m = new Main();
            list.addAll(Arrays.asList(1,2,3));
            
            int n = list.size();
            
            List<Integer> opera = new ArrayList<>();// 存入一系列 1 0 ,1代表进栈 0 代表出栈
            
            m.allSeq(opera, n, n, n);
            
        }
        public void allSeq(List<Integer> opera, int push, int pop ,int size)
        {
            if(push >= 1) //入栈
            {
                opera.add(1);
                -- push;
                allSeq(opera, push, pop, size);
                ++ push;
                opera.remove(opera.size()-1);
            }
            if(pop >= 1 && pop > push) // 出栈
            {
                opera.add(0);
                -- pop;
                allSeq(opera, push, pop, size);
                ++ pop;
                opera.remove(opera.size()-1);
            }
            if(opera.size() == size * 2) 
            {
                int pos = 0;
                for(int n : opera) // 打印合法序列
                {
                    if(n == 1) stk.push(list.get(pos ++));
                    else System.out.print (stk.pop() +" ");
                }
                System.out.println();
            }
        }
    }
    View Code

     8.荷兰国旗问题

    荷兰国旗问题,红蓝白是分散的,现在要求写代码将其聚合到一起,要求时间复杂度为 O(n) ,空间为 O(1)

    void sortColor(int[] color)
        {
            int i = 0, pRed = 0, pWhi = color.length-1;
            while (i <= pWhi) 
            {
                if (color[i] == red)  // 红色
                {
                    int temp = color[pRed];
                    color[pRed ++] = color[i];
                    color[i ++] = temp;
                }
                else if (color[i] == blue) // 蓝色
                {
                    ++ i;               // 蓝色不管,红白归位蓝色自然在其位置上
                }
                else if (color[i] == white)// 白色
                {
                    int temp = color[pWhi];
                    color[pWhi--] = color[i];
                    color[i] = temp;
                }
            }
        }
    View Code

     9. 素数筛

    执行过程,很神奇的算法, 从素数 i * i 到 i*j <n 均设定为 非素, 知道 i*i >n 开始统计剩下的变为素数了 

    //素数筛算法
        public int countPrimes(int n) 
        {
            if( n <= 2) return 0; // 序列为 [0...,n-1]
            boolean notPrime[] = new boolean[n];
            int ret = 0;
            for(int i = 2 ; i < n;  ++ i)
            {
                if(!notPrime[i]) // 如果当前数是素数
                {
                    ++ ret; // 这里统计素数
                    if(i > Math.sqrt(n)) continue;
                    for(int j = i * i; j < n; j += i) 
                        notPrime[j] = true; // 对非素数打上 true 的标记
                }
            }
            return ret;
        }
    View Code

     10. Queue Reconstruction by Height (leetcode 406)

    重新对二维数组进行排序
    Input: [[7,0], [4,4], [7,1], [5,0], [6,1], [5,2]] Output: [[5,0], [7,0], [5,2], [6,1], [4,4], [7,1]]
    思路就是先按照 h 降序排列,h 相同则按照 k 升序排列,然后按照 k 重新插入记了

      输入:[[7,0], [4,4], [7,1], [5,0], [6,1], [5,2]]

      first :[[7,0],[7,1]] , k = 0, k = 1

      next:[[7,0],[6,1],[7,1]] k = 1

      next:[[5,0][7,0][5,2],[6,1],[7,1]]  k = 0,k = 2

      end: [[5,0][7,0][5,2],[6,1],[4,4],[7,1]] k = 4

     // (h,k) 代表高为 h ,且左边有 k 个高度 >= h 的
        public int[][] reconstructQueue(int[][] people) 
        {
            // 二维数组排序,先按第一个值降序,若第一个值相同,则按第二个值升序
            Arrays.sort(people, new Comparator<int[]>(){
                @Override
                public int compare(int[] o1, int[] o2) 
                {
                    return o1[0] != o2[0] ? 
                            o2[0]- o1[0] : o1[1] - o2[1];
                }   
            });
            List<int[]> ret = new LinkedList<>();
            for(int [] p : people)
            {
                ret.add(p[1], p); //将 p 插入到 位置 p[1]
            }
            return ret.toArray(new int[0][0]);
        }
    View Code

     10. 最长递增子序列

    开一个栈,每次取栈顶元素top和读到的元素temp做比较,如果temp > top 则将temp入栈;如果temp < top则二分查找栈中的比temp大的第1个数,并用temp替换它。 最长序列长度即为栈的大小top。

    这也是很好理解的,对于x和y,如果x < y且Stack[y] < Stack[x],用Stack[x]替换Stack[y],此时的最长序列长度没有改变但序列Q的''潜力''增大了。

    举例:原序列为1,5,8,3,6,7

    栈为1,5,8,此时读到3,用3替换5,得到1,3,8; 再读6,用6替换8,得到1,3,6;再读7,得到最终栈为1,3,6,7。最长递增子序列为长度4。

        //  找到 nums 中的最长上升子序列
        public void LIS(int nums[])
        {
            
            Stack<Integer> stack = new Stack<>();
            
            for(int i = 0; i < nums.length; ++ i)
            {
                if(stack.isEmpty()||nums[i] > stack.peek())
                {
                    stack.push(nums[i]);
                }
                else // 用 nums[i] 替换第一个比 nums[i]大的数 ,
                {
                    int lo  = 0, hi = stack.size();
                    
                    while(lo <= hi)
                    {
                        int mid = lo + (hi -lo)/2;
                        if(nums[i] > stack.get(mid))
                        {
                            lo = mid + 1;
                        }
                        else
                        {
                            hi = mid - 1;
                        }
                    }
                    stack.set(lo, nums[i]); //替换之
                }
            }
            System.out.println(stack);
        }
    View Code

     11.构造 AliasTable 的方法

    public void AliasMethod(double p[], int alias[])
        {
            // 构造 p[i] * k 的数组 
            double pk[] = new double[p.length]; 
            
            Queue<Integer> small = new LinkedList<>();
            Queue<Integer> large = new LinkedList<>();
            
            for(int i = 0; i < p.length; ++ i)
            {
                pk[i] = p[i] * p.length;
                if(pk[i] < 1d)
                    small.offer(i);
                else
                    large.offer(i);
            }
            while(!small.isEmpty() && !large.isEmpty())
            {
                int less = small.poll(); // 找到不够 1 的
                int more = large.poll(); // 找到多于 1 的 
                p[less] = pk[less];
                alias[less] = more;
                pk[more] -= (1 - pk[less]);
                if(pk[more] < 1)
                    small.offer(more);
                else 
                    large.offer(more);
            }
            
            while(!small.isEmpty()) p[small.poll()] = 1;
            
            while(!large.isEmpty()) p[large.poll()] = 1;
        }
        
        
        public int Sampling(double[] p, int[] alias)
        {
            Random rand = new Random();
            
            int i = rand.nextInt(p.length);
            
            return rand.nextDouble() < p[i] ? i : alias[i];
        }
        public static void main(String[] args) 
        {
            double p[] = {0.1, 0.2, 0.3, 0.4};
            int alias[] = new int[p.length];
            
            Main m = new Main();
            m.AliasMethod(p, alias);
            Map<Integer,Integer> map = new HashMap<>();
            for(int i = 0 ; i < 1000; ++ i)
            {
                int s = m.Sampling(p, alias);
                if(map.containsKey(s))
                    map.put(s, map.get(s) + 1);
                else 
                    map.put(s, 1);
            }
            System.out.println(map);
        }
    View Code

    12. 迪杰特拉斯  DFS  BFS Prime

    DIJK

    package graph;
    
    import java.util.Arrays;
    
    public class Dijkstra {
        static int INF = Integer.MAX_VALUE;
        /** 
         * 求解点 v0 到图中所有其他节点的最短路径
         * @param graph 邻接表
         * @param n     节点数目
         * @param v0            求 v0 到 
         */
        public static void dijk(int[][] graph, int n, int v0)
        {
            int[] dist  = new int [n]; // v0到所有节点的最短路径
            int[] path  = new int [n]; // 最短路径的距离
            
            boolean[] visit = new boolean[n]; // 判断节点已经被访问 
            
            //初始化节点
            path[v0] = -1; dist[v0] = 0; visit[v0] = true; 
            
            for(int i = 1 ; i < n ; i ++) // 初始化 dist 数组
            {
                dist [i] = graph[v0][i]; 
                path [i] = dist[i] == INF ? -1 : v0;
            }
            
            for(int i = 0 ; i < n ; i ++) 
            {
                int min = INF, k = v0;
                // 从dist 数组中找到一个没被访问的 dist 最小的节点
                for(int j = 0 ; j < n ; j ++) 
                {
                    if(!visit[j] && dist[j] < min)
                    {
                        min = dist[j];
                        k = j;
                    }
                }
                visit[k] = true; // 标记为访问
                
                // 以当前节点 k 为中转节点,看未访问节点的 dist 能否更短
                for(int j = 0 ; j < n ; j ++)
                {
                    if(!visit[j] && graph[k][j] < INF)
                    {
                        if(dist[k] + graph[k][j] < dist[j])
                        {
                            dist[j] =dist[k] + graph[k][j];
                            path[j] = k;
                        }
                    }
                }
            }
            
            System.out.println(Arrays.toString(path));
        }
        
        public static void main(String[] args) {
            int U = INF; // 有向图
            int graph[][] = { {U, 4, 6, 6, U, U, U},
                              {U, U, 1, U, 7, U, U},
                              {U, U, U, U, 6, 4, U},
                              {U, U, 2, U, U, 5, U},
                              {U, U, U, U, U, U, 6},
                              {U, U, U, U, 1, U, 8},
                              {U, U, U, U, U, U, U} };
            dijk(graph, graph.length , 0);
        }
    }
    View Code

    DFS BFS

    package graph;
    
    import java.util.Arrays;
    import java.util.LinkedList;
    import java.util.Queue;
    
    // 图的遍历
    
    public class Graph_DFS_BFS {
        
        static int INF;
        
        //     邻接表,和标记数组
        public static void DFSTraverse(int[][] graph, int n) {
            boolean[] visit = new boolean[n];
            for (int i = 0; i < n; i++)
            {
                if (visit[i] == false)    // 当前顶点没有被访问 
                {    
                    DFS(graph, i, visit, n);
                }
            }
        }
    
        // 图的深度优先递归算法
        static void DFS(int[][] graph, int i, boolean[] visit, int n) {
            visit[i] = true; // 第i个顶点被访问
            System.out.print(i + " ");
            for (int j = 0; j < n; j++) 
            {
                if (visit[j] == false && graph[i][j] == 1) 
                {
                    DFS(graph, j, visit, n);
                }
            }
        }
    
        // 图的广度遍历操作
        public static void BFSTraverse(int[][] graph, int n) 
        {
            boolean[] visit = new boolean[n];
            Queue<Integer> queue = new LinkedList<Integer>();
            for (int i = 0; i < n; i++) 
            {
                if (visit[i] == true) 
                    continue;
                visit[i] = true;
                System.out.print(i + " ");
                queue.add(i);
                while (!queue.isEmpty()) 
                {
                    int j = queue.poll();
                    for (int k = 0; k < n; k++) 
                    {
                        if (graph[j][k] == 1 && visit[k] == false) 
                        {
                            visit[k] = true;
                            System.out.print(k + " ");
                            queue.add(k);
                        }
                    }
                }
            }
        }
    
        // 测试
        public static void main(String[] args) {
            
            int U = INF;
            int[][] graph = { { U, 1, U, U, U, 1, 1, U, U },
                              { 1, U, 1, U, U, U, 1, U, 1 }, 
                              { U, 1, U, 1, U, U, U, U, 1 },
                              { U, U, 1, U, 1, U, 1, 1, 1 }, 
                              { U, U, U, 1, U, 1, U, 1, U },
                              { 1, U, U, U, 1, U, 1, U, U }, 
                              { U, 1, U, 1, U, 1, U, 1, U },
                              { U, U, U, 1, 1, U, 1, U, U }, 
                              { U, 1, 1, 1, U, U, U, U, U } };
    
            System.out.println("图的深度遍历操作(递归):");
            DFSTraverse(graph, graph.length);
            System.out.println("
    -------------");
            System.out.println("图的广度遍历操作:");
            BFSTraverse(graph, graph.length);
            System.out.println();
        }
    }
    View Code

    Prime

    package graph;
    
    
    public class Prime {
        static int INF = Integer.MAX_VALUE;
        /**
         * @param graph 邻接矩阵
         * @param n     节点数
         * @param v0            从 v0 开始产生 最小生成树
         */
        public static void prim(int[][] graph, int n , int v0)
        {
            int sum = 0; // 记录总的开销
            
            int[] cost  = new int[n]; //保存当前生成树到没访问节点的权值
            int[] adj   = new int[n]; // 保存相关顶点的下标
            // cost 的值为0,在这里就是此下标的顶点已经加入生成树    
            cost[0] = 0; adj [0] = 0; 
            
            for(int i = 1 ; i < n ; i ++)
            {
                //跳过没有边连接的点
                cost[i] = graph[v0][i];
                adj[i] = v0;  // 初始化为 v0 的下标
            } 
            for(int i = 1 ; i < n ; i ++)
            {
                int min  = INF,  k = v0;
                for(int j = 1 ; j < n ; ++ j)
                { 
                    if(cost[j] != 0 && cost[j] < min)
                    {
                        min = cost[j];
                        k = j;
                    }
                }
                // 这里权值 可以根据 adj 数组去求,
                if(cost[k] != 0) sum += min; // 更新总权值
                
                System.out.println("arc :" +adj[k]+ "->" + k + ", w : " + min);
                
                cost[k] = 0; // 标记此节点以完成任务
                
                //找出下一次迭代的候选边
                for(int j = 1 ; j < n ; ++ j) 
                {
                    if(cost[j] != 0 && graph[k][j] < cost[j])
                    {
                        cost[j] = graph[k][j];
                        adj [j] = k;
                    }
                }
            }
            System.out.println("total cost : " + sum);
        }
        
        
        public static void main(String[] args) {
            //带权图,不能的都设置为 INF
            int U = INF;
            int[][] graph = { { U, 6, 1, 5, U, U },
                              { 6, U, 5, U, 3, U },
                              { 1, 5, U, 5, 6, 4 },
                              { 5, U, 5, U, U, 2 }, 
                              { U, 3, 6, U, U, 6 },
                              { U, U, 4, 2, 6, U },};
            prim(graph, graph.length, 0);
        }
    }
    View Code
  • 相关阅读:
    Git
    Shell-sed之替换字符
    Linux IO/NFS tunning 性能优化及检测
    利用Java Flight Recorder(JFR)诊断timing及内存问题
    Get/Post
    SQL-1
    HTTP协议简要
    nmap简单使用
    (C语言)买东西找零钱
    今日错误(C语言)(定义二维数组储存)
  • 原文地址:https://www.cnblogs.com/ooon/p/5564547.html
Copyright © 2011-2022 走看看