这道题目要求我们在给定平面上的一组点中,找出能够构成矩形的最小面积。与基础版本不同,这里允许矩形旋转,因此我们需要考虑斜向矩形的情况。题目给出的点都是整数坐标,但最终面积可能是浮点数。
理解题目时需要注意几个关键点:
判断四个点能否构成矩形,有以下几种数学方法:
在实际编码中,第一种方法实现起来最为简便。具体来说:
暴力枚举所有四点组合的O(n^4)方法虽然可行,但对于n=50来说计算量较大(约230万次循环)。我们可以采用更聪明的O(n^2)方法:
这种方法利用了矩形的几何特性,将复杂度从O(n^4)降低到O(n^2)。
c复制typedef struct {
int x;
int y;
} Point;
typedef struct {
int dx;
int dy;
} Vector;
typedef struct {
Point p1;
Point p2;
long distance_sq;
Point center;
} Segment;
我们定义了三种主要数据结构:
c复制long distance_sq(Point p1, Point p2) {
long dx = p1.x - p2.x;
long dy = p1.y - p2.y;
return dx*dx + dy*dy;
}
Point center(Point p1, Point p2) {
Point c = {
.x = p1.x + p2.x,
.y = p1.y + p2.y
};
return c;
}
这里使用距离平方而非实际距离,避免了浮点数运算和开方操作,同时不影响比较结果。
由于C标准库没有内置哈希表,我们需要实现一个简易版本:
c复制#define HASH_SIZE 20000
typedef struct HashNode {
Segment seg;
struct HashNode* next;
} HashNode;
HashNode* hashTable[HASH_SIZE];
unsigned int hash_func(Point center, long dist_sq) {
return ((center.x * 31 + center.y) * 31 + dist_sq) % HASH_SIZE;
}
void insert_segment(Segment seg) {
unsigned int idx = hash_func(seg.center, seg.distance_sq);
HashNode* node = malloc(sizeof(HashNode));
node->seg = seg;
node->next = hashTable[idx];
hashTable[idx] = node;
}
c复制double calculate_area(Point p1, Point p2, Point p3) {
Vector v1 = {p1.x - p3.x, p1.y - p3.y};
Vector v2 = {p2.x - p3.x, p2.y - p3.y};
long dot = v1.dx * v2.dx + v1.dy * v2.dy;
if (dot != 0) return DBL_MAX;
long len1_sq = v1.dx * v1.dx + v1.dy * v1.dy;
long len2_sq = v2.dx * v2.dx + v2.dy * v2.dy;
return sqrt(len1_sq * len2_sq);
}
这里通过向量点积判断是否垂直,然后计算两边长度的乘积得到面积。
c复制double minAreaFreeRect(int** points, int pointsSize, int* pointsColSize) {
// 初始化哈希表
memset(hashTable, 0, sizeof(hashTable));
// 预处理所有点对
Point* pts = malloc(pointsSize * sizeof(Point));
for (int i = 0; i < pointsSize; i++) {
pts[i].x = points[i][0];
pts[i].y = points[i][1];
}
// 填充哈希表
for (int i = 0; i < pointsSize; i++) {
for (int j = i + 1; j < pointsSize; j++) {
Segment seg = {
.p1 = pts[i],
.p2 = pts[j],
.distance_sq = distance_sq(pts[i], pts[j]),
.center = center(pts[i], pts[j])
};
insert_segment(seg);
}
}
// 查找最小面积矩形
double min_area = DBL_MAX;
// ... (后续处理)
free(pts);
return min_area == DBL_MAX ? 0 : min_area;
}
c复制for (int i = 0; i < HASH_SIZE; i++) {
for (HashNode* node1 = hashTable[i]; node1 != NULL; node1 = node1->next) {
for (HashNode* node2 = node1->next; node2 != NULL; node2 = node2->next) {
Segment seg1 = node1->seg;
Segment seg2 = node2->seg;
if (seg1.center.x == seg2.center.x &&
seg1.center.y == seg2.center.y &&
seg1.distance_sq == seg2.distance_sq) {
double area = calculate_area(seg1.p1, seg1.p2, seg2.p1);
if (area < min_area) {
min_area = area;
}
}
}
}
}
需要考虑的特殊情况包括:
提示:在实际面试中,可以先实现暴力解法,再逐步优化。解释清楚每种优化背后的几何原理非常重要。
以下是整合所有优化后的完整实现:
c复制#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <limits.h>
#include <math.h>
#include <float.h>
typedef struct {
int x;
int y;
} Point;
typedef struct {
int dx;
int dy;
} Vector;
typedef struct {
Point p1;
Point p2;
long distance_sq;
Point center;
} Segment;
typedef struct HashNode {
Segment seg;
struct HashNode* next;
} HashNode;
#define HASH_SIZE 20000
HashNode* hashTable[HASH_SIZE];
unsigned int hash_func(Point center, long dist_sq) {
return ((center.x * 31 + center.y) * 31 + dist_sq) % HASH_SIZE;
}
void insert_segment(Segment seg) {
unsigned int idx = hash_func(seg.center, seg.distance_sq);
HashNode* node = malloc(sizeof(HashNode));
node->seg = seg;
node->next = hashTable[idx];
hashTable[idx] = node;
}
long distance_sq(Point p1, Point p2) {
long dx = p1.x - p2.x;
long dy = p1.y - p2.y;
return dx*dx + dy*dy;
}
Point center(Point p1, Point p2) {
Point c = {
.x = p1.x + p2.x,
.y = p1.y + p2.y
};
return c;
}
double calculate_area(Point p1, Point p2, Point p3) {
Vector v1 = {p1.x - p3.x, p1.y - p3.y};
Vector v2 = {p2.x - p3.x, p2.y - p3.y};
long dot = v1.dx * v2.dx + v1.dy * v2.dy;
if (dot != 0) return DBL_MAX;
long len1_sq = v1.dx * v1.dx + v1.dy * v1.dy;
long len2_sq = v2.dx * v2.dx + v2.dy * v2.dy;
return sqrt(len1_sq * len2_sq);
}
double minAreaFreeRect(int** points, int pointsSize, int* pointsColSize) {
memset(hashTable, 0, sizeof(hashTable));
Point* pts = malloc(pointsSize * sizeof(Point));
for (int i = 0; i < pointsSize; i++) {
pts[i].x = points[i][0];
pts[i].y = points[i][1];
}
for (int i = 0; i < pointsSize; i++) {
for (int j = i + 1; j < pointsSize; j++) {
Segment seg = {
.p1 = pts[i],
.p2 = pts[j],
.distance_sq = distance_sq(pts[i], pts[j]),
.center = center(pts[i], pts[j])
};
insert_segment(seg);
}
}
double min_area = DBL_MAX;
for (int i = 0; i < HASH_SIZE; i++) {
for (HashNode* node1 = hashTable[i]; node1 != NULL; node1 = node1->next) {
for (HashNode* node2 = node1->next; node2 != NULL; node2 = node2->next) {
Segment seg1 = node1->seg;
Segment seg2 = node2->seg;
if (seg1.center.x == seg2.center.x &&
seg1.center.y == seg2.center.y &&
seg1.distance_sq == seg2.distance_sq) {
double area = calculate_area(seg1.p1, seg1.p2, seg2.p1);
if (area < min_area) {
min_area = area;
if (min_area == 1) goto cleanup; // 提前终止
}
}
}
}
}
cleanup:
for (int i = 0; i < HASH_SIZE; i++) {
HashNode* node = hashTable[i];
while (node != NULL) {
HashNode* temp = node;
node = node->next;
free(temp);
}
}
free(pts);
return min_area == DBL_MAX ? 0 : min_area;
}
建议使用以下测试用例验证代码正确性:
常规测试:
c复制int points1[4][2] = {{1,2},{2,1},{1,0},{0,1}};
// 应返回 2.0
无矩形情况:
c复制int points2[4][2] = {{0,1},{2,1},{1,1},{1,0}};
// 应返回 0
最小面积:
c复制int points3[4][2] = {{0,1},{1,0},{2,1},{1,2}};
// 应返回 1.0
多点共线:
c复制int points4[5][2] = {{0,0},{0,1},{0,2},{1,0},{1,1}};
// 应返回 0
这个问题可以进一步扩展为:
在实际工程应用中,类似的几何算法常用于: