Array

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;

    }
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值