编程总结
每每刷完一道题后,其思想和精妙之处没有地方记录,本篇博客用以记录刷题过程中的遇到的算法和技巧
412. Fizz BuzzI
写一个程序,输出从 1 到 n 数字的字符串表示。
- 如果 n 是3的倍数,输出“Fizz”;
- 如果 n 是5的倍数,输出“Buzz”;
3.如果 n 同时是3和5的倍数,输出 “FizzBuzz”。
本题重点测试 sprintf函数和指向指针的指针的空间分配处理
char **fizzBuzz(int n, int* returnSize) {
int len = 0;
int in = n;
int i;
char **ret;
ret = (char**)malloc(sizeof(char*) * n); //分配n个指针,每个指针分别指向一个地址
*returnSize = n;
do {
len++;
in /= 10;
} while (in);
for (i = 1; i <= n; i++) { // 0 不记录,从1开始记录
if (i % 3 == 0 && i % 5 == 0) {
ret[i - 1] = (char *)malloc(strlen("fizzfuzz") + 1);
sprintf(ret[i - 1], "%s", "FizzBuzz");
}
else if (i % 5 == 0) {
ret[i - 1] = (char *)malloc(strlen("Buzz") + 1);
sprintf(ret[i - 1], "%s", "Buzz");
}
else if (i % 3 == 0) {
ret[i - 1] = (char *)malloc(strlen("Fizz") + 1);
sprintf(ret[i - 1], "%s", "Fizz");
}
else {
ret[i - 1] = (char *)malloc(len + 1);
sprintf(ret[i - 1], "%d", i);
}
}
return ret;
}
14. 最长公共前缀
关键手法:横向扫描法
char *longestCommonPrefix(char **strs, int strsSize)
{
int i = 0, j = 0;
char *ret = (char *)malloc(sizeof(char) * (strlen(strs[0])+1)); // 细节啊,这里必须要分配多一个'\0'的空间才行.
int cnt = 0;
int flag = 0;
if (strsSize == 1) {
return strs[0];
}
// 横向扫描法,保持列不动,看字符串数组里多少个列为同字符.
// 遇到不同的字符即退出
for (j = 0; j < strlen(strs[0]); j++) {
for (i = 0; i < (strsSize - 1); i++) {
if (strs[i][j] != strs[i + 1][j]) {
flag = 1;
break; // 一个break只会跳出一层
}
}
if (flag == 0) {
ret[cnt++] = strs[i][j];
} else {
break;
}
}
ret[cnt] = '\0';
return ret;
}
3. 无重复字符的最长子串
关键手法:滑动窗口法/双指针法
// 双指针法 -- 滑动窗口法
int lengthOfLongestSubstring(char * s) {
int res = 0;
int len = strlen(s);
int hash[256] = { 0 };
// 定义滑动窗口为 s[l...r]
int l = 0, r = -1;
while (l < len) {
// hash 中不存在该字符,右边界右移,并将该字符出现的次数记录在 hash 中
if (r < len - 1 && hash[s[r + 1]] == 0) {
hash[s[++r]]++;
}
else {
// 右边界无法拓展,左边界右移,刨除重复元素,并将此时左边界对应的字符出现的次数在 hash 的记录中减一
hash[s[l++]]--;
}
// 当前子串的长度和已找到的最长子串的长度取最大值 */
res = fmax(res, r - l + 1);
}
return res;
}
151. 翻转字符串里的单词
#define MAXSIZE 10001
#define STRSIZE 256
typedef struct Node {
char str[STRSIZE];
} Node_t;
char *reverseWords(char *s)
{
char *buf;
const char p[2] = " ";
char *token;
int len = strlen(s);
const char *plim = " ";
const char *blank = "\0";
int i;
Node_t *node = (Node_t *)malloc(sizeof(Node_t) * MAXSIZE);
int cnt = 0;
char res[MAXSIZE] = {0};
memset(res, 0, MAXSIZE * sizeof(char));
// 去除字符串的空格
token = strtok_s(s, p, &buf);
while (token != NULL) {
memcpy(node[cnt].str, token, strlen(token));
node[cnt].str[strlen(token)] = '\0';
cnt++;
token = strtok_s(NULL, p, &buf);
}
// 翻转字符串
i = cnt - 1;
while (i >= 0) {
strcat_s(res, node[i].str);
if (i != 0) {
strcat_s(res, plim);
}
else {
strcat_s(res, blank);
}
i--;
}
free(node);
memset(s, 0 , len);
memcpy(s, res, strlen(res)); // 其实这里返回 res/s都可以,但如果是返回res,不能是局部变量
return s;
}
165. 比较版本号
手法1: strtok 的分割,按 “.” 分割开后,利用atoi函数都转成整数,再比较一下即可,思路比较清晰.
#define MAX_SIZE 501
void get_version_num(char *version, int *res, int *num)
{
char strs[MAX_SIZE] = {0};
char *token = NULL;
const char *plim = "."; // 分隔符
int n = 0;
// 调用strtok函数时,形参要给字符串数组,所以多做了一次strncpy拷贝.
strncpy(strs, version, strlen(version) + 1);
token = strtok(strs, plim); // char *strtok(char *restrict str,const char *restrict delim);
while (token != NULL) {
res[n] = atoi(token);
n = n + 1;
token = strtok(NULL, ".");
}
*num = n;
}
int compareVersion(char *version1, char *version2)
{
int num1 = 0;
int num2 = 0;
int maxNum;
int res1[MAX_SIZE] = {0};
int res2[MAX_SIZE] = {0};
int i;
if (version1 == NULL || version2 == NULL) {
return -1;
}
get_version_num(version1, res1, &num1);
get_version_num(version2, res2, &num2);
// 手法:取最大的Num作为比较的次数,空缺的话,默认值为0.
maxNum = num1 > num2 ? num1 : num2;
for (i = 0; i < maxNum; i++) {
if (res1[i] > res2[i]) {
return 1;
} else if (res1[i] < res2[i]) {
return -1;
}
}
return 0;
}
手法2:num1 = num1 * 10 + (version1[i++] - ‘0’); 利用每一次到 ‘.’ 之前都记录下整数,每次直接比较整数,更为高效。
int compareVersion(char * version1, char * version2){
int len1 = strlen(version1);
int len2 = strlen(version2);
int i = 0;
int j = 0;
while (i < len1 || j < len2) {
int num1 = 0;
int num2 = 0;
while (i < len1 && version1[i] != '.') {
num1 = num1 * 10 + (version1[i++] - '0');
}
while (j < len2 && version2[j] != '.') {
num2 = num2 * 10 + (version2[j++] - '0');
}
if (num1 < num2) {
return -1;
} else if (num1 > num2) {
return 1;
}
i++;
j++;
}
return 0;
}
5789. 你完成的完整对局数
借鉴题解思路后:我们可以将 t_1 转化为 t_1 或之前时刻最后一场完整对局的结束时间 t_1′,或将 t_0转化为t_0或之后时刻第一场完整对局的开始时间t_0′即可,这样计算总的对局次数没有变.
需要处理 finish_time 在start_time之前的情况(包夜了),此时如果没有转成分钟来做,要注意这种情况的处理:
start_time – 00:01
finish_time – 00:00
所以还是统一转成分钟判断处理吧.
#define MAXSIZE 6
int numberOfRounds(char *startTime, char *finishTime)
{
char strs1[MAXSIZE] = { 0 };
char strs2[MAXSIZE] = { 0 };
const char *plim = ":"; // 存放分隔符
char *token; // 返回值,切割完的字符串
int i = 0;
int start[2] = { 0 };
int finish[2] = { 0 };
int res = 0;
int times0 = 0;
int times1 = 0;
// 1. 将时间字符串切割
strcpy(strs1, startTime);
strcpy(strs2, finishTime);
token = strtok(strs1, plim);
while (token != NULL) {
start[i++] = atoi(token);
token = strtok(NULL, plim);
}
token = strtok(strs2, plim);
i = 0;
while (token != NULL) {
finish[i++] = atoi(token);
token = strtok(NULL, plim);
}
times0 = 60 * (start[0]) + start[1];
times1 = 60 * (finish[0]) + finish[1];
// 2. 计算对局次数
if (times1 < times0) {
times1 += 24*60; // 包夜了,增加24小时
}
times1 = times1 / 15 * 15; // 按15min对齐
res = ((times1 - times0)/15);
return res;
}
401. 二进制手表
手法1:sprintf(tmp, “%d:%02d”,h, m); 来处理,为啥是%02d
手法2:__builtin_popcount()内嵌函数处理1在整数中的数量
char **readBinaryWatch(int turnedOn, int *returnSize)
{
int h;
int m;
char **ans = (char **)malloc(sizeof(char *) * 12 * 60);
*returnSize = 0;
for (h = 0; h < 12; ++h) {
for (m = 0; m < 60; ++m) {
if ((__builtin_popcount(h) + __builtin_popcount(m)) == turnedOn) {
printf("%d\n", m);
char *tmp = (char *)malloc(sizeof(char) * 6);
sprintf(tmp, "%d:%02d",h, m);
ans[(*returnSize)++] = tmp;
}
}
}
return ans;
}
179. 最大数
int camp(const void *v1, const void *v2)
{
static char buf1[512], buf2[512];
int len;
//返回写入buffer 的 字符数,出错返回-1
len = sprintf(buf1, "%d%d", *(int *)v1, *(int *)v2);
len = sprintf(buf2, "%d%d", *(int *)v2, *(int *)v1);
// 对数组进行降序排列
for (int i = 0; i < len; ++i) {
if (buf1[i] > buf2[i]) {
return -1;
}
if (buf1[i] < buf2[i]) {
return 1;
}
}
return 0;
}
char *largestNumber(int *nums, int numsSize)
{
static char str[1024];
int i = 0, j = 0;
qsort(nums, numsSize, sizeof(int), camp);
str[0] = '0';
str[1] = '\0';
while (i < numsSize && nums[i++] == 0); // 有个用例是 [0,0] -》“0” 而不是“00”, 去掉前导零
for (i--; i < numsSize; ++i) {
j += sprintf(str + j, "%d", nums[i]);
}
return str;
}
面试题 01.03. URL化
//双指针法,一个从旧数组末尾开始遍历,一个从新数组开始遍历添加
char* replaceSpaces(char* S, int length)
{
int SpaceNums = 0;
for (int i = 0; i < length; i++)
{
if (S[i] == ' ')
SpaceNums++;//判断有几个空格
}
int NewLength = length + SpaceNums * 2;//新长度=原长度+空格数*2
S[NewLength] = '\0';//避免过长导致最后一位字符为原题所给的空格;
int newlength = NewLength - 1;
for (int i = length - 1; i >= 0; i--)//不开辟新数组的情况下,从后往前遍历,避免覆盖
{
if (S[i] == ' ') {
S[newlength--] = '0';
S[newlength--] = '2';
S[newlength--] = '%';
}
else {
S[newlength--] = S[i];
}
}
return S;
}
1071. 字符串的最大公因子
试试巧妙的递归也是一个思路
// 1071. 字符串的最大公因子
// 如果两个字符串之间存在最大公因子:
//(1) 短串一定是和在长串的前strlen(短串)字符组成的子串相同;
//(2) 长串不断减去短串,得到的两个字符串(原有短串和差串)同样满足(1)中关系;
//(3) 当等到的两个串相同时,即为最大公因子
char *gcdOfStrings(char *str1, char *str2)
{
char *lger = strlen(str1) > strlen(str2) ? str1 : str2;
char *sher = strlen(str1) > strlen(str2) ? str2 : str1;
//(1) 短串一定是和在长串的前strlen(短串)字符组成的子串相同;
if (strcmp(lger, sher) == 0) {
return sher;
}
// 字符串不存在,返回 ""
if (strncmp(lger, sher, strlen(sher)) != 0) {
return "";
}
//(2) 长串不断减去短串,得到的两个字符串(原有短串和差串)同样满足(1)中关系;
return gcdOfStrings(lger + strlen(sher), sher);
}