经典面试题目笔记二

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)

  • 1
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值