算法题目积累0721
1,两个字符串的最长公共子串
基本思想是扫描两次矩阵,第一次进行字符比较;第二次找出对角线为1的最长长度
package com.bobo.interview; import com.bobo.util.MetrixPrint; /** * 这个类实现的功能室查找两个字符串的最长公共子串(要求连续) * 扫描两次矩阵,第一次的时候是两个字符串的比较,相等的位置赋值1,不等的位置赋值0 * 第二次扫描,找出对角线为1的最大长度 * 需要注意的是:分支不止一个的情况 * @author weibo.lwb * */ public class LargestCommonStr { /** * @param args */ public static void main(String[] args) { // TODO Auto-generated method stub LargestCommonStr test=new LargestCommonStr(); test.largestCommonStr("abce", "ebcen"); } public int largestCommonStr(String str1,String str2){ int n=str1.length(); int m=str2.length(); int metrix[][]=new int[n][m]; //第一遍扫描,两个字符串相等的地方赋值1,否则赋值为0 for(int i=0;i<n;i++){ for(int j=0;j<m;j++){ if(str1.charAt(i)==str2.charAt(j)){ metrix[i][j]=1; }else{ metrix[i][j]=0; } } } System.out.println("第一次扫描之后构建的矩阵为:"); for(int i=0;i<n;i++){ for(int j=0;j<m;j++){ System.out.print(metrix[i][j]+" "); } System.out.println(); } int max=0; int xpos=0; int ypos=0; //第二遍扫描,看上个矩阵对角线为1的最大长度 for(int i=1;i<n;i++){ for(int j=1;j<m;j++){ if(metrix[i-1][j-1]>=1&&metrix[i][j]>=1){ metrix[i][j]=metrix[i-1][j-1]+1; if(max<metrix[i][j]){ max=metrix[i][j]; xpos=i; ypos=j; } } } } System.out.println("第2次扫描之后构建的矩阵为:"); for(int i=0;i<n;i++){ for(int j=0;j<m;j++){ System.out.print(metrix[i][j]+" "); } System.out.println(); } //两次扫描之后完成,之后需要做的是输出公共子串 System.out.println("最长公共子串的长度为:"+max); System.out.println(str1.substring(xpos-max+1,xpos+1)); return max; } }
2,两个字符串的最长公共子序列
基本思想,注意这里是构建一个(len1+1)*(len2+1)的矩阵
利用动态规划的递推公式:
f(i,j)=f(i-1,j-1)+1 如果str1[i]=str2[j]
f(i,j)=max{f(i-1,j),f(i,j-1)}如果str1[i]!=str2[j]
对于该矩阵赋予的初值是第一行和第一列都等于0
package com.bobo.interview; import com.bobo.util.MetrixPrint; /** * 该类主要是实现计算两个字符串的最长公共子序列(不要求连续) * 采用动态规划的方法 * @author weibo.lwb * */ public class LargestCommonList { /** * @param args */ public static void main(String[] args) { // TODO Auto-generated method stub LargestCommonList test=new LargestCommonList(); test.largestCommonList("agbic", "abcmn"); } public static int largestCommonList(String str1,String str2){ int len1=str1.length(); int len2=str2.length(); int[][] result=new int[len1+1][len2+1]; for(int i=0;i<=len1;i++){ result[i][0]=0; } for(int j=0;j<=len2;j++){ result[0][j]=0; } for(int i=1;i<=len1;i++){ for(int j=1;j<=len2;j++){ if(str1.charAt(i-1)==str2.charAt(j-1)){ System.out.print(str1.charAt(i-1)); result[i][j]=result[i-1][j-1]+1; }else{ result[i][j]=result[i-1][j]>result[i][j-1]?result[i-1][j]:result[i][j-1]; } } } System.out.println(); MetrixPrint.printMetrix(result); return result[len1][len2]; } }
3,计算2的n次方
动态规划,减少计算次数
package com.bobo.interview; public class cal2mi { /** * @param args */ public static void main(String[] args) { cal2mi test=new cal2mi(); System.out.println(test.cal2byn(3)); System.out.println(test.cal2byn(10)); } public static int cal2byn(int n){ if(n==0){ return 1; } if(n==1){ return 2; } if(n%2==0){ return cal2byn(n/2)*cal2byn(n/2); }else { return 2*cal2byn((n-1)/2)*cal2byn((n-1)/2); } } }
4,计算所有的全排列
package com.bobo.interview; public class Permutation { /** * @param args */ public static void main(String[] args) { Permutation test=new Permutation(); test.startPermutation("abcd"); // System.out.println(test.swap("abcd", 0, 3)); // System.out.println("abcd".charAt(3)); System.out.println("done"); } public void startPermutation(String str){ permutation(str,0); } //利用递归来实现,先不考虑重复 public void permutation(String str,int start){ if(start==str.length()){ System.out.println(str); return; } String newStr=""; for(int i=start;i<str.length();i++){ newStr=swap(str,i,start); permutation(newStr,start+1); newStr= swap(newStr,i,start); } } //java中字符串是常量,不能通過類似于c++中的方法來修改,必須產生一個新的str對象,哎 private String swap(String str, int i, int j) { char[] charArray=str.toCharArray(); if(charArray[i]!=charArray[j]){ char temp=charArray[i]; charArray[i]=charArray[j]; charArray[j]=temp; } return new String(charArray); } }
package com.bobo.interview; public class PermutationDealWithRepeat { /** * @param args */ public static void main(String[] args) { PermutationDealWithRepeat test=new PermutationDealWithRepeat(); test.startPermutation("abcd"); } public void startPermutation(String str){ permutation(str,0); } //去除重复的全排列的基本思想是,每个字符均和起后面不重复的字符进行交换 public boolean isSwap(String str,int start,int i){ for(int k=start;k<i;k++){ if(str.charAt(k)==str.charAt(i)){ return false; } } return true; } public void permutation(String str,int start){ if(start==str.length()){ System.out.println(str); return; } String newStr=""; for(int i=start; i<str.length();i++){ if(isSwap(str,start,i)){ newStr=swap(str,start,i); permutation(newStr,start+1); newStr=swap(str,start,i); } } } //java中字符串是常量,不能通過類似于c++中的方法來修改,必須產生一個新的str對象,哎 private String swap(String str, int i, int j) { char[] charArray=str.toCharArray(); if(charArray[i]!=charArray[j]){ char temp=charArray[i]; charArray[i]=charArray[j]; charArray[j]=temp; } return new String(charArray); } }
采用非递归的方法实现字符串的全排列(字典序的方式)
package com.bobo.interview; import java.util.Arrays; public class DictoryPermutation { //利用字典序的方式,找到一个字符串所有的全排列 /** * @param args */ public static void main(String[] args) { DictoryPermutation test=new DictoryPermutation(); test.dictoryPermutation("221"); } public void dictoryPermutation(String str){ char[] tmp=str.toCharArray(); Arrays.sort(tmp); String sortedStr=new String(tmp); while(sortedStr!=null){ System.out.println(sortedStr); sortedStr=nextStr(sortedStr); } } public String nextStr(String str){ //首先把str从小大进行排序 //第一步,从后往前找到第一个相邻顺序的序列 //因为如果是逆序的话,不管怎么交换,都不会使得交换之后的字符串比原来的字符串大 int i,j; for(i=str.length()-2;i>=0;i--){ if(str.charAt(i)<str.charAt(i+1)){ break; } } if(i<0){ return null; } //System.out.println(str.charAt(i)); //第二步:从后往前,找到第一个比sorted[i]大的数字 for(j=str.length()-1;j>i;j--){ if(str.charAt(j)>str.charAt(i)){ break; } } //第三步,讲j和i进行互换 String newStr=swap(str,i,j++); //System.out.println(newStr); //注意,java中substring的用法是包括前面的,不包括后面的 //第四步,讲i+1到length-1的子字符串进行反转 String backStr=new StringBuilder(newStr.substring(i+1)).reverse().toString(); //System.out.println(backStr); String resultStr=newStr.substring(0,i+1)+backStr; return resultStr; } //java中字符串是常量,不能通過類似于c++中的方法來修改,必須產生一個新的str對象,哎 private String swap(String str, int i, int j) { char[] charArray=str.toCharArray(); if(charArray[i]!=charArray[j]){ char temp=charArray[i]; charArray[i]=charArray[j]; charArray[j]=temp; } return new String(charArray); } }
5,字符串是否包含问题
public boolean isContain(String str1,String str2){ //return str1.contains(str2); //java的contain函数的内部实现 int len1=str1.length(); int len2=str2.length(); int i=0,j=0; while(i<len1-len2+1){ while(j<len2&&str1.charAt(i)==str2.charAt(j)){ i++; j++; } if(j==len2){ return true; } i=i-j+1; j=0; } return false; }
字符串是否包含的KMP解决方法:
1)注意next数组如何构造
2)引入next指针之后,主串的指针不再回溯
public int isContainByKMP(String str1,String str2){ //第一步首先構建next數組 char[] charArray=str2.toCharArray(); int[] next=buildNext(charArray); //之后开始进行查找,注意主串不进行回溯 int i=0,j=0; while(i<str1.length()&&j<str2.length()){ if(j==-1||str1.charAt(i)==str2.charAt(j)){ i++; j++; }else{ j=next[j]; } } if(j<str2.length()){ System.out.println(false); return -1; }else{ System.out.println(true); return i-str2.length(); } } private int[] buildNext(char[] charArray){ int[] next=new int[charArray.length]; next[0]=-1; int i,j,index; for( i=1;i<next.length;i++){ j=i-1; index=next[j]; while(index!=-1&&charArray[i-1]!=charArray[index]){ index=next[index]; } if(index==-1){ next[i]=0; }else{ next[i]=next[i-1]+1; } } for(i=0;i<next.length ;i++){ System.out.print(next[i]+","); } System.out.println(); return next; }
4,数组中找出和为定值的多个数
package com.bobo.interview; public class PrintSum { private boolean[] flags={false,false,false,false,false}; /** * @param args */ public static void main(String[] args) { int[] array={10,6,7,4,3}; PrintSum test=new PrintSum(); test.initCal(array); } public void initCal(int[] array){ printSum(array,0,array.length,13); } public void printSum(int[] array,int begin,int n,int sum){ if(begin>=n){ return; } //如果包含当前元素 flags[begin]=true; printSum(array,begin+1,n,sum-array[begin]); //如果不包含当前元素 flags[begin]=false; printSum(array,begin+1,n,sum); if(sum==array[begin]){ flags[begin]=true; for(int i=0;i<n;i++){ if(flags[i]==true){ System.out.print(array[i]+","); } } System.out.println(); return; } } }
5,寻找和为定值的两个数(参见编程艺术)
(动态规划一般都有一个递推或者条件递推式,可以用递归或者填充矩阵的两种方式进行求解)
动态规划的几道题目
1)最长公共子序列LCS问题
2)数组中和为sum的问题(与数组中两个数的和为sum进行对比)
3)第二题也可以转换为一个恰好装满的背包问题,和普通的0-1背包问题对比(采用填充矩阵的形式)
4)字符串的编辑距离问题
6,快速排序和荷兰国旗问题
7,boolean 过滤器
(经常用来在大数据量的集合中判断某个元素是否存在,实质上是一个很长的二进制向量和一系列hash函数)
Bloom-Filter算法的核心思想就是利用多个不同的Hash函数来解决“冲突”。 计算某元素x是否在一个集合中,首先能想到的方法就是将所有的已知元素保存起来构成一个集合R,然后用元素x跟这些R中的元素一一比较来判断是否存在于集合R中;我们可以采用链表等数据结构来实现。但是,随着集合R中元素的增加,其占用内存将越来越大。试想,如果有几千万个不同网页需要下载,所需的内存将足以占用掉整个进程的内存地址空间。即使用MD5,UUID这些方法将URL转成固定的短小的字符串,内存占用也是相当巨大的 日常生活中,包括在设计计算机软件时,我们经常要判断一个元素是否在一个集合中。比如在字处理软件中,需要检查一个英语单词是否拼写正确(也就是要判断它是否在已知的字典中);在 FBI,一个嫌疑人的名字是否已经在嫌疑名单上;在网络爬虫里,一个网址是否被访问过等等。最直接的方法就是将集合中全部的元素存在计算机中,遇到一个新元素时,将它和集合中的元素直接比较即可。 例如邮件服务器中的垃圾邮件过滤器。在搜索引擎领域,Bloom-Filter最常用于网络蜘蛛(Spider)的URL过滤,网络蜘蛛通常有一个 URL列表,保存着将要下载和已经下载的网页的URL,网络蜘蛛下载了一个网页,从网页中提取到新的URL后,需要判断该URL是否已经存在于列表中。此时,Bloom-Filter算法是最好的选择。 布隆过滤器决不会漏掉任何一个在黑名单中的可疑地址。但是,它有一条不足之处。也就是它有极小的可能将一个不在黑名单中的电子邮件地址判定为在黑名单中,因为有可能某个好的邮件地址正巧对应八个都被设置成一的二进制位。好在这种可能性很小。我们把它称为误识概率。在上面的例子中,误识概率在万分之一以下。 布隆过滤器的好处在于快速,省空间。但是有一定的误识别率。常见的补救办法是在建立一个小的白名单,存储那些可能别误判的邮件地址。
具体的思想参见这篇博客
http://www.cnblogs.com/hitwtx/archive/2011/08/24/2152180.html
package com.bobo.interview; import java.util.BitSet; public class BloomFilter { private static final int DEFAULT_SIZE = 2 << 24; private static final int[] seeds = new int[] { 5, 7, 11, 13, 31, 37, 61 }; private BitSet bits = new BitSet(DEFAULT_SIZE); private SimpleHash[] func = new SimpleHash[seeds.length]; public BloomFilter() { for (int i = 0; i < seeds.length; i++) { func[i] = new SimpleHash(DEFAULT_SIZE, seeds[i]); } } public void add(String value) { for (SimpleHash f : func) { bits.set(f.hash(value), true); } } public boolean contains(String value) { if (value == null) { return false; } // java里面的boolean占用1个字节(可能和具体平台实现有关系吧),true是1,false是0 boolean ret = true; for (SimpleHash f : func) { ret = ret && bits.get(f.hash(value)); } return ret; } // 内部类,simpleHash public static class SimpleHash { private int cap; private int seed; public SimpleHash(int cap, int seed) { this.cap = cap; this.seed = seed; } public int hash(String value) { int result = 0; int len = value.length(); for (int i = 0; i < len; i++) { result = seed * result + value.charAt(i); } return (cap - 1) & result; } } public static void main(String[] args) { String value = "http://stone2083@yahoo.cn"; BloomFilter filter = new BloomFilter(); System.out.println(filter.contains(value)); filter.add(value); System.out.println(filter.contains(value)); } }
8,有一亿个url,找出其中的top10
基本步骤:
1,使用md5或者uuid将其转换为定长的,而且较短的字符串
2,将1)步中得到的字符串进行hash,(hash成为一个int%1000(分割到的子文件个数),确定分配到哪个子文件中)将相同的字符串放到一个文件中(只利用md5或者只用hash一个就成了吧)
3,取出每个文件中的top10(这里再用一次hash,不过这次的hash是为了统计出现次数),最后归并得到所有中的top10
9,跳表问题
查找,删除,插入时间复杂度都是log(n),空间复杂度是o(n)http://blog.sina.com.cn/s/blog_72995dcc01017w1t.html
10,八皇后问题
(用一个一维数组即可,坐标代表行(0-7),值代表列(0-7);然后计算出所有的全排列,之后排除其中是对角线的情况|i1-i2/j1-j2|!=1)
11,最长递增子序列
例如9,5,6,7,12,8的最长递增子序列是5,6,7,8
方法一:记录原来数组为array1,将原来数组从小到大排序后的数组为array2.求两个数组的最长公共子序列(array1保证相对位置不变,array2保证递增)
方法二: