经典面试题目笔记一

1、给定一个有序数组arr,从左到右依次表示X轴上从左往右点的位置,给定一个正整数K,返回如果有一根长度为K的绳子,最多能盖住几个点,绳子的边缘点碰到X轴上的点,也算盖住

滑动窗口,没到一个点,看他能往右扩到哪里,每次记录值,时间复杂度O(N)

       public static int maxPoint2(int[] arr, int L) {
		int left = 0;
		int right = 0;
		int N = arr.length;
		int max = 0;
		while (left < N) {
			while (right < N && arr[right] - arr[left] <= L) {
				right++;
			}
			max = Math.max(max, right - (left++));
		}
		return max;
	}

2、括号有效配对是指:
1)任何一个左括号都能找到和其正确配对的右括号
2)任何一个右括号都能找到和其正确配对的左括号
有效的:    (())  ()()   (()())  等
无效的:     (()   )(     等
问题一:怎么判断一个括号字符串有效?
问题二:如果一个括号字符串无效,返回至少填几个字符能让其整体有效

有效的括号

一、

1、count 左增,右减,count < 0 返回false

2、遍历完,count == 0 true, !=0 false

       public static boolean valid(String s){
		if (s == null){
			return false;
		}
		char[] str = s.toCharArray();
		int count = 0;
		for (int i= 0; i < str.length; i++){
			count += str[i] == '(' ? 1 : -1;
			if (count < 0){
				return false;
			}
		}
		return  count == 0;
	}

二、

1、count 左增,右减,

count == -1, need ++ ,count = 0 说明多出一个右括号,需要加一个左括号,添在前面哪里都可以,总之是需要一个

如果count是正数 need+=count 说明剩余了左括号,需要count个右补足

      public static int needParentheses(String s) {
		char[] str = s.toCharArray();
		int t = 0;
		int needSolveRight = 0;
		for (int i = 0; i < str.length; i++) {
			if (str[i] == '(') {
				t++;
			} else { // 遇到的是')'
				if (t == 0) {
					needSolveRight++;
				} else {
					t--;
				}
			}
		}
		return t + needSolveRight;
	}

 

3、括号有效配对是指:
1)任何一个左括号都能找到和其正确配对的右括号
2)任何一个右括号都能找到和其正确配对的左括号
返回一个括号字符串中,最长的括号有效子串的长度

最长有效括号

子串,子数组常见的设计,以某个位置开头或结尾是怎么怎么样

以i位置为结尾的情况下,有效的括号子串长度,求所有的位置为结尾,就能找到最大的长度

看看i位置之前的数据能不能帮助i得出长度

1、如果i位置是左括号,那么以i结尾一定是0

2、0位置不管是什么,一定是0

3、求i位置的长度,就是i-1位置往前推dp[i-1],如果这个位置的前一个x的是右括号一定dp[i] = 0,是左括号,那么长度起码是 dp[i]=dp[i-1]+2, 还要加上这个位置x-1 的最大长度,因为可以连起来是有效的

      public static int maxLength(String s) {
		if (s == null || s.equals("")) {
			return 0;
		}
		char[] str = s.toCharArray();
		int[] dp = new int[str.length];
		int pre = 0;
		int res = 0;
		for (int i = 1; i < str.length; i++) {
			if (str[i] == ')') {
				pre = i - dp[i - 1] - 1; // 与str[i]配对的左括号的位置 pre
				if (pre >= 0 && str[pre] == '(') {
					dp[i] = dp[i - 1] + 2 + (pre > 0 ? dp[pre - 1] : 0);
				}
			}
			res = Math.max(res, dp[i]);
		}
		return res;
	}

有效括号的嵌套深度

 遇到左count++,遇到右count--,count最大值就是嵌套深度

     public static int deep(String s) {
		char[] str = s.toCharArray();
		if (!isValid(str)) {
			return 0;
		}
		int count = 0;
		int max = 0;
		for (int i = 0; i < str.length; i++) {
			if (str[i] == '(') {
				max = Math.max(max, ++count);
			} else {
				count--;
			}
		}
		return max;
	}

4、有一些排成一行的正方形。每个正方形已经被染成红色或者绿色。现在可以选择任意一个正方形然后用这两种颜色的任意一种进行染色,这个正方形的颜色将 会被覆盖。目标是在完成染色之后,每个红色R都比每个绿色G距离最左侧近。 返回最少需要涂染几个正方形。
如样例所示: s = RGRGR 我们涂染之后变成RRRGG满足要求了,涂染的个数为2,没有比这个更好的涂染方案。

1、枚举每个分界线,让左边都变R,右边都变G,需要涂的数量

2、准备辅助数组,统计0-i有几个G,统计 i- length 有几个R

这样枚举的时候,直接可以拿值

        // RGRGR -> RRRGG
	public static int minPaint(String s) {
		if (s == null || s.length() < 2) {
			return 0;
		}
		char[] chs = s.toCharArray();
		int[] right = new int[chs.length];
		right[chs.length - 1] = chs[chs.length - 1] == 'R' ? 1 : 0;
		for (int i = chs.length - 2; i >= 0; i--) {
			right[i] = right[i + 1] + (chs[i] == 'R' ? 1 : 0);
		}
		int res = right[0]; // 如果数组所有范围,都是右侧范围,都变成G
		int left = 0;
		for (int i = 0; i < chs.length - 1; i++) {
			left += chs[i] == 'G' ? 1 : 0;
			res = Math.min(res, left + right[i + 1]);
		}
		res = Math.min(res, left + (chs[chs.length - 1] == 'G' ? 1 : 0));
		return res;
	}

5、给定一个N*N的矩阵matrix,只有0和1两种值,返回边框全是1的最大正方形的边长长度。
例如:
01111
01001
01001
01111
01011
其中边框全是1的最大正方形的大小为4*4,所以返回4。

221. 最大正方形

在一个N*N的矩阵中,选择长方形的时间复杂度是N的四次方,正方形是N的三次方

for 枚举行

    for 枚举列

         for 枚举边长   枚举正方形的复杂度

               for 验证边框是否是1    达到了N的四次方

优化边框使用辅助数组

r[i][j]  (i,j) 点右边连续为1的数量

d[i][j]  (i,j) 点下边连续为1的数量

 

     public static void setBorderMap(int[][] m, int[][] right, int[][] down) {
		int r = m.length;
		int c = m[0].length;
		if (m[r - 1][c - 1] == 1) {
			right[r - 1][c - 1] = 1;
			down[r - 1][c - 1] = 1;
		}
		for (int i = r - 2; i != -1; i--) {
			if (m[i][c - 1] == 1) {
				right[i][c - 1] = 1;
				down[i][c - 1] = down[i + 1][c - 1] + 1;
			}
		}
		for (int i = c - 2; i != -1; i--) {
			if (m[r - 1][i] == 1) {
				right[r - 1][i] = right[r - 1][i + 1] + 1;
				down[r - 1][i] = 1;
			}
		}
		for (int i = r - 2; i != -1; i--) {
			for (int j = c - 2; j != -1; j--) {
				if (m[i][j] == 1) {
					right[i][j] = right[i][j + 1] + 1;
					down[i][j] = down[i + 1][j] + 1;
				}
			}
		}
	}

	public static int getMaxSize(int[][] m) {
		int[][] right = new int[m.length][m[0].length];
		int[][] down = new int[m.length][m[0].length];
		setBorderMap(m, right, down); // O(N^2); + 
		
		for (int size = Math.min(m.length, m[0].length); size != 0; size--) {
			if (hasSizeOfBorder(size, right, down)) {
				return size;
			}
		}
		return 0;
	}

	public static boolean hasSizeOfBorder(int size, int[][] right, int[][] down) {
		for (int i = 0; i != right.length - size + 1; i++) {
			for (int j = 0; j != right[0].length - size + 1; j++) {
				if (right[i][j] >= size && down[i][j] >= size
						&& right[i + size - 1][j] >= size
						&& down[i][j + size - 1] >= size) {
					return true;
				}
			}
		}
		return false;
	}


6、给定一个正整数M,请构造出一个长度为M的数组arr,要求,对任意的i、j、k三个位置,如果i<j<k,都有arr[i] + arr[k] != 2*arr[j],返回构造出的arr

假设[a,b,c] 符合规则,那么可以得到 a + c != 2 * b

推论 [2*a - 1,2*b-1,2*c-1] 也符合条件

代入得 2 * a - 1 + 2 * c - 1 != 2 * (2 * b -1)  => 2a + 2c -2 != 4b - 2 => a + c != 2b

不光是三个数字,更多也一样

变偶数也可以  2  * a + 2 * c != 4 * b

        // 生成长度为size的达标数组
	// 达标:对于任意的 i<k<j,满足   [i] + [j]  != [k] * 2
	public static int[] makeNo(int size) {
		if (size == 1) {
			return new int[] { 1 };
		}
		// size
		// 一半长达标来
		// 7   :   4
		// 8   :   4
		// [4个奇数] [3个偶]
		int halfSize = (size + 1) / 2;
		int[] base = makeNo(halfSize);
		// base -> 等长奇数达标来
		// base -> 等长偶数达标来
		int[] ans = new int[size];
		int index = 0;
		for(; index < halfSize;index++) {
			ans[index] = base[index] * 2 - 1;
		}
		for(int i = 0 ;index < size;index++,i++) {
			ans[index] = base[i] * 2; 
		}
		return ans;
	}

T(N) = T(N/2) + O(N) master公式 => O(N)

7、给定一个二叉树的头节点head,路径的规定有以下三种不同的规定:

1)路径必须是头节点出发,到叶节点为止,返回最大路径和

2)路径可以从任何节点出发,但必须往下走到达任何节点,返回最大路径和

3)路径可以从任何节点出发,到任何节点,返回最大路径和,沿途节点只能走一次

1)

        public static int maxSum = Integer.MIN_VALUE;

	public static int maxPath(Node head) {
		p(head, 0);
		return maxSum;
	}

	public static void p(Node x, int pre) {
		if (x.left == null && x.right == null) {
			maxSum = Math.max(maxSum, pre + x.value);
		}
		if (x.left != null) {
			p(x.left, pre + x.value);
		}
		if (x.right != null) {
			p(x.right, pre + x.value);
		}
	}

 

        public static int maxDis(Node head) {
		if (head == null) {
			return 0;
		}
		return process2(head);
	}

	// x为头的整棵树上,最大路径和是多少,返回。
	// 路径要求,一定从x出发,到叶节点,算做一个路径, 不能把空作为basecase
	public static int process2(Node x) {
		if (x.left == null && x.right == null) {
			return x.value;
		}
		int next = Integer.MIN_VALUE;
		if (x.left != null) {
			next = process2(x.left);
		}
		if (x.right != null) {
			next = Math.max(next, process2(x.right));
		}
		return x.value + next;
	}

2)  

与X无关  左大,右大,取最大

与X有关

1、X不动

2、X决定往左走

3、X决定往右走

       public class Info1{
		public int fromHeadMaxSum;
		public int allTreeMaxSum;

		public Info1(int fromHeadMaxSum, int allTreeMaxSum) {
			this.fromHeadMaxSum = fromHeadMaxSum;
			this.allTreeMaxSum = allTreeMaxSum;
		}
	}
	
	public Info1 process4(Node node){
		if (node == null){
			return null;
		}
		Info1 leftInfo = process4(node.left);
		Info1 rightInfo = process4(node.left);
		
		int p1 = Integer.MIN_VALUE;
		if (leftInfo != null){
			p1 = leftInfo.allTreeMaxSum;
		}
		int p2 = Integer.MIN_VALUE;
		if (rightInfo != null){
			p2 = rightInfo.allTreeMaxSum;
		}
		int p3 = node.value;
		int p4 = Integer.MIN_VALUE;
		if (leftInfo != null){
			p4 = node.value + leftInfo.fromHeadMaxSum;
		}
		int p5 = Integer.MIN_VALUE;
		if (rightInfo != null){
			p5 = node.value + rightInfo.fromHeadMaxSum;
		}
		int allTreeMaxSum = Math.max(Math.max(Math.max(p1,p2),p3),Math.max(p4,p5));
		int fromHeadMaxSum = Math.max(Math.max(p3,p4),p5);
		return new Info1(fromHeadMaxSum,allTreeMaxSum);
		
	}

3)

X无关,左右取最大

X有关

1、自己

2、往走走

3、往右走

4、即左,又右

     public Info1 process4(Node node){
		if (node == null){
			return null;
		}
		Info1 leftInfo = process4(node.left);
		Info1 rightInfo = process4(node.left);

		int p1 = Integer.MIN_VALUE;
		if (leftInfo != null){
			p1 = leftInfo.allTreeMaxSum;
		}
		int p2 = Integer.MIN_VALUE;
		if (rightInfo != null){
			p2 = rightInfo.allTreeMaxSum;
		}
		int p3 = node.value;
		int p4 = Integer.MIN_VALUE;
		if (leftInfo != null){
			p4 = node.value + leftInfo.fromHeadMaxSum;
		}
		int p5 = Integer.MIN_VALUE;
		if (rightInfo != null){
			p5 = node.value + rightInfo.fromHeadMaxSum;
		}
		int p6 = Integer.MIN_VALUE;
		if (leftInfo != null && rightInfo != null){
			// 算作不是从node出发,只是路过node能得到的最大值
			p6 = node.value + leftInfo.fromHeadMaxSum + rightInfo.fromHeadMaxSum;
		}
		int allTreeMaxSum = Math.max(Math.max(Math.max(Math.max(p1,p2),p3),Math.max(p4,p5)),p6);
		int fromHeadMaxSum = Math.max(Math.max(p3,p4),p5);
		return new Info1(fromHeadMaxSum,allTreeMaxSum);

	}

 4、

从任意节点出发到叶节点为止

        public static int max = Integer.MIN_VALUE;
  
	public static int process5(Node head){
		if (head.left == null && head.right == null){
			max = Math.max(max,head.value);
			return head.value;
		}
		int nextMax = 0;
		if (head.left != null){
			nextMax = process5(head.left);
		}
		if (head.right != null){
			nextMax = Math.max(nextMax, process5(head.right));
		}
		int ans = head.value + nextMax;
		max = Math.max(ans,max);
		return ans;
	}

笔试一般给的时间限制是常数操作是 10的8次方到10的9次方,如果题目给的N是10的6次方,反推出时间复杂度必须是O(N) 或者 O(logN)

 

 

 

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值