算法学习笔记----前缀树、堆维持中位数、n皇后位运算加速

目录

前缀树

贪心(对数器、全排列模板)

堆维持中位数(随时添加数,动态取值随时可以获取中位数)

对数器测试

抽取为类

n皇后

经典做法

位运算加速


前缀树

前缀树: 树节点有属性pass表示多少单词经过了当前字母,end表示有多少单词以当前字母结尾。 属性nexts可以用数组表示,也可以用哈希表,若想有序可以用有序表。表示当前字母下的分叉字母 前缀树添加:沿路所有字母节点pass++,如果没有节点就创建 前缀树删除:沿路所有字母节点pass--,如果减为0就把指向这个节点的指针置空

package com.wtp.前缀树;
​
public class TrieNode {
​
    public int pass;
    public int end;
    public TrieNode[] nexts;
    
    //public HashMap<Char,Node> nexts;
    
    public TrieNode() {
        pass = 0;
        end = 0;
        nexts = new TrieNode[26];
    }
}
​
package com.wtp.前缀树;
​
public class Trie {
​
    private TrieNode root;
    
    public Trie() {
        root = new TrieNode();
    }
    
    public void insert(String word) {
        
        if(word == null) {
            return;
        }
        
        TrieNode cur = root;
        cur.pass++;
        char[] chs = word.toCharArray();
        for(int i = 0;i < chs.length;i++) {
            int index = chs[i] - 'a';
            if(cur.nexts[index] == null) {
                cur.nexts[index] = new TrieNode();
            }
            cur = cur.nexts[index];
            cur.pass++;
        }
        cur.end++;
    }
    
    //word这个单词之前加入过几次   
    public int search(String word) {
        if(word == null) {
            return 0;
        }
        
        TrieNode cur = root;
        char[] chs = word.toCharArray();
        for(int i = 0;i < chs.length;i++) {
            int index = chs[i] - 'a';
            if(cur.nexts[index] == null) {
                return 0;
            }
            cur = cur.nexts[index];
        }
        
        return cur.end;
    }
    
    //有多少个单词以pre作为前缀
    public int prefixNumber(String pre) {
        if(pre == null) {
            return 0;
        }
        
        TrieNode cur = root;
        char[] chs = pre.toCharArray();
        for(int i = 0;i < chs.length;i++) {
            int index = chs[i] - 'a';
            if(cur.nexts[index] == null) {
                return 0;
            }
            cur = cur.nexts[index];
        }
        return cur.pass;
    }
    
    public void del(String word) {
        if(search(word) != 0) {
            TrieNode cur = root;
            cur.pass--;
            char[] chs = word.toCharArray();
            for(int i = 0;i < chs.length;i++) {
                int index = chs[i] - 'a';
                if(--cur.nexts[index].pass == 0) {
                    cur.nexts[index] = null;
                    return;
                }
                cur = cur.nexts[index];
            }
            cur.end--;
        }
    }
}
​
贪心(对数器、全排列模板)
package Mains.day26.全排列;
​
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashSet;
import java.util.LinkedList;
import java.util.Set;
import java.util.TreeSet;
import java.util.List;
 
public class test {
 
    static TreeSet<String> res = new TreeSet<String>();
    public static void main(String[] args) {
        int[] arr = {1,2,3,3};
 
        //dfs(arr, new LinkedList<Integer>());
        dfs2(arr, 0);
        System.out.println(res);
    }
 
    
    public static void dfs(int[] arr,LinkedList<Integer> r) {
        
        if(r.size() == arr.length) {
            String string = "";
            for(int index : r) {
                string += arr[index];//可以用StringBuffer拼接
            }
            res.add(string);
            return;
        }
        LinkedList<Integer> temp = new LinkedList<Integer>(r);
        for(int i = 0;i < arr.length;i++) {
            if(!temp.contains(i)) {
                temp.add(i);
                dfs(arr, temp);
                temp.remove();
            }
        }
    }
    
    public static void dfs2(int[] arr,int index) {
        
        if(index == arr.length) {
            String string = "";
            for(int num : arr) {
                string += num;
            }
            res.add(string);
            return;
        }
        
        for(int i = index;i < arr.length;i++) {
            swap(arr, index, i);
            dfs2(arr, index + 1);
            swap(arr, index, i);
        }
    }
    
    public static void swap(int[] arr,int i,int j) {
        int temp = arr[i];
        arr[i] = arr[j];
        arr[j] = temp;
    }
 
}

堆维持中位数(随时添加数,动态取值随时可以获取中位数)

准备一个大根堆一个小根堆,准备记录堆中元素个数的count。第一个数先添加进大根堆,后续数比较如果小于等于大根堆的堆顶就进大根堆否则进小根堆。在每一次加入一个数后判断大根堆和小根堆的元素个数是否相差大于等于2,将多的那个弹出堆顶元素加入到另一个堆中。无论添加多少个数,始终可以随时在两个堆的堆顶中找到答案。

原理:大根堆的堆顶小于小根堆的堆顶(大根堆的最大值小于小根堆的最小值),如果两个堆中的元素个数相同,相当于大根堆的堆顶压住了加入的所有的数有序时的前一半,小根堆的堆顶压住了加入的所有的数有序时的后一半。所以无论加入多少个数最终答案都可以根据两个堆的堆顶求出答案。

对数器测试
package com.wtp.基础.排序.堆排序;
​
import java.util.Arrays;
import java.util.PriorityQueue;
import java.util.Scanner;
​
import com.wtp.基础.排序.快速排序.法1;
import com.wtp.基础.排序.快速排序.法2;
​
public class 维持中位数 {
    
    public static void main(String[] args) {
        int[] arr = {1,6,2,3,4,4,5};
//      System.out.println(process1(arr));
        
        test();
    }
    
    public static void test() {
        int maxLength = 1000;//数组最大长度
        int maxCount = 500000; //测试500000次
        
        for(int i = 0;i < maxCount;i++) {
            
            int length = (int)(Math.random() * maxLength) + 1;
            int[] arr = new int[length];
            full(arr);
            int[] copyArr = copyArr(arr);
            
            double res1 = process1(arr);
            double res2 = process2(copyArr);
            
            if(res1 != res2) {
                System.out.println("error! " + "process1:" + res1 + "  process2:" + res2);
            }
            
        }
        System.out.println("success!");
    }
    
    public static boolean check(int[] arr1,int[] arr2) {
        for(int i = 0;i < arr1.length;i++) {
            if(arr1[i] != arr2[i]) {
                return false;
            }
        }
        return true;
    }
    
    public static int[] copyArr(int[] arr) {
        int[] copy = new int[arr.length];
        for(int i = 0;i < arr.length;i++) {
            copy[i] = arr[i];
        }
        return copy;
    }
    
    public static void full(int[] arr) {
        for(int i = 0;i < arr.length;i++) {
            arr[i] = (int)(Math.random() * arr.length) + (int)(Math.random() * arr.length);
        }
    }
    //傻瓜方法 数组排好序后取中位数
    public static double process2(int[] arr) {
        Arrays.sort(arr);
        return arr.length % 2 == 1 ? arr[arr.length / 2] : (arr[arr.length/2] + arr[arr.length/2 - 1]) * 1.0 / 2;
    }
​
    //堆维持中位数 传入的数组为固定元素,只测试方法是否正确
    public static double process1(int[] arr) {
        PriorityQueue<Integer> min = new PriorityQueue<Integer>((o1,o2)->o1-o2);
        PriorityQueue<Integer> max = new PriorityQueue<Integer>((o1,o2)->o2-o1);
        int count = 0;
        
        max.add(arr[0]);
        count++;
        int num;
        for(int i = 1;i < arr.length;i++) {
            num = arr[i];
            if(num <= max.peek()) {
                max.add(num);
                count++;
            }else {
                min.add(num);
                count--;
            }
            
            if(Math.abs(count) == 2) {
                //小于0时min中的元素多两个
                PriorityQueue<Integer> p1 = count < 0 ? min : max;
                PriorityQueue<Integer> p2 = p1 == min ? max : min;
                
                p2.add(p1.poll());
                count = 0;
            }
        }
        
        return count == 0 ? ((min.poll() + max.poll()) * 1.0 / 2) : (count < 0 ? min.poll() : max.poll());
    }
}
​

抽取为类

调用者可以随时加入数,随时获取中位数

public static class Median{
        
        private PriorityQueue<Integer> min;
        private PriorityQueue<Integer> max;
        private int count;
        
        public Median() {
            min = new PriorityQueue<Integer>((o1,o2)->o1-o2);
            max = new PriorityQueue<Integer>((o1,o2)->o2-o1);
            count = 0;
        }
        
        public void add(int num) {
            
            if(max.isEmpty()) {
                max.add(num);
                count++;
                return;
            }
            //保证大根堆的最大值大于小根堆的最小值 这样小根堆和大根堆的堆顶就为这些数的中间数
            if(num <= max.peek()) {
                max.add(num);
                count++;
            }else {
                min.add(num);
                count--;
            }
            
            if(Math.abs(count) == 2) {
                //小于0时min中的元素多两个
                PriorityQueue<Integer> p1 = count < 0 ? min : max;
                PriorityQueue<Integer> p2 = p1 == min ? max : min;
        
                p2.add(p1.poll());
                count = 0;
            }
        }
        
        public double getMedian() {
            return count == 0 ? ((min.poll() + max.poll()) * 1.0 / 2) : (count < 0 ? min.poll() : max.poll());
        }
    }
n皇后
经典做法
package com.wtp.n皇后;
​
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
​
public class 经典做法 {
​
    public static void main(String[] args) {
        
        process(new int[max],0);
        for(String str : res) {
            System.out.println(str);
        }
        System.out.println(count);
    }
    
    static int max = 8;
    static int count = 0;
    static List<String> res = new ArrayList<>();
    public static void process(int[] arr,int n) {
        
        if(n == max) {
            res.add(Arrays.toString(arr));
            count++;
            return;
        }
        //第n个皇后放在i列
        for(int i = 0;i < max;i++) {
            arr[n] = i;
            if(check(arr,n)) {
                process(arr,n + 1);
            }
        }
        
    }
    
    //判断能否放皇后
    public static boolean check(int[] arr,int j) {
        
        for(int i = 0;i < j;i++) {
            if(arr[i] == arr[j] || Math.abs(arr[j] - arr[i]) == j - i) {
                return false;
            }
        }
        return true;
    }
}
​

位运算加速
package com.wtp.n皇后;
​
public class 位运算加速 {
​
    
    public static void main(String[] args) {
        int max = 15;
        
        long start = System.currentTimeMillis();
        System.out.println(process(new int[max],0,max));
        long end = System.currentTimeMillis();
        System.out.println("常规用时:" + (end - start) + "毫秒");
        
        start = System.currentTimeMillis();
        System.out.println(process2(max));
        end = System.currentTimeMillis();
        System.out.println("位运算加速用时:" + (end - start) + "毫秒");
    }
    
    public static int process2(int n) {
        if(n < 1 || n > 32) {
            return 0;
        }
        return process2(n == 32 ? -1 : (1 << n) - 1,0,0,0);
    }
    
    public static int process2(int limit,int colLim,int leftDiaLim,int rightDiaLim) {
        
        if(limit == colLim) {
            return 1;
        }
        //limit为1不能放皇后的位置 变为1可以放皇后的位置
        int pos = limit & (~(colLim | leftDiaLim | rightDiaLim));
        int mostRightOne = 0;
        
        int res = 0;
        while(pos != 0) {
            mostRightOne = pos & (~pos + 1);
            pos = pos - mostRightOne;
            res += process2(limit,colLim | mostRightOne,
                    (leftDiaLim | mostRightOne) << 1,
                    (rightDiaLim | mostRightOne) >>> 1);
        }
        
        return res;
    }
    
    public static int process(int[] arr,int n,int max) {
        
        if(n == max) {
            return 1;
        }
        int res = 0;
        
        //有i到max列
        for(int i = 0;i < max;i++) {
            //把第n个皇后放到i列
            arr[n] = i;
            if(check(arr,n)) {//检查通过放下一个皇后
                res += process(arr,n+1,max);
            }
        }
        return res;
    }
    
    //检查放完第n个皇后后是否有冲突
    public static boolean check(int[] arr,int n) {
        for(int i = 0;i < n;i++) {
            if(arr[i] == arr[n] || Math.abs(arr[n] - arr[i]) == n - i) {
                return false;
            }
        }
        return true;
    }
}
​
  • 53
    点赞
  • 25
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值