主元素(数组中出现次数超过一半的数字)
数组中的逆序对
第一个只出现一次的字符位置
把数组排成最小的数
整数中1出现的次数
连续子数组的最大和
字符串的全排列
扑克牌顺子
顺时针打印矩阵
调整数组顺序使奇数位于偶数前面
最小的K个数
数组中只出现一次的数字
数字在排序数组中出现的次数
数组中重复的数字
构建乘积数组
正则表达式匹配
矩阵中的路径
矩阵中的路径
请设计一个函数,用来判断在一个矩阵中是否存在一条包含某字符串所有字符的路径。路径可以从矩阵中的任意一个格子开始,每一步可以在矩阵中向左,向右,向上,向下移动一个格子。如果一条路径经过了矩阵中的某一个格子,则该路径不能再进入该格子。 例如 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);
}
}
最小的K个数
输入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出现的次数
从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;
}
}