1、在行也有序、列也有序的二维数组中,找num,找到返回true,否则false
从右上角开始
public static boolean isContains(int[][] matrix, int K) {
int row = 0;
int col = matrix[0].length - 1;
while (row < matrix.length && col > -1) {
if (matrix[row][col] == K) {
return true;
} else if (matrix[row][col] > K) {
col--;
} else {
row++;
}
}
return false;
}
2、
有n个打包机器从左到右一字排开,上方有一个自动装置会抓取一批放物品到每个打 包机上,放到每个机器上的这些物品数量有多有少,由于物品数量不相同,需要工人 将每个机器上的物品进行移动从而到达物品数量相等才能打包。每个物品重量太大、 每次只能搬一个物品进行移动,为了省力,只在相邻的机器上移动。请计算在搬动最 小轮数的前提下,使每个机器上的物品数量相等。如果不能使每个机器上的物品相同, 返回-1。 例如[1,0,5]表示有3个机器,每个机器上分别有1、0、5个物品,经过这些轮后:
第一轮:1 0 <- 5 => 1 1 4 第二轮:1 <- 1 <- 4 => 2 1 3 第三轮:2 1 <- 3 => 2 2 2
移动了3轮,每个机器上的物品相等,所以返回3
例如[2,2,3]表示有3个机器,每个机器上分别有2、2、3个物品, 这些物品不管怎么移动,都不能使三个机器上物品数量相等,返回-1
(1) 、 整个数的累加和 % N != 0 怎么分都不行
(2)、每一轮,任何一台机器都能往左和往右,最少需要几轮
public static int MinOps(int[] arr) {
if (arr == null || arr.length == 0) {
return 0;
}
int size = arr.length;
int sum = 0;
for (int i = 0; i < size; i++) {
sum += arr[i];
}
if (sum % size != 0) {
return -1;
}
int avg = sum / size;
int leftSum = 0;
int ans = 0;
// 每个位置都求各自的
for (int i = 0; i < arr.length; i++) {
// i号机器,是中间机器,左(0~i-1) i 右(i+1~N-1)
// 负 需要输入 正需要输出
int leftRest = leftSum - i * avg; // a-b
// 负 需要输入 正需要输出
// c - d
int rightRest = (sum - leftSum - arr[i]) - (size - i - 1) * avg;
if (leftRest < 0 && rightRest < 0) {
ans = Math.max(ans, Math.abs(leftRest) + Math.abs(rightRest));
} else {
ans = Math.max(ans, Math.max(Math.abs(leftRest), Math.abs(rightRest)));
}
leftSum += arr[i];
}
return ans;
}
3、给定一个数组arr长度为N,你可以把任意长度大于0且小于N的前缀作为左部分,剩下的 作为右部分。
但是每种划分下都有左部分的最大值和右部分的最大值,请返回最大的, 左部分最大值减去右部分最大值的绝对值。
找到全局最大max,最大就是max - Math.min(arr[0],arr[arr.length - 1]);
左边包含max,右边的最大值要尽量小
右边包含max,左边也要最大值尽量小
无论如何怎么划分,左部分都要包含arr[0],右部分都要包含arr[arr.length -1]
public static int maxABS3(int[] arr) {
int max = Integer.MIN_VALUE;
for (int i = 0; i < arr.length; i++) {
max = Math.max(arr[i], max);
}
return max - Math.min(arr[0], arr[arr.length - 1]);
}
4、给定一个数组arr,已知其中所有的值都是非负的,将这个数组看作一个容器, 请返回容器能装多少水
比如,arr = {3,1,2,5,2,4},根据值画出的直方图就是容器形状,该容 器可以装下5格水
再比如,arr = {4,5,1,3,2},该容器可以装下2格水
思考方式,单独的i位置上方会有多少水
就是求左边的max和右边的max
i 位置的水量 = max {min {左max, 右max} - [i] , 0}
一般做法是做两个辅助数组,左max和右max再遍历数组,求出每个位置的水量
public static int water2(int[] arr) {
if (arr == null || arr.length < 2) {
return 0;
}
int N = arr.length;
int[] leftMaxs = new int[N];
leftMaxs[0] = arr[0];
for (int i = 1; i < N; i++) {
leftMaxs[i] = Math.max(leftMaxs[i - 1], arr[i]);
}
int[] rightMaxs = new int[N];
rightMaxs[N - 1] = arr[N - 1];
for (int i = N - 2; i >= 0; i--) {
rightMaxs[i] = Math.max(rightMaxs[i + 1], arr[i]);
}
int water = 0;
for (int i = 1; i < N - 1; i++) {
water += Math.max(Math.min(leftMaxs[i - 1], rightMaxs[i + 1]) - arr[i], 0);
}
return water;
}
最优解:
设置两个指针,初始left = 1, right = n - 2
那么 leftMax = nums[0], rightMax = nums[n - 1]
如果 leftMax < rightMax 说明 left位置,leftMax是真实的, rightMax 只会比中间的等于或者小,不可能中间还有rightMax比当前的大,所以 left 位置的水量就可以算出是 leftMax - nums[i] 小于0 变 0
大于类比,等于两边计算都可以
public static int water4(int[] arr) {
if (arr == null || arr.length < 2) {
return 0;
}
int N = arr.length;
int L = 1;
int leftMax = arr[0];
int R = N - 2;
int rightMax = arr[N - 1];
int water = 0;
while (L <= R) {
if (leftMax <= rightMax) {
water += Math.max(0, leftMax - arr[L]);
leftMax = Math.max(leftMax, arr[L++]);
} else {
water += Math.max(0, rightMax - arr[R]);
rightMax = Math.max(rightMax, arr[R--]);
}
}
return water;
}
5、如果给你一个二维数组,每一个值表示这一块地形的高度,求整块地形能装下多少水。
认为一个点只和上下左右相连,相当于从上往下看一个容器,数字代表高度,如果大数字包住了小数字
333
313
333
表明值为1可以装2格水
首先把边缘全部放入,弹出最小值就是边缘最薄弱的点max,整个蓄水量就是以这个做瓶颈,每次弹出没有更新就说明之前弹出的数都是以这个值max作为瓶颈的,当max被更新了,说明已经到了另外一片以更新后max为瓶颈的区域
public static class Node {
public int value;
public int row;
public int col;
public Node(int v, int r, int c) {
value = v;
row = r;
col = c;
}
}
public static class NodeComparator implements Comparator<Node> {
@Override
public int compare(Node o1, Node o2) {
return o1.value - o2.value;
}
}
public static int trapRainWater(int[][] heightMap) {
if (heightMap == null || heightMap.length == 0 || heightMap[0] == null || heightMap[0].length == 0) {
return 0;
}
int N = heightMap.length;
int M = heightMap[0].length;
// isEnter[i][j] == true 之前进过
// isEnter[i][j] == false 之前没进过
boolean[][] isEnter = new boolean[N][M];
// 小根堆
PriorityQueue<Node> heap = new PriorityQueue<>(new NodeComparator());
// 边缘进入
for (int col = 0; col < M - 1; col++) {
isEnter[0][col] = true;
heap.add(new Node(heightMap[0][col], 0, col));
}
for (int row = 0; row < N - 1; row++) {
isEnter[row][M - 1] = true;
heap.add(new Node(heightMap[row][M - 1], row, M - 1));
}
for (int col = M - 1; col > 0; col--) {
isEnter[N - 1][col] = true;
heap.add(new Node(heightMap[N - 1][col], N - 1, col));
}
for (int row = N - 1; row > 0; row--) {
isEnter[row][0] = true;
heap.add(new Node(heightMap[row][0], row, 0));
}
int water = 0; // 每个位置的水量,累加到water上去
int max = 0; // 每个node在弹出的时候,如果value更大,更新max,否则max的值维持不变
while (!heap.isEmpty()) {
Node cur = heap.poll();
max = Math.max(max, cur.value);
int r = cur.row;
int c = cur.col;
if (r > 0 && !isEnter[r - 1][c]) { // 如果有上面的位置并且上面位置没进过堆
water += Math.max(0, max - heightMap[r - 1][c]);
isEnter[r - 1][c] = true;
heap.add(new Node(heightMap[r - 1][c], r - 1, c));
}
if (r < N - 1 && !isEnter[r + 1][c]) {
water += Math.max(0, max - heightMap[r + 1][c]);
isEnter[r + 1][c] = true;
heap.add(new Node(heightMap[r + 1][c], r + 1, c));
}
if (c > 0 && !isEnter[r][c - 1]) {
water += Math.max(0, max - heightMap[r][c - 1]);
isEnter[r][c - 1] = true;
heap.add(new Node(heightMap[r][c - 1], r, c - 1));
}
if (c < M - 1 && !isEnter[r][c + 1]) {
water += Math.max(0, max - heightMap[r][c + 1]);
isEnter[r][c + 1] = true;
heap.add(new Node(heightMap[r][c + 1], r, c + 1));
}
}
return water;
}
时间复杂度O(M * N * log k), k 为小根堆内平均数量
6、给定一个有序数组arr,给定一个正数aim
leetcode上的题需要先排序
1)返回累加和为aim的,所有不同二元组
2)返回累加和为aim的,所有不同三元组
1)
public static void printUniquePair(int[] arr, int k) {
if (arr == null || arr.length < 2) {
return;
}
int left = 0;
int right = arr.length - 1;
while (left < right) {
if (arr[left] + arr[right] < k) {
left++;
} else if (arr[left] + arr[right] > k) {
right--;
} else { // L + R = aim
if (left == 0 || arr[left - 1] != arr[left]) {
System.out.println(arr[left] + "," + arr[right]);
}
left++;
right--;
}
}
}
2)
public static void printUniqueTriad(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]) {
printRest(arr, i, i + 1, arr.length - 1, k - arr[i]);
}
}
}
public static void printRest(int[] arr, int f, int l, int r, int k) {
while (l < r) {
if (arr[l] + arr[r] < k) {
l++;
} else if (arr[l] + arr[r] > k) {
r--;
} else {
if (l == f + 1 || arr[l - 1] != arr[l]) {
System.out.println(arr[f] + "," + arr[l] + "," + arr[r]);
}
l++;
r--;
}
}
}
7、长度为N的数组arr,一定可以组成N^2个数值对。
例如arr = [3,1,2],
数值对有(3,3) (3,1) (3,2) (1,3) (1,1) (1,2) (2,3) (2,1) (2,2),
也就是任意两个数都有数值对,而且自己和自己也算数值对。
数值对怎么排序?规定,第一维数据从小到大,第一维数据一样的,第二维数组也从小到大。所以上面的数值对排序的结果为:
(1,1)(1,2)(1,3)(2,1)(2,2)(2,3)(3,1)(3,2)(3,3)
给定一个数组arr,和整数k,返回第k小的数值对。
// O(N*logN)的复杂度,你肯定过了
public static int[] kthMinPair2(int[] arr, int k) {
int N = arr.length;
if (k > N * N) {
return null;
}
// O(N*logN)
Arrays.sort(arr);
// 第K小的数值对,第一维数字,是什么 是arr中
int fristNum = arr[(k - 1) / N];
int lessFristNumSize = 0;// 数出比fristNum小的数有几个
int fristNumSize = 0; // 数出==fristNum的数有几个
// <= fristNum
for (int i = 0; i < N && arr[i] <= fristNum; i++) {
if (arr[i] < fristNum) {
lessFristNumSize++;
} else {
fristNumSize++;
}
}
int rest = k - (lessFristNumSize * N);
return new int[] { fristNum, arr[(rest - 1) / fristNumSize] };
}
不排序可以使用bfprt来选数 利用辅助数组达到 O(N)
改写快排不需要使用额外的空间达到 O(N)