LeetCode刷题 --杂篇 --数组,链表,栈,队列
武汉加油,中国加油。希望疫情早日结束。
由于疫情,二狗寒假在家不能到处乱逛,索性就在家里系统的刷一下算法的内容,一段时间下来倒也有些小小的收获。只是一来家中的小破笔记本写起博客来实在不是很顺手,二来家中吃喝玩乐的诱惑也不少了,就连着几天没有更新,惭愧惭愧。看来2020年还是要加强自己计划的执行能力。
每个人都有适合自己的学习方式。虽然也挺喜欢看书,但对我来说,在学习新内容,不熟悉的内容的时候单纯的啃课本还是有些事倍功半,尤其是像算法这种这么容易看得一脸懵逼的内容。大名鼎鼎的《算法导论》买回来了挺长时间了,只看了感兴趣的几章,并且在看得时候经常怀疑自己,我是不是脑子不行啊,要不转行算了(苦笑)。基于之前学习数据库的经验,趁着寒假整了个算法的音频课程听了一下,感觉还不错,恩,看来我比较适合这种方式吧。以后再要学习什么新东西的时候记得提醒自己,先选对了学习的方式哈。
女朋友问我,算法是什么,学这个有啥用? 其实之前我也没有想明白,最开始从leetcode刷题目的很简单,多长点见识,万一面试用上了呢。所以女朋友这么问的时候我就随口一胡扯:武功知道吧,之前的实战经验算是身法,这玩意儿(算法)是心法。现在对这个抖机灵的回答还有点小骄傲,我还挺机智的呀。可不是嘛,各种常见的数据结构,常见的算法,各大高级语言都帮我们实现好了,大家日常“CRUD”哪里用得上这些啊?但其实也不对,比如两种数据结构都可以做的时候,哪种更合适呢?如果想效率更高呢?如果必须要在限制的空间内完成呢?或者接地气一点:和同事关于内容battle的时候,怎么优雅地说服他呢?
来点干货:
反正博客写给自己看,记录一下这两天笔记中让我有耳目一新的感觉的内容吧。
数组:
- 数组是一种线性表数据结构。它用一组连续的内存空间,存储一组具有相同类型的数据,最大的特点是支持随机访问。
- 根据下标来随机访问数组中的元素,所以数组这种结构的”查找“其实很高效。
- 又因为它是连续存储的,所以相对来说,”插入“和”删除“这两个操作会比较低效。因为这两个操作很有可能使得数组内大量元素重新移动位置。
- 因为是连续的内存空间,所以如果内存中剩下一个40m的内存块,一个60m的内存块,其实是无法申请一个80m大小的数组的。但是链表可以。
链表:
- 与数组相反,链表不需要连续的内存,它通过”指针“来将一组零散的内存块串起来使用。所以上面数组中的那种情况,链表是可以完成的。
- 相对数组来说,链表的”插入“和”删除“会高效很多,毕竟只要改变相关联的指针的指向即可。
这两种数据结构实在是太基础了,基础到基本不会用到。拿二狗子来说,如果有类似的东西需要实现的话我会怎么做呢,我会申请一个List出来,剩下的都交给List去做了。但看看上面两中数据结构的比较,其实差别还是挺大的。而关于List,写这篇博客的时候我又去微软官方文档瞅了一眼:
- List<T>类是 ArrayList<T>类的泛型等效项。 它通过使用大小根据需要动态增加的数组来实现IList<T>泛型接口。
在决定是使用List<T> 还是 ArrayList类(两者都具有类似的功能)时,请记住List<T> 类在大多数情况下性能更佳并且是类型安全的。 如果引用类型用于 List<T>类的类型
T
,则这两个类的行为是相同的。 但是,如果将值类型用于类型T
,则需要考虑实现和装箱问题。- 如果值类型用于类型
T
,则编译器将为该值类型专门生成 List<T>类的实现。 这意味着,在使用元素之前,不需要对 List<T>对象的 list 元素进行装箱,在创建了大约500个列表元素后,不会对其进行装箱的内存列表元素大于用于生成类实现的内存。
看来,List应该是用数组来实现的,并且.net有一些特殊的处理使得这家伙既安全,又高效。至于第三条,看来如果要存储的是值类型的数据,并且数据量较多的情况下还是使用List比较高效。另外微软也是建议大家尽量使用List而不是自己手动实现,原因如下:
使用 List<T> 类的特定于类型的实现,而不是使用 ArrayList类或自行编写强类型包装集合,这一点非常有利。 原因在于,你的实现必须执行 .NET Framework 的操作,并且公共语言运行时可以共享你的实现不能的 Microsoft 中间语言代码和元数据。
栈
后进者先出,先进者后出,是一种”操作受限“的线性表。
队列
先入队列的先出队列,后入的后出。同样,队列也是一种”操作受限“的线性表。
这两种数据机构也很常见,新的发现是在做练习时遇到了这样一道题,怎么用栈来实现一个队列,题目不难,但是挺有趣的。贴一下题目与笔者的做法,题目来源于Leetcode。
232. 使用栈实现队列的下列操作:
push(x) -- 将一个元素放入队列的尾部。
pop() -- 从队列首部移除元素。
peek() -- 返回队列首部的元素。
empty() -- 返回队列是否为空。
示例:
MyQueue queue = new MyQueue();
queue.push(1);
queue.push(2);
queue.peek(); // 返回 1
queue.pop(); // 返回 1
queue.empty(); // 返回 false
说明:
你只能使用标准的栈操作 -- 也就是只有 push to top, peek/pop from top, size, 和 is empty 操作是合法的。
你所使用的语言也许不支持栈。你可以使用 list 或者 deque(双端队列)来模拟一个栈,只要是标准的栈操作即可。
假设所有操作都是有效的 (例如,一个空的队列不会调用 pop 或者 peek 操作)。
public class MyQueue
{
private Stack<int> stack1;
private Stack<int> stack2; /** Initialize your data structure here. */
public MyQueue()
{
stack1 = new Stack<int>();
stack2 = new Stack<int>();
} /** Push element x to the back of queue. */
public void Push(int x)
{
if(stack1.Count > ||(stack1.Count == && stack2.Count == ))
{
stack1.Push(x);
}
else
{
while(stack2.Count > )
{
stack1.Push(stack2.Pop());
} stack1.Push(x);
}
} /** Removes the element from in front of queue and returns that element. */
public int Pop()
{
if (stack1.Count > )
{
while (stack1.Count > )
{
stack2.Push(stack1.Pop());
}
} return stack2.Pop();
} /** Get the front element. */
public int Peek()
{
if (stack1.Count > )
{
while (stack1.Count > )
{
stack2.Push(stack1.Pop());
}
} return stack2.Peek();
} /** Returns whether the queue is empty. */
public bool Empty()
{
return (stack1.Count == && stack2.Count == );
}
}
上面的做法还行,思路是利用两个栈来进行实现。而想到这个方法时其实是有点小开心的,有点灵机一动的快感,哈哈。队列中的入队操作会将一个元素添加到队列中去,而这个队列会在当前队列中所有元素都出队之后才会被访问到。而栈呢,如果不向栈中添加新的元素,那么下一次出栈操作就会把这个元素给pop出来。因此很容易联想到用两个栈来实现,当需要出队列的时候,我们就把栈中的元素依次pop,并push到另一个栈中,这样最先进入栈中的元素反而就到了栈顶。剩下的就和队列很类似了。
其实算法的学习中还有很多”类似“的情况,但前提是你要了解两种不同数据结构的特点与作用。优势是什么,劣势是什么。而数据结构与算法也不是孤立的,比如大家都了解的各种排序,如插入排序,冒泡排序,快速排序等等都不是单一孤立使用的。比如各大高级语言的集合类一般都会提供排序方法,作为码农我们直接拿来主义就可以使用了。但其实它们内部是会根据不同的数据量啊,大小等进行不同的处理,调用不同的算法来进行实现的。这些其实也都挺有意思的。
附加一道题目:
这道题其实感觉有些类似与脑筋急转弯,启发是有时候可能是我们没有按照计算机的方式去思考,反而复杂化了。反正笔者看这道题的解法和欢乐多的网友类似----原来我是个傻子,哈哈。Leetcode第6题:
将一个给定字符串根据给定的行数,以从上往下、从左到右进行 Z 字形排列。
比如输入字符串为 "LEETCODEISHIRING" 行数为 3 时,排列如下:
L C I R
E T O E S I I G
E D H N
之后,你的输出需要从左往右逐行读取,产生出一个新的字符串,比如:"LCIRETOESIIGEDHN"。
请你实现这个将字符串进行指定行数变换的函数:
string convert(string s, int numRows);
示例 1:
输入: s = "LEETCODEISHIRING", numRows = 3
输出: "LCIRETOESIIGEDHN"
示例 2:
输入: s = "LEETCODEISHIRING", numRows = 4
输出: "LDREOEIIECIHNTSG"
解释:
L D R
E O E I I
E C I H N
T S G
public class Solution {
public string Convert(string s, int numRows)
{
if(numRows == )
{
return s;
} var charArray = s.ToCharArray(); string[] resultList = new string[Math.Min(charArray.Length, numRows)]; int currentRow = ;
bool goDown = false; for (int i = ; i < charArray.Length; i++)
{
resultList[currentRow] = resultList[currentRow] + charArray[i]; if (currentRow == || currentRow == numRows - )
{
goDown = !goDown;
} currentRow = goDown ? currentRow + : currentRow - ;
} string result = string.Empty; for (int i = ; i < resultList.Length; i++)
{
result += resultList[i];
} return result;
}
}
官方的解法传送门在这里, 传送门。
我原本的思路是如何把字符串处理成想要的格式,然后再依次输出。但是看了高手的解答真的很惊喜,何必这样呢,其实我们更简单一些,所见即所得的输出不好嘛,每行看到的是什么就把什么输出就好了,总之感觉十分奇妙。
这两天还看了一些二分法和树的内容,但是还没找些题练练手,晚些再水一篇博客把,嘿嘿。
另外,希望疫情早点结束,大家的生活回到正规,感谢在一线奋战的医务工作者们。
LeetCode刷题 --杂篇 --数组,链表,栈,队列的更多相关文章
- LeetCode刷题总结-数组篇(上)
数组是算法中最常用的一种数据结构,也是面试中最常考的考点.在LeetCode题库中,标记为数组类型的习题到目前为止,已累计到了202题.然而,这202道习题并不是每道题只标记为数组一个考点,大部分习题 ...
- LeetCode刷题总结-数组篇(中)
本文接着上一篇文章<LeetCode刷题总结-数组篇(上)>,继续讲第二个常考问题:矩阵问题. 矩阵也可以称为二维数组.在LeetCode相关习题中,作者总结发现主要考点有:矩阵元素的遍历 ...
- LeetCode刷题总结-数组篇(下)
本期讲O(n)类型问题,共14题.3道简单题,9道中等题,2道困难题.数组篇共归纳总结了50题,本篇是数组篇的最后一篇.其他三个篇章可参考: LeetCode刷题总结-数组篇(上),子数组问题(共17 ...
- LeetCode刷题总结-数组篇(番外)
本期共7道题,三道简单题,四道中等题. 此部分题目是作者认为有价值去做的一些题,但是其考察的知识点不在前三篇总结系列里面. 例1解法:采用数组索引位置排序的思想. 例2解法:考察了组合数学的组合公式应 ...
- leetcode刷题记录——数组与矩阵
@ 目录 283. 移动零 566. 重塑矩阵 485. 最大连续1的个数 240. 搜索二维矩阵 II 378. 有序矩阵中第K小的元素 645. 错误的集合 287. 寻找重复数 667. 优美的 ...
- leetcode 刷题(数组篇)26题 删除有序数组中的重复值 (双指针)
题目描述 给你一个有序数组 nums ,请你 原地 删除重复出现的元素,使每个元素 只出现一次 ,返回删除后数组的新长度. 不要使用额外的数组空间,你必须在 原地 修改输入数组 并在使用 O(1) 额 ...
- leetcode 刷题(数组篇)4题 寻找两个正序数组的中位数(二分查找)
题目描述 给定两个大小分别为 m 和 n 的正序(从小到大)数组 nums1 和 nums2.请你找出并返回这两个正序数组的 中位数 . 示例 1: 输入:nums1 = [1,3], nums2 = ...
- leetcode 刷题(数组篇)1题 两数之和(哈希表)
题目描述 给定一个整数数组 nums 和一个整数目标值 target,请你在该数组中找出 和为目标值 的那 两个 整数,并返回它们的数组下标. 你可以假设每种输入只会对应一个答案.但是,数组中同一个元 ...
- leetcode 刷题(数组篇)152题 乘积最大子数组 (动态规划)
题目描述 给你一个整数数组 nums ,请你找出数组中乘积最大的连续子数组(该子数组中至少包含一个数字),并返回该子数组所对应的乘积. 示例 1: 输入: [2,3,-2,4] 输出: 6 解释: 子 ...
随机推荐
- LeetCode 343.整数拆分 - JavaScript
题目描述:给定一个正整数 n,将其拆分为至少两个正整数的和,并使这些整数的乘积最大化. 返回你可以获得的最大乘积. 题目分析 题目中"n 至少可以拆分为两个正整数的和",这个条件说 ...
- 纯JavaScript实现页面行为的录制
在网上有个开源的rrweb项目,该项目采用TypeScript编写(不了解该语言的可参考之前的<TypeScript躬行记>),分为三大部分:rrweb-snapshot.rrweb和rr ...
- 「硬核干货」总结IDEA开发的26个常用设置
前言 程序员对待IDE都是虔诚的,经常因为谁是最好的IDE而在江湖上掀起波澜,曾经我也是. 后来我遇到了IDEA,从此是它,余生都是它. IDEA 毫无疑问是目前最强大的Java开发工具了,但是大部分 ...
- Classmethod and Staticmethod - Python 类方法 和 静态方法
classmethod and staticmethod classmethod 的是一个参数是类对象 cls (本类,或者子类), 而不是实例对象 instance (普通方法). classmet ...
- [redis读书笔记] 第一部分 数据结构与对象 链表
二 链表 1.链表节点使用ListNode结构,是一个双向的链表,同时,还实现了一个控制所有ListNode的结构list: typedef struct listNode { // 前置节点 str ...
- Tomcat 修改日志输出配置 定期删除日志
tomcat的下的日志catalina.out 和 qc.log疯狂增长,以下是解决办法 我生产环境tomcat版本 Server version: Apache Tomcat/7.0.35 Serv ...
- ssh_key认证
ssh认证流程步骤: 1.主机host_key认证 2.身份验证 3.身份验证通过 原理及更多知识点,请查看好友博客 http://www.cnblogs.com/f-ck-need-u/p/7129 ...
- vue路由--静态路由
vue的单页面应用是基于路由和组件的,路由用于设定访问路径,并将路径和组件映射起来.传统的页面应用,是用一些超链接来实现页面切换和跳转的.在vue-router单页面应用中,则是路径之间的切换,也就是 ...
- toj 4353 Estimation(树状数组+二分查找)
Estimation 时间限制(普通/Java):5000MS/15000MS 运行内存限制:65536KByte总提交: 6 测试通过: 1 描述 “There are ...
- leetcode--js--Median of Two Sorted Arrays
问题描述: There are two sorted arrays nums1 and nums2 of size m and n respectively. Find the median of ...