编程总结
每每刷完一道题后,其思想和精妙之处没有地方记录,本篇博客用以记录刷题过程中的遇到的算法和技巧
279)完全平方数
给定正整数 n,找到若干个完全平方数(比如 1, 4, 9, 16, …)使得它们的和等于 n。你需要让组成和的完全平方数的个数最少。
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
// 队列 && BFS
struct Link
{
int data;
struct Link* next;
};
struct Queue
{
struct Link *front; // 队头删除
struct Link *rear; // 队尾新增
int size;
};
void QueueInit(struct Queue *queue)
{
queue->front = NULL;
queue->rear = NULL;
queue->size = 0;
}
int QueueEmpty(struct Queue *queue)
{
return (queue->size == 0);
}
void QueuePush(struct Queue *queue, const int data)
{
struct Link *node;
node = (struct Link *)malloc(sizeof(struct Link));
node->data = data;
node->next = NULL;
printf("push queue %d\n", data);
if (QueueEmpty(queue)) {
queue->front = node;
queue->rear = node;
} else {
queue->rear->next = node; // 往队尾增加一个元素
queue->rear = node; // 设置当前元素为队尾
}
++queue->size;
}
int QueuePop(struct Queue *queue, int *data)
{
if (QueueEmpty(queue)) {
return 0;
}
struct Link *tmp = queue->front; // 队列头删除节点
*data = queue->front->data;
queue->front = queue->front->next;
printf("pop queue %d\n", *data);
free(tmp);
--queue->size;
return 1;
}
void QueueDestroy(struct Queue* queue)
{
struct Link *tmp;
while(queue->front) {
tmp = queue->front;
queue->front = queue->front->next;
free(tmp);
}
}
#define LENGTH 10000
int numSquares(int n) {
int i, j,next = 0, curr = 0, size = 0, steps = 0, *visited;
int *data = NULL;
struct Queue *queue = NULL;
queue = (struct Queue *)malloc(sizeof(struct Queue)); // 为根节点分配空间
if (n == 0 || n == 1) {
return n;
}
QueueInit(queue);
visited = (int *)malloc(sizeof(int) * (LENGTH+1));
data = (int *)malloc(sizeof(int));
memset(visited, 0, sizeof(int)*(LENGTH+1));
QueuePush(queue, 0); // 根节点入队列,入队为队尾
visited[0] = 1; // 访问标记
// 顺序遍历每一行,所以当节点差出现 0 时,此时一定是最短的路径
while (!QueueEmpty(queue))
{
steps++;
size = queue->size;
printf("------------steps is %d------------\n", steps);
for (i = 0; i < size; i++) {
QueuePop(queue, data); // 队首元素出队
curr = *data; // 队首元素值
printf("------curr is %d------\n", curr);
for(j = 1; j*j <= n; j++) {
next = curr+j*j;
printf("next is %d\n", next);
if (next == n) {
printf("!! bingo !! next == %d\n", n);
return steps;
} if (next > n) {
break;
} if (visited[next]) {
continue;
}
visited[next] = 1;
QueuePush(queue, next); // 节点入队
}
}
}
QueueDestroy(queue);
return steps;
}
int main() {
printf("\nresult is %d\n",numSquares(10));
return 0;
}
解题思路::求解 10 的完全平方数为例,利用图的BFS遍历(之前我们学习过BFS是利用队列),即按层次进行遍历,遍历值不超过 n 的平方根(j*j < n),如下图所示,所以依次进队列的是 0,然后0出队列,进 1->4->9,出队列1,然后入队 2->5->10,10 满足题意退出(图中多画了10 右面的情况是为了说明,如果出现之前出现过的值,则直接返回结果,这一小技巧,避免BFS遍历已访问的元素,如下图中黑色的点)
如果借助打印,结果将如下所示: