左程云-数组问题

1、打印转圈数组

思路:按圈打印,并判断圈的四条边是否存在

public class RotatePrint {

    public static void printOneCircle(int[][] arr,int start,int rows,int cols){
        //上面一行
        for(int i=start;i<cols-start;i++)
            System.out.println(arr[start][i]);
        //右边一列,至少有两行
        if(rows > (start * 2 + 1)){
            for(int i=start+1;i<rows-start;i++)
                System.out.println(arr[i][cols-start-1]);
        }
        //下方一行,至少两行两列
        if(rows > (start * 2 + 1) && cols > (start * 2 + 1)){
            for(int i=cols-start-2;i>=start;i--)
                System.out.println(arr[rows-start-1][I]);
        }
        //左边一列,至少三行两列
        if(rows > (start * 2 + 2) && cols > (start * 2 + 1)){
            for(int i = rows-start-2;i>=start+1;i--)
                System.out.println(arr[i][start]);
        }
    }



    public static void printRotateMatrix(int[][] arr){
        int rows = arr.length;
        int cols = arr[0].length;
        for(int i = 0;i<(rows + 1) / 2;i++){
            printOneCircle(arr,i,rows,cols);
        }
    }

    public static void main(String[] args){
        int[][] arr = {
                {1,2,3,4},
                {5,6,7,8},
                {9,10,11,12},
                {13,14,15,16}
        };

        printRotateMatrix(arr);
    }
}

2、将正方形矩阵顺时针转动90度

思路:不同于书中的做法,我们首先将数组按照正对角线进行翻转,然后按照中线左右翻转,即可得到最终的结果。

public class RotateArray90 {

    public static void rotateArrayBy90(int[][] arr){
        for(int i=0;i<arr.length;i++){
            for(int j=i+1;j<arr[i].length;j++){
                int tmp = arr[i][j];
                arr[i][j] = arr[j][i];
                arr[j][i] = tmp;
            }
        }
        for(int i=0;i<arr.length;i++){
            for(int j=0;j<arr[0].length / 2;j++){
                int tmp = arr[i][j];
                arr[i][j] = arr[i][arr[0].length-j-1];
                arr[i][arr[0].length-j-1] = tmp;
            }
        }
        for(int i=0;i<arr.length;i++){
            for(int j=0;j<arr[0].length;j++){
                System.out.print(arr[i][j] + " ");
            }
            System.out.println(" ");
        }
    }
    public static void main(String[] args){
        int[][] arr = {
                {1,2,3,4},
                {5,6,7,8},
                {9,10,11,12},
                {13,14,15,16}
        };
        rotateArrayBy90(arr);
    }
}

3、找到无序数组中最小的K个数

思路:这里我们只介绍O(Nlogk)的思路,O(N)的思路感兴趣的同学可以翻阅原书。我们使用的是堆排序,对于堆排序来说,因为是要找到最小的K个数,因此我们首先用前K个数建立大根堆,然后对于每一个新的元素,判断其与堆顶元素的大小,如果比堆顶元素还要大,那说明肯定不是最小的K个数中的,如果比堆顶元素小,则说明可能是最小的K个数之一,因此替换掉堆顶元素,并调整堆。

public class HeapSort {

    public static void perceDown(int[] arr,int i,int k){
        int tmp = arr[i];
        int child = i * 2 + 1;
        while(child < k){
            if(child + 1 < k && arr[child+1] > arr[child])
                child = child + 1;
            if(arr[child] > tmp){
                arr[i] = arr[child];
                i = child;
            }
            else
                break;
            child = i * 2 + 1;
        }
        arr[i] = tmp;
    }


    public static void heapSort(int[] arr,int k){
        for(int i=(k-1)/2;i>=0;i--){
            perceDown(arr,i,k-1);
        }
        for(int i=k;i<arr.length;i++){
            if(arr[i] < arr[0]){
                int tmp = arr[i];
                arr[i] = arr[0];
                arr[0] = tmp;
                perceDown(arr,0,k);
            }
        }
        for(int i=0;i<k;i++){
            System.out.println(arr[i]);
        }
    }

    public static void main(String[] args){
        int[] arr = {49,38,65,97,76,13,27,49,78,34,12,64,18};

        heapSort(arr,3);

    }
}

4、需要排序的最短子数组长度

思路:这里我们就是要找到两个位置,为需要排序的数组的两端,

public class MinSortArrayLength {

    public static int getMinSortArrayLength(int[] arr){
        if(arr==null || arr.length==0)
            return 0;
        int min = arr[arr.length-1];
        int noMinIndex = -1;
        for(int i = arr.length-2;i>=0;i--){
            if(arr[i] > min){
                noMinIndex = I;
            }
            else{
                min = Math.min(min,arr[I]);
            }
        }
        if(noMinIndex == -1)
            return 0;
        int max = arr[0];
        int noMaxIndex = -1;
        for(int i=1;i<arr.length;i++){
            if(arr[i] < max)
                noMaxIndex = I;
            else
                max = Math.max(max,arr[I]);
        }
        return noMaxIndex - noMinIndex + 1;
    }


    public static void main(String[] args){
        int[] arr = {1,5,3,4,2,6,7};
        System.out.println(getMinSortArrayLength(arr));
    }
}

5、在数组中找到出现次数大于N/K的数

5.1 在数组中找到出现次数大于一半的数

思路:这里我们运用这样一种消除的思想,选一个候选者,如果当前遍历到的位置和候选者不同,那么我们就把这两个数一并从数组中消除,每次从数组中消除两个不一样的数,那么最后的数要么是符合我们条件的数,要么是数组中最后一个数,再判断一下这个数的出现次数是否真的是大于一半即可。

public class FindAppearMoreThanHalfTimes {

    public static void getAppearMoreThanHalfTimes(int[] arr){

        int cand = arr[0];
        int times= 1;
        for(int i=1;i<arr.length;i++){
            if (times == 0){
                cand = arr[i];
                times = 1;
            }
            else if(arr[i] == cand){
                times++;
            }
            else{
                times--;
            }
        }
        times = 0;
        for(int i=0;i<arr.length;i++){
            if(arr[i] == cand)
                times ++;
        }
        if(times > arr.length / 2){
            System.out.println(cand);
        }
        else{
            System.out.println("no such number");
        }

    }

5.2 在数组中找到出现次数大于N/K的数

思路:同样的道理,我们设置K-1个候选者,如果新遇到的数和K-1个候选者都不同,那么我们就从数组中消除K个数。遍历完数组后,判断候选者出现次数是否符合要求即可。

public class FindAppearMoreThanNDivKTimes {

    public static void allCandsMinusOne(HashMap<Integer,Integer> cands){
        List<Integer> removeList = new LinkedList<Integer>();
        for(Map.Entry<Integer,Integer> set: cands.entrySet()){
            Integer key = set.getKey();
            Integer value = set.getValue();
            if(value == 1){
                removeList.add(key);
            }
            cands.put(key,value-1);
        }
        for(Integer removeKey:removeList){
            cands.remove(removeKey);
        }
    }

    public static HashMap<Integer,Integer> getReals(int[] arr,HashMap<Integer,Integer> cands){
        HashMap<Integer,Integer> reals = new HashMap<Integer,Integer>();
        for(int i=0;i<arr.length;i++){
            int curNum = arr[i];
            if (cands.containsKey(curNum)) {
                if(reals.containsKey(curNum))
                    reals.put(curNum,reals.get(curNum) + 1);
                else
                    reals.put(curNum,1);
            }
        }
        return reals;
    }

    public static void printKMajor(int[] arr,int k){
        if(k<2)
            return;
        HashMap<Integer,Integer> cands = new HashMap<Integer,Integer>();
        for(int i =0;i<arr.length;i++){
            if(cands.containsKey(arr[I]))
                cands.put(arr[i],cands.get(arr[i])+1);
            else{
                if(cands.size() == k-1){
                    allCandsMinusOne(cands);
                }
                else{
                    cands.put(arr[i],1);
                }
            }
        }
        HashMap<Integer,Integer> reals = getReals(arr,cands);
        boolean hasPrint = false;
        for(Map.Entry<Integer,Integer> set:cands.entrySet()){
            Integer key = set.getKey();
            if(reals.get(key) > arr.length/k){
                hasPrint = true;
                System.out.print(key + " ");
            }
        }
        System.out.println(hasPrint ? "":"no such number!");
    }

    public static void main(String[] args){
        int[] arr = {1,3,3,6,43,5,6,3,23643,3,52423,3,446,2,32,562,4,2,2,2,4,4,3,5,2,2,2,4,4,4};
        printKMajor(arr,5);
    }
}

6、在行列都排好序的矩阵中找数

思路:从矩阵的右上角或者左下角开始即可

public class FindKin2DMatrix {
    public static boolean findK(int[][] arr,int k){
        int rows = arr.length;
        int cols = arr[0].length;
        int m = 0;
        int n = cols - 1;
        while(m < rows && n >= 0){
            if(arr[m][n] == k)
                return true;
            else if(arr[m][n] < k){
                m++;
            }
            else
                n--;
        }
        return false;
    }

    public static void main(String[] args){
        int[][] arr = {{0,1,2,5},{2,3,4,7},{4,4,4,8},{5,7,7,9}};
        System.out.println(findK(arr,7));
    }
}

7、不重复打印排序数组中相加和为给定值的所有二元组和三元组

7.1 二元组

思路:由于是排好序的数组,因此设置两个指针,一个从左侧开始遍历,一个从右侧开始遍历,每次判断两个指针对应位置的数是否和目标相同即可。这里需要注意的是要去除重复值

public class FindAllTwoTuples {

    public static void findAllTwoTuples(int[] arr,int k){
        int left = 0;
        int right = arr.length-1;
        while(left < right){
            if(arr[left] + arr[right] == k ){
                if(left == 0 || arr[left] != arr[left-1]){
                    System.out.println(arr[left] + "," + arr[right]);
                }
                left++;
                right--;
            }
            else if(arr[left] + arr[right] < k)
                left ++;
            else
                right--;
        }
    }
    public static void main(String[] args){
        int[] arr = {-8,-4,-3,0,1,2,4,5,8,9};
        findAllTwoTuples(arr,10);
    }
}

7.2 三元组

思路:三元组和二元组的思路相同,我们每次先指定一个值,可以三元组问题转化为二元组问题。同样需要注意判断重复值的问题

public class FindAllThreeTuples {

    public static void findAllThreeTuples(int[] arr,int k){
        if(arr == null || arr.length < 3)
            return;
        for(int i = 0;i<arr.length - 2;i++){
            if(i==0 || arr[i] != arr[i-1]){
                int tmp = k - arr[i];
                int left = i + 1;
                int right = arr.length - 1;
                while(left < right){
                    if(arr[left] + arr[right] < tmp)
                        left++;
                    else if(arr[left] + arr[right] > tmp)
                        right--;
                    else{
                        if(left==i+1 || arr[left] != arr[left-1]){
                            System.out.println(arr[i] + "," + arr[left]+ "," + arr[right]);
                        }
                        left++;
                        right--;
                    }
                }
            }
        }
    }

    public static void main(String[] args){
        int[] arr = {-8,-4,-3,0,1,2,4,5,8,9};
        findAllThreeTuples(arr,10);
    }
}

8、未排序数组中累加和为给定值的最长子数组长度

8.1 数组全为正数

思路:设置两个指针left和right,指针都从index=0的位置开始,指针的位置代表left-right的子数组的和。如果当前的和小于目标值,right++,如果大于目标值,则left++。

public class MaxLengthSubArrayWithSumK {

    public static int findMaxLengthSubArrayWithSumK(int[] arr,int k){
        if(arr==null || arr.length==0)
            return 0;
        int left = 0;
        int right = 0;
        int maxlen = 0;
        int sum = arr[0];
        while(right < arr.length){
            if(sum == k){
                maxlen = Math.max(maxlen,right-left+1);
                sum -= arr[left++];
            }
            else if(sum < k){
                right++;
                if(right == arr.length)
                    break;
                sum += arr[right];
            }
            else{
                sum -= arr[left];
                left++;
            }

        }
        return maxlen;
    }

    public static void main(String[] args){
        int[] arr = {1,2,1,1,1};
        System.out.println(findMaxLengthSubArrayWithSumK(arr,3));
    }
}

8.2 数组可正可负

思路:用一个map结构,map的key是sum值,代表数组从0到i的一个累积和,value是对应的sum第一次出现的位置index。很关键的一点是,我们要预先在map中存储一个(0,-1)。

public class MaxLengthSubArrayWithSumKV2 {
    public static int findMaxLengthSubArrayWithSumK(int[] arr,int k){
        if(arr==null || arr.length==0)
            return 0;
        HashMap<Integer,Integer> map = new HashMap<Integer,Integer>();
        map.put(0,-1);
        int maxlen = 0;
        int sum = 0;
        for(int i=0;i<arr.length;i++){
            sum += arr[i];
            if(map.containsKey(sum - k)){
                maxlen = Math.max(i-map.get(sum-k),maxlen);
            }
            if(!map.containsKey(sum)){
                map.put(sum,i);
            }
        }
        return maxlen;
    }

    public static void main(String[] args){
        int[] arr = {-1,1,2,0,1,1,1};
        System.out.println(findMaxLengthSubArrayWithSumK(arr,3));
    }
}

9、子数组的最大累积和问题

思路:

public class MaxCumSumSubArray {
    public static int findMaxCumSumSubArray(int[] arr){
        int maxCur = arr[0];
        int maxSoFar = arr[0];
        for(int i=1;i<arr.length;i++){
            maxCur = Math.max(arr[i],maxCur + arr[i]);
            maxSoFar = Math.max(maxCur,maxSoFar);
        }
        return maxSoFar;
    }

    public static void main(String[] args){
        int[] arr = {1,-2,3,5,-2,6,-1};
        System.out.println(findMaxCumSumSubArray(arr));
    }
}

10、子矩阵的最大累积和问题

思路:跟第九题一样的思路,我们把矩阵按列相加,转换为一维数组的问题即可。

public class MaxCumSumSubMatrix {

    public static int findMaxCumSumSubMatrix(int[][] arr){
        if(arr == null || arr.length == 0 || arr[0].length == 0)
            return 0;
        int max = Integer.MIN_VALUE;
        int cur = 0;
        int[] s = null;
        for(int i=0;i<arr.length;i++){
            s = new int[arr[0].length];
            for(int j = i;j<arr.length;j++){
                s[0] += arr[j][0];
                cur = s[0]; // 每一行开始的时候,都变成0
                max = Math.max(cur,max);
                for(int k = 1;k<s.length;k++){
                    s[k] += arr[j][k];
                    cur = Math.max(s[k] ,s[k] + cur);
                    max = Math.max(max,cur);
                }

            }
        }
        return max;
    }

    public static void main(String[] args){
        int[][] arr = {{-90,48,78},{64,-40,64},{-81,-7,66}};
        System.out.println(findMaxCumSumSubMatrix(arr));

    }
}

11、数组中子数组的累乘积

思路:维护两个值,一个是以i位置结尾的子数组最大乘积,一个是以i位置结尾的子数组最小乘积。

public class MaxCumMulitSubArray {

    public static double findMaxCumMultiSubArray(double[] arr){
        if(arr==null || arr.length==0)
            return 0;
        double max = arr[0];
        double min = arr[0];
        double res = arr[0];
        double maxEnd = 0;
        double minEnd = 0;

        for(int i = 1;i<arr.length;i++){
            maxEnd = max * arr[i];
            minEnd = min * arr[i];
            max = Math.max(Math.max(maxEnd,minEnd),arr[i]);
            min = Math.min(Math.min(maxEnd,minEnd),arr[i]);
            res = Math.max(max,res);
        }
        return res;
    }

    public static void main(String[] args){
        double[] arr = {-2.5,4,0,3,0.5,8,-1};
        System.out.println(findMaxCumMultiSubArray(arr));
    }
}

12、边界都是1的最大正方形大小

思路:维护两个数组,一个right,表示从i,j位置开始,向右最多有多少个连续的1,一个down,表示从i,j位置开始,向下最多有多少个连续的1。要想得到这两个数组,必须从原数组的右下角开始向上向左遍历。

public class MaxSquareWithAllOne {

    public static int findMaxSquareWithAllOne(int[][] arr){
        if(arr==null || arr.length==0 || arr[0].length ==0)
            return 0;
        int[][] right = new int[arr.length][arr[0].length];
        int[][] down = new int[arr.length][arr[0].length];
        for(int i =arr.length-1;i>=0;i--){
            for(int j=arr[0].length-1;j>=0;j--){
                if(i==arr.length-1 && j==arr[0].length-1){
                    right[i][j] = arr[i][j] == 1 ? 1:0;
                    down[i][j] = arr[i][j] == 1? 1:0;
                }
                else if(i==arr.length-1){
                    down[i][j] = arr[i][j] == 1?1:0;
                    right[i][j] = arr[i][j] == 1?right[i][j+1] + 1:0;
                }
                else if(j==arr.length-1){
                    right[i][j] = arr[i][j] == 1 ? 1:0;
                    down[i][j] = arr[i][j] == 1?down[i+1][j] + 1:0;
                }
                else{
                    down[i][j] = arr[i][j] == 1?down[i+1][j] + 1:0;
                    right[i][j] = arr[i][j] == 1?right[i][j+1] + 1:0;
                }
            }
        }
        int maxSize = 0;
        for(int i=0;i<arr.length;i++){
            for(int j=0;j<arr[0].length;j++){
                int n = Math.min(right[i][j],down[i][j]);
                for(int k=1;k<=n;k++){
                    if((j + k - 1) <arr[0].length && down[i][j+k-1] >= k && (i+k-1) < arr.length && right[i+k-1][j] >= k)
                        maxSize = Math.max(maxSize,k);
                }
            }
        }
        return maxSize;

    }

    public static void main(String[] args){
        int[][] arr = {{0,1,1,1,1},{0,1,0,0,1},{0,1,0,0,1},{0,1,1,1,1},{0,1,0,1,1}};
        System.out.println(findMaxSquareWithAllOne(arr));
    }
}

13、不包含本位置的累乘数组

思路:维护两个数组,left和right,left[i]表示数组从0到i-1位置的累乘,right[I]表示数组从i+1位置到数组结束的累乘值。最后将两个数组的对应位置相乘即可得到最后的结果。

public class ProductWIthoutSelf {

    public static void getProductWithOutSelf(int[] arr){
        if(arr==null || arr.length==0)
            return;
        int[] left = new int[arr.length];
        int[] right = new int[arr.length];
        int[] res = new int[arr.length];
        left[0] = 1;
        for(int i=1;i<arr.length;i++){
            left[i] = left[i-1] * arr[i-1];
        }
        right[arr.length-1] = 1;
        for(int i=arr.length-2;i>=0;i--){
            right[i] = right[i+1] * arr[i+1];
        }
        for(int i=0;i<arr.length;i++){
            res[i] = left[i] * right[i];
            System.out.println(res[i]);
        }

    }
    public static void main(String[] args){
        int[] arr = {2,3,1,4};
        getProductWithOutSelf(arr);
    }
}

推荐阅读更多精彩内容