❤ 2021.11.17 ❤
今天的题目是:
位1的个数
我的思路:
那就一位一位的比呗
int hammingWeight(uint32_t n) {
int count = 0;
for (int i = 0; i < 32; i++)
{
if ((n & 1) == 1)
count++;
n = n >> 1;
}
return count;
}
这里需要注意uint32_t这种类型需要include <stdint.h>这个库。
然后我看了下答案
int hammingWeight(uint32_t n) {
int ret = 0;
for (int i = 0; i < 32; i++) {
if (n & (1 << i)) {
ret++;
}
}
return ret;
}
大概意思就是他没把n移位,而是把1移位了,然后再统计,原理差不多。
然后是进阶版
其实我并没有看懂。。。
但是!但是! 这道题其实还不止如此!
他的函数名给了提示
hammingweight
翻译过来就是“汉明重量”,我百度了一下
→→→汉明重量
这道题的标准的解法应该是这样的
涨姿势涨姿势
❤ 2021.11.18 ❤
今天的题目是:
汉明距离
我的思路:
就很简单嘛!
不就是昨天的题之前加一个异或么~
int hammingDistance(int x, int y) {
int count = 0;
unsigned int temp = x ^ y;
for (int i = 0; i < 32; i++)
{
if (temp & 1)
count++;
temp = temp >> 1;
}
return count;
}
看了答案之后也是确实如此,哈哈哈
❤ 2021.11.19 ❤
今天的题目是:
颠倒二进制位
我的思路:
最直接的大概就是我新建个数,然后对原数字逐位读取然后以相对应的位置写到新的数字里吧,但是这样就觉得有点傻傻的,毕竟不是每一位都需要修改。
于是我略一沉思,我觉得可以先把原数字对半分,对称的比较其对应为是否相同,如果相同就跳过,如果不同则把对应位取反,取反的操作通过异或来实现。
uint32_t reverseBits(uint32_t n) {
for (int i = 0; i < 16; i++)
{
if (((n >> i) & 1) != ((n >> (31 - i)) & 1)) //判断对称的两位是否相同
//如果对称的两位不同,则将n与一个除了对应两位为1,其余都为0的二进制数相异或,以达到对相应位取反的效果
n = n ^ (((uint32_t)1 << i) | ((uint32_t)1 << (31 - i)));
}
return n;
}
在调试的过程中我遇到一个问题
这个问题我昨天也遇到过,但是没细想。
现在来看这个错误不是每个输入都会触发,从文字描述来看是当把1左移31位时,不能用int来表达。
于是我把各个地方加(uint32_t),直到把他加到1的前边才顺利通过。看来编译器默认把数字当做int型的来处理,修改之后就通过了。
其实我还犯了个小错误,我一开始把i和32-i作为对称位置了,于是整个结果就歪了一位,后来想想应该是31-i才对。
下面看看答案
第一种就是我想到的第一种暴力方法,第二种就比较厉害了!
大概意思就是先把相邻的两位互相交换,然后两两一组互相交换,以此类推。
不得不佩服大佬们的脑袋。
❤ 2021.11.24 ❤
今天的题目是:
杨辉三角
我的思路:
实现的过程应该没有什么困难,就当再练习下二维数组的动态分配吧。
int** generate(int numRows, int* returnSize, int** returnColumnSizes) {
*returnSize = numRows;
int** result = (int*)malloc(sizeof(int*) * numRows);
(*returnColumnSizes) = (int*)malloc(sizeof(int) * numRows);
for (int i = 0; i < numRows; i++)
{
result[i] = (int*)malloc(sizeof(int) * (i + 1));
(*returnColumnSizes)[i] = i + 1;
for (int j = 0; j < i + 1; j++)
{
if (j == 0 || j == i)
result[i][j] = 1;
else
result[i][j] = result[i - 1][j - 1] + result[i - 1][j];
}
}
return result;
}
主函数如下
int main()
{
int a = 3;
int m;
int* n;
int** k = generate(a, &m, &n);
for (int i = 0; i < m; i++)
{
printf("[");
for (int j = 0; j < n[i]; j++)
{
printf("%d ", k[i][j]);
}
printf("]\n");
}
}
我最开始写的程序是这样的
int** generate1(int numRows, int* returnSize, int** returnColumnSizes) {
int n = 1;
for (int i = 1; i <= numRows; i++)
{
n *= i;
}
*returnSize = numRows;
int** result = (int**)malloc(sizeof(int) * n);
(*returnColumnSizes) = (int*)malloc(sizeof(int) * numRows);
for (int i = 0; i < numRows; i++)
{
result[i] = (int*)malloc(sizeof(int) * (i + 1));
(*returnColumnSizes)[i] = i + 1;
for (int j = 0; j < i + 1; j++)
{
if (j == 0 || j == i)
result[i][j] = 1;
else
result[i][j] = result[i - 1][j - 1] + result[i - 1][j];
}
}
return result;
}
在vs里测试是可以通过的,但是在leetcode上面提交的时候就会报错,在层数为1和2的时候会出现
但是在层数大于等于3的时候就没事。看错误信息大概是内存的非法访问什么的。
开始我以为是数组越界,但是注释掉数组操作之后还是报错,于是我确定是内存分配的问题。
我这里用的是按层数的阶乘分配内存,后来发现不对应该是累加,但是修改之后还是报错。
于是经过思考我把问题定位到了malloc这个指令上。
对于二维数组的内存分配我基本是从之前做的二叉树层序遍历那道题学到的方法,可能是我的理解有一些误区。
我一直觉得二维数组需要一次性分配足够整个数组存储的内存空间,然后再储存数据,但是看了一些别人的程序,正确的方法应该是先分配每行的指针的内存,然后再给每行各自分配内存。
经过修改终于通过了,但是为什么以前这样写没有问题以及为什么只有层数为1和2的时候报错,其他数字没有问题这一点还是没有想清楚。。。
不过如果用c++的vector容器来做的话应该会很简单吧。
❤ 2021.11.26 ❤
今天的题目是:
有效的括号
我的思路:
这不就是个栈么!
正好我还没有在c语言里面写过栈的操作,就在这里手撸一下吧。
首我建立一个节点结构体,和普通链表节点一样一个数据成员一个指针成员,然后建立一个栈结构体,两个指针成员一个指向栈顶一个指向栈底。当栈顶指针和栈底指针指向相同时栈为空。
我想其实可以只有一个栈顶指针,当其指针为空指针时栈为空,这样还节省了一个空节点。不过既然能够实现就先这样吧
struct LNode {
char data;
struct LNode* next;
};
typedef struct LNode LNode;
struct Stack {
struct LNode* top;
struct LNode* bottom;
};
typedef struct Stack Stack;
然后我就不再写栈的操作函数了,直接在函数里操作。过程就是如果是左半括号就入栈,右半括号就看栈顶是否为对应左半括号,如果不是就false,是就出栈下一步,最后判断栈是否为空,是则true不是false。
bool isValid(char* s) {
Stack* S = (Stack*)malloc(sizeof(Stack));
LNode* L = (LNode*)malloc(sizeof(LNode));
L->data = 0;
S->bottom = L;
S->top = L;
int i = 0;
while (s[i] != 0)
{
if (s[i] == '(' || s[i] == '[' || s[i] == '{')
{
LNode* p = (LNode*)malloc(sizeof(LNode));
p->data = s[i];
p->next = S->top;
S->top = p;
i++;
}
else
{
if (s[i] == ')')
{
if (S->top->data != '(')
return false;
else
{
LNode* t;
t = S->top;
S->top = S->top->next;
free(t);
i++;
}
}
else if (s[i] == ']')
{
if (S->top->data != '[')
return false;
else
{
LNode* t;
t = S->top;
S->top = S->top->next;
free(t);
i++;
}
}
else
{
if (S->top->data != '{')
return false;
else
{
LNode* t;
t = S->top;
S->top = S->top->next;
free(t);
i++;
}
}
}
}
if (S->top != S->bottom)
return false;
return true;
}
我在调试过程中还是遇到了一些问题的
最开始我出栈的语句是这样写的,如下
我给临时指针t分配了内存空间,然后free的时候先free掉出栈的节点,再free临时节点,但是如下报错了。更神奇的是这个错误只有在输入一对括号以上的情况时会出现
经过分析我发现临时指针其实是不需要分配内存的。。。(我傻了),只需要释放掉其指向的节点的内存即可,他自身作为局部变量会随函数一起释放掉。
不过我在找资料的过程中看到这么一篇文章,虽然和我的问题关系不大但是比较有学习意义
→→→leetcode报错之 ERROR: AddressSanitizer: attempting double-free on 0x7fa0cd690800 in thread T0: …
另外,我最开始没有去判断循环结束时栈是否为空,于是在以下输入是报错了
我最开始想到的是判断循环结束时循环变量是否为偶数,但是又遇到了这个情况。。。
好吧,后来改成判断栈是否为空就好了。
然后我看了答案,感觉虽然思路差不多,但是和答案相比我的代码就太啰嗦了。
答案使用的是数组作为栈的存储空间,并且直接在栈中存入左括号对应的右括号,然后直接判断是否相等,非常有借鉴意义,我先来自己敲一遍的。
bool isValid(char* s) {
int n = strlen(s);
char* c = (char*)malloc(sizeof(char) * n);
int top = 0;
int i = 0;
char ch;
while (s[i])
{
if (s[i] == ')') //如果数组中为右括号返回对应的左括号,如果左括号则返回0
ch = '(';
else if (s[i] == ']')
ch = '[';
else if (s[i] == '}')
ch = '{';
else
ch = 0;
if (ch)
{
if (top == 0) //判断栈是否为空,为空false
return false;
if (ch != c[top - 1]) //判断返回的括号和栈顶储存括号是否一致,不一致false,一致则出栈。因为top指向新元素,所以栈顶实际是top-1
return false;
else
{
top--;
i++;
}
}
else
{
c[top] = s[i]; //入栈
top++;
i++;
}
}
if (top != 0) //判断栈是否为空(是否存入的左括号都出栈成功)
return false;
return true;
}
虽然是我自己敲的但是是按照答案的思路。奇怪的是同样是5.6MB为啥排名不一样。。
❤ 2021.11.29 ❤
今天的题目是
缺失数字
我的思路:
这个就很简单嘛,最无脑的方法先排序然后遍历呗。
进阶一点的用哈希表,这个题目比较简单,用数组代替哈希表应该是可行的。
int missingNumber(int* nums, int numsSize) {
int* hash = (int*)malloc(sizeof(int) * (numsSize + 1));
int j = 0;
for (int i = 0; i < numsSize + 1; i++)
hash[i] = 0;
for (int i = 0; i < numsSize; i++)
hash[nums[i]]++;
while (hash[j] != 0)
j++;
return j;
}
感觉还可以吧,虽然说用了三次循环,但是看了答案之后我依然觉得我真是太年轻了。。。
首先,先排序再遍历的方法只需要遍历两次,这样看起来我的方法挺废的。。。
然后是数学的方法,用求和公式先求出n个数之和,然后减去给出的数,最后剩下的就是缺失的数,嗯。。。
不过我觉得最有意思的是用按位异或的方法,根据按位异或的原理,异或两次的数会被屏蔽掉,因此经过两次遍历,先将从0到n的数异或一次,然后再与给出的数异或一次,最后剩下的就是缺失的数。
如果我把遍历过程写成一个呢?
int missingNumber(int* nums, int numsSize) {
int temp = 0;
for (int i = 0; i < numsSize; i++)
{
temp ^= i;
temp ^= nums[i];
}
temp ^= numsSize;
return temp;
}
但是这个结果吧还没有我循环三遍的速度快
就挺奇怪的。
我在调试的过程中遇到了一个问题,就是传入的数字是数组的长度,而数组里面实际上是少了一个元素的,也就是说遍历的时候需要加1,我刚开始没注意后来调试的时候结果总是不对,然后我想用在线调试的方法监测变量,但是我的变量是动态数组,默认情况下只能显示第一个元素的数值,我查了一下资料找到了方法,就是用“快速监视”功能,然后在窗口里面设置数组的元素数。如下
→→→vs中使用快速监视查看动态数组中元素的值(查看new出来的数组中元素的值)
啊 初级算法就这么做完了。(其实还没有因为之前有没做的。。。)