感觉每天都写有点懒,还是每两天写一下,两天的时间做个总结
主要考察两知识点:
其实判断一个链表是否存在环还是挺容易的,但是找到这个环的入口可能还是需要在思考一下怎么去寻找解决的方法的。
在这里要用到数学的知识内容,画图可以看出,自己也可以通过计算验证一下
还是没有通过运行,我感觉自己的代码的思路是没有问题的,可能是一些特殊情况的处理细节是没有考虑充分的。
class Solution {
public:
ListNode* detectCycle(ListNode* head)
{
if(head->next == nullptr) return nullptr;
//先判断是否存在环
ListNode* Fast = head;
ListNode* Slow = head;
while ( Fast!=nullptr )
{
Fast = Fast->next->next;
Slow = Slow->next;
if (Fast == Slow) break;
if (Fast == nullptr) return nullptr;
}
//跳出了循环则说明这是一个有环的链表
ListNode* IndexMet = Fast;
ListNode* IndexHead = head;
int index = 0;
while (IndexMet != IndexHead)
{
index++;
IndexMet = IndexMet->next;
IndexHead = IndexHead->next;
}
return IndexMet;
}
};
后面对比代码检查发现,是我的while里面对fast的判断条件是少了的,因为后面的fast要移动
Fast = Fast->next->next;所以我还要再添加一个条件Fast->next != nullptr;
class Solution {
public:
ListNode* detectCycle(ListNode* head)
{
if (head == nullptr) return nullptr;
//先判断是否存在环
ListNode* Fast = head;
ListNode* Slow = head;
while (Fast != nullptr && Fast->next!=nullptr)
{
Fast = Fast->next->next;
Slow = Slow->next;
if (Fast == Slow)
{
ListNode* IndexMet = Fast;
ListNode* IndexHead = head;
while (IndexMet != IndexHead)
{
IndexMet = IndexMet->next;
IndexHead = IndexHead->next;
}
return IndexMet;
}
}
return nullptr;
}
};
对比自己以前完全只可以对着抄代码,现在自己可以多少写一点东西出来了,感觉还是有进步在里面的,希望自己可以再接再厉,算法这一块非常重要,是要学一辈子的东西,不需要太急功近利。
如何自己去涉及一个链表,去设计链表的增删查改,对每一种操作都要掌握
虚拟头节点
虚拟头节点可以很方便的对前面一个节点进行操作,因为在链表中我们很多时候都需要找到当前需要操作的节点的前面一个节点才可以对当前的节点进行操作,虚拟头节点可以让我们少了对头节点操作的额外判断。
双指针
快慢指针的方法真的非常的常用,它可以减少时间复杂度,有些操作的时间复杂是n²,双指针可以减少到n的情况
题目类型
写这个项目真的感觉到很痛苦,经常出现错误问题,但是自己又找不到问题在哪里
外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传
哈希表一般来说是为了帮助判断一个元素是否出现在了集合里面,可以帮助更快速的去查询
哈希函数,把学生的姓名直接映射为哈希表上的索引,然后就可以通过查询索引下标快速知道这位同学是否在这所学校里了。
哈希函数如下图所示,通过hashCode把名字转化为数值,一般hashcode是通过特定编码方式,可以将其他数据格式转化为不同的数值,这样就把学生名字映射为哈希表上的索引数字了。
此时问题又来了,哈希表我们刚刚说过,就是一个数组。
因为数组毕竟是存在大小的,存储的位置是会发生冲突的
其实拉链法就是要选择适当的哈希表的大小,这样既不会因为数组空值而浪费大量内存,也不会因为链表太长而在查找上浪费太多时间
使用线性探测法,一定要保证tableSize大于dataSize。 我们需要依靠哈希表中的空位来解决碰撞问题。
例如冲突的位置,放了小李,那么就向下找一个空位放置小王的信息。所以要求tableSize一定要大于dataSize ,要不然哈希表上就没有空置的位置来存放 冲突的数据了。如图所示:
总结一下,当我们遇到了要快速判断一个元素是否出现集合里的时候,就要考虑哈希法。
但是哈希法也是牺牲了空间换取了时间,因为我们要使用额外的数组,set或者是map来存放数据,才能实现快速的查找。
如果在做面试题目的时候遇到需要判断一个元素是否出现过的场景也应该第一时间想到哈希法!
这个题目我刚刚开始看的时候还没有看懂他想要表达的意思是什么
暴力解决的办法我感觉反而没有那么容易写,因为要记录字符是否重复出现了,两层for循环的时间复杂度也高。
不要看这个有三个for就以为是n的平方或者三次方,其实这个是3n
class Solution {
public:
bool isAnagram(string s, string t)
{
int Hash_Arr[26]{};
//对哈希表中的内容进行赋值
for (int i = 0; i < s.size(); i++)
{
Hash_Arr[s[i] - 'a']++;
}
for (int j = 0; j < t.size(); j++)
{
Hash_Arr[t[j] - 'a']--;
}
for (int i = 0; i < 26; i++)
{
if (Hash_Arr[i] != 0) {
return false;
}
}
return true;
}
};
这个大佬讲解的这个代码挺好的,很容易理解明白他的意思
void Swap(int a, int b)
{
a = a ^ b;
b = a ^ b;
a = a ^ b;
std::cout << a << char(10);
std::cout << b << char(10);
}
int main()
{
Swap(10, 20);
return 0;
}
其实如果函数时间久了不去使用,就会忘记,就当作复习一下这四种函数的使用
看一些文章讲解一些内容就ok了,感觉学习还是会忘记
可以很清楚的看到,无符号数和有符号数在一起操作的时候,会自动的转换成无符号数的类型,因此这一点要多注意
void Test()
{
unsigned int a = 6;
int b = -20;
std::cout << a + b << char(10);
}
int main()
{
Test();
return 0;
}
关于static的作用和内容实在是太多了,每一次看都可以当做是一次复习
static的作用:定义static可以限制变量的作用范围,当我们定义全局变量的时候可能会出现作用域的一些问题,但是static可以让变量的访问只限定在当前的这个文件中。
这里 static 作用主要影响着变量或函数的生命周期,作用域,以及存储位置。
静态局部变量
变量的存储区域由栈变为静态常量区。
● 变量的生命周期由局部调用结束变为程序运行结束。
● 变量的作用域不变。
函数调用开辟栈帧,函数中的局部变量在栈上分配存储空间,当函数执行完毕,函数栈帧销毁,栈空间由系统回收
而在static
修饰函数局部变量的时,其修饰的静态局部变量只执行初始化一次,延长了局部变量的生命周期,直到程序运行结束以后才释放,但不改变作用域。
void Test()
{
static int a = 10;
a++;
std::cout << a << char(10);
}
int main()
{
Test();
Test();
Test();
Test();
Test();
return 0;
}
在所有函数外部定义的变量称为全局变量(Global Variable),它的作用域默认是整个程序
当 static
修饰全局变量时:
● 变量的存储区域在全局数据区的静态常量区。
● 变量的作用域由整个程序变为当前文件。(extern声明也不行)
● 变量的生命周期不变。
一个全局变量被 static 修饰,使全局变量只能在定义变量的当前文件使用,不能在其余文件使用,即使 extern外部声明也不行。
原因: 属于文件作用域的声明在缺省的情况下为 external 链接属性, 如定义个全局变量int g_a = 1;, a的链接属性为external,而加上 static会修改变量的缺少链接属性,改为internal。
声明了全局变量 g_a 和 g_b (具有 external 链接属性 )的其他源文件在使用这两个变量时实际访问的是声明与此处的这两个变量;但是 g_c 只能由这个源文件访问,因为链接属性为internal。
关于extren的说明:
一个函数被 static
修饰,使函数只能在定义的源文件使用,不能在其余文件使用,即使 extern
外部声明也不行。(同static
修饰全局变量)
声明为static
的类成员称为类的静态成员,用static
修饰的成员变量,称之为静态成员变量;用static
修饰的 成员函数,称之为静态成员函数。
注:静态成员无法在构造函数初始化列表初始化!!
静态的成员变量一定要在类外进行初始化
这是一个面试的题目
两种方法去解决:
void Test()
{
int a = 1;
char b = (char)a;
if ((int)b == 1) std::cout << "这是小端存储的模式" << char(10);
else std::cout << "这是大端存储的模式" << char(10);
}
int main()
{
Test();
return 0;
}
因为联合体是采用的是从低地址开始存储的方式,判断的方式和上面的其实本质上是相同的
void Test2()
{
union MyUnion
{
int a;
char b;
};
MyUnion u;
u.a = 1;
std::cout << (int)u.b << char(10);
}
int main()
{
Test2();
return 0;
}
式" << char(10);
else std::cout << “这是大端存储的模式” << char(10);
}
int main()
{
Test();
return 0;
}
##### 第二种方式是采用联合体检验的方式
因为联合体是采用的是从低地址开始存储的方式,判断的方式和上面的其实本质上是相同的
```C++
void Test2()
{
union MyUnion
{
int a;
char b;
};
MyUnion u;
u.a = 1;
std::cout << (int)u.b << char(10);
}
int main()
{
Test2();
return 0;
}