目录

每日算法(四)数组

按要求打印数组

转圈打印矩阵

题目:

思路

利用对角进行打印

public static void spiralOrderPrint(int[][] matrix) {
		// 左上角
		int tR = 0;
		int tC = 0;
		// 右底角
		int dR = matrix.length - 1;
		int dC = matrix[0].length - 1;
		while (tR <= dR && tC <= dC) {
			printEdge(matrix, tR++, tC++, dR--, dC--);
		}
	}

	public static void printEdge(int[][] m, int tR, int tC, int dR, int dC) {
      // 处于同一行
		if (tR == dR) { 
			for (int i = tC; i <= dC; i++) {
				System.out.print(m[tR][i] + " ");
			}
          // 处于同一列
		} else if (tC == dC) { 
			for (int i = tR; i <= dR; i++) {
				System.out.print(m[i][tC] + " ");
			}
		} else { 
          // 正常打印
			int curC = tC;
			int curR = tR;
			while (curC != dC) {
				System.out.print(m[tR][curC] + " ");
				curC++;
			}
			while (curR != dR) {
				System.out.print(m[curR][dC] + " ");
				curR++;
			}
			while (curC != tC) {
				System.out.print(m[dR][curC] + " ");
				curC--;
			}
			while (curR != tR) {
				System.out.print(m[curR][tC] + " ");
				curR--;
			}
		}
	}

正方形矩阵顺时针打印

题目:

给定一个N×N的矩阵 matrIx,把这个矩阵调整成顺时针转动90°后的形式

思路:

还是利用左上角和右下角 观察到转动90° 就是移动左上角的横坐标和右底角的纵坐标的差

public static void rotate(int[][] matrix) {
		int tR = 0;
		int tC = 0;
		int dR = matrix.length - 1;
		int dC = matrix[0].length - 1;
		while (tR < dR) {
			rotateEdge(matrix, tR++, tC++, dR--, dC--);
		}
	}

	public static void rotateEdge(int[][] m, int tR, int tC, int dR, int dC) {
		int times = dC - tC; // 右底角 - 左上角
		int tmp = 0;
      // 交换
		for (int i = 0; i != times; i++) { 
			tmp = m[tR][tC + i];
			m[tR][tC + i] = m[dR - i][tC];
			m[dR - i][tC] = m[dR][dC - i];
			m[dR][dC - i] = m[tR + i][dC];
			m[tR + i][dC] = tmp;
		}
	}

之字型打印矩阵

题目:

给定一个矩阵 matrIx,按照“之”字形的方式打印这个矩阵,例如

1 2 3 4

5 6 7 8

9 10 11 12

打印结果为1,2,5,9,6,3,4,7,10,11,8,12

思路:

两个点开始都在(0,0) A往右移动 再往下移动 B往下移动 再往右移动

	public static void printMatrixZigZag(int[][] matrix) {
		int tR = 0;
		int tC = 0;
		int dR = 0;
		int dC = 0;
		// 终点
		int endR = matrix.length - 1;
		int endC = matrix[0].length - 1;
		// 打印方向
		boolean fromUp = false;
		// 没到终点
		while (tR != endR + 1) {
			printLevel(matrix, tR, tC, dR, dC, fromUp);
			tR = tC == endC ? tR + 1 : tR;
			tC = tC == endC ? tC : tC + 1;
			dC = dR == endR ? dC + 1 : dC;
			dR = dR == endR ? dR : dR + 1;
			fromUp = !fromUp;
		}
		System.out.println();
	}

	public static void printLevel(int[][] m, int tR, int tC, int dR, int dC,
			boolean f) {
			// 左下->右上
		if (f) {
			while (tR != dR + 1) {
				System.out.print(m[tR++][tC--] + " ");
			}
			// 右上->左下
		} else {
			while (dR != tR - 1) {
				System.out.print(m[dR--][dC++] + " ");
			}
		}
	}

未排序数组中累加和问题

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

题目:

给定一个数组arr,该数组无序,但每个值均为正数,再给定一个正数k。求ar的所有子数组中所有元素相加和为k的最长子数组长度

思路: 滑动窗口

 public static int getMaxLength(int[] arr, int k) {
        if (arr == null || arr.length == 0 || k < 1) return -1;
        int left = 0;
        int right = 0;
        int len = 0;
        int sum = arr[0];
        while (right < arr.length) {
          // 相等 记录此时长度 继续往后遍历
            if (sum == k) {
                len = Math.max(len, right - left + 1);
                sum -= arr[left++];
              // 小于 right往右扩一位
            } else if (sum < k) {
                right++;
                if (right == arr.length) break;
                sum += arr[right];
            } else {
              // 大于 left往右扩一位
                sum -= arr[left++];
            }
        }
        return len;
    }
// 时间复杂度 O(N)  空间复杂度 O(1)

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

题目:

给定一个数组arr,该数组无序,每个值可正 可负 可0,再给定一个整数k。求ar的所有子数组中所有元素相加和为k的最长子数组长度

思路:

arr[j+1,i]: 的累计和为s(i) - (j) 并且应该从-1位置开始考虑

  • sum: 从0位置一直加到i位置所有元素的和
  • len: 累加和为k的最长子数组长度
  • map:
    • key: 从arr最左边开始累加的过程中出现过的sum值
    • value: sum值最早出现的位置

map.put(0,-1)

意义在于一个数也不加时 累加和为0 且位置为 - 1

例如 [ 1, 2, 3, 3] k = 6

遍历到第一个3 此时是 sum = 6 map : key 1 : value 0 key3 : value 1

map.get (sum - k) 没有值 则 [1,2,3]被忽略

若加了 则长度是 2 - (- 1) = 3

public int maxLength(int[] arr,int k){
        if(arr == null || arr.length == 0 ||  k < 1) return -1;
        HashMap<Integer,Integer> map = new HashMap<>();
        // 重要
        map.put(0,-1);
        int len = 0;
        int sum = 0;
        for(int i = 0; i != arr.length;i++){
            sum += arr[i];
            if(map.containsKey(sum - k)){
                len = Math.max(i - map.get(sum - k),len);
            }
            if(!map.containsKey(sum)){
                map.put(sum,i);
            }
        }
        return len;
    }

未排序数组中累加和小于或等于给定值的最长子数组长度

题目:

给定一个无序数组arr,其中元素可正、可负、可0,给定一个整数k。求ar所有的子数组中累加和小于或等于k的最长子数组长度

思路:

求以0~j位置的累加和sum[0,j] 以j结尾的相加小于等于k的最长子数组 要得到大于等于sum[0,j] - k 出现在什么位置 假如为i 则区间为arr[i + 1,j]

  • sumArr : 从左到右的累加和数组 sumArr 第一个数为0
  • helpArr: sumar的左侧最大值数组
public static int maxLength(int[] arr, int k) {
		int[] h = new int[arr.length + 1];
		int sum = 0;
		h[0] = sum;
  // 生成helpArr数组
		for (int i = 0; i != arr.length; i++) {
			sum += arr[i];
			h[i + 1] = Math.max(sum, h[i]);
		}
		sum = 0;
		int res = 0;
		int pre = 0;
		int len = 0;
  //遍历查找数组 满足大于等于sum[0,j] - k 出现在什么位置 记录长度
		for (int i = 0; i != arr.length; i++) {
			sum += arr[i];
			pre = getLessIndex(h, sum - k);
			len = pre == -1 ? 0 : i - pre + 1;
			res = Math.max(res, len);
		}
		return res;
	}

// 二分查找出 大于等于num的位置
	public static int getLessIndex(int[] arr, int num) {
		int low = 0;
		int high = arr.length - 1;
		int mid = 0;
		int res = -1;
		while (low <= high) {
			mid = (low + high) / 2;
			if (arr[mid] >= num) {
				res = mid;
				high = mid - 1;
			} else {
				low = mid + 1;
			}
		}
		return res;
	}

子数组的最大累加和问题

题目:

给定一个数组arr,返回子数组的最大累加和

思路:

要求是返回最大累加和而不是长度 就没必要用滑动窗口 对于负数 直接归为0即可 这样能保证是最大的

public static int maxSum(int[] arr) {
		if (arr == null || arr.length == 0) {
			return 0;
		}
		int max = Integer.MIN_VALUE;
		int cur = 0;
		for (int i = 0; i != arr.length; i++) {
			cur += arr[i];
			max = Math.max(max, cur);
          // 小于0 直接归0
			cur = cur < 0 ? 0 : cur;
		}
		return max;
	}

子矩阵的最大累加和问题

题目:

给定一个矩阵 matrIx,其中的值有正、有负、有0,返回子矩阵的最大累加和

思路:

利用上面一题的子数组判定一个数组的最大累加和 矩阵从第一行..最后一行开始累加 求值 再第二行…最后一行

public static int maxSum(int[][] m) {
		if (m == null || m.length == 0 || m[0].length == 0) {
			return 0;
		}
		int max = Integer.MIN_VALUE;
		int cur = 0;
		int[] s = null; 
  // 从第几行开始累加 
		for (int i = 0; i != m.length; i++) {
			s = new int[m[0].length];
          // 第几行
			for (int j = i; j != m.length; j++) {
				cur = 0;
              //第几列
				for (int k = 0; k != s.length; k++) {
					s[k] += m[j][k];
					cur += s[k];
					max = Math.max(max, cur);
					cur = cur < 0 ? 0 : cur;
				}
			}
		}
		return max;
	}
// 时间复杂度O(N^3)  空间复杂度 O(N)

数组中子数组的最大累乘积

题目:

给定一个 double类型的数组ar,其中的元素可正、可负、可0,返回子数组累乘的最大乘积

思路:

假设以arr[i-1]尾的最小累乘积为min,以arr[i-1]结尾的最大累乘积为max。那么,以arri结尾的最大累乘积只有以下三种可能

  1. max * arr[i] : [1 ,2 ,3]
  2. min * arr[i] : [-1,2,-3]
  3. arr[i] : [0,1,2]
public static double maxProduct(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(res, max);
		}
		return res;
	}
// 时间复杂度 O(N) 空间复杂度 O(1)

小结

滑动窗口

设置两个指针 left right 满足条件时 right 向右扩一位 不满足时left 向右阔一位 可以利用 一个变量max 去记录最大值什么的