每日博客Day9 10

每日博客Day 9 and Day 10

感觉每天都写有点懒,还是每两天写一下,两天的时间做个总结

每日算法

142.环形链表II

主要考察两知识点:

  • 判断链表是否环
  • 如果有环,如何找到这个环的入口

其实判断一个链表是否存在环还是挺容易的,但是找到这个环的入口可能还是需要在思考一下怎么去寻找解决的方法的。

在这里要用到数学的知识内容,画图可以看出,自己也可以通过计算验证一下

还是没有通过运行,我感觉自己的代码的思路是没有问题的,可能是一些特殊情况的处理细节是没有考虑充分的。

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的情况

题目类型

  1. 如何判断两个链表存在交点?(明确告知了这链表没有环的情况)
  2. 如何删除倒数第n个节点?(双指针解法)
  3. 自己设计一个链表,包含增删查改的操作(考察基本功)
  4. 环形链表(判断是否存在环,如果存在又如何找到环的开始位置:双指针,在用点数学知识)
  5. 删除链表中的元素(虚拟头节点,不需要对头节点head进行额外情况的处理)

项目进度

写这个项目真的感觉到很痛苦,经常出现错误问题,但是自己又找不到问题在哪里

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

哈希表

哈希表的基础知识

哈希表一般来说是为了帮助判断一个元素是否出现在了集合里面,可以帮助更快速的去查询

哈希函数

哈希函数,把学生的姓名直接映射为哈希表上的索引,然后就可以通过查询索引下标快速知道这位同学是否在这所学校里了。

哈希函数如下图所示,通过hashCode把名字转化为数值,一般hashcode是通过特定编码方式,可以将其他数据格式转化为不同的数值,这样就把学生名字映射为哈希表上的索引数字了。src="C:/Users/13868/Desktop/%E4%BB%A3%E7%A0%81%E9%9A%8F%E6%83%B3%E5%BD%95%E8%AE%AD%E7%BB%83%E8%90%A5/Day%209/%E6%AF%8F%E6%97%A5%E5%8D%9A%E5%AE%A2Day9.assets/2021010423484818.png" alt="哈希表2" style="zoom:50%;" />

此时问题又来了,哈希表我们刚刚说过,就是一个数组。

哈希碰撞

因为数组毕竟是存在大小的,存储的位置是会发生冲突的

拉链法

在这里插入图片描述

其实拉链法就是要选择适当的哈希表的大小,这样既不会因为数组空值而浪费大量内存,也不会因为链表太长而在查找上浪费太多时间

线性探测法

使用线性探测法,一定要保证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;
	}
};

面试内容

位运算的内容
1. 如何用位运算来交换a和b的数值

这个大佬讲解的这个代码挺好的,很容易理解明白他的意思

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;
}
2. 四个强制类型转换的函数使用

其实如果函数时间久了不去使用,就会忘记,就当作复习一下这四种函数的使用

看一些文章讲解一些内容就ok了,感觉学习还是会忘记

3. 有符号数和无符号数的操作

可以很清楚的看到,无符号数和有符号数在一起操作的时候,会自动的转换成无符号数的类型,因此这一点要多注意

void Test()
{
	unsigned int a = 6;
	int b = -20;
	std::cout << a + b << char(10);
}
int main()
{
	Test();
    return 0;
}

4. static的作用和内容

关于static的作用和内容实在是太多了,每一次看都可以当做是一次复习

static的作用:定义static可以限制变量的作用范围,当我们定义全局变量的时候可能会出现作用域的一些问题,但是static可以让变量的访问只限定在当前的这个文件中。

这里 static 作用主要影响着变量或函数的生命周期作用域,以及存储位置

  1. 静态局部变量

    变量的存储区域由变为静态常量区
     ● 变量的生命周期由局部调用结束变为程序运行结束
     ● 变量的作用域不变。

函数调用开辟栈帧,函数中的局部变量在栈上分配存储空间,当函数执行完毕,函数栈帧销毁,栈空间由系统回收
而在static修饰函数局部变量的时,其修饰的静态局部变量只执行初始化一次,延长了局部变量的生命周期,直到程序运行结束以后才释放,但不改变作用域。

void Test()
{
	static int a = 10;
	a++;
	std::cout << a << char(10);
}
int main()
{
	Test();
	Test();
	Test();
	Test();
	Test();
    return 0;
}
  1. 静态全局变量

在所有函数外部定义的变量称为全局变量(Global Variable),它的作用域默认是整个程序

static 修饰全局变量时:

● 变量的存储区域在全局数据区的静态常量区。
 ● 变量的作用域由整个程序变为当前文件。(extern声明也不行)
 ● 变量的生命周期不变。

一个全局变量被 static 修饰,使全局变量只能在定义变量的当前文件使用,不能在其余文件使用,即使 extern外部声明也不行。
原因: 属于文件作用域的声明在缺省的情况下为 external 链接属性, 如定义个全局变量int g_a = 1;, a的链接属性为external,而加上 static会修改变量的缺少链接属性,改为internal。
声明了全局变量 g_a 和 g_b (具有 external 链接属性 )的其他源文件在使用这两个变量时实际访问的是声明与此处的这两个变量;但是 g_c 只能由这个源文件访问,因为链接属性为internal。

关于extren的说明:

  1. static修饰函数

一个函数被 static 修饰,使函数只能在定义的源文件使用,不能在其余文件使用,即使 extern外部声明也不行。(同static 修饰全局变量)

  1. C++类内部的静态函数和静态成员

声明为static类成员称为类的静态成员,用static修饰的成员变量,称之为静态成员变量;用static修饰的 成员函数,称之为静态成员函数

注:静态成员无法在构造函数初始化列表初始化!!

静态的成员变量一定要在类外进行初始化

大端存储和小端存储

这是一个面试的题目

两种方法去解决:

第一种方式使用char和int转换的方式
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;
}
  • 18
    点赞
  • 15
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值