常用算法魔板

常用算法魔板

快速排序

	/**
     * 快排模板
     * @param q 要排序的数组
     * @param l 数组第一个元素下标:0
     * @param r 数组最后一个元素下标:num - 1
     */
    public static void quickSort(int[] q, int l, int r) {
        //判边界:如果数组为空或就一个元素就直接返回
        if (l >= r) {
            return;
        }
        //确定分界点:从数组中取出一个数:这里取中间值
        int x = q[l + r >> 1];
        //定义两个指针分别指向边界的左右两侧:以调整区间
        int i = l - 1;
        int j = r + 1;
        //比较指针下标对应的值
        while (i < j) {
            //先移动指针:再和分界点比较
            do i++;
                while (q[i] < x);
            do j--;
                while (q[j] > x);
            //交换下标对应的值
            if (i < j) {
                int temp = q[i];
                q[i] = q[j];
                q[j] = temp;
            }
        }
        //递归处理左右两段
        quickSort(q, l, j);
        quickSort(q, j + 1, r);
    }

归并排序

	/**
     * 归并排序模板
     * @param arr 要排序的数组
     * @param l 数组第一个元素下标:0
     * @param r 数组最后一个元素下标:length - 1
     */
    public static void mergeSort(int[] arr, int l, int r) {
        //判边界:如果数组为空或就一个元素就直接返回
        if (l >= r) {
            return;
        }
        //确定分界点:取数组中间值的下标
        int mid = l + r >> 1;
        //递归排序左边和右边
        mergeSort(arr, l, mid);
        mergeSort(arr, mid + 1, r);
        //定义两个指针分别指向两个数组的最左侧:以方便合并
        int i = l, j = mid + 1;
        //作为临时数组的下标计数
        int k = 0;
        //临时数组, 用于临时存储[l,r]区间内排好序的数据
        int[] temp = new int[r - l + 1];
        //通过指针比较两数组的值:进行归并
        while (i <= mid && j <= r) {
            if (arr[i] <= arr[j])
                temp[k++] = arr[i++];
            else
                temp[k++] = arr[j++];
        }
        //当一个数组比较完后:将另一个数组剩下的元素添加到临时数组后
        while (i <= mid)
            temp[k++] = arr[i++];
        while (j <= r)
            temp[k++] = arr[j++];
        //将归并后的数据赋值原数组
        for (i = l, j = 0; i <= r; i++, j++)
            arr[i] = temp[j];
    }

二分查找

	/**
     * 使用二分代码模板:非递归方式二分:找到数组中指定数字的起始位置和终止位置(找全部)
     * @param arr 要查找的数组
     * @param l 数组第一个元素下标:0
     * @param r 数组最后一个元素下标:length - 1
     * @param k 要查找的值
     * @return
     */
    private static void search(int[] arr, int l, int r, int k){
        //二分模板查找:查找左边界
        while(l < r){
            int mid = l + r >> 1;
            if(arr[mid] >= k){
                r = mid;
            }else {
                l = mid + 1;
            }
        }
        //跳出循环时, l == r, 判断数组中是否存在指定数字
        if(arr[l] != k){
            //数组中不存在指定数字
            System.out.println("-1 -1");
        }else {
            System.out.print(l + " ");
            //存在指定数字:初始化 l 和 r, 查找右边界
            l = 0;
            r = arr.length - 1;
            while(l < r){
                int mid = 1 + l + r >> 1;
                if(arr[mid] <= k){
                    l = mid;
                }else {
                    r = mid - 1;
                }
            }
            System.out.println(l);
        }
    }

高精度加减乘除

	/**
     * 高精度加法运算
     * 需要先将数放到集合中(按照个位放第一个的顺序)
     * @param A 第一个数的集合
     * @param B 第二个数的集合
     * @return 返回个位数在第一个的集合(反转即是结果)
     */
    public static List<Integer> add(List<Integer> A, List<Integer> B){
        //判断哪个数的位数大:按位数大的处理
        if (A.size() < B.size())
            return add(B, A);
        //结果集
        List<Integer> C = new ArrayList<>();
        //进位
        int t = 0;
        //加法运算:按位数较多的数处理
        for (int i = 0; i < A.size(); i ++)
        {
            t += A.get(i);
            if (i < B.size())
                t += B.get(i);
            C.add(t % 10);
            t /= 10;
        }
        if (t > 0)
            C.add(t);
        return C;
    }


    /**
     * 高精度减法运算
     * 需要先将数放到集合中(按照个位放第一个的顺序)
     * 注意:要保证传递进来的第一个数大于第二个数(如果第一个数小:就算第二减第一加负号)
     * 减负数 = 加正数
     * @param A 第一个数的集合
     * @param B 第二个数的集合
     * @return 返回个位数在第一个的集合(反转即是结果)
     */
    public static List<Integer> sub(List<Integer> A, List<Integer> B){
        //结果集
        List<Integer> C = new ArrayList<>();
        //减法运算:按位数较多的数处理
        for (int i = 0, t = 0; i < A.size(); i ++ )
        {
            t = A.get(i) - t;
            if (i < B.size())
                t -= B.get(i);
            C.add((t + 10) % 10);
            if (t < 0)
                t = 1;
            else
                t = 0;
        }
        while (C.size() > 1 && C.get(C.size()-1)==0)
            C.remove(C.size()-1);
        return C;
    }

    /**
     * 高精度乘低精度
     * 需要先将高精度数放到集合中(按照个位放第一个的顺序)
     * @param A 高精度数的集合
     * @param b 低精度数
     * @return 返回个位数在第一个的集合(反转即是结果)
     */
    public static List<Integer> mul(List<Integer> A, int b){
        List<Integer> C = new ArrayList<Integer>();
        int t = 0;
        for (int i = 0; i < A.size() || t > 0; i ++ )
        {
            if (i < A.size())
                t += A.get(i) * b;
            C.add(t % 10);
            t /= 10;
        }
        while (C.size() > 1 && C.get(C.size() - 1) == 0)
            C.remove(C.size() - 1);
        return C;
    }

    /**
     * 高精度除以低精度
     * 需要先将高精度数放到集合中(按照个位放第一个的顺序)
     * @param A 高精度数的集合
     * @param b 低精度数
     * @param r 余数
     * @return 返回个位数在第一个的集合(反转即是结果)
     */
    public static List<Integer> div(List<Integer> A, int b,int r){
        List<Integer> C = new ArrayList<Integer>();
        r = 0;
        for (int i = A.size() - 1; i >= 0; i -- )
        {
            r = r * 10 + A.get(i);
            C.add(r / b);
            r %= b;
        }
        //反转
        Collections.reverse(C);
        //去前导0
        while (C.size() > 1 && C.get(C.size()-1) == 0)
            C.remove(C.size()-1);
        return C;
    }

位运算

	/**
     * 求n的第k位数字: n >> k & 1    k:最右边第一位是0
     * @param n 整数
     * @param k k:最右边第一位是0
     * @return n转换为二进制后的第k位数字(k从右边开始计数)
     */
    public int findK(int n,int k){
        return n >> k & 1;
    }

    /**
     * 返回n(二进制)的最后一位 1
     * @param n 整数
     * @return 返回n的二进制数最后为1及后面的数(包括1)
     * java中结果又自动转化为十进制数
     */
    public static int lowBit(int n){
        return n & -n;
    }

区间合并

	/**
     * 可以得到合并后的区间结果集
     * @param list :ArrayList中存储的是数组:每个数组中存储2个数:每个区间的左端点[0]和右端点[1]
     * @return 返回合并后的区间个数即:集合大小
     */
    public static int merge(ArrayList<int[]> list){
        //结果集合
        ArrayList<int[]> res = new ArrayList<>();
        //先对集合中存储的数组按照区间的左端点排序
        list.sort(new Comparator<int[]>() {
            @Override
            public int compare(int[] o1, int[] o2) {
                return o1[0] - o2[0];
            }
        });
        //根据左端点区间判断:给定一个默认区间
        int l = Integer.MIN_VALUE;
        int r = Integer.MIN_VALUE;
        //遍历集合
        for(int[] itme : list){
            //如果新区间的左端点 > 上一个区间的右端点
            if(itme[0] > r){
                if(l != Integer.MIN_VALUE){
                    //找到了新区间,就将老区间添加(不直接加新区间,因为新区间端点还没确定)
                    res.add(new int []{l,r});
                }
                l = itme[0];
                r = itme[1];
            }else {
                //新区间的左端点 <= 上一个区间的右端点:合并:更新右端点
                r = Math.max(r,itme[1]);    //取新区间的右端点和老区间的右端点较大的那个
            }
        }
        //最后一个合并区间,判断条件防止list为空
        if (l != Integer.MIN_VALUE){
            res.add(new int[]{l,r});
        }

        for(int[] temp : res){
            for(int i = 0;i < temp.length;i++){
                System.out.println(temp[i] + " ");
            }
        }
        return res.size();
    }

集合中元素去重复

	/**
     * 集合中元素去重复
     * @param list
     * @return 从左开始没有重复元素的下标
     */
    public static int unique(List<Integer> list) {
        //先排序
        Collections.sort(list);
        int j = 0;
        for (int i = 0; i < list.size(); i++) {
            if (i == 0 || list.get(i) != list.get(i - 1)) {
                list.set(j, list.get(i));
                j++;
            }
        }
        return j;
        //1 2 2 3 3 3 4 5 6 6       去重复前的集合
        //1 2 3 4 5 6     res = 6   去重之后无重复元素的个数
        //1 2 3 4 5 6 4 5 6 6       去重复后的集合
    }

KMP字符串匹配

	public static void main(String[] args) throws IOException {
        int n = Integer.parseInt(reader.readLine());    //子串长度
        String P = reader.readLine();                   //子串
        char[] p = new char[n + 1];                     //存储子串
        //注意下标从1开始
        for (int i = 1; i <= n; i++)
            p[i] = P.charAt(i - 1);

        int m = Integer.parseInt(reader.readLine());    //字符串长度
        String S = reader.readLine();                   //字符串
        char[] s = new char[m + 1];                     //存储字符串
        //注意下标从1开始
        for (int i = 1; i <= m; i++)
            s[i] = S.charAt(i - 1);

        // 构造前缀数组:即next[]数组                     //只需要字串
        int prefix[] = new int[n + 1];
        for (int i = 2, j = 0; i <= n; i++) {
            //i从2开始,因为prefix[1]肯定为0
            while (j != 0 && p[i] != p[j + 1])
                j = prefix[j];
            if (p[i] == p[j + 1])
                j++;
            prefix[i] = j;
        }
        // kmp匹配
        for (int i = 1, j = 0; i <= m; i++) {
            while (j != 0 && s[i] != p[j + 1]) {
                j = prefix[j];      // s[i] != p[j + 1]即不匹配,则往后移动
            }
            if (s[i] == p[j + 1])
                j++;                // 匹配时将j++进行下一个字符得匹配
            if (j == n) {           // 匹配了n字符了即代表完全匹配了
                //输出的是所有出现位置的起始下标(下标从0开始计数),整数之间用空格隔开。
                System.out.print(i - n +" ");
                j = prefix[j];      // 完全匹配后继续搜索
            }
        }
    }

字符串哈希:适用于不同区间的字符串比较

	/**
     * 字符串哈希:就是把一个字符串映射成一个数字
     * 求到一个字符串中任意两个区间的子串是否相同
     * 转换为求两个区间子串的哈希值是否相等
     * 求[L,R]字符串的哈希值
     * @param l : 左边界
     * @param r :右边界
     * @return :[L,R]字符串的哈希值
     */
	public static void main(String[] args) throws IOException {
        BufferedReader read = new BufferedReader(new InputStreamReader(System.in));
        String[] s = read.readLine().split(" ");
        //字符串长度
        int n = Integer.valueOf(s[0]);
        int t = Integer.valueOf(s[1]);  //询问次数
        //给定的字符串
        String w = read.readLine();
        //P为权重:131为经验值:即P=131或13331时:哈希冲突的可能性最小
        int P = 131;
        //由于前缀值的值很大:所以应该将数组中的数据定义为long型
        long[] h = new long[n + 10];        //h[]存放字符串的前缀值
        long[] p = new long[n + 10];        //p[]存放各个位数的相应权值
        p[0] = 1;                           //最开始的权值必须赋值为1
        //计算每个位上的相应权值
        for(int i = 1; i <= n; i++){
            p[i] = p[i - 1] * P;                    //计算每个位上的相应权值
            //最新加入的数的权值为p的0次:所以直接加上str[i]即可
            h[i] = h[i - 1] * P + w.charAt(i - 1);  //计算字符串前缀值
        }

        while(t-- > 0){
            s = read.readLine().split(" ");
            int l1 = Integer.valueOf(s[0]);  int r1 = Integer.valueOf(s[1]);
            int l2 = Integer.valueOf(s[2]);  int r2 = Integer.valueOf(s[3]);
            String out = h[r1] - h[l1 - 1] * p[r1 - l1 + 1] == h[r2] - h[l2 - 1] * p[r2 - l2 + 1] ? "Yes" : "No";
            System.out.println(out);
        }
   	 }

dfs求一个数的全排列

    static int n;           //需要搜索的个数(给定的数字)
    static int[] path;      //path[]用于保存路径
    static boolean[] st;    //用于记录:该步是否已经走过

    public static void dfs(int u){
        if(u==n){                                   //一条路搜索完成
            for(int i=0;i<n;i++){
                System.out.print(path[i]+" ");       //输出结果
            }
            System.out.println();
            return;
        }

        //控制位置:一次循环代表一个位置
        for(int i=1;i<=n;i++){      //未搜素完成则继续尝试 即一条走到底为止
            if(!st[i]) {            //判断是否走过
                path[u] = i;        //标记当前步防止重复
                st[i] = true;       //保存当前路径
                dfs(u + 1);      	//进行下一步搜索
                st[i] = false;      //恢复现场 否则会影响下一条路的搜索
            }
        }
    }

    public static void main(String[] args) {
        Scanner sc = new Scanner(System.in);
        n = sc.nextInt();           //3
        path = new int[n];          //3
        st = new boolean[n+1];      //4
        dfs(0);     //从第0个位置开始搜索
    }
end

评论