package code_02_chapter; import java.util.Arrays; import java.util.Comparator; /* *Created by William on 2018/6/15 0015 */ public class Question2InChapter2 { /** * Q1: * 考虑查找给定输入单词的所有变位词的问题。仅给定单词和字典的情况下,如何解决该问题? * 如果有一些时间和空间可以响应任何查询之前预处理字典,又会如何处理? * 不能预处理时,为给点单词做标记,如mississippi的标记为(i4m1p2s4)标记内字母按字典序排列, * 遍历字典,每查询一个单词,同样为其作出标记,做完后比较标记。还有种方式是在做标记的时候就进行判断, * 如果查询单词中存在字母数量大于目标单词,break,如果存在字母字典序小于目标单词的第一个字母,break。 * 如果可以预处理,建立森林,根结点依次为字母。 */ /** * Q2: * 给定包含4 300 000 000个32位整数的顺序文件,如何找出一个至少出现两次的整数。 * 给定的32位整数的个数是4 300 000 000大于2^32-1, 如果其中没有任何一个缺失的32整数, * 那么重复整数个数就是(4300000000-2^32+1)个。 * 方法1:如果有足够的内存,使用位图技术就更方便了! * 方法2:使用几个中间文件,按每次中间值的大小作为临界值分割整数, * 统计每个文件中的应该有的整数个数和实际的整数个数,最终可以确定重复的整数应该在哪个文件中! * 对该文件排序,使用二分搜索! */ public static class Q2 { //先行搜索 public static void resolve1() { int[] arr = {2, 2, 3, 4, 5, 7, 11, 12, 13, 14, 15}; int increase = arr[0]; for (int iCount = 0; iCount < arr.length; iCount++) { if (arr[iCount] > iCount + increase) { increase += (arr[iCount] - iCount - increase); continue; } if (arr[iCount] < iCount + increase) { System.out.println("重复的数字是:" + arr[iCount]); break; } } } public static void main(String[] args) { resolve1(); } } /** * Q3: * 前面涉及了两个需要精巧代码来实现的向量旋转算法。将其分别作为独立的程序实现。 * 在每个程序中,i和n的最大公约数如何出现? */ public static class Q3 { /** * 题目:将一个n元一维数组a[n]左移i个位置。例如,当n=8,i=3时,数组abcdefgh旋转为defghabc。 * 请设计一个算法完成这个任务。 */ /** * 1. 块交换法 * 分析:将n元一维数组a[n]分解为两块,将第一块存储在临时数组中, * 将第二块前移i个单位,再将临时数组加入到第二块后面。 * 如:n=8,i=3时,将第一块abc存储为临时变量,第二块defgh前移3个单位,再将abc放入到defgh后面。 * 思考:这种方法最大的缺陷至少需要与两块中较小的一块大小的临时变量。 */ /** * 2.杂技算法 * 分析:将a[0]存储在一个临时变量中,然后将a[i]替换a[0],a[2i]替换a[i]….当一个循环结束的时候, * 若替换次数小于n,则从a[1]开始替换…,需要经过gcd(n,i)(n和i的最大公约数)次循环后, * 才能把每一个元素都移到该移的地方。 */ //求最大公约数,辗转相除法 public static int gcd(int a, int b) { while (a != 0) { if (a >= b) a -= b; else { int t = a; a = b; b = t; } } return b; } //杂技算法 public static String rotate1(String a, int i) { int length = a.length(); int gcdNum = gcd(i, length); char[] chars = a.toCharArray(); for (int j = 0; j < gcdNum; j++) { char temp = chars[j]; int first = j; while (true) { int second = (first + i) % length; if (second == j) break; chars[first] = chars[second]; first = second; } chars[first] = temp; } return String.valueOf(chars); } public static void main(String[] args) { System.out.println(rotate1("abcdefg", 3)); } /** * 求逆算法 * 分析:将a[n]看做是向量BC组成,最终的结果需要时CB,过程如下:将BC各分别取逆B^-1C^-1, * 再对整个式子取逆,得到CB。 */ public static class Reverse { public static String revert(String str, int start, int end) { char[] chars = str.toCharArray(); while (start < end) { char temp = chars[start]; chars[start] = chars[end]; chars[end] = temp; start++; end--; } return String.valueOf(chars); } public static void main(String[] args) { String str = "abcdefgh"; String a = revert(str, 0, 2); String b = revert(a, 3, 7); String result = revert(b, 0, 7); System.out.println(result); } } } /** * Q5: * 将向量abc转为cba * 对abc取a^ b^ c^ 再取(a^ b^ c^ )^ ,^ 表示对向量的转置。 */ /** * Q6: * 题目太长,不想看。。。 */ /** * Q7: * 在20世纪60年代早期,Vic Vyssotsky与一个程序员一起工作,该程序员需要转置一个存储在磁带上的4000*4000的矩阵 * (每条记录的格式相同,为数十个字节).他的同事最初提出的程序需要运行50个小时. * Vyssotsky如何将运行时间减少到半小时呢? * 思想:为每条记录插入行号和列号,先按 列 排序,然后删除行号和列号 */ public static class Q7 { public static class Data { public Data() { init(); } public void init() { row = 0; col = 0; element = 0; } int row; int col; int element; } static class ComByCol implements Comparator<Data> { public int compare(Data o1, Data o2) { return o1.col - o2.col; } } public static int[][] generateMatrix(int rows, int cols) { int[][] matrix = new int[rows][cols]; for (int i = 0; i < rows; i++) { for (int j = 0; j < cols; j++) { matrix[i][j] = i * rows + j + 1; } } return matrix; } public static Data[] matrixToDataArray(int[][] matrix) { int row = matrix.length; int cols = matrix[0].length; Data[] data = new Data[row * cols]; int count = 0; for (int i = 0; i < row; i++) { for (int j = 0; j < cols; j++) { data[count] = new Data(); data[count].row = i; data[count].col = j; data[count].element = matrix[i][j]; count++; } } return data; } public static void printMatrix(int[][] matrix) { for (int i = 0; i < matrix.length; i++) { for (int j = 0; j < matrix[i].length; j++) { System.out.print(matrix[i][j] + " "); } System.out.println(); } } public static void printDataArray(Data[] datas) { System.out.println("-------------------- data array -----------------"); int width = (int) Math.sqrt(datas.length); for (int i = 0; i < datas.length; i++) { if (datas[i].element < 10) { System.out.print(" " + datas[i].element + ":[" + datas[i].row + "," + datas[i].col + "] "); } else { System.out.print(datas[i].element + ":[" + datas[i].row + "," + datas[i].col + "] "); } if ((i + 1) % width == 0) { System.out.println(); } } System.out.println(); } //矩阵过大的话不能直接这么干 public static int[][] revertMatrix(int[][] matrix) { int[][] newMatrix = new int[matrix.length][matrix[0].length]; for (int i = 0; i < matrix.length; i++) { for (int j = 0; j < matrix[0].length; j++) { newMatrix[i][j] = matrix[j][i]; } } return newMatrix; } public static void main(String[] args) { int[][] matrix = generateMatrix(6, 6); printMatrix(matrix); Data[] data = matrixToDataArray(matrix); printDataArray(data); int[][] matrixReverted = revertMatrix(matrix); //算法1,最笨的算法,也浪费空间,矩阵过大的话不能直接这么干 Data[] matrixRevertedDatas = matrixToDataArray(matrixReverted); printDataArray(matrixRevertedDatas); System.out.println("---------------分割线-----------------"); Arrays.sort(data, new ComByCol()); //算法2,优化后的算法,按列排序 printDataArray(data); } } /** * Q8: * 给定一个n元实数集合、一个实数t和一个整数k,如何快速确定是否存在一个k元子集,其元素之和不超过t? * 将n个元素从小到大排序,计算头k个数和,如果包含k个最小元素的子集的和不超过t时, * 那么就肯定存在和不超过t的k元子集存在。 * * 法1:利用快速排序,对元素进行排序,排序时间复杂度是nlogn,然后取前k个, * 所以在正比于nlogn时间内确定是不是有这个子集。 * 法2:不用全部排序。比如原集合存放在数组A[0,...,n-1]中,定义辅助数组B[0,...,k-1], * 先将A中的前k个元素排序放在B中。A中从第k+1个元素(A[k])开始,用插入排序的方法, * 插入到B数组中(B中最大的自然会被丢弃),最后B中就是最小的k个。时间复杂度是O(kn)。 * 法3:构建一个最小堆,取其前k个最小的数。 也可以构建一个k个元素的最大堆来获得k个最小的元素。 * 时间复杂度是O(nlogk)。 * 法4:《算法导论》第九章中位数和顺序统计学的9.2节中以期望线性时间找出n元素集合中第k小的元素。 * 若知道第k小元素了,以此作为游标,在线性时间内将k个最小元素都找到。 * 两个线性时间满足题目的正比于n的时间复杂度的要求。这带来另一个问题如何找到出n元集合中第k小的元素呢?(见算法导论) * 法5:改进快速排序不用排序出全部的数,只需要快速排序+二分搜索在线性时间找到第K小的数同时从这个数开始 * 往左都是最小的k个数。时间复杂度是O(n). */ }
算法------编程珠玑(ProgrammingPeals)第二章习题(JAVA)
猜你喜欢
转载自blog.csdn.net/weianluo/article/details/80719657
今日推荐
周排行