剑指offer(一)
请设计一个函数,用来判断在一个矩阵中是否存在一条包含某字符串所有字符的路径。路径可以从矩阵中的任意一个格子开始,每一步可以在矩阵中向左,向右,向上,向下移动一个格子。如果一条路径经过了矩阵中的某一个格子,则该路径不能再进入该格子。 例如 a b c e s f c s a d e e 矩阵(3*4)中包含一条字符串"bcced"的路径,但是矩阵中不包含"abcb"路径,因为字符串的第一个字符b占据了矩阵中的第一行第二个格子之后,路径不能再次进入该格子。
/** 回溯法 用一个状态数组保存之前访问过的字符,然后再分别按上,下,左,右递归 */ public class Solution { public boolean hasPath(char[] matrix, int rows, int cols, char[] str) { int flag[] = new int[matrix.length]; for (int i = 0; i < rows; i++) { for (int j = 0; j < cols; j++) { if (helper(matrix, rows, cols, i, j, str, 0, flag)) return true; } } return false; } private boolean helper(char[] matrix, int rows, int cols, int i, int j, char[] str, int k, int[] flag) { int index = i * cols + j; if (i < 0 || i >= rows || j < 0 || j >= cols || matrix[index] != str[k] || flag[index] == 1) return false; if(k == str.length - 1) return true; flag[index] = 1; if (helper(matrix, rows, cols, i - 1, j, str, k + 1, flag) || helper(matrix, rows, cols, i + 1, j, str, k + 1, flag) || helper(matrix, rows, cols, i, j - 1, str, k + 1, flag) || helper(matrix, rows, cols, i, j + 1, str, k + 1, flag)) { return true; } flag[index] = 0; return false; } }
请实现一个函数用来匹配包括'.'和'*'的正则表达式。模式中的字符'.'表示任意一个字符,而'*'表示它前面的字符可以出现任意次(包含0次)。
在本题中,匹配是指字符串的所有字符匹配整个模式。例如,字符串"aaa"与模式"a.a"和"ab*ac*a"匹配,但是与"aa.a"和"ab*a"均不匹配。
/* 当模式中的第二个字符不是“*”时: 1、如果字符串第一个字符和模式中的第一个字符相匹配,那么字符串和模式都后移一个字符,然后匹配剩余的。 2、如果 字符串第一个字符和模式中的第一个字符相不匹配,直接返回false。 而当模式中的第二个字符是“*”时: 如果字符串第一个字符跟模式第一个字符不匹配,则模式后移2个字符,继续匹配。如果字符串第一个字符跟模式第一个字符匹配,可以有3种匹配方式: 1、模式后移2字符,相当于x*被忽略; 2、字符串后移1字符,模式后移2字符; 3、字符串后移1字符,模式不变,即继续匹配字符下一位,因为*可以匹配多位; */ public class Solution { public boolean match(char[] str, char[] pattern) { if (str == null || pattern == null) { return false; } int strIndex = 0; int patternIndex = 0; return matchCore(str, strIndex, pattern, patternIndex); } public boolean matchCore(char[] str, int strIndex, char[] pattern, int patternIndex) { //有效性检验:str到尾,pattern到尾,匹配成功 if (strIndex == str.length && patternIndex == pattern.length) { return true; } //pattern先到尾,匹配失败 if (strIndex != str.length && patternIndex == pattern.length) { return false; } //模式第2个是*,且字符串第1个跟模式第1个匹配,分3种匹配模式;如不匹配,模式后移2位 if (patternIndex + 1 < pattern.length && pattern[patternIndex + 1] == '*') { if ((strIndex != str.length && pattern[patternIndex] == str[strIndex]) || (pattern[patternIndex] == '.' && strIndex != str.length)) { return matchCore(str, strIndex, pattern, patternIndex + 2)//模式后移2,视为x*匹配0个字符 || matchCore(str, strIndex + 1, pattern, patternIndex + 2)//视为模式匹配1个字符 || matchCore(str, strIndex + 1, pattern, patternIndex);//*匹配1个,再匹配str中的下一个 } else { return matchCore(str, strIndex, pattern, patternIndex + 2); } } //模式第2个不是*,且字符串第1个跟模式第1个匹配,则都后移1位,否则直接返回false if ((strIndex != str.length && pattern[patternIndex] == str[strIndex]) || (pattern[patternIndex] == '.' && strIndex != str.length)) { return matchCore(str, strIndex + 1, pattern, patternIndex + 1); } return false; } }
给定一个数组A[0,1,...,n-1],请构建一个数组B[0,1,...,n-1],其中B中的元素B[i]=A[0]*A[1]*...*A[i-1]*A[i+1]*...*A[n-1]。不能使用除法。
/* 设 n=5 对于第一个for循环 第一步:b[0] = 1; 第二步:b[1] = b[0] * a[0] = a[0] 第三步:b[2] = b[1] * a[1] = a[0] * a[1]; 第四步:b[3] = b[2] * a[2] = a[0] * a[1] * a[2]; 第五步:b[4] = b[3] * a[3] = a[0] * a[1] * a[2] * a[3]; 然后对于第二个for循环 第一步 b[4] = b[4]*tmp = a[0] * a[1] * a[2] * a[3]; 第一步 temp *= a[4] = a[4]; b[3] = b[3] * temp = a[0] * a[1] * a[2] * a[4]; 第二步 temp *= a[3] = a[4] * a[3]; b[2] = b[2] * temp = a[0] * a[1] * a[4] * a[3]; 第三步 temp *= a[2] = a[4] * a[3] * a[2]; b[1] = b[1] * temp = a[0] * a[4] * a[3] * a[2]; 第四步 temp *= a[1] = a[4] * a[3] * a[2] * a[1]; b[0] = b[0] * temp = a[4] * a[3] * a[2] * a[1]; 由此可以看出从b[4]到b[0]均已经得到正确计算。 */ public class Solution { public int[] multiply(int[] A) { int len = A.length; int[] B = new int[len]; B[0] = 1; for(int i=1; i<len; i++){ B[i] = B[i-1] * A[i-1]; } int tmp = 1; for(int i=len-1; i>=0; i--){ B[i] = B[i] * tmp; tmp = tmp * A[i]; } return B; } }
在一个长度为n的数组里的所有数字都在0到n-1的范围内。 数组中某些数字是重复的,但不知道有几个数字是重复的。也不知道每个数字重复几次。
请找出数组中任意一个重复的数字。 例如,如果输入长度为7的数组{2,3,1,0,2,5,3},那么对应的输出是重复的数字2或者3。
public class Solution { //找出任意重复的一个,赋值duplication[0] // Return value: true if the input is valid, and there are some duplications in the array number otherwise false /** (一)、从头扫到尾,只要当前元素值与下标不同,就做一次判断,numbers[i]与numbers[numbers[i]],相等就认为找到了 重复元素,返回true,否则就交换两者,继续循环。直到最后还没找到认为没找到重复元素,返回false */ public boolean duplicate(int numbers[],int length,int [] duplication){ for(int i=0; i<length; i++){ while(i!=numbers[i]){ if(numbers[i] == numbers[numbers[i]]){ duplication[0] = numbers[i]; return true; } int tmp = numbers[i]; numbers[i] = numbers[tmp]; numbers[tmp] = tmp; } } return false; } // (二) public boolean duplicate(int numbers[],int length,int [] duplication) { StringBuffer sb = new StringBuffer(); for(int i = 0; i < length; i++){ sb.append(numbers[i] + ""); } for(int j = 0; j < length; j++){ if(sb.indexOf(numbers[j]+"") != sb.lastIndexOf(numbers[j]+"")){ duplication[0] = numbers[j]; return true; } } return false; } // (三) public boolean duplicate(int numbers[],int length,int [] duplication) { for(int i=0; i<length; i++){ int index = numbers[i]; if(index>=length){ index -= length; } if(numbers[index]>=length){ duplication[0] = index; return true; } numbers[index] = numbers[index]+length; } return false; } }
在一个排序数组中,找出给定数字出现的次数。
// 二分查找 找到第一个K 和 最后一个K, 二者位置相减 public class Solution{ public int getNumOfK(int[] arr, int k){ int number = 0; int first = getFirstIndex(arr, k, 0, arr.length-1); int last = getLastIndex(arr, k, 0, arr.length-1); if(first > -1 && last > -1){ number = last - first + 1; } return number; } // 找到第一个k private int getFirstIndex(int[] arr, int k, int start, int end){ if(start>end){ return -1; } int mid = (start+end)/2; if(arr[mid] == k){ if((mid>0 && arr[mid-1]!=k) || mid==0){ return mid; }else{ end = mid - 1; } }else{ if(mid > k){ end = mid - 1; }else{ start = mid + 1; } } return getFirstIndex(arr, k, start, end); } // 找到最后一个k private int getLastIndex(int[] arr, int k, int start, int end){ if(start>end){ return -1; } int mid = (start+end)/2; if(arr[mid] == k){ if((mid<end && arr[mid+1]!=k) || mid==end){ return mid; }else{ start = mid + 1; } }else{ if(mid > k){ end = mid - 1; }else{ start = mid + 1; } } return getLastIndex(arr, k, start, end); } }
一个整型数组里除了两个数字之外,其他的数字都出现了两次。请写程序找出这两个只出现一次的数字。
// num1,num2分别为长度为1的数组。传出参数将num1[0],num2[0]设置为返回结果 public void FindNumsAppearOnce(int [] array,int num1[] , int num2[]) { ArrayList<Integer> list = new ArrayList<>(); for(int i=0; i<array.length; i++){ if(!list.contains(array[i])){ list.add(array[i]); }else{ list.remove(new Integer(array[i])); } } if(list.size()>1){ num1[0] = list.get(0); num2[0] = list.get(1); } }
输入n个整数,找出其中最小的K个数。例如输入4,5,1,6,2,7,3,8这8个数字,则最小的4个数字是1,2,3,4
public ArrayList<Integer> getLeastNumbers(int[] input, int k){ if(input==null) return null; ArrayList<Integer> list = new ArrayList<>(); if(k > input.length){ return list; } for(int i=0; i<k; i++){ for(int j=i+1; j<input.length; j++){ if(input[i]>input[j]){ int tmp = input[i]; input[i] = input[j]; input[j] = tmp; } } list.add(input[i]); } return list; }
//1. 保证奇数和奇数,偶数和偶数之间的相对位置不变。 public void partitionArray(int[] arr){ int len = arr.length; for(int i=1; i<len; i++){ if(arr[i]%2!=0){ for(int j=i; j>0; j--){ if(arr[j-1]%2==0){ int t = arr[j]; arr[j] = arr[j-1]; arr[j-1] = t; } } } } } //2. 不用保证奇数和奇数,偶数和偶数之间的相对位置不变。 public void partitionArray(int[] arr){ int left = 0; int right = arr.length-1; while(left<right){ while(arr[left]%2!=0 && left<right){ left++; } while(arr[rigth]%2==0 && left<right){ right--; } if(left<right){ int tmp = arr[left]; arr[left] = arr[right]; arr[right] = tmp; left++; right--; } } }
必须先得到层数。对于m行n列,layer=(Math.min(m, n)-1)/2+1;
public ArrayList<Integer> printMatrix(int[][] matrix){ ArrayList<Integer> list = new ArrayList<>(); if(matrix.length==0){ return list; } int m = matrix.length; int n = matrix[0].length; int layer = (Math.min(m,n)-1)/2+1; for(int i=0; i<layer; i++){ // leftTop-->rightTop for(int a=i; a<n-i; a++){ list.add(matrix[i][a]); } // rightTop-->rightDown for(int b=i+1; b<m-i; b++){ list.add(matrix[b][n-1-i]); } // rightDown-->leftDown for(int c=n-1-i-1; (c>=i)&&(m-1-i)!=i; c--){ list.add(matrix[m-1-i][c]); } // leftDown-->leftTop for(int d=m-1-i-1; (d>i)&&(n-1-i)!=i; d--){ list.add(matrix[d][i]); } } return list; }
LL今天心情特别好,因为他去买了一副扑克牌,发现里面居然有2个大王,2个小王(一副牌原本是54张^_^)...
他随机从中抽出了5张牌,想测测自己的手气,看看能不能抽到顺子,如果抽到的话,他决定去买体育彩票,嘿嘿!!“红心A,黑桃3,小王,大王,方片5”,“Oh My God!”
不是顺子.....LL不高兴了,他想了想,决定大\小 王可以看成任何数字,并且A看作1,J为11,Q为12,K为13。
上面的5张牌就可以变成“1,2,3,4,5”(大小王分别看作2和4),“So Lucky!”。LL决定去买体育彩票啦。
现在,要求你使用这幅牌模拟上面的过程,然后告诉我们LL的运气如何。为了方便起见,你可以认为大小王是0。
即:输入一个数组,包含5个从0-13的数,0可以当作任何数,判断这5个数是不是连续的。
// 一:排序-->统计0的个数-->统计需要0的个数-->比较 public boolean isContinuous(int[] nums){ if(nums==null || nums.length!=5){ return false; } Arrays.sort(nums); int cnt0 = 0; int cntNeed = 0; for(int i=0; i<nums.length; i++){ if(nums[i]==0){ cnt0++; }else{ if(i<4){ if(nums[i+1]==nums[i]){ return false; } cntNeed += nums[i+1]-nums[i]-1; } } } if(cntNeed > cnt0){ return false; } return true; }
// 二:必须满足两个条件:1.除0外没有重复数;2.max-min<5” public boolean isContinuous(int[] nums){ if(nums==null || nums.length!=5){ return false; } Arrays.sort(nums); int cnt0 = 0; for(int i=0; i<nums.length; i++){ if(nums[i]==0){ cnt0++; }else{ if(i<4 && (nums[i+1]==nums[i])){ return false; } } } if(cnt0>=4){ return true; } if((nums[nums.length-1]-nums[cnt0])<5){ return true; } return false; }
输入一个字符串,按字典序打印出该字符串中字符的所有排列。例如输入字符串abc,则打印出由字符a,b,c所能排列出来的所有字符串abc,acb,bac,bca,cab和cba。 结果请按字母顺序输出。
public ArrayList<String> Permutation(String str) { ArrayList<String> re = new ArrayList<String>(); if (str == null || str.length() == 0) { return re; } TreeSet<String> set = new TreeSet<String>(); fun(set, str.toCharArray(), 0); re.addAll(set); return re; } private void fun(TreeSet<String> re, char[] str, int k) { if (k == str.length) { re.add(new String(str)); return; } for (int i = k; i < str.length; i++) { swap(str, i, k); fun(re, str, k + 1); swap(str, i, k); } } private void swap(char[] str, int i, int j) { if (i != j) { char t = str[i]; str[i] = str[j]; str[j] = t; } }
public int maxSumOfSubarray(int[] array){ if(arr==null || array.length==0){ return 0; } int maxEndingHere = array[0]; int maxSoFar = array[0]; for(int i=0; i<array.length; ++i){ maxEndingHere = Math.max(array[i], maxEndingHere+array[i]); maxSoFar = Math.max(maxSoFar, maxEndingHere); } return maxSoFar; }
从1到n的整数中1出现的次数(1~13中包含1的数字有1、10、11、12、13因此共出现6次)
public int numOfOne(int n){ if(n<1){ return 0; } if(n<=9){ return 1; } int count = 1; for(int i=10; i<n; ++i){ int j = i; while(j/10!=0){ // j>=10 if(j%10==1){ // 各位数为1 count++; } if(j<20){ count++; } j /= 10; } } return count; }
输入一个正整数数组,把数组里所有数字拼接起来排成一个数,打印能拼接出的所有数字中最小的一个。例如输入数组{3,32,321},则打印出这三个数字能排成的最小数字为321323。
分析:四步:int[] --> String[] --> sort --> joint
public class Solution { public String PrintMinNumber(int [] numbers) { if(numbers==null || numbers.length==0) return ""; String[] strArray = new String[numbers.length]; StringBuffer sb = new StringBuffer(); for(int i=0; i<strArray.length; i++){ strArray[i] = String.valueOf(numbers[i]); } Arrays.sort(strArray, new Comparator<String>(){ public int compare(String s1, String s2){ String c1 = s1+s2; String c2 = s2+s1; return c1.compareTo(c2); } }); for(int i=0; i<strArray.length; i++){ sb.append(strArray[i]); } return sb.toString(); } }
在一个字符串(1<=字符串长度<=10000,全部由字母组成)中找到第一个只出现一次的字符的位置。若为空串,返回-1。位置索引从0开始.
public int firstNotRepeatingChar(String str){ if(str==null || str.length()==0){ return -1; } int[] count = new int[26]; char c = 'a'; for(int i=0; i<str.length(); ++i){ count[str.charAt(i)-c]++; } for(int i=0; i<str.length(); ++i){ if(count[str.charAt(i)-c]==1){ return i; } } return -1; }
数组中有一个数字出现的次数超过数组长度的一半,请找出这个数字。例如输入一个长度为9的数组{1,2,3,2,2,2,5,4,2}。由于数字2在数组中出现了5次,超过数组长度的一半,因此输出2。如果不存在则输出0。
public class Solution { public int MoreThanHalfNum_Solution(int [] array) { // 不存在主元素时,就不能用此方法 /* int cur = array[0]; int count = 0; for(int i=0; i<array.length; i++){ if(count==0){ cur = array[i]; count = 1; }else{ if(cur==array[i]){ count++; }else{ count--; } } } return cur; */ HashMap<Integer, Integer> map = new HashMap<>(); for(int i=0; i<array.length; i++){ int num = array[i]; if(map.containsKey(num)){ map.put(num, map.get(num)+1); }else{ map.put(num, 1); } if(map.get(num)>array.length/2){ return num; } } return 0; } }
数组中的逆序对
在数组中的两个数字,如果前面一个数字大于后面的数字,则这两个数字组成一个逆序对。输入一个数组,求出这个数组中的逆序对的总数。
分析:利用归并排序思想。
public class Solution { // 利用归并排序思想 public int InversePairs(int[] A) { return mergeSort(A, 0, A.length - 1); } private int mergeSort(int[] A, int start, int end) { if (start >= end) { return 0; } int mid = (start + end) / 2; int sum = 0; sum += mergeSort(A, start, mid); sum += mergeSort(A, mid+1, end); sum += merge(A, start, mid, end); return sum; } private int merge(int[] A, int start, int mid, int end) { int[] temp = new int[A.length]; int leftIndex = start; int rightIndex = mid + 1; int index = start; int sum = 0; while (leftIndex <= mid && rightIndex <= end) { if (A[leftIndex] <= A[rightIndex]) { temp[index++] = A[leftIndex++]; } else { temp[index++] = A[rightIndex++]; sum += mid - leftIndex + 1; } } while (leftIndex <= mid) { temp[index++] = A[leftIndex++]; } while (rightIndex <= end) { temp[index++] = A[rightIndex++]; } for (int i = start; i <= end; i++) { A[i] = temp[i]; } return sum; } }

浙公网安备 33010602011771号