package com.duoduo.day329;
/*
* 一个二维数组,每一行从左到右递增,每一列从上到下递增.输入一个二维数组和一个整数,判断数组中是否含有整数。
*/
public class ArrSearch {
public static void main(String [] args) {
int [][] arr= {{1,2,8,9},{2,4,9,12},{4,7,10,13},{6,8,11,15}};
boolean result=arrSearch(arr,7);
System.out.println(result);
boolean result1=arrSearch(arr,5);
System.out.println(result1);
}
private static boolean arrSearch(int[][] arr, int key) {
if(key<0)
return false;
if(arr==null)
return false;
int i=0; //行数row
int j=arr[0].length-1; //列数column
boolean flag=false; //查找标志
while(i=0) { //从右上角开始查找
if(key==arr[i][j]) {
flag=true;
break;
}
else if(key
思路:
缺点:
package com.duoduo.day329;
/**
* 题目:请实现一个函数,把字符串中的每个空格替换成“%20”
* @author 多多
*方法1 :原地替换
*/
public class ReplaceBlank {
public static void main(String [] args) {
String s="we are happy";
int len=s.length();
String s1=replaceBlank(s,len);
System.out.println(s1);
//we%20are%20happy
}
//替换空格
private static String replaceBlank(String s,int len) {
if(s==null || len<0)
return null;
char[] c=s.toCharArray(); //原数组
int count=0; //计数空格
for(int i=0;i=0) {
if(c[i]==' ') { //1 ---3
c1[j--]='0';
c1[j--]='2';
c1[j--]='%';
i--;
}else {
c1[j--]=c[i--]; //依次赋值
}
}
return new String(c1);
//return String.valueOf(c1); //返回字符数组的字符串形式
/*方法2:利用字符串本身的性质
public static void main(String [] args) {
String s="we are happy";
s=s.replaceAll(" ","%20"); //直接String.replaceAll(A,b);
System.out.println(s);
}
/*方法3: 利用可变字符串容器去装新的字符串
public class ReplaceBlank {
public static void main(String [] args) {
String s="we are happy";
s=replaceBlank(s);
System.out.println(s);
}
public static String replaceBlank(String s) {
if(s==null || s.length()<=0)
return null;
StringBuffer sb=new StringBuffer(); //sb容器
for(int i=0;i
public class Sum {
public static double power(double base,int exponent) {
double result=1.0;
for(int i=1;i<=exponent;i++) {
result*=base;
}
return result;
}
public static void main(String [] args) {
double result=power(0,-1); //基数为0 指数为-1
//double result=power(2,3);
System.out.println(result);
}
}
问题:
import java.math.*;
public class Sum {
static boolean invalid_flag=false; //设置全局变量 标志 是否为无效输入
public static double power(double base,int exponent) {
if(equal(base,0.0) && exponent<=0) { //当基数是0 且指数<=0 时 无效 0的0次方也无效
invalid_flag=true;
return 0.0; //约定这种情形就返回0.0
}
int absExponent=Math.abs(exponent); //考虑有负数存在的情况
double result=powerWithExponent(base,absExponent);
if(exponent<0) //若指数为负数 则需要对结果取倒数
result=1.0/result;
return result;
}
//正常计算基数的指数次
private static double powerWithExponent(double base, int absExponent) {
double result=1.0;
for(int i=0;i-0.00001 && base-d< 0.00001)
return true;
else
return false;
}
public static void main(String [] args) {
//double result=power(0,0);
double result=power(0,-1);
//double result=power(2,3);
System.out.println(result);
System.out.println(invalid_flag);
}
问题:
依然有不完美的地方:
函数powerWithExponent效率如何提高?
折半法
//正常计算基数的指数次
private static double powerWithExponent(double base, int absExponent) {
double result=1.0;
if(absExponent==0)
return 1.0;
if(absExponent==1)
return base;
result=powerWithExponent(base,absExponent>>1); //除以2 ()*()
result*=result;
if((absExponent & 1)==1) //奇数 多出一个1 相当于取余运算
result*=base;
return result;
}
public class GetN {
public static void main(String [] args) {
getNDigits(3);
}
public static void getNDigits(int n) {
long number=1; //初始值大小
int i=0; //初始位大小
while(i++
问题:
解决方法:
import java.util.Arrays;
public class GetN {
public static void main(String [] args) {
printToMax(-1);
//printToMax(3);
}
public static void printToMax(int n){
if(n < 0)
return;
char[] number = new char[n];
Arrays.fill(number, '0');
while(!increment(number)){
printNumber(number);
}
}
//使用数组实现对数进行加1操作
public static boolean increment(char[] num){
if(num.length<1)
throw new RuntimeException("invalid lenth of array");
boolean isOverflow = false; //最高位产生进位标志(数组中的数已经达到最大数)
int size = num.length;
int carry = 0; //进位
for(int i = size - 1; i >= 0; i--){ //没有产生进位的+1,循环只运行1次,产生一个进位,循环多运行一次
int temp = num[i] - '0' + carry;
if(i == size - 1)
temp++; //最低位+1
if(temp >= 10){
if(i == 0) //最高位溢出
isOverflow = true;
else{ //普通位产生进位
temp -= 10;
carry = 1;
num[i] = (char) ('0' + temp);
}
}else{ //普通位+1的结果保存在数组中 +1后程序退出循环
num[i] = (char)('0' + temp);
break;
}
}
return isOverflow;
}
public static void printNumber(char[] num){
int size = num.length;
int i = 0;
while(i < size && num[i] == '0') //i < size在前,否则越界
i++;
//char[] printNum = new char[size - i];
//System.arraycopy(num, i, printNum, 0, size - i);//复制数组
if(i == size)//不打印全0
return;
char[] printNum = Arrays.copyOfRange(num, i, size);//复制数组
System.out.println(printNum);
}
}
最佳方法:全排列
import java.util.Arrays;
public class GetN {
public static void main(String [] args) {
//print1ToMax(3);
print1ToMax(-1);
}
/*字符每一位进行全排列*/
public static void print1ToMax(int n) {
if(n<=0)
return ;
char[] arr=new char[n]; //存放n位数字字符
Arrays.fill(arr, '0'); //初始化数组字符为0
printOrder(arr,n,0); //数组--长度--下标开始
}
//全排列 用递归实现
public static void printOrder(char[] arr,int length,int index) {
if(index>=length)
return;
for(int i=0;i<=9;i++) {
arr[index]=(char)('0'+i); //字符数组的每一位数字均0--9
if(index==length-1) //已经到最后一位 则需要打印数组元素
printNumber(arr);
printOrder(arr,length,index+1); //递归下一位数字
}
}
//打印数字
private static void printNumber(char[] arr) {
int size=arr.length;
int i=0;
while(i
注:结果均经过测试 符合要求~
public void reOrderArray(int [] array) {
if(array.length==0||array.length==1)
return;
int len=array.length;
int i=0;
int j=len-1;
while(i
优化:将判断和操作函数分开 确定分组标准,便于标准修改
//创建一个新数组 然后移动 复制给原数组即可
if(array.length==0||array.length==1)
return;
int len=array.length;
int [] newArray=new int[len];
//统计奇数个数
int oddCount=0;
for(int i=0;i
题目变为:
输入一个整数数组,实现一个函数来调整该数组中数字的顺序,使得所有的奇数位于数组的前半部分,所有的偶数位于位于数组的后半部分,并保证奇数和奇数,偶数和偶数之间的相对位置不变。
1.要想保证原有次序,则只能顺次移动或相邻交换。用冒泡相邻交换----
public void reOrderArray(int [] array) {
if(array.length==0||array.length==1)
return;
int len=array.length; //冒泡排序做法 相邻元素交换 前偶后奇则交换
for(int i=len-1;i>0;i--){
for(int j=0;j
2.用新数组进行存储
首先统计奇数的个数
然后新建一个等长数组,设置两个指针,奇数指针从0开始,偶数指针从奇数个数的末尾开始 遍历,填数
public void reOrderArray(int [] array) {
//创建一个新数组 然后移动 复制给原数组即可
if(array.length==0||array.length==1)
return;
int len=array.length;
int [] newArray=new int[len];
//统计奇数个数
int oddCount=0;
for(int i=0;i
3.插入排序 前偶后奇才移动 插入 (标准)
public void reOrderArray(int [] array) {
for(int i=0;i=0 && array[j]%2==0){
array[j+1]=array[j];
j--;
}
array[j+1]=temp;
}
}
}
思路分析:
import java.util.ArrayList;
public class Solution {
public ArrayList printMatrix(int [][] matrix) {
int rows=matrix.length;
int columns=matrix[0].length;
if(matrix==null || rows<=0|| columns<=0)
return null;
ArrayList al=new ArrayList();
int start=0; //记录每一圈起始坐标
while( start*2< rows && start*2 printMatrixinCircle(int[][] matrix,int rows,int columns,int start,ArrayList al){
int endX=columns-1-start; //打印内圈最右列
int endY=rows-1-start; //打印内圈最下行
//打印第一行(肯定会打印,无需限制条件)
for(int i=start;i<=endX;i++){
al.add(matrix[start][i]);
}
//打印第一列
if(startstart && endX>start){
for( int i=endX-1;i>=start;i--){
al.add(matrix[endY][i]);
}
}
//打印第二列
if(start+1start){
for(int i=endY-1 ;i>=start+1 ;i--){
al.add(matrix[i][start]);
}
}
return al;
}
}
思路:
暗含: 有字典序 所以最后要加排序操作 直接交换递归顺序不一样
递归法
import java.util.ArrayList;
import java.util.Collections;
public class Solution {
public ArrayList Permutation(String str) {
//对输入进行判断
ArrayList al=new ArrayList();
if(str!=null ){
al=fullPermutation(str.toCharArray(),0,al);
Collections.sort(al);
}
return al;
}
//全排列 (考虑字符串重复问题ABA这种)
public ArrayList fullPermutation(char[] c,int start,ArrayList al){
if(start==c.length-1){ //递归到一个字符情况 该返回此字符串(叶子节点)
al.add(new String(c)); //或者String.valueOf(c) 也行
}else{
for(int i=start; i
用栈的方法:
import java.util.*;
public class Solution {
public ArrayList Permutation(String str) {
TreeSet tree = new TreeSet<>();
Stack stack = new Stack<>();
ArrayList results = new ArrayList<>();
stack.push(new String[]{str,""});
do{
String[] popStrs = stack.pop();
String oldStr = popStrs[1];
String statckStr = popStrs[0];
for(int i =statckStr.length()-1;i>=0;i--){
String[] strs = new String[]{statckStr.substring(0,i)+statckStr.substring(i+1),oldStr+statckStr.substring(i,i+1)};
if(strs[0].length()==0){
tree.add(strs[1]);
}else{
stack.push(strs);
}
}
}while(!stack.isEmpty());
for(String s : tree)
results.add(s);
return results;
}
}
思路:
import java.util.*;
public class Solution{
public void Permutation(String s){
StringBuffer sb=new StringBuffer();
if(str!=null){
for(int i=1;i<=s.length;i++){
fullPermutation(s.toCharArray(),0,i,sb);
}
}
}
public void fullPermutation(char[] c,int begin,int len,StringBuffer sb){
//len 指的是需要字符串的长度
if(len==0){
System.out.println(sb+" "); //打印符合要求的字符串
return;
}
if(begin==c.length)
return;
//取元素
sb.append(c[begin]);
fullPermutation(c,begin+1,len-1,sb); //在剩下的begin+1开始的元素中取len-1
sb.deleteCharAt(sb.length-1); //不取元素
fullPermutation(c,begin+1,len;sb); //在剩下的里面取len个元素
}
}
要点: 1 排序后 位于中间位置的一定是次数最多的数字
2 根据快排的思路 寻找中间位置middle的数字(用到找第k小的数字 思路)
3 判断该位置数字的次数是否超过总长度的一半
public class Solution {
public int MoreThanHalfNum_Solution(int [] array) {
if(array==null)
return -1;
int len=array.length;
if(len==1)
return array[0];
int middle=len>>1;
int begin=0;
int end=len-1;
int index =partition(array,begin,end); //快排的思路用到其中partition函数 定基准的位置
while(index!=middle){ //用递归
if(index middle){
end=index-1;
index=partition(array,begin,end);
}
}
//找到middle
int result=array[index]; //也就是middle位置的值
//判断middle位置的数字 出现次数 是否大于一半
if(! checkMoreThanHalf(array,result))
result=0;
return result;
}
//快排的划分函数
public int partition(int [] array, int begin, int end){
int i=begin;
int j=end;
int temp=array[begin];
while(i temp)
j--;
if(i
注意:
1 找到这个数字之后依然记得 判断次数是否符合要求
public class Solution {
public int MoreThanHalfNum_Solution(int [] array) {
if(array==null)
return -1;
int len=array.length;
if(len==1)
return array[0];
//遍历数组 预设值 同则加1 不同则-1 计数为0 则重新赋值 计数 最后一个值一定是数组中出现次数最多的值
int count=1; //计数器
int temp=array[0]; //预设值
for(int i=0;i< len;i++){
if(count==0){
temp=array[i];
count=1;
}else if(array[i]==temp)
count++;
else
count--;
}
//还需要判断该数字出现次数是否超过一半长度
if(!checkMoreThanHalf(array, temp))
temp=0;
return temp;
}
public boolean checkMoreThanHalf(int [] array, int temp ){
int count=0;
boolean flag=false;
for(int i=0;iarray.length/2 )
flag=true;
}
return flag;
}
}
import java.util.HashMap;
import java.util.Map;
/*
* 利用map存值,找出存在最多的数字,若大于长度一半,返回此数,否则返回0
*/
public class Solution {
public int MoreThanHalfNum_Solution(int [] array) {
if(array.length==0||array==null)
return 0;
Map map=new HashMap();
for(int i=0;i entry : map.entrySet()) {
if(entry.getValue()>array.length/2)
return entry.getKey();
}
return 0;
}
}
import java.util.Arrays;
public class Solution {
public int MoreThanHalfNum_Solution(int [] array) {
if(array==null)
return -1;
int len=array.length;
if(len==1)
return array[0];
//思路: 排序之后在中间的数字肯定为次数最多的数字 之后判断次数是否符合要求即可
Arrays.sort(array); //排序
int temp=array[len/2];
int count=0;
for(int i=0;i len/2){
return temp;
}
return 0;
}
}
import java.util.ArrayList;
public class Solution {
public ArrayList GetLeastNumbers_Solution(int [] input, int k) {
ArrayList list=new ArrayList();
if(input==null || k<=0 || k>input.length)
return list;
//依然采用partition函数思想
int begin=0;
int end=input.length-1;
int index=partition(input, begin, end);
while(index!= k-1){
if(index > k-1){
end=index-1;
index=partition(input,begin,end);
}else{
begin=index+1;
index=partition(input,begin,end);
}
}
//此时基准位置在k-1位置 左边均比此位置元素小 直接记录在list中(不用管顺序)
for(int i=0;i temp)
j--;
if(i
import java.util.ArrayList;
import java.util.PriorityQueue;
import java.util.Comparator;
public class Solution {
public ArrayList GetLeastNumbers_Solution(int[] input, int k) {
//对输入进行鲁棒性检查
ArrayList result = new ArrayList();
int length = input.length;
if(k > length || k == 0){
return result;
}
//定义优先级队列(充当最大堆) 传入初始容量和比较器comparator
PriorityQueue maxHeap = new PriorityQueue(k, new Comparator() {
@Override
public int compare(Integer o1, Integer o2) {
return o2.compareTo(o1);
}
});
//循环遍历输入数组元素
for (int i = 0; i < length; i++) {
if (maxHeap.size() != k) { //如果容器大小不为k 则继续加入元素
maxHeap.offer(input[i]);
} else if (maxHeap.peek() > input[i]) { //容器已满 则比较大小
Integer temp = maxHeap.poll(); //删除最大值 并存入上一个较小的元素
temp = null;
maxHeap.offer(input[i]);
}
}
for (Integer integer : maxHeap) { //对于容器中的k个元素 即为所求
result.add(integer);
}
return result;
}
}
public class Solution {
public ArrayList GetLeastNumbers_Solution(int[] input, int k) {
//对输入进行鲁棒性检查
ArrayList result = new ArrayList();
int len = input.length;
if(k > len || k == 0){
return result;
}
//用冒泡的思维去做 最右侧极值只保留k位 所以范围缩小
for(int i=len-1; i> len-1-k ;i--){
for(int j=0;j
import java.util.*;
public class Solution {
public ArrayList GetLeastNumbers_Solution(int [] input, int k) {
ArrayList result = new ArrayList();
if(input==null||input.length==0||input.length=0;i--){
adjustHeap(input,i,k-1);
}
//我们前k个元素的大顶堆已经构建好了,剩下的就是其余的和大顶堆的最大值比较了
for(int i=k;iinput[j]){
break;
}
input[i]=input[j];
i=j;
}
input[i]=temp;
}
}
public class Solution {
public int FindGreatestSumOfSubArray(int[] array) {
boolean invalid_input=false;
if(array==null || array.length==0){
invalid_input=true;
return 0;
}
//思路: 设置flag表明返回值为0代表的是和为0还是输入无效为0
// 当前项和< 0 那和重新从下一元素开始
//当前项和>0 那继续加上下一元素 并在每一轮求和之后和最大和比较 更新最大和
int curSum=0;
int maxSum=Integer.MIN_VALUE;
for(int i=0;i maxSum)
maxSum=curSum;
}
return maxSum;
}
}
时间复杂度: O(n)
代码基本相同
public class Solution {
public int NumberOf1Between1AndN_Solution(int n) {
int count=0;
for(int i=1;i<=n;i++){
count+=numberSum(i); //统计每一个数字含有1的个数
}
return count;
}
public int numberSum(int i){
int number=0;
while(i>0){
if(i%10==1) //通过对10取余的结果判断个位是否为1
number++;
i=i/10; //如果数字>10则不断的除10 来不断缩减位数
}
return number;
}
}
解法2: 思路新奇 但是时空复杂度较高
public class Solution {
public int NumberOf1Between1AndN_Solution(int n) {
int count=0;
StringBuffer s=new StringBuffer();
for(int i=1;i
/*
设N = abcde ,其中abcde分别为十进制中各位上的数字。
如果要计算百位上1出现的次数,它要受到3方面的影响:百位上的数字,百位以下(低位)的数字,百位以上(高位)的数字。
① 如果百位上数字为0,百位上可能出现1的次数由更高位决定。比如:12013,则可以知道百位出现1的情况可能是:100~199,1100~1199,2100~2199,,...,11100~11199,一共1200个。可以看出是由更高位数字(12)决定,并且等于更高位数字(12)乘以 当前位数(100)。
② 如果百位上数字为1,百位上可能出现1的次数不仅受更高位影响还受低位影响。比如:12113,则可以知道百位受高位影响出现的情况是:100~199,1100~1199,2100~2199,,....,11100~11199,一共1200个。和上面情况一样,并且等于更高位数字(12)乘以 当前位数(100)。
但同时它还受低位影响,百位出现1的情况是:12100~12113,一共14个,等于低位数字(13)+1。
③ 如果百位上数字大于1(2~9),则百位上出现1的情况仅由更高位决定,比如12213,则百位出现1的情况是:100~199,1100~1199,2100~2199,...,11100~11199,12100~12199,一共有1300个,并且等于更高位数字+1(12+1)乘以当前位数(100)。
*/
public class Solution {
public int NumberOf1Between1AndN_Solution(int n) {
int count = 0;//1的个数
int i = 1;//当前位
int current = 0,after = 0,before = 0;
while((n/i)!= 0){
current = (n/i)%10; //当前位数字
before = n/(i*10); //高位数字
after = n-(n/i)*i; //低位数字
//如果为0,出现1的次数由高位决定,等于高位数字 * 当前位数
if (current == 0)
count += before*i;
//如果为1,出现1的次数由高位和低位决定,高位*当前位+低位+1
else if(current == 1)
count += before * i + after + 1;
//如果大于1,出现1的次数由高位决定,//(高位数字+1)* 当前位数
else{
count += (before + 1) * i;
}
//前移一位
i = i*10;
}
return count;
}
}
编程之美上给出的规律:
1. 如果第i位(自右至左,从1开始标号)上的数字为0,则第i位可能出现1的次数由更高位决定(若没有高位,视高位为0),等于更高位数字X当前位数的权重10i-1。
2. 如果第i位上的数字为1,则第i位上可能出现1的次数不仅受更高位影响,还受低位影响(若没有低位,视低位为0),等于更高位数字X当前位数的权重10i-1+(低位数字+1)。
3. 如果第i位上的数字大于1,则第i位上可能出现1的次数仅由更高位决定(若没有高位,视高位为0),等于(更高位数字+1)X当前位数的权重10i-1。
二、X的数目
这里的 X∈[1,9] ,因为 X=0 不符合下列规律,需要单独计算。
首先要知道以下的规律:
依此类推,从 1 至 10 i ,在它们的左数第二位(右数第 i 位)中,任意的 X 都出现了 10 i−1 次。
这个规律很容易验证,这里不再多做说明。
接下来以 n=2593,X=5 为例来解释如何得到数学公式。从 1 至 2593 中,数字 5 总计出现了 813 次,其中有 259 次出现在个位,260 次出现在十位,294 次出现在百位,0 次出现在千位。
现在依次分析这些数据,首先是个位。从 1 至 2590 中,包含了 259 个 10,因此任意的 X 都出现了 259 次。最后剩余的三个数 2591, 2592 和 2593,因为它们最大的个位数字 3 < X,因此不会包含任何 5。(也可以这么看,3
然后是十位。从 1 至 2500 中,包含了 25 个 100,因此任意的 X 都出现了 25×10=250 次。剩下的数字是从 2501 至 2593,它们最大的十位数字 9 > X,因此会包含全部 10 个 5。最后总计 250 + 10 = 260。(也可以这么看,9>X,则十位上可能出现的X的次数仅由更高位决定,等于更高位数字(25+1)X102-1=260)。
接下来是百位。从 1 至 2000 中,包含了 2 个 1000,因此任意的 X 都出现了 2×100=200 次。剩下的数字是从 2001 至 2593,它们最大的百位数字 5 == X,这时情况就略微复杂,它们的百位肯定是包含 5 的,但不会包含全部 100 个。如果把百位是 5 的数字列出来,是从 2500 至 2593,数字的个数与百位和十位数字相关,是 93+1 = 94。最后总计 200 + 94 = 294。(也可以这么看,5==X,则百位上可能出现X的次数不仅受更高位影响,还受低位影响,等于更高位数字(2)X103-1+(93+1)=294)。
最后是千位。现在已经没有更高位,因此直接看最大的千位数字 2 < X,所以不会包含任何 5。(也可以这么看,2
到此为止,已经计算出全部数字 5 的出现次数。
总结一下以上的算法,可以看到,当计算右数第 i 位包含的 X 的个数时:
相应的代码非常简单,效率也非常高,时间复杂度只有 O( log 10 n) 。
public int NumberOfXBetween1AndN_Solution(int n,int x) { //x具有普适性
if(n<0||x<1||x>9)
return 0;
int high,low,curr,tmp, i = 1; //表示第几位 ---> 10的多少次方
high = n;
int total = 0;
while(high!=0){
high = n/(int)Math.pow(10, i);// 获取第i位的高位
tmp = n%(int)Math.pow(10, i); // 当前位//低位
curr = tmp/(int)Math.pow(10, i-1);// 获取第i位
low = tmp%(int)Math.pow(10, i-1);// 获取第i位的低位
if(curr==x){
total+= high*(int)Math.pow(10, i-1)+low+1;
}else if(curr
思路:
解法1:将整数数组中的数字读取到Arraylist中,利用Collections.sort(list,new Comparator())进行排序
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
public class Solution {
public String PrintMinNumber(int [] numbers) {
String result="";
if(numbers==null || numbers.length<=0)
return result;
int len=numbers.length; //数组长度
ArrayList list=new ArrayList(); //记录所有数字
for(int i=0;i (){ //自定义比较规则进行排序
public int compare(Integer str1, Integer str2){
String s1=str1+""+str2;
String s2=str2+""+str1;
return s1.compareTo(s2);
}
});
for(Integer j: list){
result+=j;
}
return result;
}
}
证明比较规则的有效性:
解法2:将整数数组--->字符串数组 Arrays.sort(数组,new Comparator()) 进行排序
import java.util.*;
public class Solution {
public String PrintMinNumber(int [] numbers) {
//基本思路str[] stringBuffer最后存再输出(转化为字符串)
if(numbers==null || numbers.length<=0)
return "";
int len=numbers.length;
String [] str=new String[len];
//将整数数组依次变成字符串数组
for(int i=0;i (){
public int compare(String s1,String s2){
String str1=s1+s2;
String str2=s2+s1;
return str1.compareTo(str2);
}
});
//排序完毕将字符串数组元素依次记录在StringBuffer里 连接起来成为字符串
StringBuffer sb=new StringBuffer();
for(int i=0;i
该解法超时!!
public class Solution {
public int GetUglyNumber_Solution(int index) {
//判断输入序号是否满足要求
if(index<=0)
return 0;
//开始按照顺序统计判断数字是否为丑数
int count=1;
int number=1;
while(count< index){
number++;
if(isUglyNumber(number)){
count++;
}
}
return number;
}
//判断一个数字是否为丑数
public boolean isUglyNumber(int number){
while(number%2==0) //如果能够整除2 则一直除2
number/=2;
while(number%3==0)
number/=3;
while(number%5==0)
number/=5;
return number==1?true:false;//若数字为丑数则最后数字为1
}
}
解法2: 用数组去按序存放丑数(*2/3/5)
public class Solution {
public int GetUglyNumber_Solution(int index){
//思路:用空间换取时间效率
//用一个数组去不断存入丑数,每个丑数都是之前丑数基础上*2/3/5
//需要对数组中的这些数排序 从而输出所需的第N个丑数
if(index<7) //前几个丑数 1 2 3 4 5 6
return index;
int [] res=new int[index]; //存入index个丑数
res[0]=1; //初始值为1
int t2=0,t3=0,t5=0;
for(int i=1; i< index;i++){
res[i]=min(res[t2]*2,res[t3]*3,res[t5]*5); //三者当中的最小值
if(res[i]==res[t2]*2)
t2++;
if(res[i]==res[t3]*3)
t3++;
if(res[i]==res[t5]*5)
t5++;
}
return res[index-1]; //返回存入的最后一个元素即可
}
public int min(int i,int j,int k){
int min=i
解法3: 用队列的思想去分别压入弹出
/*
1)初始化array和队列:Q2 Q3 Q5
2)将1插入array
3)分别将1*2、1*3 、1*5插入Q2 Q3 Q5
4)令x为Q2 Q3 Q5中的最小值,将x添加至array尾部
5)若x存在于:
Q2:将 x * 2、x * 3、x*5 分别放入Q2 Q3 Q5,从Q2中移除x
Q3:将 x * 3、x*5 分别放入Q3 Q5,从Q3中移除x
Q5:将 x * 5放入Q5,从Q5中移除x
6)重复步骤4~6,知道找到第k个元素
*/
import java.util.*;
public class Solution {
public int GetUglyNumber_Solution(int index) {
if(index==0)
return 0;
int n=1, ugly=1 ,min;
Queue q2=new LinkedList();
Queue q3=new LinkedList();
Queue q5=new LinkedList();
q2.add(2); q3.add(3); q5.add(5);
while(n!=index){
ugly=Math.min(q2.peek(),Math.min(q3.peek(),q5.peek()));
if(ugly==q2.peek()){
q2.add(ugly*2);q3.add(ugly*3);q5.add(ugly*5); q2.poll();
}
if(ugly==q3.peek()){
q3.add(ugly*3);q5.add(ugly*5); q3.poll();
}
if(ugly==q5.peek()){
q5.add(ugly*5); q5.poll();
}
n++; //统计个数
}
return ugly; //丑数的值
}
}
解法1 :用hashmap来做 两次循环遍历给定字符串
//用哈希表的思路去解决 空间复杂度o(n) 时间复杂度O(n)
import java.util.HashMap;
public class Solution {
HashMap map = new HashMap<>();
public int FirstNotRepeatingChar(String str) {
if (str==null)
return -1;
int length = str.length();
for(int i = 0;i
for(int i=0;i
解法2 :用bit数组进行存储
//思路:用bit数组 来存放字母的ASCII值 作为数组下标
public class Solution {
public int FirstNotRepeatingChar(String str) {
char[] chars = str.toCharArray();
int[] map = new int[256]; //char 8位 2的8次方为256
for (int i = 0; i < chars.length; i++) {
map[chars[i]]++;
}
for (int i = 0; i < chars.length; i++) {
if (map[chars[i]] == 1) return i;
}
return -1;
}
}
//利用归并排序的思路 在每次排序比较时进行统计反序对个数
public class Solution {
int cnt;
public int InversePairs(int[] array) {
cnt = 0;
if (array != null)
mergeSortUp2Down(array, 0, array.length - 1);
return cnt;
}
/*
* 归并排序(从上往下)
*/
public void mergeSortUp2Down(int[] a, int start, int end) {
if (start >= end)
return;
int mid = (start + end) >> 1;
mergeSortUp2Down(a, start, mid);
mergeSortUp2Down(a, mid + 1, end);
merge(a, start, mid, end);
}
/*
* 将一个数组中的两个相邻有序区间合并成一个
*/
public void merge(int[] a, int start, int mid, int end) {
int[] tmp = new int[end - start + 1];
int i = start, j = mid + 1, k = 0;
while (i <= mid && j <= end) {
if (a[i] <= a[j])
tmp[k++] = a[i++];
else {
tmp[k++] = a[j++];
cnt +=( mid - i + 1); // core code, calculate InversePairs............
cnt%=1000000007;
}
}
while (i <= mid)
tmp[k++] = a[i++];
while (j <= end)
tmp[k++] = a[j++];
for (k = 0; k < tmp.length; k++)
a[start + k] = tmp[k];
}
}
解法1 :常规二分查找 直接查找相应的目标数字 然后在其左右顺序扫描 时间复杂度为O(n)
public class Solution {
public int GetNumberOfK(int [] array , int k) {
if(array==null || array.length==0 )
return 0;
//最笨的二分查找
int begin=0;
int end=array.length-1;
int loc=-1;
while(begin<=end){
int middle=(begin+end)/2;
if(array[middle]==k){
loc=middle;
break;
}else if(k >array[middle]){
begin=middle+1;
}else
end=middle-1;
}
//记录找到目标数字位置 顺序遍历
if(loc==-1)
return 0;
int i=loc-1;
int j=loc+1;
while(i>=0 && array[i]==k )
i--;
while( j<=array.length-1 && array[j]==k)
j++;
return j-i-1; //注意这块计算!!
}
}
解法2 :加强版的二分查找----查找该目标数字的第一个和最后一个 然后下标作差--- 时间复杂度O(logn)
查找first目标数字步骤: 1 拿中间值和目标数字做比较
2 若相等 则判断下标是否为0 或者 下标>0 且其前面下标对应数字是否为该数字 -----不是 则返回下标 -----是则继续在前半段查找
3 若key>array[middle] 则递归在后半部分查找
4 其余(即key< array[middle] ) 则递归在前半部分查找
public class Solution {
public int GetNumberOfK(int [] array , int k) {
int number=0;
//分别找第一个和最后一个目标数字所在位置 然后作差
if(array!=null && array.length>0){
int first=getFirstKey(array,k,0,array.length-1);
int last=getLastKey(array,k,0,array.length-1);
if(first>-1 && last>-1)
number=last-first+1;
}
return number;
}
//查找第一个
public int getFirstKey(int [] array, int key, int begin, int end){
int i=begin;
int j=end;
while(i<=j){
int middle=(i+j)>>1;
if(array[middle]==key){
//判断第一个目标数字是位置0 或者 不是0 且前面元素不等
if(middle==0 || (middle>0 && array[middle-1]!=key)) {
return middle;
}else{
j=middle-1;
}
}else if(array[middle]< key){
i=middle+1;
}else{
j=middle-1;
}
}
return -1;
}
//查找最后一个
public int getLastKey(int [] array, int key, int begin, int end){
int i=begin;
int j=end;
while(i<=j){
int middle=(i+j)>>1;
if(array[middle]==key){
if(middle==array.length-1 || (middle
由浅入深: 只有一个出现一次,其他均为两次?
解法:异或
则 :两个出现一次的数字:
1 依然先整体异或 结果一定不为0
2 得到二进制为1 的第一个位置 以此为标准 将数组分解为两个子数组
3 由此可知 这两个数一定被分到不同的子数组---再运用一个数字出现一次的解法即可~~
//num1,num2分别为长度为1的数组。传出参数
//将num1[0],num2[0]设置为返回结果
public class Solution {
public void FindNumsAppearOnce(int [] array,int num1[] , int num2[]) {
if(array==null || array.length<2)
return ;
int sum=0; //计算整个数组元素异或的结果
for(int i=0;i0){
if((sum & 1)==1){
break;
}
sum>>=1;
index++;
}
return index;
}
public boolean isBit1(int num, int index){
num=num>>index;
return((num&1)==1);
}
}
拓展到 其他数字出现n次 则利用二进制形式去做 多少次则对应二进制位上和为多少 进行取余即为 那唯一多余的一个数字
/**
* 数组中有两个出现一次的数字,其他数字都出现两次,找出这两个数字
* @param array
* @param num1
* @param num2
*/
public static void findNumsAppearOnce(int [] array,int num1[] , int num2[]) {
if(array == null || array.length < 2){
num1[0] = num2[0] = 0;
return;
}
int len = array.length, index = 0, sum = 0;
for(int i = 0; i < len; i++){
sum ^= array[i];
}
for(index = 0; index < 32; index++){
if((sum & (1 << index)) != 0) break;
}
for(int i = 0; i < len; i++){
if((array[i] & (1 << index))!=0){
num2[0] ^= array[i];
}else{
num1[0] ^= array[i];
}
}
}
/**
* 数组a中只有一个数出现一次,其他数都出现了2次,找出这个数字
* @param a
* @return
*/
public static int find1From2(int[] a){
int len = a.length, res = 0;
for(int i = 0; i < len; i++){
res = res ^ a[i];
}
return res;
}
/**
* 数组a中只有一个数出现一次,其他数字都出现了3次,找出这个数字
* @param a
* @return
*/
public static int find1From3(int[] a){
int[] bits = new int[32];
int len = a.length;
for(int i = 0; i < len; i++){
for(int j = 0; j < 32; j++){ //int型 为32位二进制位
bits[j] +=( (a[i]>>>j ) & 1); //把每一个数字对应的二进制位求和
}
}
int res = 0;
for(int i = 0; i < 32; i++){
if(bits[i] % 3 !=0){
res += (1 << i); //取余剩下的即为唯一的那个数字的二进制形式
}
}
return res;
}
额外要求:
如果有多对数字的和等于S,输出两个数的乘积最小的。
解法: 有序数组 和为固定值 ------头尾指针 分别移动
import java.util.ArrayList;
public class Solution {
public ArrayList FindNumbersWithSum(int [] array,int sum) {
ArrayList list=new ArrayList();
if(array==null || array.length==0)
return list;
int i=0;
int j=array.length-1;
while(i
分析:乘积最小一定是第一对?
假设:找到两组满足条件的数组对(x,y)、(x+a,y-a),其中(x+y=S, 0
解法
例子: 和为s 从1 开始
import java.util.ArrayList;
public class Solution {
public ArrayList< ArrayList > FindContinuousSequence(int sum) {
ArrayList> listAll = new ArrayList>();
if (sum < 3)
return listAll;
int small = 1;
int big = 2;
int middle = (sum + 1) / 2;
int curSum = 3;
while (small < middle) {
//若当前和 list = new ArrayList<>();
for (int i = small; i <= big; i++) {
list.add(i);
}
listAll.add(list);
}
//若当前和>sum 则将small右移
curSum -= small;
small++;
}
return listAll;
}
}
public class Solution {
public String ReverseSentence(String str) {
if(str.length()<=0 || str=="")
return "";
char [] c=str.toCharArray();
reverse(c,0,c.length-1); //整体反转
int begin=0;
int end=0;
for(int i=0;i
另解: 使用split trim 不过这样就是简便 可能会增加额外的空间
public String trim()
public class Solution {
public String ReverseSentence(String str) {
//判断输入为"" 或 " "这些情况
if(str.trim().equals("")){
return str;
}
//按空格切割句子
String[] arr = str.split(" ");
StringBuilder sb = new StringBuilder();
for(int i=arr.length-1;i>=0;i--){
sb.append(arr[i]);
//最后一个单词后面不需要有空格
if(i != 0){
sb.append(" ");
}
}
return sb.toString();
}
}
原理:YX = (XTY T)T
public class Solution {
public String LeftRotateString(String str,int n) {
if(str=="" || n<0 ||str.length()< n)
return "";
char[] c=str.toCharArray();
reverse(c,0,n-1); //经过3次分段反转 思路同题目1
reverse(c,n,c.length-1); //注意各段的范围
reverse(c,0,c.length-1);
return new String(c);
}
public void reverse(char[] c,int begin, int end){
while(begin< end){
char temp=c[begin];
c[begin]=c[end];
c[end]=temp;
begin++;
end--;
}
}
}
将一个字符串转换成一个整数,要求不能使用字符串转换整数的库函数。 数值为0或者字符串不是一个合法的数值则返回0
输入描述:
输入一个字符串,包括数字字母符号,可以为空
输出描述:
如果是合法的数值表达则返回该数字,否则返回0
+2147483647 1a33
2147483647 0
需要考虑的:
方法1:
public class Solution {
public int StrToInt(String str) {
//四种特殊用例: 空指针null ""字符串 首位的+-号 溢出问题
//功能测试用例 :合法数字字符判断
if (str.equals("") || str.length() == 0)
return 0;
char[] a = str.toCharArray();
int fuhao = 0;
if (a[0] == '-')
fuhao = 1;
int sum = 0;
for (int i = fuhao; i < a.length; i++)
{
if (a[i] == '+') //不能少这句 因为当第一位是+号时,也是正常的
continue;
if (a[i] < 48 || a[i] > 57) //是否是合法数字字符
return 0;
sum = sum * 10 + a[i] - 48; //0的ascii值就是48 '1'=49 要想转换为整数 必须达到统一
if(sum >Integer.MAX_VALUE) //此时无论sum为正或负 只要值>整数最大值 则一定溢出
return Integer.MAX_VALUE;
}
return fuhao == 0 ? sum : sum * -1;
}
}
在一个长度为n的数组里的所有数字都在0到n-1的范围内。 数组中某些数字是重复的,但不知道有几个数字是重复的。也不知道每个数字重复几次。请找出数组中任意一个重复的数字。 例如,如果输入长度为7的数组{2,3,1,0,2,5,3},那么对应的输出是第一个重复的数字2。
public boolean duplicate(int numbers[],int length,int [] duplication) {
//采用最原始的方法
//先判断输入是否合法
if(numbers==null || length==0)
return false;
//开始双层循环 穷举判断
for(int i=0;i
思想:哈希数组 下标 代表数字 储存数值为个数 直接循环查找值>1的----对应下标即为所求值
//采用hashMap储存 时间O(n) 空间O(n)
HashMap map=new HashMap();
for(int i=0;i1的键值
Iterator iter=map.entrySet().iterator();
while(iter.hasNext()){
Map.Entry entry=(Map.Entry)iter.next();
int key=(Integer)entry.getKey();
int value=(Integer)entry.getValue();
if(value>1){
duplication[0]=key;
return true;
}
}
return false;
}
public boolean duplicate(int numbers[],int length,int [] duplication) {
if(numbers==null || length==0)
return false;
//采用将数组排序 比较相邻元素
Arrays.sort(numbers);
for(int i=0;i
public boolean duplicate(int numbers[],int length,int [] duplication) {
/*
(1)boolean不是占1位,计算机处理处理数据的最小单元是1字节,一般1位的话,其余7位会被0补齐。
(2)在java虚拟机规范中,JVM没有用于操作boolean的字节码指令,在编译后用int的数据类型代替boolean,此时boolean占4字节。
(3)boolean[]数组编译后会被byte[]数组代替,此时的boolean占1字节。
总结:boolean单独存在占4字节,在boolean[]中占1字节。
*/
boolean[] k = new boolean[length]; for (int i = 0; i < k.length; i++) { if (k[numbers[i]] == true) { duplication[0] = numbers[i]; return true; } k[numbers[i]] = true; } return false; }}
此法利用了哈希的特性,但不需要额外的存储空间。 因此时间复杂度为O(n),不需要额外空间!
public boolean duplicate(int numbers[],int length,int [] duplication) {
if(numbers == null || length <= 0) {
return false; } for(int i = 0; i < length; i++) { while(numbers[i] != i) { if(numbers[i] == numbers[numbers[i]]) { duplication[0] = numbers[i]; return true; } int temp = numbers[i]; numbers[i] = numbers[temp]; numbers[temp] = temp; } } return false; }}
详细分析:
与一种经典的排序算法——基数排序非常类似
类基数排序:如果当前的数字不等于当前的位置,就把当前数字换到其对应的位置上去,然后依次类推,直到找到重复的元素为止。
下面以2,4,1,5,7,6,1,9,0,2这十个数为例,展示下如何用基数排序来查找重复元素。
下标 |
0 |
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
数据 |
2 |
4 |
1 |
5 |
7 |
6 |
1 |
9 |
0 |
2 |
(1)由于第0个元素a[0] 等于2不为0,故交换a[0]与a[a[0]]即交换a[0]与a[2]得:
下标 |
0 |
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
数据 |
1 |
4 |
2 |
5 |
7 |
6 |
1 |
9 |
0 |
2 |
(2)由于第0个元素a[0] 等于1不为0,故交换a[0]与a[a[0]]即交换a[0]与a[1]得:
下标 |
0 |
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
数据 |
4 |
1 |
2 |
5 |
7 |
6 |
1 |
9 |
0 |
2 |
(3)由于第0个元素a[0] 等于4不为0,故交换a[0]与a[a[0]]即交换a[0]与a[4]得:
下标 |
0 |
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
数据 |
7 |
1 |
2 |
5 |
4 |
6 |
1 |
9 |
0 |
2 |
(4)由于第0个元素a[0] 等于7不为0,故交换a[0]与a[a[0]]即交换a[0]与a[7]得:
下标 |
0 |
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
数据 |
9 |
1 |
2 |
5 |
4 |
6 |
1 |
7 |
0 |
2 |
(5)由于第0个元素a[0] 等于9不为0,故交换a[0]与a[a[0]]即交换a[0]与a[9]得:
下标 |
0 |
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
数据 |
2 |
1 |
2 |
5 |
4 |
6 |
1 |
7 |
0 |
9 |
(6)由于第0个元素a[0] 等于2不为0,故交换a[0]与a[a[0]]即交换a[0]与a[2],但a[2]也为2与a[0]相等,因此我们就找到了一个重复的元素——2。
下标 |
0 |
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
数据 |
2 |
1 |
2 |
5 |
4 |
6 |
1 |
7 |
0 |
9 |
数组里数字的范围保证在0 ~ n-1 之间,所以可以利用现有数组设置标志,当一个数字被访问过后,可以设置对应位上的数 + n,之后再遇到相同的数时,会发现对应位上的数已经大于等于n了,那么直接返回这个数即可。
public boolean duplicate(int numbers[],int length,int [] duplication) {
//采用标记法 时间O(n)
if(numbers==null || length==0)
return false;
for(int i=0;i=length){
index-=length;
}
if(numbers[index]>=length){
duplication[0]=index;
return true;
}
numbers[index]=numbers[index]+length;
}
return false;
}
我们遍历一遍数字,把nums[i]指向的数(即nums[nums[i]])做一个+n的操作,那么如果遇到一个nums[nums[i]]的值已经大于n了,说明这个数已经被其他数字指到过了,也就是找到了重复值。在执行的过程中,我们还要先判断一下nums[i]是否大于n(因为可能先前被别人指过所以+n了),用一个值来保存其原来的值。
给定一个数组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]。不能使用除法。
import java.util.ArrayList;
public class Solution {
public int[] multiply(int[] A) {
int n=A.length;
int [] B=new int[n];
if(A==null || n==0)
return B;
B[0]=1;
//分为下半部分
for(int i=1;i=0;j--){
temp*=A[j+1];
B[j]*=temp;
}
return B;
}
}