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

大厂必备技能:数据结构之栈

时间:2023-06-24 14:50:09  来源:  作者:尚硅谷教育

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

栈的核心特点是先进后出,咱们生活中,类似的例子比比皆是,像蒸包子这个过程,我们总是一层一层的放入蒸笼,靠下层的包子总是先放入的,但是蒸熟过后,我们又总是从最上层开始一层一层取出的,这个时候我相信不会有人这么傻,蒸熟过后从下层一层一层取。此外呢,在咱们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)  加入收藏
▌简易百科推荐
即将过时的 5 种软件开发技能!
作者 | Eran Yahav编译 | 言征出品 | 51CTO技术栈(微信号:blog51cto) 时至今日,AI编码工具已经进化到足够强大了吗?这未必好回答,但从2023 年 Stack Overflow 上的调查数据来看,44%...【详细内容】
2024-04-03    51CTO  Tags:软件开发   点击:(6)  评论:(0)  加入收藏
跳转链接代码怎么写?
在网页开发中,跳转链接是一项常见的功能。然而,对于非技术人员来说,编写跳转链接代码可能会显得有些困难。不用担心!我们可以借助外链平台来简化操作,即使没有编程经验,也能轻松实...【详细内容】
2024-03-27  蓝色天纪    Tags:跳转链接   点击:(13)  评论:(0)  加入收藏
中台亡了,问题到底出在哪里?
曾几何时,中台一度被当做“变革灵药”,嫁接在“前台作战单元”和“后台资源部门”之间,实现企业各业务线的“打通”和全域业务能力集成,提高开发和服务效率。但在中台如火如荼之...【详细内容】
2024-03-27  dbaplus社群    Tags:中台   点击:(9)  评论:(0)  加入收藏
员工写了个比删库更可怕的Bug!
想必大家都听说过删库跑路吧,我之前一直把它当一个段子来看。可万万没想到,就在昨天,我们公司的某位员工,竟然写了一个比删库更可怕的 Bug!给大家分享一下(不是公开处刑),希望朋友们...【详细内容】
2024-03-26  dbaplus社群    Tags:Bug   点击:(5)  评论:(0)  加入收藏
我们一起聊聊什么是正向代理和反向代理
从字面意思上看,代理就是代替处理的意思,一个对象有能力代替另一个对象处理某一件事。代理,这个词在我们的日常生活中也不陌生,比如在购物、旅游等场景中,我们经常会委托别人代替...【详细内容】
2024-03-26  萤火架构  微信公众号  Tags:正向代理   点击:(11)  评论:(0)  加入收藏
看一遍就理解:IO模型详解
前言大家好,我是程序员田螺。今天我们一起来学习IO模型。在本文开始前呢,先问问大家几个问题哈~什么是IO呢?什么是阻塞非阻塞IO?什么是同步异步IO?什么是IO多路复用?select/epoll...【详细内容】
2024-03-26  捡田螺的小男孩  微信公众号  Tags:IO模型   点击:(9)  评论:(0)  加入收藏
为什么都说 HashMap 是线程不安全的?
做Java开发的人,应该都用过 HashMap 这种集合。今天就和大家来聊聊,为什么 HashMap 是线程不安全的。1.HashMap 数据结构简单来说,HashMap 基于哈希表实现。它使用键的哈希码来...【详细内容】
2024-03-22  Java技术指北  微信公众号  Tags:HashMap   点击:(11)  评论:(0)  加入收藏
如何从头开始编写LoRA代码,这有一份教程
选自 lightning.ai作者:Sebastian Raschka机器之心编译编辑:陈萍作者表示:在各种有效的 LLM 微调方法中,LoRA 仍然是他的首选。LoRA(Low-Rank Adaptation)作为一种用于微调 LLM(大...【详细内容】
2024-03-21  机器之心Pro    Tags:LoRA   点击:(12)  评论:(0)  加入收藏
这样搭建日志中心,传统的ELK就扔了吧!
最近客户有个新需求,就是想查看网站的访问情况。由于网站没有做google的统计和百度的统计,所以访问情况,只能通过日志查看,通过脚本的形式给客户导出也不太实际,给客户写个简单的...【详细内容】
2024-03-20  dbaplus社群    Tags:日志   点击:(4)  评论:(0)  加入收藏
Kubernetes 究竟有没有 LTS?
从一个有趣的问题引出很多人都在关注的 Kubernetes LTS 的问题。有趣的问题2019 年,一个名为 apiserver LoopbackClient Server cert expired after 1 year[1] 的 issue 中提...【详细内容】
2024-03-15  云原生散修  微信公众号  Tags:Kubernetes   点击:(6)  评论:(0)  加入收藏
站内最新
站内热门
站内头条