《剑指offer》------字符串/数组专题


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<arr.length  && j>=0) {     //从右上角开始查找
				if(key==arr[i][j]) {   
					flag=true;
					break;
				}
				else if(key<arr[i][j])   
					j--;
				else 
					i++;
		}
		return flag;
	}
}





思路:


缺点:







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<len;i++) {
			if(c[i]==' ')
				count++;
		}
		char[] c1=new char[count*2+len];  //新建长度字符数组
		int i=len-1;      //指向c末端
		int j=c1.length-1;     //指向c1末端
		//循环遍历c,一步步移动赋值c1,若遇空格则c移动1  c1移动3  直到c,c1均同时移动至位置0结束
		while(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<s.length();i++) {
				char c=s.charAt(i);       
				if(c==' ') {
					sb.append("%20");         
				}else {
					sb.append(c);
				}
			}
			return sb.toString();
		}
}








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<absExponent;i++) {
			result*=base;
		}
		return result;
	}
	
	//double/float均不能直接使用==来评判是非相等 涉及到精度问题 故一般自己写判断函数  存在一定误差范围内
	private static boolean equal(double base, double d) {
		if(base-d>-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++<n) {
        	        number*=10;       //计算比n位最大数大1的那个整数
                }
                for(int i1=1;i1<number;i1++) {      //作为界限打印
        	        System.out.print(i1+" ");
                }
}

问题:



解决方法:





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<size && arr[i]=='0')    //不越界且为0 的则继续寻找
			i++;
		if(i==size)      //不打印全0数字
			return;
		char[] printArr=Arrays.copyOfRange(arr, i, size);  //复制非0字符开头的数组
		System.out.println(printArr);                      //可以直接打印char[] 数组内容 即具体数字表示
	}
}

注:结果均经过测试 符合要求~







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<j){ 
				while(i<j && (array[i]&1)==1) //若是奇数则继续遍历 i++; 
					while(i<j && (array[j]&1)==0) //若是偶数则继续遍历 j--; 
						if(i<j){ //前偶后奇 则进行交换 
							int temp=array[i]; 
							array[i]=array[j]; 
							array[j]=temp; 
						}
			}
		}

优化:将判断和操作函数分开   确定分组标准,便于标准修改

//创建一个新数组 然后移动  复制给原数组即可
        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<len;i++){
            if((array[i]&1)==1)
                oddCount++;
        }
        int i=0;    //奇数存放开始位置
        int j=oddCount;   //偶数初始存放位置
        for(int k=0;k<len;k++){
            if((array[k]&1)==1)
                newArray[i++]=array[k];
            else
                newArray[j++]=array[k];
        }
        
        //将新数组原样复制给原数组
        for(int m=0;m<len;m++){
            array[m]=newArray[m];
        }

题目变为:

输入一个整数数组,实现一个函数来调整该数组中数字的顺序,使得所有的奇数位于数组的前半部分,所有的偶数位于位于数组的后半部分,并保证奇数和奇数,偶数和偶数之间的相对位置不变。

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<i;j++){ 
				if((array[j]&1)==0&&(array[j+1]&1)==1){ 
					int temp=array[j]; 
					array[j]=array[j+1]; 
					array[j+1]=temp; 
			}
		}
	}

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<len;i++){
            if((array[i]&1)==1)
                oddCount++;
        }
        int i=0;    //奇数存放开始位置
        int j=oddCount;   //偶数初始存放位置
        for(int k=0;k<len;k++){
            if((array[k]&1)==1)
                newArray[i++]=array[k];
            else
                newArray[j++]=array[k];
        }
        
        //将新数组原样复制给原数组
        for(int m=0;m<len;m++){
            array[m]=newArray[m];
        }
    }

3.插入排序  前偶后奇才移动 插入  (标准)

 public void reOrderArray(int [] array) { 
        for(int i=0;i<array.length;i++){
                    if(array[i]%2==1){
                        int temp=array[i];
                        int j=i-1;
                        while(j>=0 && array[j]%2==0){
                            array[j+1]=array[j];
                            j--;
                        }
                        array[j+1]=temp;
                    }            
                }
    }





思路分析:






import java.util.ArrayList;
public class Solution {
    public ArrayList<Integer> printMatrix(int [][] matrix) {
        int rows=matrix.length;
        int columns=matrix[0].length;
        if(matrix==null || rows<=0|| columns<=0)
            return null;
        ArrayList<Integer> al=new ArrayList<Integer>();
        int start=0;   //记录每一圈起始坐标
        while( start*2< rows && start*2 <columns){
            printMatrixinCircle(matrix,rows,columns,start,al);
            start++;
        }
        return al;
    }
    
    public ArrayList<Integer> printMatrixinCircle(int[][] matrix,int rows,int columns,int start,ArrayList<Integer> al){
        int endX=columns-1-start;     //打印内圈最右列
        int endY=rows-1-start;        //打印内圈最下行
        //打印第一行(肯定会打印,无需限制条件)
        for(int i=start;i<=endX;i++){
            al.add(matrix[start][i]);
        }
        
        //打印第一列
        if(start<endY){
            for(int i=start+1; i<=endY ;i++){
                al.add(matrix[i][endX]);
            }
        }
        
        //打印第二行
        if(endY>start && endX>start){
            for( int i=endX-1;i>=start;i--){
                al.add(matrix[endY][i]);
            }
        }
        
        //打印第二列
        if(start+1<endY  && endX>start){
            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<String> Permutation(String str) {
        //对输入进行判断
        ArrayList<String> al=new ArrayList<String >();
        if(str!=null ){
            al=fullPermutation(str.toCharArray(),0,al);
            Collections.sort(al);
        }
        return al;
    }
    //全排列  (考虑字符串重复问题ABA这种)
    public ArrayList<String> fullPermutation(char[] c,int start,ArrayList<String> al){
        if(start==c.length-1){         //递归到一个字符情况  该返回此字符串(叶子节点)
            al.add(new String(c));     //或者String.valueOf(c) 也行
        }else{
            for(int i=start; i<c.length; i++){   //循环字符串
                if((i!=start && c[i]!=c[start]) || i==start){   //避免出现ABA这种重复情况
                    char tmp=c[start];                //每个元素均和第一个元素进行交换再递归
                    c[start]= c[i];
                    c[i]=tmp;
                    fullPermutation(c,start+1,al);    //start+1 继续递归
                    tmp=c[start];                    //之后再换回来
                    c[start]= c[i];
                    c[i]=tmp;
                }

            }
        }
        return al;  
    }
}

用栈的方法:

import java.util.*;
public class Solution {
    public ArrayList<String> Permutation(String str) {
            TreeSet<String> tree = new TreeSet<>();
            Stack<String[]> stack = new Stack<>();
            ArrayList<String> 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){
                begin=index+1;
                index=partition(array,begin,end);
            }else 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<j){
            while(i<j && array[j]> temp)
                j--;
            if(i<j)
                array[i++]=array[j];
            while(i<j && array[i] <temp)
                i++;
            if(i<j)
                array[j--]=array[i];
        }
        array[i]=temp;
        return i;
    }
    //查看次数
    public boolean checkMoreThanHalf(int[] array ,int result){
        int count=0;
        boolean flag=true;
        for(int i=0;i<array.length;i++){
            if(array[i]==result)
                count++;
        }
        if(count <= array.length/2)
            flag=false;
        return flag;
    }
}



注意:

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;i<array.length;i++){
            if(array[i]==temp){
                count++;
            }
            if(count >array.length/2 )
                flag=true;
        }
        return flag;
    }
}



解法3: 使用hashmap集合存储元素  看个数是否有符合条件项  O(n)

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<Integer,Integer> map=new HashMap<Integer,Integer>();
        for(int i=0;i<array.length;i++){
            if(map.containsKey(array[i])){
                map.put(array[i], map.get(array[i])+1);
            }else{
                map.put(array[i], 1);
            }
        }
        for (Map.Entry<Integer, Integer> entry : map.entrySet()) { 
            if(entry.getValue()>array.length/2)
                return entry.getKey();
        } 
        return 0;
    }
}


解法4:  使用内置排序  选择中间数字  统计次数  O(nlogn)

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;i++){
            if(array[i]==temp)
                count++;
        }
        if(count> len/2){
            return temp;
        }
        return 0;
    }
}







import java.util.ArrayList;
public class Solution {
    public ArrayList<Integer> GetLeastNumbers_Solution(int [] input, int k) {
        ArrayList<Integer> list=new ArrayList<Integer>();
        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<k;i++){
            list.add(input[i]);    
        }
        return list;
    }
    
    public int partition(int [] array, int begin, int end){
        int i=begin;
        int j=end;
        int temp=array[begin];
        while(i<j){
            while(i<j && array[j]> temp)
                j--;
            if(i<j)
                array[i++]=array[j];
            while(i<j && array[i] <temp)
                i++;
            if(i<j)
                array[j--]=array[i];
        }
        array[i]=temp;
        return i;
    }
}




堆思想(最大堆)+  优先队列实现 (O(nlogk)
import java.util.ArrayList;
import java.util.PriorityQueue;
import java.util.Comparator;
public class Solution {
   public ArrayList<Integer> GetLeastNumbers_Solution(int[] input, int k) {
       //对输入进行鲁棒性检查
       ArrayList<Integer> result = new ArrayList<Integer>();
       int length = input.length;
       if(k > length || k == 0){
           return result;
       }
       //定义优先级队列(充当最大堆)  传入初始容量和比较器comparator
        PriorityQueue<Integer> maxHeap = new PriorityQueue<Integer>(k, new Comparator<Integer>() {
            @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;
    }
}







解法3 :冒泡排序法(只是求出前k个最小的即可) O(nk)
public class Solution {
   public ArrayList<Integer> GetLeastNumbers_Solution(int[] input, int k) {
       //对输入进行鲁棒性检查
       ArrayList<Integer> result = new ArrayList<Integer>();
       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<i;j++){
               if(input[j] <input[j+1]){     //注意极值需要调整为最小值
                   int temp=input[j];
                   input[j]=input[j+1];
                   input[j+1]=temp;
               }
           } 
           result.add(input[i]);
       }
       return result;
   }
}

解法4:建立最大堆  

import java.util.*;
public class Solution {
    public ArrayList<Integer> GetLeastNumbers_Solution(int [] input, int k) {
        ArrayList<Integer> result = new ArrayList<Integer>();
        if(input==null||input.length==0||input.length<k){
            return result;
        }
        //构建大顶堆
        for(int i=k/2-1;i>=0;i--){
            adjustHeap(input,i,k-1);
        }
        //我们前k个元素的大顶堆已经构建好了,剩下的就是其余的和大顶堆的最大值比较了
        for(int i=k;i<input.length;i++){
            if(input[i]<input[0]){
                int temp=input[i];
                input[i]=input[0];
                input[0]=temp;
                adjustHeap(input,0,k-1);
                 
            }
        }
        //我们将调整好的前k个数放进链表里面
        for(int i=0;i<k;i++){
            result.add(input[i]);
        }
        return result;
         
         
    }
             
            //构建大顶堆
    public  void adjustHeap(int[] input,int i,int k){
        //先把最上面根节点保存了
        int temp=input[i];
        for(int j=i*2+1;j<=k;j=j*2+1){
            //j可以等于k,但是下面的程序不能,我们还要判断j和j+1呢
            if(j<k&&input[j]<input[j+1]){
                j++;
            }
            if(temp>input[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<array.length;i++){
            if(curSum<0){
                curSum=array[i];
            }else {
                curSum+=array[i];
            }
            
            if(curSum> 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+1;i++){
             s.append(i);
        }
        String str=s.toString();
        for(int i=0;i<str.length();i++){
            if(str.charAt(i)=='1')
                count++;
        }
        return count;
    }
}

/*
设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的数目

编程之美上给出的规律:

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,在它们的个位数中,任意的 X 都出现了 1 次。
  • 从 1 至 100,在它们的十位数中,任意的 X 都出现了 10 次。
  • 从 1 至 1000,在它们的百位数中,任意的 X 都出现了 100 次。

依此类推,从 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<X,则个位上可能出现的X的次数仅由更高位决定,等于更高位数字(259)X101-1=259)。

然后是十位。从 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<X,则千位上可能出现的X的次数仅由更高位决定,等于更高位数字(0)X104-1=0)。

到此为止,已经计算出全部数字 5 的出现次数。

总结一下以上的算法,可以看到,当计算右数第  i  位包含的 X 的个数时:

  1. 取第  i  位左边(高位)的数字,乘以  10 i−1 ,得到基础值  a 。
  2. 取第  i  位数字,计算修正值
    1. 如果大于 X,则结果为  a+ 10 i−1
    2. 如果小于 X,则结果为  a 。
    3. 如果等 X,则取第  i  位右边(低位)数字,设为  b ,最后结果为  a+b+1 。

相应的代码非常简单,效率也非常高,时间复杂度只有  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<x){
    			total+=high*(int)Math.pow(10, i-1);
    		}else{
    			total+=(high+1)*(int)Math.pow(10, i-1);
    		}
    		i++;
    	}
		return total;        
    }




思路:





解法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<Integer>  list=new ArrayList<Integer>();  //记录所有数字
        for(int i=0;i<len;i++){
            list.add(numbers[i]);
        }
        
        Collections.sort(list,new Comparator<Integer> (){   //自定义比较规则进行排序
            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<len;i++){
            str[i]=String.valueOf(numbers[i]);
        }
        //开始给字符串数组按照自定义的排序规则进行排序
        Arrays.sort(str,new Comparator<String> (){
            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<len;i++){
            sb.append(str[i]);
        }
        
        return  sb.toString();  

    }
}





该解法超时!!

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<j? i:j;
        min=min< k? min:k;
        return min;
    }
}


解法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<Integer> q2=new LinkedList<Integer>();
    Queue<Integer> q3=new LinkedList<Integer>();
    Queue<Integer> q5=new LinkedList<Integer>();  
    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<Character, Integer> map = new HashMap<>();
 
    public int FirstNotRepeatingChar(String str) {
        if (str==null)
            return -1;
        int length = str.length();
        for(int i = 0;i<length;i++) {
            if(map.containsKey(str.charAt(i))){
                int value = map.get(str.charAt(i));
                map.put(str.charAt(i),value+1);
            }else{
                map.put(str.charAt(i),1);
            }
        }
       for(int i=0;i<length;i++){
            if(map.get(str.charAt(i))==1) //这句话很关键  其实就是在按照顺序遍历原字符串并观察对应哈希表中次数是否为1
                return i;
        }
        return -1;
    }
}

解法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<array.length-1 && array[middle+1]!=key)) {
                    return middle;
                }else{
                    i=middle+1;
                }
            }else if(array[middle]< key){
                i=middle+1;
            }else{
                j=middle-1;
            }
        }
        return -1;
    }
}



由浅入深: 只有一个出现一次,其他均为两次?


解法:异或



则 :两个出现一次的数字:  

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;i<array.length;i++){
            sum^=array[i];
        }
        int index=findFirstBit1(sum);      //找到第一个1出现的位置
        num1[0]=0;      //存放最终输出的那两个只出现一次的数字
        num2[0]=0;
        for(int i=0;i<array.length;i++){  //分别分组 判断 
            if(isBit1(array[i],index))
                num1[0]^=array[i];
            else
                num2[0]^=array[i];
        }
    }
    
    public int findFirstBit1(int sum){
        int index=0;
        while(sum>0){
            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,输出两个数的乘积最小的。


和为S的两个数字:

解法: 有序数组 和为固定值 ------头尾指针 分别移动

import java.util.ArrayList;
public class Solution {
    public ArrayList<Integer> FindNumbersWithSum(int [] array,int sum) {
        ArrayList<Integer> list=new ArrayList<Integer>();
        if(array==null || array.length==0)
            return list;
        int i=0;
        int j=array.length-1;
        while(i<j){
            int sum2=array[i]+array[j];
            if(sum2==sum){
                list.add(array[i]);
                list.add(array[j]);
                break;
            }else if(sum2<sum){
                i++;
            }else {
                j--;
            }
        }
        return list;
    }
}

析:乘积最小一定是第一对?

假设:找到两组满足条件的数组对(x,y)、(x+a,y-a),其中(x+y=S, 0<a<y-x)
 * x*y-[(x+a)(y-a)]
 *  =x*y-x*y-(y-x)a+a2
 *  =a(x-y)+a2               x<y  故差为负数


和为S的连续正数序列:



解法


例子:  和为s    从1 开始



import java.util.ArrayList;
public class Solution {
    public ArrayList< ArrayList<Integer> > FindContinuousSequence(int sum) {
        ArrayList<ArrayList<Integer>> listAll = new ArrayList<ArrayList<Integer>>();
        if (sum < 3)
            return listAll;
        int small = 1;
        int big = 2;
        int middle = (sum + 1) / 2;
        int curSum = 3;
        while (small < middle) {
            //若当前和<sum 则继续加大big
            while (curSum < sum) {
                big++;
                curSum += big;
            }
            //若当前和==sum 则记录下来
            if (curSum == sum) {
                ArrayList<Integer> 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<c.length;i++){
            if(c[i]==' '){
                end=i-1;                      //按照空格切割的段进行反转
                reverse(c,begin,end);
                begin=i+1;
            }
        }
        reverse(c, begin, 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--;
        }
    }
}


另解:  使用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

示例1

输入

+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。


方法1:最常规的双层循环做法  时间O(n2)  空间无
public boolean duplicate(int numbers[],int length,int [] duplication) {
//采用最原始的方法
        //先判断输入是否合法
        if(numbers==null || length==0)
            return false;
        //开始双层循环 穷举判断
        for(int i=0;i<length-1;i++){
            for(int j=i+1;j< length;j++){
                if(numbers[i]==numbers[j]){
                    duplication[0]=numbers[i];
                    return true;
                }
            }
        }
        return false;
   }

方法2 :hashmap结构  空间O(n)  时间O(n)    

  思想:哈希数组  下标  代表数字   储存数值为个数 直接循环查找值>1的----对应下标即为所求值

//采用hashMap储存  时间O(n) 空间O(n)
        HashMap<Integer,Integer>  map=new HashMap<Integer,Integer>();
        for(int i=0;i<length;i++){
            if(map.containsKey(numbers[i])){
                map.put(numbers[i],map.get(numbers[i])+1);
            }else{
                map.put(numbers[i],1);
            }
        }
        //筛选次数>1的键值
        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;
    }

方法3:排序数组 比较相邻元素 ----时间 O(nlogn)    无空间
public boolean duplicate(int numbers[],int length,int [] duplication) {
        if(numbers==null || length==0)
            return false;
        //采用将数组排序 比较相邻元素
        Arrays.sort(numbers); 
        for(int i=0;i<length-1;i++){
            if(numbers[i]==numbers[i+1]){
                duplication[0]=numbers[i];
                return true;
            }
        }
        return false;
    }

前3种都是我想出来的方法  后面参考更为简洁的别人的方法:
方法4 :标记法    时间O(n)     空间 1字节
   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;    }}


方法5: 基数排序  时间复杂度O(n),空间复杂度O(1),需要移动原数组,不用改动原数组。

此法利用了哈希的特性,但不需要额外的存储空间。 因此时间复杂度为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;    }}

详细分析:

与一种经典的排序算法——基数排序非常类似

类基数排序:如果当前的数字不等于当前的位置,就把当前数字换到其对应的位置上去,然后依次类推,直到找到重复的元素为止。


下面以2415761902这十个数为例,展示下如何用基数排序来查找重复元素。

下标

  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]也为2a[0]相等,因此我们就找到了一个重复的元素——2

下标

  0

  1

  2

  3

  4

  5

  6

  7

  8

  9

数据

  2

  1

  2

  5

  4

  6

  1

  7

  0

  9


方法6: 时间复杂度O(n),空间复杂度O(1),不用移动原数组,需要改动原数组。

数组里数字的范围保证在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;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;
    }

我们遍历一遍数字,把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]。不能使用除法。

B[i]的值可以看作下图的矩阵中每行的乘积。
下三角用连乘可以很容求得,上三角,从下向上也是连乘。
因此我们的思路就很清晰了,先算下三角中的连乘,即我们先算出B[i]中的一部分,然后倒过来按上三角中的分布规律,把另一部分也乘进去。
时间/空间复杂度均为O(n)    其实质就是以i将乘积分为左右两部分,分别计算后再统一起来



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<n;i++){
            B[i]=B[i-1]*A[i-1];
        }
        //分为上半部分
        int temp=1;
        for(int j=n-2;j>=0;j--){
            temp*=A[j+1];
            B[j]*=temp;
        }
        return B;
    }
}


  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值