开发工具
未读 TensorFlow2 安装
TensorFlow2.10.0后,Windows平台便不再支持直接调用GPU,想要在Windows平台使用GPU要么通过WSL,或者使用2.10.0版本。本文将介绍如何安装TensorFlow2.10.0-GPU
下载安装包
安装TensorFlow-GPU需要用到CUDA驱动,cuDnn这个两个都是需要下载的,问题就出现在下载哪个版本的安装包才行。
版本不能随意组合,可以根据官网上的来,也可以自己寻找到可以组合的版本,我这里使用的是CUDA11.7+cuDNN8.5+TensorFlow2.10.0
官网:组合列表(下拉到最下面就能看见)
首先是需要确认英伟达(NVIDIA)显卡的情况,有CUDA核心(这个绝大多数NVIDIA显卡都有)
可以用这个命令查看nvidia-smi
如果支持的最高CUDA版本太低了,就更新显卡驱动。
下载CUDA
下载CUDA11.7.1
官网:CUDA Toolkit 11.7 Update 1 Downloads | NVIDIA Developer
官网访问有点慢,可以使用代理加速,但是下载是不用代理的,下 ...
拓扑排序
拓扑排序要解决的问题是给一个有向无环图的所有节点排序。
适用条件
每个顶点出现且只出现一次。
若存在一条从顶点 A 到顶点 B 的路径,那么在序列中顶点 A 出现在顶点 B 的前面。
有向无环图(DAG)才能有拓扑排序,非DAG没有拓扑排序。
这里的条件通俗一点讲,就是假设每个节点都是一个事件,事件与事件之间存在先后关系,比如吃饭之前需要做饭,但如果这些事件里面有个环,便不能构成先后顺序了,所以拓扑排序只适用于DAG
实现思路
在有向图中选一个没有前驱的顶点并且输出
从图中删除该顶点和所有以它为尾的弧(白话就是:删除所有和它有关的边)
重复上述两步,直至所有顶点输出,或者当前图中不存在无前驱的顶点为止,后者代表我们的有向图是有环的,因此,也可以通过拓扑排序来判断一个图是否有环。
假设我们有这样一个DAG
我们得选择一个没有前驱的节点,这里有1和6,我们先选择1。这里我们输出1,并且删除跟1有关的边
这里我们发现4也是没有前驱的节点,可以输出4,并且删除4有关的边
然后选择6节点,输出6节点并且删除6有关的边
然后选择节点5
最后输出2和3
所以我们拓扑 ...
经典好题
未读 25. K 个一组翻转链表
25. K 个一组翻转链表
题目
给你链表的头节点 head ,每 k 个节点一组进行翻转,请你返回修改后的链表。
k 是一个正整数,它的值小于或等于链表的长度。如果节点总数不是 k 的整数倍,那么请将最后剩余的节点保持原有顺序。
你不能只是单纯的改变节点内部的值,而是需要实际进行节点交换。
思路
首先是k的不确定导致我们需要得知有多少个总的节点,才能实现题目的要求,所以先统计节点个数。
剩下的实现有两个关键点:第一个是如何翻转链表,第二个是哨兵节点
如何翻转链表
翻转的第一步得是翻转1,2,3,4之间的顺序
然后把4放到前面,把1放到后面,让1指向5
接下来我们详细进行实现
如果我们要改变一个节点的顺序,我们得把当前节点的next变成pre,所以我们一共要记录三个节点。
如果我们需要改变2的顺序(在此之前已经改变了1的顺序了)然后我们将cur->next = pre
接着把指针们都往后移
也就是
pre = cur
cur = nxt
nxt = nxt.next
后面往复便可以了,值得一提的是k个节点之后,我们需要将前后拼 ...
经典好题
未读 142.环形链表 II
142. 环形链表 II 题解
题目
给定一个链表的头节点 head ,返回链表开始入环的第一个节点。 如果链表无环,则返回 null。
思路
采用快慢指针的思路,但此法妙在如何计算head和环开始节点之间的距离。
声明两个指针,一个fast,一个slow。fast指针每次走两步,slow指针每次走一步。
那么会有两种情况:
一、如果fast走到了nullptr,那么就说明这个链表没有环
二、如果有环,那么fast和slow一定会会相遇,这便是第一次相遇
第一次相遇时我们记录然后退出,接下来思考如何计算得出环的第一个节点。
这是我们现在的情况
那么我们怎么求a呢?
假设slow指针前进的距离为x,那么fast指针前进的距离为2x
所以我们能得出:x = a+b
而fast比slow多走的距离,恰好就是环的长度,也就是c
所以,我们能得到等式:x = c
所以a+b = c a = c-b
a就是start到node的距离,c-b就是slow指针还剩下的没走的环的距离
所以,我们让fast = start,fast++的同时slow++
当fas ...
经典好题
未读 240.搜索二维矩阵Ⅱ
240. 搜索二维矩阵 II 题解
题目
编写一个高效的算法来搜索 m x n 矩阵 matrix 中的一个目标值 target 。该矩阵具有以下特性:
每行的元素从左到右升序排列。
每列的元素从上到下升序排列。
思路
如果我们把矩阵旋转45度,就会发现变成了一个类似二叉树,在节点的右边的数都比节点大,左边的数都比节点小
这个时候,我们从左下或者右上进行比较,如果num<target那么列加一,往前走一列,如果num>target那么排减一
代码
123456789101112131415161718192021222324252627282930313233343536373839/** ****************************************************************************** * @number : 240 * @title : 搜索二维矩阵 II * @attention : 数组、二分查找、分治、矩阵 * ...
位运算
位运算就是基于整数的二进制表示进行的运算。由于计算机内部就是以二进制来存储数据,位运算是相当快的。
与、或和异或
注意这些运算都是建立在二进制的基础上的
与
and,运算符为&
123int a=6,b=5;cout<<(a&b)<<endl;4
或
or,运算符为|
123int a=6,b=5;cout<<(a|b)<<endl;7
异或
xor,运算符为^
值得注意的是,异或的逆运算是自己的本身,所以一个数与两个相同的数进行异或运算,最后的结果是自己本身。
123int a=6,b=5;cout<<(a^b)<<endl;3
左移右移
左移
num << i 表示将 num 的二进制表示向左移动 i 位所得的值。
左移n位其实也就是除以2的n次方
1234int num=5;int a,b;b = num<<2;b = 1
右移
num >> i 表示将 num 的二进制表示向右移动 i 位所得的值。
右移n位其实也就是乘以2的n ...
二分查找
二分查找也被称为折半搜索。
简介
在一个升序的数组里,它每次考察数组当前部分的中间元素,如果中间元素刚好是要找的,就结束搜索过程;如果中间元素小于所查找的值,那么左侧的只会更小,不会有所查找的元素,只需到右侧查找;如果中间元素大于所查找的值同理,只需到左侧查找
算法实现
STL
lowr_bound和upper_bound
C++ 标准库中实现了查找首个不小于给定值的元素的函数 std::lower_bound 和查找首个大于给定值的元素的函数 std::upper_bound,二者均定义于头文件 <algorithm> 中。
二者均采用二分实现,所以调用前必须保证元素有序。
bsearch 函数
bsearch 函数为 C 标准库实现的二分查找,定义在 <stdlib.h> 中。在 C++ 标准库里,该函数定义在 <cstdlib> 中。qsort 和 bsearch 是 C 语言中唯二的两个算法类函数。
bsearch 函数相比 qsort(排序相关 STL)的四个参数,在最左边增加了参数「待查元素的地址」。之所以按照地 ...
滑动窗口
滑动窗口指的是这样一类问题的求解方法,在数组上通过双指针同向移动而解决的一类问题。
滑动
滑动就是我们在什么时候需要滑动窗口。实现滑动也很简单,主要就是left和right两个指针自增,窗口自然而然就往前走了。
如果窗口进行了滑动,那么就会有进入窗口和退出窗口的元素,对窗口内的元素进行处理,就是滑动窗口的关键
窗口
窗口大小并不是固定的,可以不断扩容直到满足一定的条件;也可以不断缩小,直到找到一个满足条件的最小窗口;当然也可以是固定大小。
窗口的大小变化也是通过左右指针right和left进行的。
代码模板
12345678910111213141516171819202122232425262728293031/* 滑动窗口算法框架 */void slidingWindow(string s, string t) { unordered_map<char, int> need, window; for (char c : t) need[c]++; int left = 0, right = 0; int ...
并查集
并查集是一种用于管理元素所属集合的数据结构,实现为一个森林,其中每棵树表示一个集合,树中的节点表示对应集合中的元素。
初始化
123456int fa[MAXN];inline void init(int n){ for (int i = 1; i <= n; ++i) fa[i] = i;}
查询
我们需要沿着树向上移动,直至找到根节点。
1234567int find(int x){ if(fa[x] == x) return x; else return find(fa[x]);}
路径压缩
为了避免形成链,优化查询的过程我们可以根据这个思路:把沿途的每个节点的父节点都设为根节点。去优化查询的过程,进行路径压缩。
123456789int find(int x){ if(x == fa[x]) return x; else{ fa[x] = find(fa[x]); //父节点设为根节点 ...
哈希表
哈希表又称散列表,一种以「key-value」形式存储数据的数据结构,可以理解为一种另类的数组
哈希函数
为了让键和值一一对应,实现这个条件的方法就被叫做哈希函数或者叫散列函数。
哈希函数应当易于计算,并且尽量使计算出来的索引均匀分布。
假设我们用数组 a 存放数据,哈希函数是 f,那键值对 (key, value) 就应该放在 a[f(key)] 上。不论键值是什么类型,范围有多大,f(key) 都是在可接受范围内的整数,可以作为数组的下标。
冲突的解决
如果对于任意的键值,哈希函数计算出来的索引都不相同,那只用根据索引把 (key, value) 放到对应的位置就行了。但实际上,常常会出现两个不同的键值,他们用哈希函数计算出来的索引是相同的。这时候就需要一些方法来处理冲突。
拉链法(常用)
在存值的地方开链表,把所有key相同的都放进去,需要的时候都遍历一遍。
闭散列法
闭散列方法把所有记录直接存储在散列表中,如果发生冲突则根据某种方式继续进行探查。
有线性探测法、二次探测法、双哈希法
结束
此外还有哈希表的删除问题