您当前的位置:首页 > 电脑百科 > 程序开发 > 算法

数据结构之栈

时间:2023-06-21 15:21:23  来源:  作者:尚硅谷教育

学过编程的小伙伴们大多数都知道栈和队列这两种数据结构,这两个词我相信对大家来说并不陌生。栈和队列是一种特殊的线性结构,是咱们根据编码的需求衍生出来的两种逻辑上的数据结构,在这篇文章中,我们将详细的讲解一下栈这种数据结构,至于队列,咱们放在下一篇文章细讲。

栈的核心特点是先进后出,咱们生活中,类似的例子比比皆是,像蒸包子这个过程,我们总是一层一层的放入蒸笼,靠下层的包子总是先放入的,但是蒸熟过后,我们又总是从最上层开始一层一层取出的,这个时候我相信不会有人这么傻,蒸熟过后从下层一层一层取。此外呢,在咱们JAVA中,局部变量和方法调用的过程都是存储在栈内存的,像咱们的方法调用,递归调用,都是使用了栈这种先进后出的思想。比如A方法中调用B方法,B方法中又调用C方法。我们运行A方法后,总是会先让B方法执行,B方法执行时,又会优先让C方法执行,直到C方法执行结束后,返回给它的调用者B时,我们的B才会继续执行,B结束过后,返回给调用者A,A才会得以继续执行。在此过程中,我们方法的调用顺序为A->B->C,但是执行结束顺序则为C->B->A,刚好符合咱们栈先进后出的思想。

接下来了,我们针对栈这种数据结构,进行一个详细的讲解。

一、什么是栈?

栈是一种数据结构,是一种先进后出的特殊线性表,栈主要由两个指针组成,一部分叫做栈底,主要用来确定咱们栈的起始位置,对栈底来说,并不能操作数据。另一部分叫做栈顶,主要用于操作数据,像咱们栈的新增和删除都是在栈顶完成的。而对栈的新增操作,我们叫作入栈,而对栈的删除操作,我们叫作出栈或者弹栈。下面我们来看一下栈的内存图:

入栈:向栈中添加元素的过程,叫做入栈,每次入栈时,栈底指针保持不动,只需要移动栈顶指针即可。如下图所示:

出栈(弹栈):向栈中删除元素的过程,叫做出栈或者弹栈,每次出栈时,栈底指针保持不动,只需要移动栈顶指针即可。如下图所示:

二、栈的实现

下面我们用最流行的Java语言完成栈的实现,咱们实现方式分为两种,第一种基于数组,第二种基于链表。如果对链表这种数据结构不熟悉的小伙伴,可以看看我之前写过的链表,在之前有专门且详细的讲解。

2.1数组实现栈

2.1.1 栈的抽象父类

因为咱们是通过两种方式实现栈,所以将一些公共的成员提取到咱们Stack抽象类中。在Stack类中,我们定义了一个属性size,用来表示咱们栈中实际的有效元素个数,isFull()方法主要用于判断咱们的栈是否满,同理isEmpty()方法用于判断咱们的栈是否存在元素,push()为入栈操作,pop()为出栈或者弹栈操作,show()方法主要查看咱们栈的存储情况。

package com.ignoarance.stack;

/**

* @ClassName Stack

* @Description 栈的父类

* @Author ignorance

* @Version 1.0

* bottom表示栈底指针,用于确定咱们栈的开始,top表示栈顶指针,用于完成咱们栈主要的操作。

**/

public abstract class Stack<T> {

private int size;//栈的实际有效元素个数

public int getSize() {

return size;

}

public void setSize(int size) {

this.size = size;

}

/**

* 判断当前栈是否满

* 是返回true,否返回false

* @return

*/

public abstract boolean isFull();

/**

* 判断当前栈是否空

* 是返回true,否返回false

* @return

*/

public abstract boolean isEmpty();

/**

* 入栈核心方法

*/

public abstract void push(T element);

/**

* 出栈方法

* 并返回出栈元素

* @return

*/

public abstract T pop();

/**

* 遍历方法

*/

public abstract void show();

}

2.1.2 ArrayStack实现类

ArrayStack主要为数组实现栈的核心类,在该类中,我们定义elementData数组主要用于存放元素,maxSize表示底层数组的初始化长度,也就是栈能够存放元素的数量,bottom和top分别表示栈底和栈顶。在构造器中,我们初始化底层数组长度,以及bottom和top的初始值都为-1。那么现在咱们的栈结构示意图如下:

核心代码如下:

package com.ignoarance.stack;

/**

* @ClassName ArrayStack

* @Description 数组实现栈核心类

* @Author ignorance

* @Version 1.0

**/

public class ArrayStack<T> extends Stack<T> {

private T[] elementData;//栈的核心存储结构【数组】

private final int maxSize;//数组最大长度

private int bottom;//栈底指针

private int top;//栈顶指针

public ArrayStack(T[] elementData) {

this.elementData = elementData;//初始化底层数组

this.maxSize = elementData.length;

this.bottom = -1;//将栈底索引置为-1

this.top = -1;//将栈顶索引置为-1

}

@Override

public boolean isFull() {

return false;

}

@Override

public boolean isEmpty() {

return false;

}

@Override

public void push(T element) {

}

@Override

public T pop() {

return null;

}

@Override

public void show() {

}

}

2.1.3 判断栈满和栈空

下面我们完成isFull()这个方法,什么时候栈满呢?也就是咱们栈中不能再添加元素了,咱们是通过数组完成栈结构的,又因为咱们的数组的最大索引为maxSize-1,所以当咱们的top移动到top=maxSize-1时也就表示咱们的栈已满。如下图所示:

核心代码如下:

@Override

public boolean isFull() {

return top == maxSize - 1;

}

同理,当咱们的bottom==top==-1则表示咱们栈当前没有一个元素,也就是栈为空。

@Override

public boolean isEmpty() {

return bottom == top;

}

2.1.4 入栈

入栈很简单,top向上移动,并且将元素添加到数组即可,如下图所示:

核心代码如下:

@Override

public void push(T element) {

if (isFull()){

throw new RuntimeException("栈已满,无法入栈...");

}

//top上移,并且添加元素

elementData[++top] = element;

//栈内有效结点+1

setSize(getSize() + 1);

}

2.1.5 出栈

出栈和入栈是一个相反的过程,只需将栈顶指针top向下移动即可:

@Override

public T pop() {

if (isEmpty()){

throw new RuntimeException("栈为空,无法出栈...");

}

setSize(getSize() - 1);

return elementData[top--];

}

2.1.6 遍历

@Override

public void show() {

if (isEmpty()){

throw new RuntimeException("栈为空,无法遍历...");

}

System.out.println("栈内存储情况...");

for (int i = top;i > bottom;i--){

System.out.println(elementData[i]);

}

}

2.1.7 测试

package com.ignoarance.stack.test;

import com.ignoarance.stack.ArrayStack;

import com.ignoarance.stack.Stack;

import org.junit.Test;

/**

* @ClassName StackTest

* @Description 栈测试类

* @Author ignorance

* @Version 1.0

**/

public class StackTest {

@Test

public void test01(){

Stack<String> stack = new ArrayStack<>(new String[5]);

stack.push("周芷若");

stack.push("夏诗涵");

stack.push("张无忌");

stack.push("杨过");

stack.push("郭靖");

stack.pop();

stack.pop();

System.out.println("【当前栈的长度:】" + stack.getSize());

stack.show();

stack.push("周伯通");

stack.push("欧阳锋");

System.out.println("【当前栈的长度:】" + stack.getSize());

stack.show();

stack.push("小龙女");

stack.show();

}

}

2.2链表实现栈

2.2.1 创建结点类

package com.ignoarance.stack.node;

/**

* @ClassName Node

* @Description 结点类

* @Author ignorance

* @Version 1.0

**/

public class Node<T> {

private T item;

public Node<T> next;

public Node(T item) {

this.item = item;

}

@Override

public String toString() {

return "Node{" +

"item=" + item +

'}';

}

}

2.2.2 创建LinkedListStack

在LinkedListStack链表实现栈结构的类中,我们定义bottom和top这两个指针,bottom用于表示栈底,也就是咱们链表的头结点,top用于表示栈顶,也就是咱们链表的尾结点。在构造器中,分别初始化两个指针。

package com.ignoarance.stack;

import com.ignoarance.stack.node.Node;

/**

* @ClassName LinkedListStack

* @Description 链表实现栈

* @Author ignorance

* @Version 1.0

**/

public class LinkedListStack<T> extends Stack<T> {

//栈底指针作为链表的头结点

private Node<T> bottom;

//栈顶指针

private Node<T> top;

public LinkedListStack() {

this.bottom = new Node<>(null);

this.top = null;

}

/**

* 判断当前栈是否满

* 是返回true,否返回false

* @return

*/

@Override

public boolean isFull() {

return false;

}

/**

* 判断当前栈是否空

* 是返回true,否返回false

* @return

*/

@Override

public boolean isEmpty() {

return false;

}

/**

* 入栈核心方法

* @param element

*/

@Override

public void push(T element) {

}

/**

* 出栈方法

* 并返回出栈元素

* @return

*/

@Override

public T pop() {

return null;

}

/**

* 遍历方法

*/

@Override

public void show() {

}

}

2.2.3 判断栈是否为空

@Override

public boolean isEmpty() {

return bottom == null && bottom.next == null;

}

2.2.4 push()方法实现

链表实现栈的先进后出,我们其实可以利用单链表的头插法,前面我们在单链表中的反转中已经介绍过这种方法,相信咱们大家一定不会陌生,以下是思路图:

通过头插法,我们每次push的时候,就会将新结点插入到链表最前方,最后pop的时候从第一个开始依次删除就可以。

比如我们加入的顺序是Node->insertNode1->insertNode2,从头开始查找为insertNode2->insertNode1->Node。从而通过头插法巧妙的实现了栈先进先出的思想。代码如下:

@Override

public void push(T element) {

//将数据组装成一个Node结点,因为链表中的最小单位为结点

Node<T> insertNode = new Node<>(element);

//如果节点为空,则加到第一个结点即可

if (isEmpty()){

bottom.next = insertNode;

top = insertNode;

setSize(getSize() + 1);

return;

}

//将新插入结点插入到链表最前端

insertNode.next = top;

bottom.next = insertNode;

top = insertNode;

//有效结点自增

setSize(getSize() + 1);

}

2.2.5 pop()方法实现

知道了链表实现栈思路,那么pop()出栈就显得比较简单了,我们每次pop的时候将链表的第一个结点返回,并将其删除即可,如下图所示:

核心代码如下:

@Override

public T pop() {

if (isEmpty()){

throw new RuntimeException("栈为空,无法出栈...");

}

//删除第一个结点

Node<T> popNode = top;

bottom.next = top.next;

top = top.next;

//有效结点自减

setSize(getSize() - 1);

return popNode.getItem();

}

2.2.6 栈的遍历

@Override

public void show() {

if (isEmpty()){

throw new RuntimeException("【栈为空,无法遍历...】");

}

System.out.println("【当前栈的存储情况为:】");

for (Node<T> cur = bottom.next;cur != null;cur = cur.next){

System.out.println(cur);

}

}

2.2.7 测试

@Test

public void test2(){

Stack<String> stack = new LinkedListStack<>();

stack.push("周芷若");

stack.push("夏诗涵");

stack.push("张无忌");

stack.push("杨过");

stack.push("郭靖");

System.out.println("【当前栈的长度:】" + stack.getSize());

stack.show();

stack.pop();

stack.pop();

stack.pop();

stack.pop();

stack.pop();

System.out.println("【当前栈的长度:】" + stack.getSize());

stack.show();

}

三、栈的经典案例

3.1括号匹配问题

力扣上有这么一道题目,给定一个字符串,需要咱们去算这个字符串中的每个"("括号是否成对存在,比如对于字符串"((我们)是)是(程序员)"每个左括号都能找到对应的右括号,说明括号能正确匹配,像"(今天))天)(气真)好"就不满足。

下面需要我们通过代码来实现这个规则,这道题目看起来挺简单,但是在我们去实现时,如果方法选择不恰当,会发现这道题目并不简单。

那么怎么去解决了,这正是咱们栈的一种经典的使用场景。使用其它方式也能实现,但是相比较而言复杂得多。以下是实现思路:

1.创建栈对象,用于存储左括号;

2.遍历字符数组,如果当前字符为左括号,则入栈;

3.如果遍历到的当前字符为右括号,则弹栈,如果弹出的元 素为空, 则表示当前右括号没有与之匹配的左括号。反之则说明当前右括号有匹配的左括号;

4.当循环完字符数组后,我们需要看咱们的栈是否为空,如果为空,说明全部匹配,返回true,反之说明还有多余的右括号,则返回false。

3.1.1 实现代码

package com.ignoarance.stack.test;

import com.ignoarance.stack.LinkedListStack;

import com.ignoarance.stack.Stack;

/**

* @ClassName StackDemo01

* @Description 栈的案例-括号匹配问题

* @Author ignorance

* @Version 1.0

**/

public class StackDemo01 {

public static void mAIn(String[] args) {

String str = "((I am ) a (pretty) b)oy";

System.out.println(isMatch(str));

}

public static boolean isMatch(String resourceStr){

Stack<Character> stack = new LinkedListStack<>();

char[] chars = resourceStr.toCharArray();

for (int i = 0;i < chars.length;i++){

char item = chars[i];

if ('(' == item){

stack.push(item);

continue;

}

if (')' == item){

Character popEle = stack.pop();

if (popEle == null){

return false;

}

}

}

return stack.isEmpty();

}

}

3.1.2 测试(正确情况)

public static void main(String[] args) {

String str = "((I am ) a (pretty) b)oy";

System.out.println(isMatch(str));

}

3.1.3 测试(错误情况)

public static void main(String[] args) {

String str = "(((I am ) a (pretty) b)oy";

System.out.println(isMatch(str));

}

3.2逆波兰表达式完成四则运算

相信计算机专业的同学肯定学过逆波兰表达式,逆波兰表达式是计算机用来进行四则运算的一个表达式,大家都知道咱们数学的四则运算,往往看起来很简单。但是你要是把这个思想告诉计算机,却并不是一件容易的事。

例如对10+(100/4)*5-9进行计算。像我们人脑可以很简单的算出答案,但是如果让你就这个表达式,让你编码让计算机完成呢?是不是就有点头疼了,可不是让你自己分几步做啊,第一步把100/4让计算机算,然后又用这个结果*5。这个问题复杂就复杂在不同的算数符号具有不同的优先级,我们怎么样让计算机知道符号的优先级成为了我们最棘手的问题。

所以呢,为了解决这个问题,就引出了咱们的逆波兰表达式。逆波兰表达式又叫后缀表达式,众所周知,一个运算符对应两个操作数,我们将运算符写在它所作用两个操作数后的一种表达式就是咱们的逆波兰表达式。比如对于a+b,所对应的逆波兰表达式为ab+,对a+(b/c)*d-e,也就是咱们的题目对应的代数式,它所对应的逆波兰表达式为abc/d*+e-。

那么有了逆波兰表达式,我们可以做些什么了,对于刚才那道题目,此时我们就可以使用咱们的栈结构轻松的解决了。实现思路如下:

1.创建栈对象,主要用于存储咱们的操作数;

2.遍历咱们的表达式数组,如果遇到操作数,则入栈;

3.如果遇到运算符,则从栈中弹出两个操作数,并且计算结果,将最后的结果重新入栈。

4.按照第3步的逻辑,当咱们的表达式数组遍历完成以后,此时栈中会存在一个元素,这个元素就是咱们最后的元素结果,将其出栈即可。

3.2.1 实现代码

package com.ignoarance.stack.test;

import com.ignoarance.stack.LinkedListStack;

import com.ignoarance.stack.Stack;

/**

* @ClassName StackDemo02

* @Description 栈应用案例-逆波兰表达式实现四则运算

* @Author ignorance

* @Version 1.0

**/

public class StackDemo02 {

public static void main(String[] args) {

String[] express = {"10","100","4","/","5","*","+","9","-"};

System.out.println(compute(express));

}

public static Integer compute(String[] expressArray){

String sign = "+-*/";

Stack<Integer> stack = new LinkedListStack<>();

for (String item : expressArray){

if (sign.contains(item)){

Integer num1 = stack.pop();

Integer num2 = stack.pop();

stack.push(oper(num1,num2,item));

continue;

}

stack.push(Integer.parseInt(item));

}

return stack.pop();

}

private static Integer oper(Integer num1,Integer num2,String oper){

switch (oper){

case "+" : return num2 + num1;

case "-" : return num2 - num1;

case "*" : return num2 * num1;

case "/" : return num2 / num1;

default:throw new RuntimeException("不支持的操作类型...");

}

}

}

3.2.2 测试

四、综合代码

4.1Stack抽象父类

package com.ignoarance.stack;

/**

* @ClassName Stack

* @Description 栈的父类

* @Author ignorance

* @Version 1.0

**/

public abstract class Stack<T> {

private int size;//栈的实际有效元素个数

public int getSize() {

return size;

}

public void setSize(int size) {

this.size = size;

}

/**

* 判断当前栈是否满

* 是返回true,否返回false

* @return

*/

public abstract boolean isFull();

/**

* 判断当前栈是否空

* 是返回true,否返回false

* @return

*/

public abstract boolean isEmpty();

/**

* 入栈核心方法

*/

public abstract void push(T element);

/**

* 出栈方法

* 并返回出栈元素

* @return

*/

public abstract T pop();

/**

* 遍历方法

*/

public abstract void show();

}

4.2ArrayStack

package com.ignoarance.stack;

import java.util.Arrays;

/**

* @ClassName ArrayStack

* @Description 数组实现栈核心类

* @Author ignorance

* @Version 1.0

**/

public class ArrayStack<T> extends Stack<T> {

private T[] elementData;//栈的核心存储结构【数组】

private final int maxSize;//数组最大长度

private int bottom;//栈底指针

private int top;//栈顶指针

public ArrayStack(T[] elementData) {

this.elementData = elementData;//初始化底层数组

this.maxSize = elementData.length;

this.bottom = -1;//将栈底索引置为-1

this.top = -1;//将栈顶索引置为-1

}

@Override

public boolean isFull() {

return top == maxSize - 1;

}

@Override

public boolean isEmpty() {

return bottom == top;

}

@Override

public void push(T element) {

if (isFull()){

throw new RuntimeException("栈已满,无法入栈...");

}

//top上移,并且添加元素

elementData[++top] = element;

//栈内有效结点+1

setSize(getSize() + 1);

}

@Override

public T pop() {

if (isEmpty()){

throw new RuntimeException("栈为空,无法出栈...");

}

setSize(getSize() - 1);

return elementData[top--];

}

@Override

public void show() {

if (isEmpty()){

throw new RuntimeException("栈为空,无法遍历...");

}

System.out.println("【栈内存储情况...】");

for (int i = top;i > bottom;i--){

System.out.println(elementData[i]);

}

}

}

4.3结点类Node

package com.ignoarance.stack.node;

/**

* @ClassName Node

* @Description 结点类

* @Author ignorance

* @Version 1.0

**/

public class Node<T> {

private T item;

public Node<T> next;

public Node(T item) {

this.item = item;

}

public T getItem() {

return item;

}

public void setItem(T item) {

this.item = item;

}

@Override

public String toString() {

return "Node{" +

"item=" + item +

'}';

}

}

4.4LinkedListStack

package com.ignoarance.stack;

import com.ignoarance.stack.node.Node;

/**

* @ClassName LinkedListStack

* @Description 链表实现栈

* @Author ignorance

* @Version 1.0

**/

public class LinkedListStack<T> extends Stack<T> {

//栈底指针作为链表的头结点

private Node<T> bottom;

//栈顶指针

private Node<T> top;

public LinkedListStack() {

this.bottom = new Node<>(null);

this.top = null;

}

/**

* 判断当前栈是否满,也就是链表是否有结点

* 是返回true,否返回false

* @return

*/

@Override

public boolean isFull() {

throw new RuntimeException("不支持的操作...");

}

/**

* 判断当前栈是否空

* 是返回true,否返回false

* @return

*/

@Override

public boolean isEmpty() {

return bottom == null || bottom.next == null;

}

/**

* 入栈核心方法

* @param element

*/

@Override

public void push(T element) {

//将数据组装成一个Node结点,因为链表中的最小单位为结点

Node<T> insertNode = new Node<>(element);

//如果节点为空,则加到第一个结点即可

if (isEmpty()){

bottom.next = insertNode;

top = insertNode;

setSize(getSize() + 1);

return;

}

//将新插入结点插入到链表最前端

insertNode.next = top;

bottom.next = insertNode;

top = insertNode;

//有效结点自增

setSize(getSize() + 1);

}

/**

* 出栈方法

* 并返回出栈元素

* @return

*/

@Override

public T pop() {

if (isEmpty()){

throw new RuntimeException("栈为空,无法出栈...");

}

//删除第一个结点

Node<T> popNode = top;

bottom.next = top.next;

top = top.next;

//有效结点自减

setSize(getSize() - 1);

return popNode.getItem();

}

/**

* 遍历方法

*/

@Override

public void show() {

if (isEmpty()){

throw new RuntimeException("【栈为空,无法遍历...】");

}

System.out.println("【当前栈的存储情况为:】");

for (Node<T> cur = bottom.next;cur != null;cur = cur.next){

System.out.println(cur);

}

}

}

4.5Stack测试类

package com.ignoarance.stack.test;

import com.ignoarance.stack.ArrayStack;

import com.ignoarance.stack.LinkedListStack;

import com.ignoarance.stack.Stack;

import org.junit.Test;

/**

* @ClassName StackTest

* @Description 栈测试类

* @Author ignorance

* @Version 1.0

**/

public class StackTest {

@Test

public void test01(){

Stack<String> stack = new ArrayStack<>(new String[5]);

stack.push("周芷若");

stack.push("夏诗涵");

stack.push("张无忌");

stack.push("杨过");

stack.push("郭靖");

stack.pop();

stack.pop();

System.out.println("【当前栈的长度:】" + stack.getSize());

stack.show();

stack.push("周伯通");

stack.push("欧阳锋");

System.out.println("【当前栈的长度:】" + stack.getSize());

stack.show();

stack.push("小龙女");

stack.show();

}

@Test

public void test2(){

Stack<String> stack = new LinkedListStack<>();

stack.push("周芷若");

stack.push("夏诗涵");

stack.push("张无忌");

stack.push("杨过");

stack.push("郭靖");

System.out.println("【当前栈的长度:】" + stack.getSize());

stack.show();

stack.pop();

stack.pop();

stack.pop();

stack.pop();

stack.pop();

System.out.println("【当前栈的长度:】" + stack.getSize());

stack.show();

}

}

4.6Stack案例一

package com.ignoarance.stack.test;

import com.ignoarance.stack.LinkedListStack;

import com.ignoarance.stack.Stack;

/**

* @ClassName StackDemo01

* @Description 栈的案例-括号匹配问题

* @Author ignorance

* @Version 1.0

**/

public class StackDemo01 {

public static void main(String[] args) {

String str = "(((I am ) a (pretty) b)oy";

System.out.println(isMatch(str));

}

public static boolean isMatch(String resourceStr){

Stack<Character> stack = new LinkedListStack<>();

char[] chars = resourceStr.toCharArray();

for (int i = 0;i < chars.length;i++){

char item = chars[i];

if ('(' == item){

stack.push(item);

continue;

}

if (')' == item){

Character popEle = stack.pop();

if (popEle == null){

return false;

}

}

}

return stack.isEmpty();

}

}

4.7Stack案例二

package com.ignoarance.stack.test;

import com.ignoarance.stack.LinkedListStack;

import com.ignoarance.stack.Stack;

/**

* @ClassName StackDemo02

* @Description 栈应用案例-逆波兰表达式实现四则运算

* @Author ignorance

* @Version 1.0

**/

public class StackDemo02 {

public static void main(String[] args) {

String[] express = {"10","100","4","/","5","*","+","9","-"};

System.out.println(compute(express));

}

public static Integer compute(String[] expressArray){

String sign = "+-*/";

Stack<Integer> stack = new LinkedListStack<>();

for (String item : expressArray){

if (sign.contains(item)){

Integer num1 = stack.pop();

Integer num2 = stack.pop();

stack.push(oper(num1,num2,item));

continue;

}

stack.push(Integer.parseInt(item));

}

return stack.pop();

}

private static Integer oper(Integer num1,Integer num2,String oper){

switch (oper){

case "+" : return num2 + num1;

case "-" : return num2 - num1;

case "*" : return num2 * num1;

case "/" : return num2 / num1;

default:throw new RuntimeException("不支持的操作类型...");

}

}

}

总结

在本篇文章中,咱们主要讲了栈这种特殊的数据结构,也是咱们特别常见的数据结构。对栈结构又通过数组和链表两种方式进行实现,代码并不算难。重点值得关注的是栈结构的核心思想,对我们来说能够理解是特别重要的。像咱们通过栈完成了两个案例,希望能够帮助大家对其思想有更深的了解和认识。

我们在不断学习的过程,会发现编程是一件特别具有挑战性的事情,有时候咱们学得越多,总会发现自己不会的也越多,当咱们真正理解了设计者为我们提供的思想,又会突然间豁然开朗,不由对他们的智慧心生敬仰。

所以呢,在学习的路途中,有无数的同道者一起都在前行,努力的人不会一直孤独的,总有一天我们也会因为不断的努力,不论是技术还是生活,都会发生美好的改变。所以,加油吧!



Tags:数据结构   点击:()  评论:()
声明:本站部分内容及图片来自互联网,转载是出于传递更多信息之目的,内容观点仅代表作者本人,不构成投资建议。投资者据此操作,风险自担。如有任何标注错误或版权侵犯请与我们联系,我们将及时更正、删除。
▌相关推荐
MySQL 记录、页、索引的数据结构简析
引言本文在介绍 MySQL 内存中记录、页、索引、游标的数据结构的基础上,通过简单分析插入操作过程中行格式的转换介绍了不同数据结构的关系,其中不涉及加锁相关逻辑。原理记录...【详细内容】
2023-12-28  Search: 数据结构  点击:(68)  评论:(0)  加入收藏
HashMap:Java中的高效数据结构
HashMap是Java中常用的数据结构之一,它实现了Map接口,并且提供了快速的查找、插入和删除操作。HashMap的底层数据结构是数组和链表(或红黑树)的组合,这种数据结构被称为哈希表(Has...【详细内容】
2023-11-24  Search: 数据结构  点击:(331)  评论:(0)  加入收藏
浅析Redis数据结构
Labs 导读Redis ( Remote Dictionary Server)远程字典服务,是一款通过Key-Value存储的NoSql数据库,数据缓存在内存中,支持网络、可持久化日志,提供多种语言的API,常用的场景有高...【详细内容】
2023-11-13  Search: 数据结构  点击:(277)  评论:(0)  加入收藏
程序员都必须知道的八种必须掌握数据结构
数据结构是一种在计算机中组织和存储数据的专门方法,使我们可以更有效地对存储的数据执行操作。数据结构在计算机科学和软件工程领域有着广泛而多样的使用范围。几乎所有已开...【详细内容】
2023-11-01  Search: 数据结构  点击:(206)  评论:(0)  加入收藏
浅谈HBase数据结构和系统架构
Part 01 LSM树模型常见的的关系型数据库,如MySQL、SQL Server、Oracle等,使用B+ Tree作为数据存储与索引的基本结构,非叶子节点只存放索引数据,叶子节点存放所有数据和指向相邻...【详细内容】
2023-10-17  Search: 数据结构  点击:(238)  评论:(0)  加入收藏
一文学会队列入门:Python数据结构与算法
队列(Queue)是一种特殊的线性数据结构,其操作遵循先进先出(FIFO)的原则,即最先添加到队列中的元素最先被移除。1. 队列的基本概念队列的基本操作包括:入队(Enqueue)将元素添加到队列...【详细内容】
2023-09-26  Search: 数据结构  点击:(235)  评论:(0)  加入收藏
栈的实现:Python数据结构与算法
栈(Stack)是一种特殊的线性数据结构,遵循后进先出(LIFO)的原则,即最后添加进栈的元素最先被移除。1. 栈的基本概念栈的操作主要有两种:压栈(Push)和弹栈(Pop)。压栈是将元素放入栈顶,弹...【详细内容】
2023-09-25  Search: 数据结构  点击:(261)  评论:(0)  加入收藏
HashMap的底层数据结构
在 JDK1.8 中,HashMap 还引入了一个新的概念,叫做负载因子(load factor),它是指哈希表中键值对的数量与数组长度的比值。当键值对的数量超过了负载因子与数组长度的乘积时,就会...【详细内容】
2023-09-15  Search: 数据结构  点击:(239)  评论:(0)  加入收藏
算法和数据结构:解析与应用
本文将探讨算法和数据结构的概念、定义、关系以及其在计算机科学中的重要性和应用。通过详细的数据和专业的解析,本文旨在帮助读者深入理解算法和数据结构的内涵,并展示它们对...【详细内容】
2023-09-15  Search: 数据结构  点击:(224)  评论:(0)  加入收藏
Redis Stream 数据结构实现原理真的很强
你好,我是码哥,一个拥抱硬核技术和对象,面向人民币编程的男人,设置星标不迷路。我在【Redis 使用 List 实现消息队列的利与弊】说过使用 List 实现消息队列有很多局限性。 没有...【详细内容】
2023-09-13  Search: 数据结构  点击:(361)  评论:(0)  加入收藏
▌简易百科推荐
小红书、视频号、抖音流量算法解析,干货满满,值得一看!
咱们中国现在可不是一般的牛!网上的网友已经破了十个亿啦!到了这个互联网的新时代,谁有更多的人流量,谁就能赢得更多的掌声哦~抖音、小红书、、视频号,是很多品牌必争的流量洼地...【详细内容】
2024-02-23  二手车小胖说    Tags:流量算法   点击:(13)  评论:(0)  加入收藏
雪花算法详解与Java实现:分布式唯一ID生成原理
SnowFlake 算法,是 Twitter 开源的分布式 ID 生成算法。其核心思想就是:使用一个 64 bit 的 long 型的数字作为全局唯一 ID。在分布式系统中的应用十分广泛,且 ID 引入了时间戳...【详细内容】
2024-02-03   一安未来  微信公众号  Tags:雪花算法   点击:(50)  评论:(0)  加入收藏
程序开发中常用的十种算法,你用过几种?
当编写程序时,了解和使用不同的算法对解决问题至关重要。以下是C#中常用的10种算法,每个算法都伴随着示例代码和详细说明。1. 冒泡排序 (Bubble Sort):冒泡排序是一种简单的比...【详细内容】
2024-01-17  架构师老卢  今日头条  Tags:算法   点击:(44)  评论:(0)  加入收藏
百度推荐排序技术的思考与实践
本文将分享百度在推荐排序方面的思考与实践。在整个工业界的推广搜场景上,特征设计通常都是采用离散化的设计,需要保证两方面的效果,一方面是记忆,另一方面是泛化。特征都是通过...【详细内容】
2024-01-09  DataFunTalk  微信公众号  Tags:百度推荐   点击:(77)  评论:(0)  加入收藏
什么是布隆过滤器?如何实现布隆过滤器?
以下我们介绍了什么是布隆过滤器?它的使用场景和执行流程,以及在 Redis 中它的使用,那么问题来了,在日常开发中,也就是在 Java 开发中,我们又将如何操作布隆过滤器呢?布隆过滤器(Blo...【详细内容】
2024-01-05  Java中文社群  微信公众号  Tags:布隆过滤器   点击:(87)  评论:(0)  加入收藏
面向推荐系统的深度强化学习算法研究与应用
随着互联网的快速发展,推荐系统在各个领域中扮演着重要的角色。传统的推荐算法在面对大规模、复杂的数据时存在一定的局限性。为了解决这一问题,深度强化学习算法应运而生。本...【详细内容】
2024-01-04  数码小风向    Tags:算法   点击:(95)  评论:(0)  加入收藏
非负矩阵分解算法:从非负数据中提取主题、特征等信息
非负矩阵分解算法(Non-negativeMatrixFactorization,简称NMF)是一种常用的数据分析和特征提取方法,主要用于从非负数据中提取主题、特征等有意义的信息。本文将介绍非负矩阵分解...【详细内容】
2024-01-02  毛晓峰    Tags:算法   点击:(63)  评论:(0)  加入收藏
再谈前端算法,你这回明白了吗?
楔子 -- 青蛙跳台阶一只青蛙一次可以跳上一级台阶,也可以跳上二级台阶,求该青蛙跳上一个n级的台阶总共需要多少种跳法。分析: 当n=1的时候,①只需要跳一次即可;只有一种跳法,即f(...【详细内容】
2023-12-28  前端爱好者  微信公众号  Tags:前端算法   点击:(108)  评论:(0)  加入收藏
三分钟学习二分查找
二分查找是一种在有序数组中查找元素的算法,通过不断将搜索区域分成两半来实现。你可能在日常生活中已经不知不觉地使用了大脑里的二分查找。最常见的例子是在字典中查找一个...【详细内容】
2023-12-22  小技术君  微信公众号  Tags:二分查找   点击:(78)  评论:(0)  加入收藏
强化学习算法在资源调度与优化中的应用
随着云计算和大数据技术的快速发展,资源调度与优化成为了现代计算系统中的重要问题。传统的资源调度算法往往基于静态规则或启发式方法,无法适应动态变化的环境和复杂的任务需...【详细内容】
2023-12-14  职场小达人欢晓    Tags:算法   点击:(165)  评论:(0)  加入收藏
站内最新
站内热门
站内头条