数据结构的简要介绍:堆栈如何工作

by Michael Olorunnisola

通过Michael Olorunnisola

数据结构的简要介绍:堆栈如何工作 (A Gentle Introduction to Data Structures: How Stacks Work)

Anyone who’s applied for a developer job at a large tech company — and spent days practicing common algorithm interview questions — has probably concluded:

任何在大型科技公司申请开发人员工作并花了几天时间练习常见算法面试问题的人都可能得出以下结论:

“Wow. I really gotta know data structures cold.”
“哇。 我真的很了解数据结构。”

What are data structures? And why are they so important? Wikipedia provides a succinct and accurate answer:

什么是数据结构? 为什么它们如此重要? 维基百科提供了简洁准确的答案:

A data structure is a particular way of organizing data in a computer so that it can be used efficiently.

数据结构是在计算机中组织数据的一种特殊方式,因此可以有效地使用它。

The key word here is efficiently, a word you’ll hear early and often as you analyze different data structures.

这里的关键词是有效的,您在分析不同的数据结构时会经常听到这个单词。

These structures provide scaffolding for data to be stored in ways that allow searches, inserts, removals, and updates to take place quickly and dynamically.

这些结构为数据的存储提供了脚手架,从而可以快速,动态地进行搜索,插入,删除和更新。

As powerful as computers are, they’re still just machines that require direction to accomplish any useful task (at least until general AI comes along). Until then, you have to give them the clearest, most efficient commands you can.

尽管它们具有与计算机一样强大的功能,但它们仍然仅仅是需要指导以完成任何有用任务的机器(至少在通用AI出现之前)。 在此之前,您必须为他们提供最清晰,最有效的命令。

Now the same way you can build a home in 50 different ways, you can structure data in 50 different ways. Luckily for you, lots of really smart people have built great scaffolds that have stood the test of time. All you have to do is learn what they are, how they work, and how to best use them.

现在,您可以使用相同的方法以50种不同的方式建造房屋,也可以以50种不同的方式构建数据。 对您来说幸运的是,许多真正聪明的人建造了经受时间考验的出色脚手架。 您要做的就是了解它们是什么,它们如何工作以及如何最好地使用它们。

The following is a list of a few of the most common data structures. I’ll cover each of these individually in future articles — this one is focused 100% on stacks. Although there is often overlap, each of these structures has nuances that make them best suited for certain situations:

以下是一些最常见的数据结构的列表。 在以后的文章中,我将逐一介绍这些内容-本文章将重点100%放在堆栈上。 尽管通常存在重叠,但是这些结构中的每一个都有细微差别,使它们最适合某些情况:

  • Stacks

    堆栈
  • Queues

    Queue列
  • Linked Lists

    链表
  • Sets

    套装
  • Trees

    树木
  • Graphs

    图表
  • Hash Tables

    哈希表

You’ll also encounter variations on these data structures, such as doubly-linked lists, b-trees, and priority queues. But once you understand these core implementations, understanding these variations should be much easier.

您还将在这些数据结构上遇到变化,例如双向链接列表,b树和优先级队列。 但是,一旦您了解了这些核心实现,就应该更容易理解这些变化。

So let’s begin part one of our data structures dive with an analysis of Stacks!

因此,让我们从分析堆栈开始开始我们的数据结构的第一部分!

堆栈 (Stacks)

  • Literally a stack of data (like a stack of pancakes)

    从字面上看是一堆数据(就像一堆煎饼)
  • Additions (push) — always add to the top of the stack

    加法(推动)—始终加到堆栈的顶部
  • Removals (pop) — always remove from the top of the stack

    移除(弹出)—始终从堆栈顶部移除
  • Pattern type: Last item In is the First item Out (LIFO)

    图案类型:L AST项目I n是开始步骤项øUT(LIFO)

  • Example use case: Using the back and forward buttons in your browser

    用例示例 :使用浏览器中的后退和前进按钮

In many programming languages, arrays have the functionality of a stack built in. But for the sake of being thorough, you’ll rebuild it here using a JavaScript object.

在许多编程语言中,数组具有内置堆栈的功能。但是,为了更加透彻,您将在此处使用JavaScript对象对其进行重建。

The first thing you need is to create a stack for you to store each site you visit, and a method on your stack to keep track of your current position:

您需要做的第一件事是为您创建一个堆栈来存储您访问的每个网站,并在堆栈上跟踪您当前位置的方法:

class Stack {  constructor(){    this._storage = {};      this._position = -1; // 0 indexed when we add items!  }  top(){    return this._position;  }}
let browserHistory = new Stack();

Note that the underscore before the variable names signifies to other developers these variables are private, and shouldn’t be manipulated externally — only by the methods on the class. For example, I shouldn’t execute something like:

请注意,变量名称前的下划线向其他开发人员表示这些变量是私有的,不应在外部进行操作-只能由类上的方法进行操作。 例如,我不应该执行类似的命令:

browserHistory._position = 2.

This is why I created the top() method to return the current position of the stack.

这就是为什么我创建top()方法以返回堆栈的当前位置的原因。

In this example, each site you visit will be stored in your browserHistory stack. To help you keep track of where it is in the stack, you can use the position as the key for each website, then increment it on each new addition. I’ll do this via the push method:

在此示例中,您访问的每个站点都将存储在browserHistory堆栈中。 为了帮助您跟踪它在堆栈中的位置,可以将该位置用作每个网站的关键字,然后在每个新添加的位置上对其进行递增。 我将通过push方法执行此操作:

class Stack {
constructor(){    this._storage = {};     this._position = -1;  }
push(value){    this._position++;     this._storage[this._position] = value   }
top(){    return this._position;  }
}
let browserHistory = new Stack();
browserHistory.push("google.com"); //navigating to MediumbrowserHistory.push("medium.com"); // navigating to Free Code CampbrowserHistory.push("freecodecamp.com"); // navigating to NetflixbrowserHistory.push("netflix.com"); // current site

After the above code is executed, your storage object will look a like this:

执行上述代码后,您的存储对象将如下所示:

{
0: “google.com”
1: “medium.com”
2: “freecodecamp.com”
3: “netflix.com”
}

So imagine you’re currently on Netflix, but feel guilty for not finishing that difficult recursion problem on Free Code Camp. You decide to hit the back button to go knock it out.

想象一下您当前正在使用Netflix,但是因为没有在Free Code Camp上解决该难题而感到内gui。 您决定按“后退”按钮将其淘汰。

How is that action represented in your stack? With pop:

该动作如何在您的堆栈中表示? 随着流行:

class Stack {   constructor(){    this._storage = {};    this._position = -1;  }   push(value){    this._position++;     this._storage[this._position] = value;   }   pop(){    if(this._position > -1){      let val = this._storage[this._position];       delete this._storage[this._position];       this._position--;      return val;    }  }
top(){    return this._position;  }}
let browserHistory = new Stack();
browserHistory.push("google.com"); //navigating to MediumbrowserHistory.push("medium.com"); // navigating to Free Code CampbrowserHistory.push("freecodecamp.com"); // navigating to NetflixbrowserHistory.push("netflix.com"); //current site
browserHistory.pop(); //Returns netflix.com
//Free Code Camp is now our current site

By hitting the back button, you remove the most recent site added to your browser History and view the one on top of your stack. You also decrement the position variable so you have an accurate representation of where in the history you are. All of this should only occur if there’s actually something in your stack of course.

通过单击“后退”按钮,可以删除添加到浏览器“历史记录”中的最新站点,并查看堆栈顶部的站点。 您还可以减小位置变量,以便准确表示历史记录中的位置。 当然,只有当您的堆栈中确实有东西时,才会发生所有这些情况。

This looks good so far, but what’s the last piece that’s missing?

到目前为止看起来不错,但是最后丢失的是什么?

When you finish crushing the problem, you decide to reward yourself by going back to Netflix, by hitting the forward button. But where’s Netflix in your stack? You technically deleted it to save space, so you don’t have access to it anymore in your browserHistory.

解决完问题后,您决定通过单击“前进”按钮返回Netflix,以奖励自己。 但是,Netflix在您的堆栈中哪里呢? 从技术上来说,您删除它是为了节省空间,因此您无法再在browserHistory中访问它。

Luckily, the pop function did return it, so maybe you can store it somewhere for later when you need it. How about in another stack!

幸运的是,pop函数确实返回了它,因此也许您可以将其存储在某个地方,以备以后需要时使用。 在另一个堆栈中怎么样!

You can create a “forward” stack to store each site that’s popped off of your browserHistory. So when you want to return to them, you just pop them off the forward stack, and push them back onto your browserHistory stack:

您可以创建一个“转发”堆栈来存储从browserHistory弹出的每个站点。 因此,当您想返回它们时,只需将它们从前向堆栈中弹出,然后将它们推回到浏览器历史记录堆栈中即可:

class Stack {   constructor(){    this._storage = {};    this._position = -1;  }   push(value){    this._position++;     this._storage[this._position] = value;   }   pop(){    if(this._position > -1){      let val = this._storage[this._position];       delete this._storage[this._position];       this._position--;      return val;    }  }
top(){    return this._position;  }}
let browserHistory = new Stack();let forward = new Stack() //Our new forward stack!
browserHistory.push("google.com");browserHistory.push("medium.com");browserHistory.push("freecodecamp.com");browserHistory.push("netflix.com");
//hit the back button
forward.push(browserHistory.pop()); // forward stack holds Netflix
// ...We crush the Free Code Camp problem here, then hit forward!
browserHistory.push(forward.pop());
//Netflix is now our current site

And there you go! You’ve used a data structure to re-implement basic browser back and forward navigation!

然后你去了! 您已经使用一种数据结构来重新实现基本的浏览器前后导航!

Now to be completely thorough, let’s say you went to a completely new site from Free Code Camp, like LeetCode to get more practice. You technically would still have Netflix in your forward stack, which really doesn’t make sense.

现在,要彻底讲解,假设您访问了Free Code Camp的全新站点,例如LeetCode,以获取更多练习。 从技术上讲,您仍将Netflix保留在您的正向堆栈中,这实际上没有任何意义。

Luckily, you can implement a simple while loop to get rid of Netflix and any other sites quickly:

幸运的是,您可以实现一个简单的while循环来快速摆脱Netflix和任何其他网站:

//When I manually navigate to a new site, make forward stack empty
while(forward.top() > -1){  forward.pop();}

Great! Now your navigation should work the way it’s supposed to.

大! 现在,您的导航应该按照预期的方式工作。

Time for a quick recap. Stacks:

是时候快速回顾一下了。 堆栈:

  1. Follow a Last In First Out (LIFO) pattern

    遵循后进先出(LIFO)模式
  2. Have a push (add) and pop (remove) method that manage the contents of the stack

    有一个push(添加)和pop(删除)方法来管理堆栈的内容
  3. Have a top property that allows us to track how large your stack is and the current top position.

    具有top属性,该属性使我们能够跟踪您的筹码量和当前的最高位置。

At the end of each post in this series, I’ll do a brief time complexity analysis on the methods of each data structure to get some extra practice.

在本系列的每篇文章的结尾,我将对每种数据结构的方法进行简短的时间复杂度分析 ,以获得更多的实践。

Here’s the code again:

再次是下面的代码:

push(value){    this._position++;     this._storage[this._position] = value;   }   pop(){    if(this._position > -1){      let val = this._storage[this._position];       delete this._storage[this._position];       this._position--;      return val;    }  }    top(){    return this._position;  }

Push (addition) is O(1). Since you’ll always know the current position (thanks to your position variable), you don’t have to iterate to add an item.

(加)为O(1) 。 由于您将始终知道当前位置(由于position变量而已),因此无需重复添加项目。

Pop (removal) is O(1). No iteration is necessary for removal since you always have the current top position.

弹出 (移除)为O(1) 。 由于您始终处于当前的最高位置,因此无需进行迭代就可以删除。

Top is O(1). The current position is always known.

顶部O(1) 。 当前位置始终是已知的。

There isn’t a native search method on stacks, but if you were to add one, what time complexity do you think it would be?

堆栈上没有本机搜索方法,但是如果要添加一个本机搜索方法,您认为将花费多少时间?

Find (search) would be O(n). You would technically have to iterate over your storage until you found the value you were looking for. This is essentially the indexOf method on Arrays.

查找 (搜索)将为O(n) 。 从技术上讲,您将必须遍历存储,直到找到所需的值。 本质上,这是数组上的indexOf方法。

进一步阅读 (Further reading)

Wikipedia has an in-depth list of abstract data types.

Wikipedia包含抽象数据类型的深入列表。

I didn’t get a chance to get into the topic of a stack overflow, but if you’ve read my post on recursion you might have a good idea on why they occur. To beef up that knowledge, there is a great post about it on StackOverflow (see what I did there?)

我没有机会讨论堆栈溢出的问题,但是如果您阅读了我关于递归的文章,那么您可能会对它们为什么会发生有个好主意。 为了增强这一知识,在StackOverflow上有一篇很棒的文章( 看看我在那儿做了什么? )

In my next post, I’ll jump right into queues.

在我的下一篇文章中,我将直接进入队列。

翻译自: https://www.freecodecamp.org/news/data-structures-stacks-on-stacks-c25f2633c529/

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值