《剑指offer》之数组专题

《剑指offer》之数组专题

  • 此文乃本人把《剑指offer》的算法题都做完之后的总结文章,用于本人复习与同仁们交流。

(一).二维数组中的查找

【题目】在一个二维数组中,每一行都按照从左到右递增的顺序排序,每一列都按照从上到下递增的顺序排序。请完成一个函数,输入这样的一个二维数组和一个整数,判断数组中是否含有该整数。

public class Solution {
    public boolean Find(int target, int [][] array) {
        /**1.从左到右递增和从上到下递增
           2.用row代表行,用col代表列,选取右上角的元素,若target>该元素,删除该行,<该元素,删除该列
        **/
        int row=0;
        int col =array[0].length-1;

        while(row<=array.length-1&&col>=0){

            if(target == array[row][col]){
                return true;
            }else if(target<array[row][col]){
                col--;
            }else{
                row++;
            }
        }
        return false;
    }
}

(二).二维数组中的查找

【题目】把一个数组最开始的若干个元素搬到数组的末尾,我们称之为数组的旋转。 输入一个非递减排序的数组的一个旋转,输出旋转数组的最小元素。
例如数组{3,4,5,1,2}为{1,2,3,4,5}的一个旋转,该数组的最小值为1。
NOTE:给出的所有元素都大于0,若数组大小为0,请返回0。

这道题其实就是有序数组的二分查找问题,以前做过并且整理为二分查找专题:二分查找专题(二)

import java.util.*;
public class Solution {
    public int minNumberInRotateArray(int [] array) {
     /**
     1.注意:非递减排序指的是严格递增序列和中间有重复数据的递增序列俩种情况
     2.有序查找第一反应应该是二分查找
     **/
        //边界条件判断
        if(array == null||array.length == 0){
            return 0;
        }

        int left=0;
        int right=array.length-1;
        int mid=0;
        while(left <right){

            mid = (left+right)/2;

            if(left == right-1){
                break;
            }
            if(array[left]<array[right]){
                return array[left];
            }

            if(array[left]>array[mid]){
                right = mid;
                continue;
            }
            if(array[left]<array[mid]){
                left = mid;
                continue;
            }

            while(left<mid){
                if(array[left]==array[mid]){
                    left++;
                }else if(array[left]<array[mid]){
                    return array[left];
                }else{
                    right =mid;
                    break;
                }
            }

        }

       return Math.min(array[left],array[right]);

    }
}

(三).调整数组顺序使奇数位于偶数前面

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

public class Solution {
    public void reOrderArray(int [] array) {
        //要保证相对位置不变,也就是说排序需要稳定!
        //思路一:采用冒泡排序思想,从后往前交换
        //思路二:首先统计奇数的个数! 然后新建一个等长的数组,遍历数组,把奇数依次加入新建数组,再把偶数依次加入数组   O(N)+O(N)

        if(array == null ||array.length ==0){
            return;
        }
        int oddCount=0;
        int begin=0;
        int[] A = new int[array.length];


        for(int i=0;i<array.length;i++){
            if(!isEven(array[i])){
                oddCount++;
            }
        }

        for(int j=0;j<array.length;j++){
            if(!isEven(array[j])){
                A[begin++]=array[j];
            }else{
                A[oddCount++]=array[j];
            }
        }

        for(int k=0;k<array.length;k++){
            array[k]=A[k];
        }


    }

    public boolean isEven(int n){
        //n&1==0,表示n为偶数
        return ((n&1)==0);
    }


}

(四).数组中出现次数超过一半的数字

【题目】数组中有一个数字出现的次数超过数组长度的一半,请找出这个数字。例如输入一个长度为9的数组{1,2,3,2,2,2,5,4,2}。由于数字2在数组中出现了5次,超过数组长度的一半,因此输出2。如果不存在则输出0。

public class Solution {
    public int MoreThanHalfNum_Solution(int [] array) {
        //【注意】有一个数出现次数超过数组长度的一半,说明该数字比数组的所有数字出现次数总和还多
        /**
        采用阵地攻守的思想:
        第一个数字作为第一个士兵,守阵地;count = 1;
        遇到相同元素,count++;
        遇到不相同元素,即为敌人,同归于尽,count--;当遇到count为0的情况,又以新的i值作为守阵地的士兵,继续下去,到最后还留在阵地上的士兵,有可能是主元素。
        再加一次循环,记录这个士兵的个数看是否大于数组一般即可。
        **/
        if(array==null||array.length==0){
            return 0;
        }
        int temp=array[0];
        int times=1;
        for(int i=0;i<array.length;i++){

            if(times == 0){
                temp=array[i];
                times=1;
            }else if(temp == array[i]){
                times++;
            }else{
                times--;
            }

        }

        int count=0;
        for(int i=0;i<array.length;i++){

            if(temp == array[i]){
                count++;
            }
        }
        if(count*2<=array.length){
            return 0;
        }


        return temp;

    }
}

(五).数组中重复的数字

【题目】在一个长度为n的数组里的所有数字都在0到n-1的范围内。
数组中某些数字是重复的,但不知道有几个数字是重复的。也不知道每个数字重复几次。请找出数组中任意一个重复的数字。
例如,如果输入长度为7的数组{2,3,1,0,2,5,3},那么对应的输出是重复的数字2或者3。

public class Solution {
    // Parameters:
    //    numbers:     an array of integers
    //    length:      the length of array numbers
    //    duplication: (Output) the duplicated number in the array number,length of duplication array is 1,so using duplication[0] = ? in implementation;
    //                  Here duplication like pointor in C/C++, duplication[0] equal *duplication in C/C++
    //    这里要特别注意~返回任意重复的一个,赋值duplication[0]
    // Return value:       true if the input is valid, and there are some duplications in the array number
    //                     otherwise false
    public boolean duplicate(int numbers[],int length,int [] duplication) {
        //思路一:排序数组,然后遍历找重复值!此方法时间复杂度为O(nlogn).
        //思路二:第一想法是用哈希表!第一次遍历把数组元素传入哈希表中,第二次遍历即可查出是否有重复值  时间复杂度O(N) 空间复杂度O(N)
        //思路三:最佳!所有数字都在0~n-1的范围内,也就是说大部分数字值和下标相同,某些下标有多个重复数字,有些下标没有数字

        if(numbers==null||length==0){
            return false;
        }
        for(int i=0;i<length;i++){

            if(numbers[i]<0||numbers[i]>length-1){
                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;
                }else{
                    swap(numbers,i,numbers[i]);
                }



            }

        }
        return false;

    }

    public void swap(int[] A, int i,int j){
        int temp=A[i];
        A[i] =A[j];
        A[j]=temp;
    }

}

(六).构建乘积数组

【题目】给定一个数组A[0,1,…,n-1],请构建一个数组B[0,1,…,n-1],其中B中的元素B[i]=A[0]A[1]A[i-1]*A[i+1]…*A[n-1]。不能使用除法。

import java.util.ArrayList;
public class Solution {
    public int[] multiply(int[] A) {

        //新建一个新数组B, 对A数组i项左侧自上往下累乘, 对A数组i项右侧自下往上累乘 时间复杂度O(n)

        int n=A.length;
        if(n==0||A==null){
            return null;
        }
        int[] B=new int[n];
        //自上往下累乘
         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;
    }
}


(七).把数组排成最小的数

【题目】输入一个正整数数组,把数组里所有数字拼接起来排成一个数,打印能拼接出的所有数字中最小的一个。例如输入数组{3,32,321},则打印出这三个数字能排成的最小数字为321323。

import java.util.*;


public class Solution {
    public String PrintMinNumber(int [] numbers) {
        /** 解题思路:
           先将整型数组转换成String数组,然后将String数组排序,最后将排好序的字符串数组拼接出来。关键就是制定排序规则。
           排序规则如下:
           若ab > ba 则 a > b,
           若ab < ba 则 a < b,
           若ab = ba 则 a = b;
           解释说明:
           比如 "3" < "31"但是 "331" > "313",所以要将二者拼接起来进行比较
        **/
        if(numbers==null||numbers.length==0){
            return "";
        }
        int n=numbers.length;
        String[] s = new String[n];
        StringBuilder sb=new StringBuilder();

        for(int i=0;i<n;i++){
            s[i]=String.valueOf(numbers[i]);
        }

        Arrays.sort(s,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<n;i++){
            sb.append(s[i]);
        }

        return sb.toString();
    }
}

(八).统计一个数字在排序数组中出现的次数。

【题目】统计一个数字在排序数组中出现的次数。

这道题其实就是有序数组的二分查找问题,以前做过并且整理为二分查找专题:二分查找专题(二)

public class Solution {
    public int GetNumberOfK(int [] array , int k) {
       //先二分查找找到最左该数字的下标,然后从该下标遍历

        if(array==null||array.length==0){
            return 0;
        }

        int n=array.length;
        int temp=0;
        int left=0;
        int mid=0;
        int right=n-1;
        while(left<=right){
            mid=(left+right)/2;
            if(array[mid]>k){
                right=mid-1;
            }else if(array[mid]<k){
                left=mid+1;
            }else if(array[mid]==k){
                temp=mid;
                right = mid-1;
            }
        }


        int count=0;
        boolean flag=true;
        for(int j=temp;j<n&&flag;j++){
            if(array[j]==k){
                count++;
            }else{
                flag = false;
            }

        }
        return count;

    }
}

(九).数组中只出现一次的数字

【题目】一个整型数组里除了两个数字之外,其他的数字都出现了两次。请写程序找出这两个只出现一次的数字。

此题考查的是位运算!我曾经做过一些位运算题目,整理为位运算专题:位运算专题

//num1,num2分别为长度为1的数组。传出参数
//将num1[0],num2[0]设置为返回结果
public class Solution {
    public void FindNumsAppearOnce(int [] array,int num1[] ,int num2[]) {
        //其实这道题适用于有俩个数出现了奇数次,其余均只出现过偶数次。
        //整数 n^0 = n  n^n=0;


        int er0=0;
        int er1=0;
        for(int cur : array){
            er0^=cur;
        }

        //因为俩个数字不一样,所以er0必不为0  找出第k位(此方法里是找出er0中最右边为1的下标),k为第k位为1,其余位为0
        int k=er0&(~er0+1);

        for(int cur1:array){
            //说明第k位为1
            if((k&cur1)!=0){

                er1^=cur1;
            }
        }


        num1[0]=er1;
        num2[0]=er0^er1;



    }
}
©️2020 CSDN 皮肤主题: 大白 设计师:CSDN官方博客 返回首页