首页

矩形面积&移掉K位数字

一、矩形面积

难度:中等

题目传送门:矩形面积

题目

给你二维平面上两个由直线构成且边与坐标轴平行/垂直的矩形,请你计算并返回两个矩形覆盖的总面积。

每个矩形由其 左下 顶点和 右上 顶点坐标表示:

  1. 第一个矩形由其左下顶点 (ax1, ay1) 和右上顶点 (ax2, ay2) 定义。
  2. 第二个矩形由其左下顶点 (bx1, by1) 和右上顶点 (bx2, by2) 定义。

示例 1:
输入:ax1 = -3, ay1 = 0, ax2 = 3, ay2 = 4, bx1 = 0, by1 = -1, bx2 = 9, by2 = 2
输出:45

示例 2:
输入:ax1 = -2, ay1 = -2, ax2 = 2, ay2 = 2, bx1 = -2, by1 = -2, bx2 = 2, by2 = 2
输出:16

提示:

  1. -10^4 <= ax1, ay1, ax2, ay2, bx1, by1, bx2, by2 <= 10^4

思路

求覆盖面积,那么重点是重叠的面积只能算一次,即总面积=两个矩形面积 - 重叠面积。

另通过左下顶点和右上顶点可以得出几个隐含条件:

  1. ax2 > ax1
  2. bx2 > bx1
  3. bx2 > bx1
  4. by2 > by1

反映到坐标轴上,就是计算重叠的w和h。

绿色部分为重叠部分,可以看出:

  1. w = min(ax2,bx2) - max(ax1,bx1)
  2. h = min(ay2,by2) - max(ay1,by1)

如果w或者h为负数或者0,则表示重叠为0,最终结果就是减去重叠面积即可。

这种方法下:

  1. 时间复杂度为O(1);
  2. 空间复杂度为O(1)。

代码

先上测试

func Test_computeArea(t *testing.T) {
	inputs := [][]int{ \{-3, 0, 3, 4, 0, -1, 9, 2 \}, \{-2, -2, 2, 2, -2, -2, 2, 2\}, \{-2, -2, 2, 2, 3, 3, 4, 4\}}
	expects := []int{45, 16, 17}

	for i := 0; i < len(inputs); i++ {
		got := computeArea(inputs[i][0], inputs[i][1], inputs[i][2], inputs[i][3], inputs[i][4], inputs[i][5], inputs[i][6], inputs[i][7])
		expected := expects[i]
		if got != expected {
			t.Fatalf("[%d,%v] want %d,got %d", i+1, inputs[i], expected, got)
		}
	}
}

方法实现:

func computeArea(ax1 int, ay1 int, ax2 int, ay2 int, bx1 int, by1 int, bx2 int, by2 int) int {
	area := (ax2-ax1)*(ay2-ay1) + (bx2-bx1)*(by2-by1)

	w := min(ax2, bx2) - max(ax1, bx1)
	if w <= 0 {
		return area
	}
	h := min(ay2, by2) - max(ay1, by1)
	if h <= 0 {
		return area
	}
	return area - w*h
}
func min(x, y int) int {
	if x < y {
		return x
	}
	return y
}

func max(x, y int) int {
	if x > y {
		return x
	}
	return y
}

性能

来自Leetcode, 0表示小于1个单位。

执行用时: 20 ms
内存消耗: 6 MB

二、移掉K位数字

难度:中等

题目传送门:移掉K位数字

题目

给你一个以字符串表示的非负整数 num 和一个整数 k ,移除这个数中的 k 位数字,使得剩下的数字最小。请你以字符串形式返回这个最小的数字。

示例 1 :
输入:num = "1432219", k = 3
输出:"1219"
解释:移除掉三个数字 4, 3, 和 2 形成一个新的最小的数字 1219 。

示例 2 :
输入:num = "10200", k = 1
输出:"200"
解释:移掉首位的 1 剩下的数字为 200. 注意输出不能有任何前导零。

示例 3 :
输入:num = "10", k = 2
输出:"0"
解释:从原数字移除所有的数字,剩余为空就是 0 。

提示:

  1. 1 <= k <= num.length <= 10^5
  2. num 仅由若干位数字(0 - 9)组成
  3. 除了 0 本身之外,num 不含任何前导零

思路

要最终的数字最小,高位的k个数一定是保持递增的,否则就不是最小的。

按照这个思路,在删除k个数之前,从左往右判断,如果上一个数比下一个大,则删除上一个。

如果结果没有删除到k个数,即遍历完成,此时结果数列是递增的。那么就需要剪尾,把排列在最后的数进行删除。

需要注意提示3,结果数不能包含前导零,所以结果遍历去掉0。

这种方法下:

  1. 每个元素都要进入结果数,时间复杂度为O(n);
  2. 需要一个数组来保存结果,空间复杂度为O(n)。

代码

先上测试

func Test_computeArea(t *testing.T) {
	type Item struct {
		num string
		k   int
	}
	inputs := []Item{\{num: "1432219", k: 3\}, \{"10200", 1\}, \{"10", 2\}, \{"9", 1\}, \{"10200", 1\}, \{"10", 1\}, \{"100", 1\}}
	expects := []string{"1219", "200", "0", "0", "200", "0", "0"}

	for i := 0; i < len(inputs); i++ {
		got := removeKdigits(inputs[i].num, inputs[i].k)
		expected := expects[i]
		if got != expected {
			t.Fatalf("[%d,%v] want %v,got %v", i+1, inputs[i], expected, got)
		}
	}
}

方法实现:

func removeKdigits(num string, k int) string {
	if k <= 0 {
		return num
	}
	var result []byte
	index := -1
	m := 0
	for i := 0; i < len(num); i++ {
		if index == -1 {
			result = append(result, num[i])
			index++
			continue
		}
		if m == k {
			result = append(result, num[i:]...)
			break
		}
		for index > -1 && m < k && num[i] < result[index] {
			result = result[:len(result)-1]
			index--
			m++
		}
		result = append(result, num[i])
		index++
	}
	if m < k {
		// 剪尾
		if len(result) <= k-m {
			return "0"
		} else {
			result = result[:len(result)-k+m]
		}
	}
	// 去除头部0
	r := strings.TrimLeft(string(result), "0")
	if r == "" {
		r = "0"
	}
	return r
}

性能

来自Leetcode, 0表示小于1个单位。

执行用时: 4 ms
内存消耗: 4.3 MB

tips: 不得不吐槽,同样的代码在leetcode运行结果不一致。