常用
文件
打开文件
1 | FILE* openfile(const char* filename, const char* mode) { // 打开文件 |
栈
顺序栈(下方两个 DFS 迷宫问题使用)
sqstack.h
1 |
|
有趣的小东西
静态链表
待检查
1 |
|
哨兵节点
1 |
字典树
1 |
猜字谜游戏
用到了字典树和回溯法
1 |
|
快速求幂算法
快速求幂算法(Fast Exponentiation Algorithm)是一种高效的算法,用于计算幂运算。它可以快速地计算 base^exponent 的值,即使指数 exponent 非常大。该算法的基本思想是将指数拆分为若干个较小的指数之和,然后利用幂运算的结合律将这些较小的幂相乘,从而快速地计算出原来的幂。
原理是将指数拆分为若干个较小的指数之和,然后利用幂运算的结合律将这些较小的幂相乘,从而快速地计算出原来的幂。具体来说,该算法使用了二进制表示法来拆分指数。例如,假设我们要计算 3^13 的值。首先,我们可以将 13 表示为二进制数 1101,即 13 = 8 + 4 + 1。然后,我们可以先计算出 3^1 = 3、3^2 = 9、3^4 = 81 和 3^8 = 6561 的值。最后,我们将这些值相乘,得到 3^13 = 3^8 _ 3^4 _ 3^1 = 6561 _ 81 _ 3 = 1594323。
例如,假设我们要计算 2^13 的值。使用快速求幂算法,我们可以将指数 13 拆分为 8 + 4 + 1。然后,我们可以先计算出 2^1 = 2、2^2 = 4、2^4 = 16 和 2^8 = 256 的值。最后,我们将这些值相乘,得到 2^13 = 2^8 _ 2^4 _ 2^1 = 256 _ 16 _ 2 = 8192。
下面是一个简单的 C 程序,它实现了快速求幂算法:
1 |
|
应用:
使用快速求幂算法来计算 (3^100) mod 5
的值。下面是对该程序的详细解释,包括前几次循环的每一步。
1 |
|
在第一次循环迭代中,指数 exponent
的值为 100
。因为 100
是偶数,所以不会执行 if
语句中的代码。然后,程序将底数 base
平方,得到 9
,并将其模 5
取余,得到 4
。最后,程序将指数 exponent
除以 2
,得到 50
。
在第二次循环迭代中,指数 exponent
的值为 50
。因为 50
是偶数,所以不会执行 if
语句中的代码。然后,程序将底数 base
平方,得到 16
,并将其模 5
取余,得到 1
。最后,程序将指数 exponent
除以 2
,得到 25
。
在第三次循环迭代中,指数 exponent
的值为 25
。因为 25
是奇数,所以会执行 if
语句中的代码。程序将结果 result
乘以底数 base
,得到 1 * 1 = 1
,并将其模 5
取余,得到 1
。然后,程序将底数 base
平方,得到 1 * 1 = 1
,并将其模 5
取余,得到 1
。最后,程序将指数 exponent
除以 2
,得到 <PhoneNumber>
。
在接下来的几次循环迭代中,程序会继续执行类似的操作,并最终计算出 (3^100) mod 5 = 1
的结果。
这个算法的原理是基于模运算的性质。根据模运算的定义,如果两个数除以同一个模数,它们的余数相同,那么这两个数在模运算下是等价的。因此,我们可以在每次乘法运算后对结果取模,以保证结果始终小于模数。这样,在最后一次乘法运算后,我们得到的结果就是 (base^exponent) mod mod 的值。
求某数组连续子序列的最大和
一、
设数组为 a[MAXSIZE],定义{A,B,C}为 A,B,C 三者中最大值
伪代码如下:
1 | index = 0 |
更简洁的实现模式如下:
1 |
|
两种方式的时间复杂度相同,核心原理也相同,只是第二种的表述更加简洁
动态规划:
动态规划(Dynamic Programming,简称 DP)是一种用来解决最优化问题的算法思想。它通过将原问题分解为若干个子问题,并将子问题的解存储下来,从而避免重复计算,提高了算法的效率。
动态规划通常用于求解具有重叠子问题和最优子结构性质的问题。所谓重叠子问题,是指在递归求解原问题时,不同的递归分支中会出现重复的子问题。而最优子结构性质是指原问题的最优解可以通过其子问题的最优解来构造。
动态规划算法通常包括两个步骤:定义状态和转移方程。状态表示原问题或子问题的某种性质,转移方程表示如何从一个状态转移到另一个状态。
例如,在求解连续子序列最大和问题时,我们可以定义状态 dp[i]
表示以第 i
个元素结尾的连续子序列的最大和。转移方程为 dp[i] = max(dp[i-1] + nums[i], nums[i])
,表示第 i
个元素可以选择加入前面的连续子序列中,或者单独成为一个新的连续子序列。
不使用辅助数组,分离顺序表中的奇偶数
(124567 → 157246)
用两个整数 i,j 当作指针进行操作
1 |
秘书问题
只检索一遍,给出使得找到最大值概率最大(37%)的算法
本质情境:所有数据只能读取一次,每次只读取一个单位,进行判断后要么选择它、要么否决它,选择后将停止进程,否决后该单位无法再次被选择,找出一种方法,使得取到最优单位的概率最大。
此时可以取定一个”观察期“ k , k+1 之前(不包括 k + 1)的单位只读不取,记下这些单位中的最大值,之后只要有比观察期的最大值更大的情况就出手,反之不出手,直到全部读取完毕
观察期 k 的求法 :
1. 此时取到最优单位的概率等价于 系数为(k/i)的调和级数,i = k 加到 n-1 ,令其取到最大值
2. 求和化简得上式等于 (k/n)ln(n/k) = (lnx)/x x = n/k
3. 求得当x = e 时有最大值,最大值大约为0.3679 (1/e)
从而可知当 n = ek 时取到最优单位的概率最高
1 |
选择问题
选出 n 个数里的第 k 个最大者
方法一 : 时间复杂度取决于排序算法,如冒泡排序是O(n²)
放入数组中并排序
方法二 : 时间复杂度为O(n)
先取k个数,取的同时使用一个“游标”标记出最小的那个值,游标可以是指针,也可以是变量
之后再取别的数,
若后取的数比游标指向的数大,则把游标指向那个数,否则不操作
求正整数 1 到 n 的和
本题启发:
可以用公式算的就尽量减少计算机的运算
int sum = 0, n = 100;
sum = (1 + n) * n / 2;
即等差数列求和
也可用 for 循环或 while 循环逐个求和,或递归求解,但时间复杂度会上升
搜索
子集
递归找出数组全部子集
1 |
gitmask 找出全部子集
1 |
回溯法找出 n 个物品中权值总和为 T 的全部子集
数组每个元素只与排在它后面的元素构成分支,避免产生重复子集影响效率和准确性;
搜索途中超出范围则剪去分支,减少搜索量;
1 | void backtrack(const int data[],int target[],int n,int T,int count,int total){ |
迷宫(所有解)
DFS 找出迷宫的所有通路
1 |
|
迷宫(最优解)
BFS 找出迷宫最优解
1 |
迷宫(可行解)
DFS 找一个迷宫可行解
1 |
|
“左手摸墙”找一个迷宫可行解
1 |
排序
快速排序
1 |
归并排序
1 |
堆排序
1 |
希尔排序
1 |
选择排序
1 |
冒泡排序
1 |
猴子排序
误
暂无命名
分治法找到数组给定范围中的最大值
数组 array,搜索范围( left,right ),m 为分割位置
1 | int findMaximun(int array[],int left,int right){ |
启发式搜索
1 | 这是代码 |
分类
树
Hafuman 树为成绩分等级
85~100 A
75~84 B
65~74 C
60~64 D
<60 E
若全使用条件判断语句,正常情况下,80%的数据需要判断 3 次以上(假设大于 65 分的人数约占 80%)
判断次数: 1F + 2D + 3C + 4B + 5A 次,
而使用哈夫曼树可以提高约 31%的效率
示例:
如图
上机题
数据结构 Lab 1_1
函数
1 |
源码
(待拆分函数)
- 链表操作
- 从一个给定的数据集合中随机、可重复地选取数据元素,并通过尾插法构建一个双向链表,然后设计算法删除该链表中的重复元素。
- 构建一个单链表和一个无头节点的循环链表,将其链接成一个有环的链表,然后设计算法确定环的入口点,并计算环的长度。
- 要求所编写的程序应采用 c 或 c++语言:
- 必须带命令行参数;
- 必须通过命令行参数指定输入、输出文件的文件名
代码如下
1 | /* |
数据结构 Lab1_4
函数
1 |
源码
1 |
|
计算机程序设计 Lab 10 & 11
大一入学第一个学期写的代码,当时的编程能力还不强
函数
功能等待拆分
- 链表结构
1 | struct stu{ |
- 头插法
1 |
|
- 尾插法
1 | struct stu *back() //尾插法 |
- 从文件提取内容
1 | struct stu *fromfile() |
源码
1 |