算法随笔

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

 

posted @ 2016-06-06 17:26  ooon  阅读(583)  评论(0编辑  收藏  举报