题目来自书或者网站。

  • 解密QQ 号——队列
  • 回文字符串---栈
  • 火柴棍等式
    • 输入数字n,要求输出从1~n的全排列
  • 【力扣】给你一个包含 n 个整数的数组 nums,判断 nums 中是否存在三个元素 a,b,c ,使得 a + b + c = 0 ?请你找出所有和为 0 且不重复的三元组
    • 设计一个循环队列重新排列栈中的元素
    • 递归反转一个栈

解密QQ 号——队列

小哈告诉了小哼一串加密过的数字,同时小哈也告诉了小哼解密规则。规则是这样的:首先将第1 个数删除,紧接着将第2 个数放到这串数的末尾,再将第3 个数删除并将第4 个数放到这串数的末尾,再将第5 个数删除……直到剩下最后一个数,将最后一个数也删除。按照刚才删除的顺序,把这些删除的数连在一起就是小哈的QQ 啦。现在你来帮帮小哼吧。小哈给小哼加密过的一串数是“6 3 1 7 5 8 9 2 4”。

【此题来自啊哈算法,以下是作者的分析】:
首先需要一个数组来存储这一串数即int q[101],并初始化这个数组即int q[101]={0,6,3,1,7,5,8,9,2,4};(此处初始化是我多写了一个0,用来填充q[0],因为我比较喜欢从q[1]开始用,对数组初始化不是很理解的同学可以去看一下我的上本书《啊哈C!思考快你一步》)。接下来就是模拟解密的过程了。
解密的第一步是将第一个数删除,你可以想一下如何在数组中删除一个数呢。最简单的方法是将所有后面的数都往前面挪动一位,但是这样的做法很耗费时间。
在这里,我将引入两个整型变量head 和tail。head 用来记录队列的队首(即第一位),tail 用来记录队列的队尾(即最后一位)的下一个位置,你可能会问:为什不直接记录队尾,却要记录队尾的下一个位置呢?这是因为当队列中只剩下一个元素时,队首和队尾重合会带来一些麻烦。我们这里规定队首和队尾重合时,队列为空。

现在有9 个数,9 个数全部放入队列之后head=1;tail=10;此时head 和tail 之间的数就是目前队列中“有效”的数。如果要删除一个数的话,就将head++就OK 了,这样仍然可以保持head 和tail 之间的数为目前队列中“有效”的数。这样做虽然浪费了一个空间,却节省了大量的时间,这是非常划算的。新增加一个数也很简单,把需要增加的数放到队尾即q[tail]之后再tail++就OK 啦

    int main()
{int q[102]={0,6,3,1,7,5,8,9,2,4},head,tail;
int i;
head = 1;
tail = 10;//q[0]用0填充
while(head < tail)
{cout<<q[head];
head++;
q[tail++] = q[head++];}}

回文字符串—栈

#include <string.h>int main()
{char a[102],s[102];
gets(a);
int n = strlen(a);
int top = 0;//栈顶
int mid = n/2 - 1;//注意要-1
for(int i = 0; i <= mid;i++)
s[++top] = a[i];
int nex;
if(n % 2 == 0)
nex = mid + 1;
if(n % 2 == 1)
nex = mid + 2;
//尤其注意这个容易错的地方:奇数和偶数的情况不同 如果i直接从mid+1开始遍历会出错
for(int i = nex;i < n;i++)
{if(s[top] != a[i]) {cout<<"no";break;}
else top--;
}cout<<"yes";}

自己想的不用栈更方便的方法:

 int main()
{char a[102],s[102];
gets(a);
int n = strlen(a);
int top = 0;//栈顶
int mid = n/2 - 1;//注意要-1
for(int i = 0,j = n-1;i != (n-1/2); i++,j--)
if(a[i] != a[j]) {cout<<"no!"; break;}
cout<<"o";

火柴棍等式

6.23日


首先分析数字特征,因为最多24根,减去符号,20根用于拼数字,1只需要1根,所以最多拼10个1,1111+1=1112 这样个数大于了20,所以ABC中最大也不能超过1111,采用枚举法,只需要对AB进行枚举即可,不需要再枚举C,否则会超过时限

int fun(int x)
{int sum = 0;
//用于计算某一个数字所用的火柴总数
int f[10] = {6, 2, 5, 5,4,5,6,3,7,6};//0-9每个数字需要的火柴棍
while(x /10 != 0)
{sum += f[x%10];x /= 10;
}
sum += f[x];
return sum;}int main()
{int m;//火柴棍的个数
int total = 0;
cin>> m;
for(int i = 0;i <= 1111; i++)
for(int j = 0; j<= 1111 ;j++)
{int c = i+j;if((fun(i) + fun(j) + fun(c)) == (m - 4))total++;
}
cout<<total;}

输入数字n,要求输出从1~n的全排列

如123的全排列:123 132 321 312 213 231
采用深度优先搜索方法
要点:

int visited[100] = {0};
int a[10],n;
void dfs(int step)
{if(step == n+1){//此时已经找到了一个全排列for(int i = 1;i <step ;i++)//a表示小盒子数组,step表示此时站在第i个小盒子前面 注意i从1开始cout<<a[i]<<" ";return;//记得return}
for(int i = 1;i <= n;i++)//1..n的顺序都尝试一遍if(!visited[i])
{a[step] = i;visited[i] = 1;
dfs(step+1);
visited[i] = 0;//这个非常重要,回退时记得再次标志为未访问
}
return;}int main()
{cin>>n;dfs(1);
}

【力扣】给你一个包含 n 个整数的数组 nums,判断 nums 中是否存在三个元素 a,b,c ,使得 a + b + c = 0 ?请你找出所有和为 0 且不重复的三元组

自己的代码:

    vector<vector<int>> threeSum(vector<int>& nums) {if (nums.size() < 3) return {};vector<vector<int>> res;//关键点:时间复杂度的优化和去重int total = nums.size();int j = total - 1,m;sort(nums.begin(), nums.end());for (int i = 0; i < total; ++i){if (i > 0 && nums[i] == nums[i - 1]) continue;//确保每次枚举的数字不一样int begin = i + 1;int third = total - 1;int c = -nums[i] ;for (int z = begin; z < total; z++){//枚举每一个不一样的zif (z > begin && nums[z] == nums[z - 1]) continue;//每一次与上次不一样while (third > z && (nums[z] + nums[third] - c) > 0) --third;if (third == z) break;//如果重合了,退出循环if (nums[z] + nums[third] == c) res.push_back({ nums[i],nums[z],nums[third] });}}return res;}

这里由于 while (third > z && (nums[z] + nums[third] - c) > 0) --third;
if (third == z) break;//如果重合了,退出循环
这两句话的顺序颠倒了,错了好多次也没看出来!!!!!!

注意点:1.不要重复,如何确保三元组不重复
2.采取先排序的策略,双指针,其中中间的指针从第一个指针的下一个开始枚举。
官方的题解:
任意一个三元组的和都为 00。如果我们直接使用三重循环枚举三元组,会得到 O(N^3)O(N
3
) 个满足题目要求的三元组(其中 NN 是数组的长度)时间复杂度至少为 O(N^3)O(N
3
)。在这之后,我们还需要使用哈希表进行去重操作,得到不包含重复三元组的最终答案,又消耗了大量的空间。这个做法的时间复杂度和空间复杂度都很高,因此我们要换一种思路来考虑这个问题。

「不重复」的本质是什么?我们保持三重循环的大框架不变,只需要保证:

第二重循环枚举到的元素不小于当前第一重循环枚举到的元素;

第三重循环枚举到的元素不小于当前第二重循环枚举到的元素。

也就是说,我们枚举的三元组 (a, b, c)(a,b,c) 满足 a \leq b \leq ca≤b≤c,保证了只有 (a, b, c)(a,b,c) 这个顺序会被枚举到,而 (b, a, c)(b,a,c)、(c, b, a)(c,b,a) 等等这些不会,这样就减少了重复。要实现这一点,我们可以将数组中的元素从小到大进行排序,随后使用普通的三重循环就可以满足上面的要求。

同时,对于每一重循环而言,相邻两次枚举的元素不能相同,否则也会造成重复。举个例子,如果排完序的数组为

[0, 1, 2, 2, 2, 3]
^ ^ ^
我们使用三重循环枚举到的第一个三元组为 (0, 1, 2)(0,1,2),如果第三重循环继续枚举下一个元素,那么仍然是三元组 (0, 1, 2)(0,1,2),产生了重复。因此我们需要将第三重循环「跳到」下一个不相同的元素,即数组中的最后一个元素 33,枚举三元组 (0, 1, 3)(0,1,3)。

下面给出了改进的方法的伪代码实现:

nums.sort()
for first = 0 … n-1
// 只有和上一次枚举的元素不相同,我们才会进行枚举
if first == 0 or nums[first] != nums[first-1] then
for second = first+1 … n-1
if second == first+1 or nums[second] != nums[second-1] then
for third = second+1 … n-1
if third == second+1 or nums[third] != nums[third-1] then
// 判断是否有 a+b+c==0
check(first, second, third)
这种方法的时间复杂度仍然为 O(N^3)O(N
3
),毕竟我们还是没有跳出三重循环的大框架。然而它是很容易继续优化的,可以发现,如果我们固定了前两重循环枚举到的元素 aa 和 bb,那么只有唯一的 cc 满足 a+b+c=0a+b+c=0。当第二重循环往后枚举一个元素 b’b

时,由于 b’ > bb

b,那么满足 a+b’+c’=0a+b

+c

=0 的 c’c

一定有 c’ < cc

<c,即 c’c

在数组中一定出现在 cc 的左侧。也就是说,我们可以从小到大枚举 bb,同时从大到小枚举 cc,即第二重循环和第三重循环实际上是并列的关系。

有了这样的发现,我们就可以保持第二重循环不变,而将第三重循环变成一个从数组最右端开始向左移动的指针,从而得到下面的伪代码:

nums.sort()
for first = 0 … n-1
if first == 0 or nums[first] != nums[first-1] then
// 第三重循环对应的指针
third = n-1
for second = first+1 … n-1
if second == first+1 or nums[second] != nums[second-1] then
// 向左移动指针,直到 a+b+c 不大于 0
while nums[first]+nums[second]+nums[third] > 0
third = third-1
// 判断是否有 a+b+c==0
check(first, second, third)
这个方法就是我们常说的「双指针」,当我们需要枚举数组中的两个元素时,如果我们发现随着第一个元素的递增,第二个元素是递减的,那么就可以使用双指针的方法,将枚举的时间复杂度从 O(N^2)
) 减少至 O(N)。为什么是 O(N) 呢?这是因为在枚举的过程每一步中,「左指针」会向右移动一个位置(也就是题目中的 bb),而「右指针」会向左移动若干个位置,这个与数组的元素有关,但我们知道它一共会移动的位置数为 O(N),均摊下来,每次也向左移动一个位置,因此时间复杂度为 O(N)。

注意到我们的伪代码中还有第一重循环,时间复杂度为 O(N)O(N),因此枚举的总时间复杂度为 O(N^2)
)。由于排序的时间复杂度为 O(N \log N),在渐进意义下小于前者,因此算法的总时间复杂度为 O(N^2) )。

设计一个循环队列重新排列栈中的元素

一个栈有2n个元素,从栈顶到栈底的元素依次是a2n,a2n-1,…,a1,要求通过一个循环队列重新排列栈中的元素,使得从栈顶到栈底依次是a1,a3,…,a2n-1,a2,a4,…,a2n

思路:先将栈中全部元素进入一个队列中,求出栈中元素总数,然后设一个计数器i,当i为奇数时进入栈,偶数时进入队列
最后一起将队列的所有元素进栈

void ReAssign(stack<T>&st)
{T e;
queue<T>qu;
int sz  = st.size();
while(!st.empty())
{e = st.top();
st.pop();
qu.push(e);
}
for(int i =1;i <= sz;i++)
{if(i%2==1) {st.push(qu.front());qu.pop();}
else
{qu.push(qu.front());
qu.pop();
}}
while(!qu.empty())
{st.push(qu.front());
qu.pop();
}}

递归反转一个栈

要求不能申请一个同样的栈,除了系统栈之外,其他空间复杂度为o(1)

思路:设计将元素X从栈底插入的算法,递归出栈元素tmp直到栈空,此时进栈x,再依次将原来退栈的元素进栈,如下:

void PushBottom(stack<int>&st,int x)
{int tmp;
if(st.empty()) st.push(x);
else{tmp = st.top();
st.pop();
PushBottom(st,x);
st.push(tmp);
}}
//现在反转整个栈
void reverseStack(stack<int>&st)
{if(st.empty())
return;
else
{int tmp = st.top();
st.pop();
reverseStack(st);
PushBottom(st,tmp);
aa
}
}

【练习】2021下半年数据结构刷题笔记和总结 (三)栈 队列 链表 枚举算法相关推荐

  1. 【练习】2021下半年数据结构刷题笔记和总结 (二) 树、查找-- 不同的排序算法、二叉排序树 平衡二叉树、哈希表查找、线索二叉树、

    记录自己下半年写题目的记录.题目来自书或者网站. 练习(一)的地址: https://blog.csdn.net/qq_41358574/article/details/117098620?ops_r ...

  2. 【练习】2021下半年数据结构刷题笔记和总结 (一)(图和动态规划)

    文章目录 1.编程将一个字符串中所有空格替换为"%20" 2.定两个字符串,判断一个字符串是否是另一个字符串的排列 3.求一个房间内有数个钩子,给定一定长度的绳索,要把所有的钩子用 ...

  3. 图解算法数据结构刷题笔记02

    系列文章目录 图解算法数据结构刷题笔记01 本篇文章目录 系列文章目录 前言 1.剑指 Offer 05. 替换空格 2.剑指 Offer 06. 从尾到头打印链表 3.剑指 Offer 09. 用两 ...

  4. 【2021/5/17 刷题笔记】买卖股票的最佳时机与动态规划

    文章目录 买卖股票的最佳时机 [题目] [我的方法] 执行结果: 动态规划算法 1.状态定义 2.设置数组边界值 3.推导状态转换方程. 参考代码 执行结果: 复杂度分析: 时间复杂度 空间复杂度 * ...

  5. 【2019暑假刷题笔记-STL绪论(二)】总结自《算法笔记》

    目录 五.queue的常见用法 六.priority_queue的常见用法 七.stack的常见用法 八.algorithm头文件下的常用函数 五.queue的常见用法 queue也就是队列,是STL ...

  6. 卷进大厂系列之LeetCode刷题笔记:二分查找(简单)

    LeetCode刷题笔记:二分查找(简单) 学算法,刷力扣,加油卷,进大厂! 题目描述 涉及算法 题目解答 学算法,刷力扣,加油卷,进大厂! 题目描述 力扣题目链接 给定一个 n 个元素有序的(升序) ...

  7. 刷题笔记(十四)--二叉树:层序遍历和DFS,BFS

    目录 系列文章目录 前言 题录 102. 二叉树的层序遍历 BFS DFS_前序遍历 107. 二叉树的层序遍历 II BFS DFS 199. 二叉树的右视图 BFS DFS 637. 二叉树的层平 ...

  8. 师兄刷题笔记、算法小抄、面试突击版必备资源,帮你走上人生巅峰

    前言 最近有很多朋友问我刷题.面试有没有什么好的资源.今天就给大家找了三个棒的开源资源,内容非常硬核,很多人靠着它进了大厂. 不绕弯子,三个分别是谷歌师兄<谷歌大佬的刷题笔记>,东哥< ...

  9. 一夜登顶GitHub!字节内网数据结构与算法刷题笔记,看完直呼卧槽

    网络上流传着一句段子"程序员两条腿,一条是算法,一条是英文,想跑的更远,这两条腿都不能弱".英文,我们暂且不谈,我们先来谈谈算法. 算法之难,在于将精巧的逻辑,通过合适的数据结构, ...

最新文章

  1. 开源库jemalloc简介
  2. android获取图片缩略图,Android系获取图片和视频的缩略图
  3. 纯css实现移动端横向滑动列表
  4. Oracle拆分字符串及排序,Oracle 字符串查询以及拆分函数
  5. vue商城项目开发:axios发送请求及列表数据展示
  6. Redis4.0 Cluster — Centos7
  7. javacv 写mp4_JavaCV实现获取视频每帧并保存
  8. CodeForces - 1058A. In Search of an Easy Problem
  9. 架构师必须掌握的各种编码:ASCII、ISO-8859-1、GB2312
  10. linux 用户、群组及权限操作
  11. A1107班拜年视频录制过程记录
  12. 使用EqualsBuilder和HashCodeBuilder生成hashcode和equal方法
  13. 关于UML 画图工具EA 在linux下的安装和界面配置
  14. 解决 Eclipse不支持tomcat9
  15. si4463跳频功能简介
  16. 金蝶系统安装后怎么连服务器,金蝶安装在云服务器上怎么连接
  17. 51nod 牛奶 bfs深度优先搜索
  18. 基于python的异方差检验_【Python】统计科学之讲讲异方差的检验
  19. python spss_SPSS语法调用Python,让spss插上翅膀
  20. ubuntu 7.04 硬盘安装与安装后的常用软件的安装与配置 [zz]

热门文章

  1. esjava 同步mysql_Elasticsearch和mysql数据同步(elasticsearch-jdbc)
  2. java 保护类型_Java 类的受保护访问(学习 Java 编程语言 046)
  3. c语言中0xde 这怎么用,为什么使用0x61c88647
  4. python socket发送数组_利用pyprocessing初步探索数组排序算法可视化
  5. ajax文件上传报400,js ajaxfileupload.js上传报错的解决方法
  6. c语言算法竞赛入门经典百度云,《算法竞赛入门经典》CH-2(C语言)
  7. 10 windows 启动虚拟机报错_Windows 系统如何安装 Docker
  8. 计算机专业的校本教材,[中职计算机专业校本教材建设思路与实践]中职计算机专业课程...
  9. android 中的组合控件的设计
  10. 深度学习和目标检测系列教程 16-300:通过全球小麦数据集训练第一个yolov5模型