1、二维数组中的查找
题目描述
在一个二维数组中(每个一维数组的长度相同),每一行都按照从左到右递增的顺序排序,每一列都按照从上到下递增的顺序排序。请完成一个函数,输入这样的一个二维数组和一个整数,判断数组中是否含有该整数。
Solution 1:
public boolean Find(int target, int [][] array) {
int rows=array.length;
int cols=array[0].length;
int i=rows-1,j=0;
while(i>=0&&j<cols){
if(target<array[i][j]){
i--;
}else if(target>array[i][j]){
j++;
}else{
return true;
}
}
return false;
}
2、替换空格
题目描述
请实现一个函数,将一个字符串中的每个空格替换成“%20”。例如,当字符串为We Are Happy.则经过替换之后的字符串为We%20Are%20Happy。
Solution 1:
//难点在于对与char与String,StringBuffer之间的转换熟悉
public String replaceSpace(StringBuffer str) {
StringBuffer sb=new StringBuffer();
for(int i=0;i<str.toString().length();i++){
char ch=str.charAt(i);
if(" ".equals(String.valueOf(ch))){
sb.append("%20");
}else{
sb.append(ch);
}
}
return sb.toString();
}
3、旋转数组的最小数字
题目描述
把一个数组最开始的若干个元素搬到数组的末尾,我们称之为数组的旋转。 输入一个非减排序的数组的一个旋转,输出旋转数组的最小元素。 例如数组{3,4,5,1,2}为{1,2,3,4,5}的一个旋转,该数组的最小值为1。 NOTE:给出的所有元素都大于0,若数组大小为0,请返回0。
Solution 1:
由题意非减排序,只要遇到递减的第一个数就是最小的
public int minNumberInRotateArray(int [] array) {
if(array==null||array.length<=0){
return -1;
}
int index=0;
for(int i=1;i<array.length;i++){
if(array[i]<array[i-1]){
index=array[i];
return index;
}
}
return -1;
}
Solution 2:
二分查找
public int minNumberInRotateArray(int [] array) {
if(array==null||array.length<=0){
return -1;
}
int low=0;
int high=array.length-1;
int middle=-1;
while(array[low]>=array[high]){
if(high-low==1){
middle=high;
break;
}
//不断缩小搜索范围
middle=low+(high-low)/2;
if(array[middle]>=array[low]){
low=middle;
}
if(array[middle]<=array[high]){
high=middle;
}
}
return array[middle];
}
Solution 3:
利用sort函数排序,取最小
时间复杂度:nlog(n),由于arrays.sort函数采用快速排序对int类型的数据排序
public int minNumberInRotateArray(int [] array) {
if(array==null||array.length<=0){
return -1;
}
Arrays.sort(array);
return array[0];
}
4、斐波那契数列
题目描述
大家都知道斐波那契数列,现在要求输入一个整数n,请你输出斐波那契数列的第n项(从0开始,第0项为0)。
n<=39
Solution 1:
递归(Recursion)
public int Fibonacci(int n) {
if(n<=1)
return n;
else
return Fibonacci(n-1)+Fibonacci(n-2);
}
Solution 2:
动态规划
public int Fibonacci(int n) {
int preNum=1;
int prepreNUm=0;
int result=0;
if(n==0)
return 0;
if(n==1)
return 1;
for(int i=2;i<=n;i++){
result=preNum+prepreNUm;
prepreNUm=preNum;
preNum=result;
}
return result;
}
5、调整数组顺序使奇数位于偶数前面
题目描述
输入一个整数数组,实现一个函数来调整该数组中数字的顺序,使得所有的奇数位于数组的前半部分,所有的偶数位于数组的后半部分,并保证奇数和奇数,偶数和偶数之间的相对位置不变。
solution 1:
新建数组,设立奇数开始指针和结束指针
public void reOrderArray(int [] array) {
int[] tmp=new int[array.length];
int oddBengin=0;
int oddCount=0;
for(int i=0;i<array.length;i++){
if(array[i]%2!=0){
oddCount++;
}
}
for(int i=0;i<array.length;i++){
if(array[i]%2!=0){
tmp[oddBengin++]=array[i];
}else
tmp[oddCount++]=array[i];
}
for(int i=0;i<array.length;i++){
array[i]=tmp[i];
}
}
6、字符串的排列
题目描述
输入一个字符串,按字典序打印出该字符串中字符的所有排列。例如输入字符串abc,则打印出由字符a,b,c所能排列出来的所有字符串abc,acb,bac,bca,cab和cba。
输入描述:
输入一个字符串,长度不超过9(可能有字符重复),字符只包括大小写字母。
Solution 1:
回溯法:
public ArrayList<String> Permutation(String str) {
List<String> res=new ArrayList<>();
PermutationHelper(str.toCharArray(),0,res);
Collections.sort(res);
return (ArrayList)res;
}
private void PermutationHelper(char[] cs,int i,List<String> list){
if(i==cs.length-1){
String str=String.valueOf(cs);
if(!list.contains(str)){
list.add(str);
}
}else{
for(int j=i;j<cs.length;j++){
swap(cs,i,j);
PermutationHelper(cs,i+1,list);
//重新保持字符串的顺序
swap(cs,i,j);
}
}
}
private void swap(char[] cs,int i,int j){
char tmp=cs[i];
cs[i]=cs[j];
cs[j]=tmp;
}
7、数组中出现次数超过一半的数字
题目描述
数组中有一个数字出现的次数超过数组长度的一半,请找出这个数字。例如输入一个长度为9的数组{1,2,3,2,2,2,5,4,2}。由于数字2在数组中出现了5次,超过数组长度的一半,因此输出2。如果不存在则输出0。
Solution 1:
数组排序后,如果符合条件的数存在,则一定是数组中间那个数。(比如:1,2,2,2,3;或2,2,2,3,4;或2,3,4,4,4等等)
此外由于用到Arrays.sort设计快排,所以时间复杂度为NlogN
public int MoreThanHalfNum_Solution(int [] array) {
int count=0;
Arrays.sort(array);
for(int i=0;i<array.length;i++){
if(array[i]==array[array.length/2]){
count++;
}
}
if(count>array.length/2)
return array[array.length/2];
else
return 0;
}
Solution 2:
利用hashMap记录每个数出现的次数,然后遍历hashmap,找出重数
public int MoreThanHalfNum_Solution(int [] array) {
HashMap<Integer,Integer> map = new HashMap<Integer,Integer>();
for(int i=0;i<array.length;i++){
if(!map.containsKey(array[i])){
map.put(array[i],1);
}else{
int count = map.get(array[i]);
map.put(array[i],++count);
}
}
Iterator iter = map.entrySet().iterator();
while(iter.hasNext()){
Map.Entry entry = (Map.Entry)iter.next();
Integer key =(Integer)entry.getKey();
Integer val = (Integer)entry.getValue();
if(val>array.length/2){
return key;
}
}
return 0;
}
8、最小的k个数
题目描述
输入n个整数,找出其中最小的K个数。例如输入4,5,1,6,2,7,3,8这8个数字,则最小的4个数字是1,2,3,4,。
关键在于排序算法的时间复杂度上,直接列上所有排序算法的时间复杂度
Solution 1:
归并排序:将数组划分为2段,一直由顶向下划分至原子单位,然后比较左右两端,开始合并
(递归版)时间复杂度:NlogN
private static void mergeSort(int[] arr,int low,int high){
int mid=(high+low)/2;
if(low<high){
mergeSort(arr,low,mid);
mergeSort(arr,mid+1,high);
merge(arr,low,mid,high);
}
}
private static void merge(int[] arr,int low,int mid,int high){
int[] tmp=new int[high-low+1];
int left=low;
int right=mid+1;
int k=0;
for(;left<=mid&&right<=high;k++){
if(arr[left]<arr[right])
tmp[k]=arr[left++];
else
tmp[k]=arr[right++];
}
while(left<=mid){
tmp[k++]=arr[left++];
}
while(right<=high){
tmp[k++]=arr[right++];
}
for(int idx=0;idx<tmp.length;idx++){
arr[low+idx]=tmp[idx];
}
}
(非递归版)时间复杂度:
public static void MergeSort2(int[] arr)
{
//使用非递归的方式来实现归并排序
int len = arr.length;
int k = 1;
while(k < len)
{
MergePass(arr, k, len);
k *= 2;
}
}
//MergePass方法负责将数组中的相邻的有k个元素的字序列进行归并
private static void MergePass(int[] arr, int k, int n)
{
int i = 0;
int j;
//从前往后,将2个长度为k的子序列合并为1个
while(i < n - 2*k + 1)
{
merge(arr, i, i + k-1, i + 2*k - 1);
i += 2*k;
}
//这段代码保证了,将那些“落单的”长度不足两两merge的部分和前面merge起来。
if(i < n - k )
{
merge(arr, i, i+k-1, n-1);
}
}
//merge函数实际上是将两个有序数组合并成一个有序数组
//因为数组有序,合并很简单,只要维护几个指针就可以了
private static void merge(int[] arr, int low, int mid, int high)
{
//temp数组用于暂存合并的结果
int[] temp = new int[high - low + 1];
//左半边的指针
int i = low;
//右半边的指针
int j = mid+1;
//合并后数组的指针
int k = 0;
//将记录由小到大地放进temp数组
for(; i <= mid && j <= high; k++)
{
if(arr[i] < arr[j])
temp[k] = arr[i++];
else
temp[k] = arr[j++];
}
//接下来两个while循环是为了将剩余的(比另一边多出来的个数)放到temp数组中
while(i <= mid)
temp[k++] = arr[i++];
while(j <= high)
temp[k++] = arr[j++];
//将temp数组中的元素写入到待排数组中
for(int l = 0; l < temp.length; l++)
arr[low + l] = temp[l];
}
}
9、连续子数组的最大和
题目描述
HZ偶尔会拿些专业问题来忽悠那些非计算机专业的同学。今天测试组开完会后,他又发话了:在古老的一维模式识别中,常常需要计算连续子向量的最大和,当向量全为正数的时候,问题很好解决。但是,如果向量中包含负数,是否应该包含某个负数,并期望旁边的正数会弥补它呢?例如:{6,-3,-2,7,-15,1,2,2},连续子向量的最大和为8(从第0个开始,到第3个为止)。给一个数组,返回它的最大连续子序列的和,你会不会被他忽悠住?(子向量的长度至少是1)
Solution 1:
public int FindGreatestSumOfSubArray(int[] array) {
if(array.length==0||array==null)
return -1;
int res=array[0];
int max=array[0];
for(int i=1;i<array.length;i++){
max=Math.max(max+array[i],array[i]);
res=Math.max(max,res);
}
return res;
}
10、把数组排成最小的数
题目描述
输入一个正整数数组,把数组里所有数字拼接起来排成一个数,打印能拼接出的所有数字中最小的一个。例如输入数组{3,32,321},则打印出这三个数字能排成的最小数字为321323。
Solution 1:
public String PrintMinNumber(int [] numbers) {
if(numbers.length==0||numbers==null)
return "";
int len=numbers.length;
String[] str=new String[len];
StringBuilder sb=new StringBuilder();
for(int i=0;i<len;i++){
str[i]=String.valueOf(numbers[i]);
}
Arrays.sort(str,new Comparator<String>(){
@Override
public int compare(String s1,String s2){
String c1=s1+s2;
String c2=s2+s1;
return c1.compareTo(c2);
}
});
for(int i=0;i<len;i++){
sb.append(str[i]);
}
return sb.toString();
}
11、丑数
题目描述
把只包含质因子2、3和5的数称作丑数(Ugly Number)。例如6、8都是丑数,但14不是,因为它包含质因子7。 习惯上我们把1当做是第一个丑数。求按从小到大的顺序的第N个丑数。
Solution 1;
public int GetUglyNumber_Solution(int index) {
if(index<=0)
return 0;
ArrayList<Integer> list=new ArrayList<>();
int i2=0,i3=0,i5=0;
list.add(1);
while(list.size()<index){
int m1=list.get(i2)*2;
int m2=list.get(i3)*3;
int m3=list.get(i5)*5;
int min=Math.min(m1,Math.min(m2,m3));
list.add(min);
if(min==m1){
i2++;
}if(min==m2){
i3++;
}if(min==m3){
i5++;
}
}
return list.get(list.size()-1);
}
12、第一个只出现一次的字符
题目描述
在一个字符串(0<=字符串长度<=10000,全部由字母组成)中找到第一个只出现一次的字符,并返回它的位置, 如果没有则返回 -1(需要区分大小写)
Solution 1:
检索表进行记录
public int FirstNotRepeatingChar(String str) {
//创建一个数组,相当于建一个‘a-z’的索引表,只要这个字符出现一次,相应字符地方的数组值就+1
char[] ch=str.toCharArray();
int[] a=new int[256];
for(char b:ch){
a[(int)b]++;
}
//遍历原字符串,如果遍历中字符串中字符在LinkedHashMap中出现次数保存为1,则返回
for(int i=0;i<ch.length;i++)
if(a[(int)ch[i]]==1)
return i;
return -1;
}
Solution 2:
使用linkedHashMap遍历记录
public int FirstNotRepeatingChar(String str) {
LinkedHashMap<Character,Integer> map=new LinkedHashMap<Character,Integer>();
for(int i=0;i<str.length();i++){
if(map.containsKey(str.charAt(i))){
int count=map.get(str.charAt(i));
map.put(str.charAt(i),++count);
}else{
map.put(str.charAt(i),1);
}
}
int pos=-1;
int i=0;
for(;i<str.length();i++){
char c=str.charAt(i);
if(map.get(c)==1)
return i;
}
return pos;
}
13、数组中的逆序对
题目描述
在数组中的两个数字,如果前面一个数字大于后面的数字,则这两个数字组成一个逆序对。输入一个数组,求出这个数组中的逆序对的总数P。并将P对1000000007取模的结果输出。 即输出P%1000000007
Solution 1:
思路分析:
看到这个题目,我们的第一反应是顺序扫描整个数组。每扫描到一个数组的时候,逐个比较该数字和它后面的数字的大小。如果后面的数字比它小,则这两个数字就组成了一个逆序对。假设数组中含有n个数字。由于每个数字都要和O(n)这个数字比较,因此这个算法的时间复杂度为O(n^2)。
我们以数组{7,5,6,4}为例来分析统计逆序对的过程。每次扫描到一个数字的时候,我们不拿ta和后面的每一个数字作比较,否则时间复杂度就是O(n^2),因此我们可以考虑先比较两个相邻的数字。
(a) 把长度为4的数组分解成两个长度为2的子数组;
(b) 把长度为2的数组分解成两个成都为1的子数组;
© 把长度为1的子数组 合并、排序并统计逆序对 ;
(d) 把长度为2的子数组合并、排序,并统计逆序对;
在上图(a)和(b)中,我们先把数组分解成两个长度为2的子数组,再把这两个子数组分别拆成两个长度为1的子数组。接下来一边合并相邻的子数组,一边统计逆序对的数目。在第一对长度为1的子数组{7}、{5}中7大于5,因此(7,5)组成一个逆序对。同样在第二对长度为1的子数组{6}、{4}中也有逆序对(6,4)。由于我们已经统计了这两对子数组内部的逆序对,因此需要把这两对子数组 排序 如上图(c)所示, 以免在以后的统计过程中再重复统计。
接下来我们统计两个长度为2的子数组子数组之间的逆序对。合并子数组并统计逆序对的过程如下图如下图所示。
我们先用两个指针分别指向两个子数组的末尾,并每次比较两个指针指向的数字。如果第一个子数组中的数字大于第二个数组中的数字,则构成逆序对,并且逆序对的数目等于第二个子数组中剩余数字的个数,如下图(a)和(c)所示。如果第一个数组的数字小于或等于第二个数组中的数字,则不构成逆序对,如图b所示。每一次比较的时候,我们都把较大的数字从后面往前复制到一个辅助数组中,确保 辅助数组(记为copy) 中的数字是递增排序的。在把较大的数字复制到辅助数组之后,把对应的指针向前移动一位,接下来进行下一轮比较。
过程:先把数组分割成子数组,先统计出子数组内部的逆序对的数目,然后再统计出两个相邻子数组之间的逆序对的数目。在统计逆序对的过程中,还需要对数组进行排序。如果对排序算法很熟悉,我们不难发现这个过程实际上就是归并排序。
public int InversePairs(int [] array) {
if(array==null||array.length==0){
return 0;
}
int[] copy=new int[array.length];
for(int i=0;i<array.length;i++){
copy[i]=array[i];
}
int count=InversePairsCore(array,copy,0,array.length-1);
return count;
}
public int InversePairsCore(int[] array,int[] copy,int low,int high){
if(low==high){
return 0;
}
int mid=(low+high)>>1;
int leftCount=InversePairsCore(copy,array,low,mid)%1000000007;
int rightCount=InversePairsCore(copy,array,mid+1,high)%1000000007;
int count=0;
int i=mid;
int j=high;
int locCopy=high;
while(i>=low&&j>mid){
if(array[i]>array[j]){
count+=j-mid;
copy[locCopy--]=array[i--];
if(count>=1000000007){//数值过大求余
count%=1000000007;
}
}else{
copy[locCopy--]=array[j--];
}
}
for(;i>=low;i--){
copy[locCopy--]=array[i];
}
for(;j>mid;j--){
copy[locCopy--]=array[j];
}
return (leftCount+rightCount+count)%1000000007;
}
14、数字在排序数组中出现的次数
题目描述
统计一个数字在排序数组中出现的次数。
基本思路:看见有序就考虑二分查找(注:考虑升序,降序问题后二分查找)
Solution 1:
遍历数组,cout++即可,但这样应该不是面试者本意考察的地方,所以优先以下解法
private int count=0;
public int GetNumberOfK(int [] array , int k) {
if(array==null||array.length==0){
return count;
}
for(int number:array){
if(number==k){
count++;
}
}
return count;
}
Solution 2:
二分查找
public int GetNumberOfK(int [] array , int k) {
int length = array.length;
if(length == 0){
return 0;
}
int firstK = getFirstK(array, k, 0, length-1);
int lastK = getLastK(array, k, 0, length-1);
if(firstK != -1 && lastK != -1){
return lastK - firstK + 1;
}
return 0;
}
//递归写法
private int getFirstK(int [] array , int k, int start, int end){
if(start > end){
return -1;
}
int mid = (start + end) >> 1;
if(array[mid] > k){
return getFirstK(array, k, start, mid-1);
}else if (array[mid] < k){
return getFirstK(array, k, mid+1, end);
}else if(mid-1 >=0 && array[mid-1] == k){
return getFirstK(array, k, start, mid-1);
}else{
return mid;
}
}
//循环写法
private int getLastK(int [] array , int k, int start, int end){
int length = array.length;
int mid = (start + end) >> 1;
while(start <= end){
if(array[mid] > k){
end = mid-1;
}else if(array[mid] < k){
start = mid+1;
}else if(mid+1 < length && array[mid+1] == k){
start = mid+1;
}else{
return mid;
}
mid = (start + end) >> 1;
}
return -1;
}
Solution 3:
比较k+0.5和k-0.5的相对位置
public int GetNumberOfK(int [] array , int k) {
if(array.length==0||array==null)
return 0;
return biSearch(array,k+0.5)-biSearch(array,k-0.5);
}
private static int biSearch(int[] array,double data){
int begin=0,end=array.length-1;
while(begin<=end){
int mid=(end-begin)/2+begin;
if(array[mid]<data){
begin=mid+1;
}else if(array[mid]>data){
end=mid-1;
}
}
return begin;
}
15、数组中只出现一次的数
题目描述
一个整型数组里除了两个数字之外,其他的数字都出现了两次。请写程序找出这两个只出现一次的数字。
Solution:
进行异或运算时,当前位的两个二进制表示不同则为1相同则为0.该方法被广泛推广用来统计一个数的1的位数
那么这个题的突破口在哪里呢?注意这个数组的特殊性:其它数字都出现了两次,只有一个数出现了一次。可以想到运用异或运算,任何一个数字异或它自己都等于0。
如果我们从头到尾依次异或数组中的每一个数,那么最终的结果就是那个只出现一次的数字,因为其他出现两次的数字全部在异或中被抵消为0了(异或运算遵循交换分配率)。
举个栗子:2 3 4 2 3
所有数字依次异或运算:2 xor 3 xor 4 xor 2 xor 3 = (2 xor 2) xor (3 xor 3) xor 4= 0 xor 0 xor 4 = 4,最终结果4就是我们要找的那个只出现一次的数字。
/**
* 数组中有两个出现一次的数字,其他数字都出现两次,找出这两个数字
* @param array
* @param num1
* @param num2
*/
public static void findNumsAppearOnce(int [] array,int num1[] , int num2[]) {
if(array == null || array.length <= 1){
num1[0] = num2[0] = 0;
return;
}
int len = array.length, index = 0, sum = 0;
for(int i = 0; i < len; i++){
sum ^= array[i];
}
//找出 第一个为1的位的位置,记为第N位
for(index = 0; index < 32; index++){
if((sum & (1 << index)) != 0) break;
}
//以第N位划分数组,找出两个数
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++){
bits[j] = bits[j] + ( (a[i]>>>j) & 1);
}
}
int res = 0;
for(int i = 0; i < 32; i++){
if(bits[i] % 3 !=0){
res = res | (1 << i);
}
}
return res;
}
Solution 2:
HashMap存储,然后取出
public void FindNumsAppearOnce(int [] array,int num1[] , int num2[]) {
if(array==null||array.length==0){
num1[0]=num2[0]=0;
return;
}
HashMap<Integer,Integer> hm=new HashMap<Integer,Integer>();
for(int i=0;i<array.length;i++){
if(hm.containsKey(array[i])){
hm.put(array[i],2);
}
else{
hm.put(array[i],1);
}
}
num1[0]=0;
for(Entry entry:hm.entrySet()){
if((Integer)entry.getValue()==1){
if(num1[0]==0){
num1[0]=(Integer)entry.getKey();
}
else{
num2[0]=(Integer)entry.getKey();
}
}
}
}
16、和为S的连续正数序列
题目描述
小明很喜欢数学,有一天他在做数学作业时,要求计算出9~16的和,他马上就写出了正确答案是100。但是他并不满足于此,他在想究竟有多少种连续的正数序列的和为100(至少包括两个数)。没多久,他就得到另一组连续正数和为100的序列:18,19,20,21,22。现在把问题交给你,你能不能也很快的找出所有和为S的连续正数序列? Good Luck!
输出描述:
输出所有和为S的连续正数序列。序列内按照从小至大的顺序,序列间按照开始数字从小到大的顺序
Solution:
两个指针动态处理。滑动窗口
public ArrayList<ArrayList<Integer> > FindContinuousSequence(int sum) {
//存放结果
ArrayList<ArrayList<Integer> > result = new ArrayList<>();
//两个起点,相当于动态窗口的两边,根据其窗口内的值的和来确定窗口的位置和大小
int plow = 1,phigh = 2;
while(phigh > plow){
//由于是连续的,差为1的一个序列,那么求和公式是(a0+an)*n/2
int cur = (phigh + plow) * (phigh - plow + 1) / 2;
//相等,那么就将窗口范围的所有数添加进结果集
if(cur == sum){
ArrayList<Integer> list = new ArrayList<>();
for(int i=plow;i<=phigh;i++){
list.add(i);
}
result.add(list);
plow++;
//如果当前窗口内的值之和小于sum,那么右边窗口右移一下
}else if(cur < sum){
phigh++;
}else{
//如果当前窗口内的值之和大于sum,那么左边窗口右移一下
plow++;
}
}
return result;
}
17、和为S的两个数字
题目描述
输入一个递增排序的数组和一个数字S,在数组中查找两个数,使得他们的和正好是S,如果有多对数字的和等于S,输出两个数的乘积最小的。
输出描述:
对应每个测试案例,输出两个数,小的先输出。
Solution 1:
排序好的,用左右夹逼
public ArrayList<Integer> FindNumbersWithSum(int [] array,int sum) {
ArrayList<Integer> list=new ArrayList<Integer>();
if(array.length==0||array==null){
return list;
}
int i=0,j=array.length-1;
while(i<j){
if(array[i]+array[j]==sum){
list.add(array[i]);
list.add(array[j]);
return list;
}else if(array[i]+array[j]>sum){
j--;
}else{
i++;
}
}return list;
}
18、左旋转字符串(翻转问题)
题目描述
汇编语言中有一种移位指令叫做循环左移(ROL),现在有个简单的任务,就是用字符串模拟这个指令的运算结果。对于一个给定的字符序列S,请你把其循环左移K位后的序列输出。例如,字符序列S=”abcXYZdef”,要求输出循环左移3位后的结果,即“XYZdefabc”。是不是很简单?OK,搞定它!
Solution 1:
原理:YX = (XTY T)T
public String LeftRotateString(String str,int n) {
char[] ch=str.toCharArray();
if(str.length()<n){
return "";
}
reverse(ch,0,n-1);
reverse(ch,n,ch.length-1);
reverse(ch,0,ch.length-1);
return new String(ch);
}
private void reverse(char[] ch,int start,int end){
while(start<end){
char tmp=ch[start];
ch[start]=ch[end];
ch[end]=tmp;
start++;
end--;
}
}
19、翻转单词顺序列(翻转再翻转)
题目描述
最近来了一个新员工Fish,每天早晨总是会拿着一本英文杂志,写些句子在本子上。同事Cat对Fish写的内容颇感兴趣,有一天他向Fish借来翻看,但却读不懂它的意思。例如,“student. a am I”。后来才意识到,这家伙原来把句子单词的顺序翻转了,正确的句子应该是“I am a student.”。Cat对一一的翻转这些单词顺序可不在行,你能帮助他么?
Solution 1:
/*
算法思想:先翻转整个句子,然后,依次翻转每个单词。
依据空格来确定单词的起始和终止位置
*/
public class Solution {
public String ReverseSentence(String str) {
char[] chars = str.toCharArray();
reverse(chars,0,chars.length - 1);
int blank = -1;
for(int i = 0;i < chars.length;i++){
if(chars[i] == ' '){
int nextBlank = i;
reverse(chars,blank + 1,nextBlank - 1);
blank = nextBlank;
}
}
reverse(chars,blank + 1,chars.length - 1);//最后一个单词单独进行反转
return new String(chars);
}
public void reverse(char[] chars,int low,int high){
while(low < high){
char temp = chars[low];
chars[low] = chars[high];
chars[high] = temp;
low++;
high--;
}
}
}
20、扑克牌顺子
题目描述
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的运气如何, 如果牌能组成顺子就输出true,否则就输出false。为了方便起见,你可以认为大小王是0。
Solution 1:
public boolean isContinuous(int [] numbers) {
if(numbers.length==0||numbers==null)
return false;
int[] d=new int[14];
d[0]=-5;
//确保max比最小值0小,min比最大值13大即可遍历记录最大小值
int min=14;int max=-1;
for(int i=0;i<numbers.length;i++){
d[numbers[i]]++;
//为0时滑过不处理
if(numbers[i]==0){
continue;
}
//有重复数肯定不是顺子,直接返回判断
if(d[numbers[i]]>1){
return false;
}
//开始记录最大最小值
if(numbers[i]<min){
min=numbers[i];
}
if(numbers[i]>max){
max=numbers[i];
}
}
//顺子的最大小之差一定小于5
if(max-min<5){
return true;
}
return false;
}
21、 把字符串转换成整数
题目描述
将一个字符串转换成一个整数(实现Integer.valueOf(string)的功能,但是string不符合数字要求时返回0),要求不能使用字符串转换整数的库函数。 数值为0或者字符串不是一个合法的数值则返回0。
输入描述:
输入一个字符串,包括数字字母符号,可以为空
输出描述:
如果是合法的数值表达则返回该数字,否则返回0
Solution 1:
public int StrToInt(String str) {
if(str.length()==0||str==null){
return 0;
}
if(str.length()==0||str==null)
return 0;
char[] ch=str.toCharArray();
int sum=0;
//label控制符号
int lable=0;
if(ch[0]=='-')
lable=1;
for(int i=label;i<ch.length;i++){
if(ch[i]=='+'){
continue;
}
//asci码表中0-9的序号,筛选出不合法的字符
if(ch[i]<48||ch[i]>57){
return 0;
}
//sum控制位数,因为筛选后的字符都是在48-57之间,所以char[i]-48就可换算成相应数字
sum=sum*10+ch[i]-48;
}
return label==0?sum:sum*-1;
}
22、数组中重复的数字
题目描述
在一个长度为n的数组里的所有数字都在0到n-1的范围内。 数组中某些数字是重复的,但不知道有几个数字是重复的。也不知道每个数字重复几次。请找出数组中任意一个重复的数字。 例如,如果输入长度为7的数组{2,3,1,0,2,5,3},那么对应的输出是第一个重复的数字2。
Solution 1:
构建一个boolean数组,更节省空间,因为boolean只占一位,然后用此数组记录该数是否出现过即可
public boolean duplicate(int numbers[],int length,int [] duplication) {
boolean[] dp=new boolean[length];
for(int i=0;i<length;i++){
if(dp[numbers[i]]==true){
duplication[0]=numbers[i];
return true;
}
dp[numbers[i]]=true;
}
return false;
}
23、构建乘积数组
题目描述
给定一个数组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]。不能使用除法。
Solution 1:
注意,体重给的元素B【i】不含和A【i】的乘积
构建前向乘积数组C[i]=A[0]A[1]…*A[i-1],即C[i]=C[i-1]*A[i-1];
构建后向乘积数组D[i]=A[n-1]A[n-2]…A[n-i+1],即D[i]=D[i+1]*A[i+1];
通过C[i],D[i]来求B[i]:B[i]=C[i]*D[i]
时间复杂度:O(n)
B[i]的值可以看作下图的矩阵中每行的乘积。
下三角用连乘可以很容求得,上三角,从下向上也是连乘。
因此我们的思路就很清晰了,先算下三角中的连乘,即我们先算出B[i]中的一部分,然后倒过来按上三角中的分布规律,把另一部分也乘进去。
如下图: B[i]的值可以看作下图的矩阵中每行的乘积。B[i]的意义是A数组不包括i位置的所有乘积,分为 i左边的元素乘积和 i右边的所有元素乘积。第一个for计算i左边的乘积,第二个for计算右边的。
public int[] multiply(int[] A) {
int length = A.length;
int[] B = new int[length];
if(length != 0 ){
B[0] = 1;
//计算下三角连乘
for(int i = 1; i < length; i++){
B[i] = B[i-1] * A[i-1];
}
int temp = 1;
//计算上三角,从倒数第二个开始
for(int j = length-2; j >= 0; j--){
temp *= A[j+1];//此处相当于创造一个临时参数,存储B[i+1]*A[i+1],因为要往前乘积
B[j] *= temp;//记得乘上此时的B[j],相当于把之前下三角乘积乘进来
}
}
return B;
}
24、字符中第一个不重复的字符
题目描述
请实现一个函数用来找出字符流中第一个只出现一次的字符。例如,当从字符流中只读出前两个字符"go"时,第一个只出现一次的字符是"g"。当从该字符流中读出前六个字符“google"时,第一个只出现一次的字符是"l"。
Solution 1:
建立一个hashMap和一个arraylist,用以分别计算字符出现次数,和字符出现顺序
HashMap<Character,Integer> hm=new HashMap<Character,Integer>();
ArrayList<Character> list=new ArrayList<Character>();
//Insert one char from stringstream
public void Insert(char ch)
{
if(hm.containsKey(ch)){
hm.put(ch,hm.get(ch)+1);
}else{
hm.put(ch,1);
}
list.add(ch);
}
//return the first appearence once char in current stringstream
public char FirstAppearingOnce()
{
char c='#';
for(char key:list){
if(hm.get(key)==1){
c=key;
break;
}
}
return c;
}