数据结构深度剖析栈与队列:结构、边界实现与进出操作全解析

文章目录

1. 栈

1.1 栈的结构和概念

概念:

栈:一种特殊的线性表,其只允许在固定的一端进行插入和删除元素操作。进行数据插入和删除操作的一端称为栈顶,另一端称为栈底。
栈中的数据元素遵守后进先出LIFO(Last In First Out)的原则。
压栈:栈的插入操作叫做进栈/压栈/入栈,入数据在栈顶。
出栈:栈的删除操作叫做出栈。出数据也在栈顶。

结构:

1. 使用数组实现:


2. 链表



使用数组实现栈和使用链表实现栈的区别。


总的来说,由于栈只在一端操作,数组在绝大多数场景下性能和简洁性都占优,实际工程中数组实现的栈更常见。

1.2 栈的实现

栈的实现一般可以使用数组或者链表实现,相对而言数组的结构实现更优一些。因为数组在尾上插入数据的代价比较小。

Stack.h 文件

c 复制代码
#pragma once


#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>
#include <assert.h>

#if 0
// 下面是定长的静态栈的结构,实际中一般不实用,所以我们主要实现下面的支持动态增长的栈
typedef int STDataType;
#define N 10
typedef struct Stack
{
	STDataType _a[N];
	int _top; // 栈顶
}Stack;
#endif




// 支持动态增长的栈
typedef int STDataType;

typedef struct Stack
{
	STDataType* a;
	int top;       //栈顶
	int capacity;  //容量
}ST;


//初始化栈
void STInit(ST* pst);

// 入栈 
void STPush(ST* pst, STDataType x);

//出栈
void STPop(ST* pst);

//获取栈顶元素 
STDataType STTop(ST* pst);

//检测栈是否为空,如果为空返回非零结果,如果不为空返回0 
bool STEmpty(ST* pst);

// 获取栈中有效元素个数 
int STSize(ST* pst);

//销毁栈
void STDestroy(ST* pst);

Stack.c 文件

c 复制代码
#define _CRT_SECURE_NO_WARNINGS

#include "Stack.h"

//实现初始化栈
void STInit(ST* pst)
{
	assert(pst);
	pst->a = NULL;
	//top指向栈顶元素的下一个可用位置。
	pst->top = 0;
	/*
	top指向栈顶元素
	pst->top = -1;
	*/
	pst->capacity = 0;
}



// 实现入栈 
void STPush(ST* pst, STDataType x)
{
	assert(pst);
	//扩容
	if (pst->top == pst->capacity)
	{
		int newcapacity = pst->capacity == 0 ? 4 : pst->capacity * 2;
		STDataType* tmp = (STDataType*)realloc(pst->a, newcapacity * sizeof(STDataType));
		if (tmp == NULL)
		{
			perror("realloc fail");
			return;
		}
		pst->a = tmp;
		pst->capacity = newcapacity;


	}

	pst->a[pst->top] = x;
	pst->top++;
}

//实现出栈
void STPop(ST* pst)
{
	assert(pst);
	assert(pst->top > 0);
	pst->top--;
}

//实现获取栈顶元素 
STDataType STTop(ST* pst)
{
	assert(pst);
	assert(pst->top > 0);//栈不为空才pop

	return pst->a[pst->top - 1];
}

//检测栈是否为空,如果为空返回非零结果,如果不为空返回0 
bool STEmpty(ST* pst)
{
	assert(pst);
	
	return pst->top == 0;
}

// 获取栈中有效元素个数 
int STSize(ST* pst)
{
	assert(pst);
	return pst->top;
}

//实现销毁栈
void STDestroy(ST* pst)
{
	assert(pst);

	free(pst->a);
	pst->a = NULL;
	pst->top = pst->capacity = 0;

}

test.c 文件

c 复制代码
#define _CRT_SECURE_NO_WARNINGS

#include "Stack.h"


//int main()
//{
//	ST s;
//
//	STInit(&s);
//	STPush(&s, 1);
//	STPush(&s, 2);
//	STPush(&s, 3);
//	STPush(&s, 5);
//
//	printf("%d\n",STTop(&s));
//
//	STTop(&s);
//	printf("%d\n", STTop(&s));
//
//
//	STTop(&s);
//	printf("%d\n", STTop(&s));
//
//
//	STDestroy(&s);
//
//	return 0;
//}

int main()
{
	ST s;

	STInit(&s);
	STPush(&s, 1);
	STPush(&s, 2);
	STPush(&s, 3);
	STPush(&s, 4);

	while (!STEmpty(&s))
	{
		printf("%d ",STTop(&s));
		STPop(&s);//弹出栈顶数据
	}

	STDestroy(&s);

	return 0;
}

这里需要特殊说明的是栈的初始化代码不同写法中 top 的意义:

1. top指向栈顶元素的下一个可用位置: pst->top = 0;

2. top指向栈顶元素: pst->top = -1;


两种方式都可以使用但是使用时应注意要匹配对应的方法,不能混用。

1.3 有关栈的OJ题:有效的括号

题目描述:

给定一个只包括 ' ( ' ,' ) ' ,' { ' ,' } ' ,' [ ' ,' ] ' 的字符串 s ,判断字符串是否有效。

有效字符串需满足:

1. 左括号必须用相同类型的右括号闭合。
2. 左括号必须以正确的顺序闭合。
3. 每个右括号都有一个对应的相同类型的左括号。

示例:


题解:

c 复制代码
// 支持动态增长的栈
typedef char STDataType;

typedef struct Stack
{
	STDataType* a;
	int top;       //栈顶
	int capacity;  //容量
}ST;
//初始化栈
void STInit(ST* pst);

// 入栈 
void STPush(ST* pst, STDataType x);

//出栈
void STPop(ST* pst);

//获取栈顶元素 
STDataType STTop(ST* pst);

//检测栈是否为空,如果为空返回非零结果,如果不为空返回0 
bool STEmpty(ST* pst);

// 获取栈中有效元素个数 
int STSize(ST* pst);

//销毁栈
void STDestroy(ST* pst);





//实现初始化栈
void STInit(ST* pst)
{
	assert(pst);
	pst->a = NULL;
	//top指向栈顶元素的下一个可用位置。
	pst->top = 0;
	/*
	top指向栈顶数据
	pst->top = -1;
	*/
	pst->capacity = 0;
}
// 实现入栈 
void STPush(ST* pst, STDataType x)
{
	assert(pst);
	//扩容
	if (pst->top == pst->capacity)
	{
		int newcapacity = pst->capacity == 0 ? 4 : pst->capacity * 2;
		STDataType* tmp = (STDataType*)realloc(pst->a, newcapacity * sizeof(STDataType));
		if (tmp == NULL)
		{
			perror("realloc fail");
			return;
		}
		pst->a = tmp;
		pst->capacity = newcapacity;
	}
	pst->a[pst->top] = x;
	pst->top++;
}

//实现出栈
void STPop(ST* pst)
{
	assert(pst);
	assert(pst->top > 0);
	pst->top--;
}

//实现获取栈顶元素 
STDataType STTop(ST* pst)
{
	assert(pst);
	assert(pst->top > 0);
	return pst->a[pst->top - 1];
}

//检测栈是否为空,如果为空返回非零结果,如果不为空返回0 
bool STEmpty(ST* pst)
{
	assert(pst);
	return pst->top == 0;
}

// 获取栈中有效元素个数 
int STSize(ST* pst)
{
	assert(pst);
	return pst->top;
}

//实现销毁栈
void STDestroy(ST* pst)
{
	assert(pst);
	free(pst->a);
	pst->a = NULL;
	pst->top = pst->capacity = 0;
}


//主要思路
bool isValid(char* s)
{
    ST st;
    STInit(&st);
    while(*s != '\0')
    {
        if(*s == '(' || *s == '{' || *s == '[')
        {
            //左括号入栈
            STPush(&st, *s); 
        }
        else
        {
            if(STEmpty(&st))
            {
                STDestroy(&st);
                return false;
            }

            //右括号取栈顶的左括号尝试匹配
            char top = STTop(&st);
            STPop(&st);

            //判断不匹配
            if((top == '(' && *s != ')')
            || (top == '{' && *s != '}')
            || (top == '[' && *s != ']'))
            {
                STDestroy(&st);
                return false;
            }
        }
        ++s;
    }
    //如果栈不为空,说明左括号比右括号多,数量不匹配
    bool ret = STEmpty(&st);
    STDestroy(&st);

    return ret;
}

2. 队列

2.1队列的概念及结构

队列:只允许在一端进行插入数据操作,在另一端进行删除数据操作的特殊线性表,队列具有先进先出FIFO(First In First Out) 的特点。
入队列:进行插入操作的一端称为队尾
出队列:进行删除操作的一端称为队头

2.2 队列的实现

队列也可以使用数组和链表的结构实现,使用链表的结构实现更优一些,因为如果使用数组的结构,出队列在数组头上出数据,效率会比较低。

1. Queue.h 文件

c 复制代码
#pragma once

#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>
#include <assert.h>

//单链表实现

typedef int QDataType;

typedef struct QueueNode
{
	struct QueueNode* next;
	QDataType val;
}QNode;

typedef struct Queue
{
	QNode* phead;
	QNode* ptail;
	int size;
}Queue;


//队列初始化
void QueueInit(Queue* pq);

//队尾插入数据
void QueuePush(Queue* pq, QDataType x);

//队头删除数据
void QueuePop(Queue* pq);

//求队列的数据个数
int QueueSize(Queue* pq);

//取队头的数据
QDataType QueueFront(Queue* pq);

//取队尾的数据
QDataType QueueBack(Queue* pq);

//判空
bool QueueEmpty(Queue* pq);

//销毁队列
void QueueDesTroy(Queue* pq);

2. Queue.c 文件

c 复制代码
#define _CRT_SECURE_NO_WARNINGS
#include "Queue.h"




//队列初始化
void QueueInit(Queue* pq)
{
	assert(pq);
	pq->phead = NULL;
	pq->ptail = NULL;
	pq->size = 0;
}


//队尾插入数据
void QueuePush(Queue* pq, QDataType x)
{
	QNode* newnode = (QNode*)malloc(sizeof(QNode));

	if (newnode == NULL)
	{
		perror("malloc fail");
		return;
	}


	newnode->next = NULL;
	newnode->val = x;

	//队列中没有数据直接插入
	if (pq->ptail == NULL)
	{
		pq->phead = pq->ptail = newnode;
	}
	else//队列中有数据需要尾插
	{
		pq->ptail->next = newnode;
		pq->ptail = newnode; //newnode成为新的尾
	}

	pq->size++;//记录数据个数


}


//队头删除数据
void QueuePop(Queue* pq)
{
	assert(pq);
	assert(pq->size != 0); //保证链表不为空

	//防止队列中只有一个数据的情况下phead为空但是ptail为野指针
	if (pq->phead->next == NULL)
	{
		free(pq->phead);
		pq->phead = pq->ptail = NULL;
	}

	//多个节点
	else
	{
		QNode* next = pq->phead->next;
		free(pq->phead);
		pq->phead = next;
	}
	pq->size--;
}


//求队列的数据个数
int QueueSize(Queue* pq)
{
	assert(pq);

	return pq->size;
}



//取队头的数据
QDataType QueueFront(Queue* pq)
{
	assert(pq);
	assert(pq->phead);

	return pq->phead->val;
}

//取队尾的数据
QDataType QueueBack(Queue* pq)
{
	assert(pq);
	assert(pq->ptail);

	return pq->ptail->val;
}



//判空
bool QueueEmpty(Queue* pq)
{
	assert(pq);

	return pq->size == 0;
}


//销毁队列
void QueueDesTroy(Queue* pq)
{
	assert(pq);

	QNode* cur = pq->phead;
	while (cur)
	{
		QNode* next = cur->next;
		free(cur);

		cur = next;
	}
	pq->phead = pq->ptail = NULL;
	pq->size = 0;
}

3. test.c 文件

c 复制代码
#define _CRT_SECURE_NO_WARNINGS

#include "Queue.h"


int main()
{
	Queue q;
	QueueInit(&q);

	QueuePush(&q, 1);
	QueuePush(&q, 2);
	QueuePush(&q, 3);
	QueuePush(&q, 4);

	while (!QueueEmpty(&q))
	{
		printf("%d ", QueueFront(&q));//取队头的数据
		QueuePop(&q);
	}
	printf("\n");

	return 0;
}

3. 栈和队列经典面试题

3.1 用队列实现栈

题目描述:

示例:

题解:

c 复制代码
//单链表实现

typedef int QDataType;

typedef struct QueueNode
{
	struct QueueNode* next;
	QDataType val;
}QNode;





typedef struct Queue
{
	QNode* phead;
	QNode* ptail;
	int size;
}Queue;


//队列初始化
void QueueInit(Queue* pq);

//队尾插入数据
void QueuePush(Queue* pq, QDataType x);

//队头删除数据
void QueuePop(Queue* pq);

//求队列的数据个数
int QueueSize(Queue* pq);

//取队头的数据
QDataType QueueFront(Queue* pq);

//取队尾的数据
QDataType QueueBack(Queue* pq);

//判空
bool QueueEmpty(Queue* pq);

//销毁队列
void QueueDesTroy(Queue* pq);







//队列初始化
void QueueInit(Queue* pq)
{
	assert(pq);
	pq->phead = NULL;
	pq->ptail = NULL;
	pq->size = 0;
}


//队尾插入数据
void QueuePush(Queue* pq, QDataType x)
{
	QNode* newnode = (QNode*)malloc(sizeof(QNode));

	if (newnode == NULL)
	{
		perror("malloc fail");
		return;
	}


	newnode->next = NULL;
	newnode->val = x;

	//队列中没有数据直接插入
	if (pq->ptail == NULL)
	{
		pq->phead = pq->ptail = newnode;
	}
	else//队列中有数据需要尾插
	{
		pq->ptail->next = newnode;
		pq->ptail = newnode; //newnode成为新的尾
	}

	pq->size++;//记录数据个数


}


//队头删除数据
void QueuePop(Queue* pq)
{
	assert(pq);
	assert(pq->size != 0); //保证链表不为空

	//防止队列中只有一个数据的情况下phead为空但是ptail为野指针
	if (pq->phead->next == NULL)
	{
		free(pq->phead);
		pq->phead = pq->ptail = NULL;
	}

	//多个节点
	else
	{
		QNode* next = pq->phead->next;
		free(pq->phead);
		pq->phead = next;
	}
	pq->size--;
}


//求队列的数据个数
int QueueSize(Queue* pq)
{
	assert(pq);

	return pq->size;
}



//取队头的数据
QDataType QueueFront(Queue* pq)
{
	assert(pq);
	assert(pq->phead);

	return pq->phead->val;
}

//取队尾的数据
QDataType QueueBack(Queue* pq)
{
	assert(pq);
	assert(pq->ptail);

	return pq->ptail->val;
}



//判空
bool QueueEmpty(Queue* pq)
{
	assert(pq);

	return pq->size == 0;
}


//销毁队列
void QueueDesTroy(Queue* pq)
{
	assert(pq);

	QNode* cur = pq->phead;
	while (cur)
	{
		QNode* next = cur->next;
		free(cur);

		cur = next;
	}
	pq->phead = pq->ptail = NULL;
	pq->size = 0;
}







typedef struct 
{
    Queue q1;
    Queue q2;
} MyStack;


MyStack* myStackCreate() 
{
    MyStack* pst = (MyStack*)malloc(sizeof(MyStack));
    QueueInit(&(pst->q1));
    QueueInit(&(pst->q2));
    
    return pst;
}

void myStackPush(MyStack* obj, int x) 
{
    if(!QueueEmpty(&(obj->q1))) //队1不为空插入队1
    {
        QueuePush(&(obj->q1),x);
    }
    else //队1为空插入队2
    {
        QueuePush(&(obj->q2),x);
    }
}

int myStackPop(MyStack* obj) 
{
    //假设法
    Queue* Empty = &(obj->q1);//假设q1为空
    Queue* nonEmpty = &(obj->q2); //假设q2不为空

    if(!QueueEmpty(&(obj->q1)))//如果q1不为空(假设错误)
    {
        nonEmpty = &(obj->q1);
        Empty = &(obj->q2);
    }
    //把不为空的队列的前size-1个数据倒走,再删除最后一个就是栈顶元素
    while(QueueSize(nonEmpty) > 1)
    {
        QueuePush(Empty,QueueFront(nonEmpty));
        QueuePop(nonEmpty);
    }
    int top = QueueFront(nonEmpty);
    QueuePop(nonEmpty);

    return top;
}

int myStackTop(MyStack* obj) //取栈顶元素
{
    //队1不为空返回队1的队尾
    if(!QueueEmpty(&(obj->q1)))
    {
        return QueueBack(&(obj->q1));
    }
    else
    {
        return QueueBack(&(obj->q2));
    }
}

bool myStackEmpty(MyStack* obj) //判空:两个队列同时为空才是空
{
    return QueueEmpty(&(obj->q1)) && QueueEmpty(&(obj->q2));
}

void myStackFree(MyStack* obj) //销毁
{
    QueueDesTroy(&(obj->q1));
    QueueDesTroy(&(obj->q2));

    free(obj);
}

3.2 用栈实现队列

题目描述:

示例:

题解:

c 复制代码
// 支持动态增长的栈
typedef int STDataType;

typedef struct Stack
{
	STDataType* a;
	int top;       //栈顶
	int capacity;  //容量
}ST;


//初始化栈
void STInit(ST* pst);

// 入栈 
void STPush(ST* pst, STDataType x);

//出栈
void STPop(ST* pst);

//获取栈顶元素 
STDataType STTop(ST* pst);

//检测栈是否为空,如果为空返回非零结果,如果不为空返回0 
bool STEmpty(ST* pst);

// 获取栈中有效元素个数 
int STSize(ST* pst);

//销毁栈
void STDestroy(ST* pst);




//实现初始化栈
void STInit(ST* pst)
{
	assert(pst);
	pst->a = NULL;
	//top指向栈顶元素的下一个可用位置。
	pst->top = 0;
	/*
	top指向栈顶元素
	pst->top = -1;
	*/
	pst->capacity = 0;
}



// 实现入栈 
void STPush(ST* pst, STDataType x)
{
	assert(pst);
	//扩容
	if (pst->top == pst->capacity)
	{
		int newcapacity = pst->capacity == 0 ? 4 : pst->capacity * 2;
		STDataType* tmp = (STDataType*)realloc(pst->a, newcapacity * sizeof(STDataType));
		if (tmp == NULL)
		{
			perror("realloc fail");
			return;
		}
		pst->a = tmp;
		pst->capacity = newcapacity;


	}

	pst->a[pst->top] = x;
	pst->top++;
}

//实现出栈
void STPop(ST* pst)
{
	assert(pst);
	assert(pst->top > 0);//栈不为空才pop
	pst->top--;
}

//实现获取栈顶元素 
STDataType STTop(ST* pst)
{
	assert(pst);
	assert(pst->top > 0);

	return pst->a[pst->top - 1];
}

//检测栈是否为空,如果为空返回非零结果,如果不为空返回0 
bool STEmpty(ST* pst)
{
	assert(pst);
	
	return pst->top == 0;
}

// 获取栈中有效元素个数 
int STSize(ST* pst)
{
	assert(pst);
	return pst->top;
}

//实现销毁栈
void STDestroy(ST* pst)
{
	assert(pst);

	free(pst->a);
	pst->a = NULL;
	pst->top = pst->capacity = 0;

}


typedef struct 
{
    ST pushst;// 专门用于入队
    ST popst; // 专门用于出队
} MyQueue;


MyQueue* myQueueCreate() 
{
    MyQueue* obj = (MyQueue*)malloc(sizeof(MyQueue));
    STInit(&(obj->pushst));
    STInit(&(obj->popst));

    return obj;
}

int myQueuePeek(MyQueue* obj) 
{
    //取队头的数据
  
    if(STEmpty(&(obj->popst)))
    {
        //如果 popst 为空,就把 pushst 中的全部元素逐个弹出并压入 popst
        while(!STEmpty(&(obj->pushst)))
        {
            int top = STTop(&(obj->pushst));
            STPush(&(obj->popst),top);
            STPop(&(obj->pushst));
        }
    }
    // 此时 popst 栈顶就是队头元素
    return STTop(&(obj->popst));//获取popst中的栈顶数据也就是队头数据
}


void myQueuePush(MyQueue* obj, int x) 
{
    //入数据
    STPush(&(obj->pushst),x);
}

int myQueuePop(MyQueue* obj) 
{
    //出数据
    // 先获取队头元素(内部保证 popst 不为空)
    int front = myQueuePeek(obj);
    STPop(&(obj->popst));
    return front;
    /*
    myQueuePop 要做两件事:
    拿到队头元素(即最早入队的值)
    把这个元素从队列中删除
    而 myQueuePeek 正好完成了第一件事,
    并保证 popst 栈顶就是队头。
    因此 myQueuePop 可以直接调用 myQueuePeek 拿到队头值,
    然后只需要再从popst 弹栈一次即可。
    */
}



bool myQueueEmpty(MyQueue* obj) 
{
    //判空
    // 两个栈都为空时队列才为空
    return STEmpty(&(obj->popst)) && STEmpty(&(obj->pushst));
}

void myQueueFree(MyQueue* obj) 
{
    //销毁
    STDestroy(&(obj->popst));
    STDestroy(&(obj->pushst));

    free(obj);
}

3.3 设计循环队列

什么是循环队列?

循环队列是一种线性数据结构,其操作表现基于 FIFO(先进先出)原则并且队尾被连接在队首之后以形成一个循环。其空间大小是固定的,它也被称为"环形缓冲器"。

循环队列的一个好处是我们可以利用这个队列之前用过的空间。在一个普通队列里,一旦一个队列满了,我们就不能插入下一个元素,即使在队列前面仍有空间。但是使用循环队列,我们能使用这些空间去存储新的值。

可以理解为:有限空间,保证先进先出,重复使用。

题目描述:


示例:

思路:

可以使用链表和数组来实现

1. 数组实现:

这种实现方法有一个隐藏的问题:假溢出

c 复制代码
typedef struct
{
    int* a;
    int head; //指向头
    int tail; //指向尾的下一个位置
    int k;    //数据个数(实际上会多开一个空间)
} MyCircularQueue;


MyCircularQueue* myCircularQueueCreate(int k) 
{
    MyCircularQueue* obj = (MyCircularQueue*)malloc(sizeof(MyCircularQueue));

    //多开一个空间解决假溢出问题
    obj->a = (int*)malloc(sizeof(int)*(k+1));

    obj->head = 0;
    obj->tail = 0;
    obj->k = k;

    return obj;
}

bool myCircularQueueIsEmpty(MyCircularQueue* obj) 
{
    //判空
    return obj->head == obj->tail;
}

bool myCircularQueueIsFull(MyCircularQueue* obj) 
{
    //判满
    return (obj->tail+1) % (obj->k+1) == obj->head;
}


bool myCircularQueueEnQueue(MyCircularQueue* obj, int value) 
{
    //入队列
   if(myCircularQueueIsFull(obj))
        return false;
    //满了就返回false

    //没有满就入数据
    obj->a[obj->tail] = value;
    obj->tail++;

    obj->tail %= (obj->k+1);

    return true;
}

bool myCircularQueueDeQueue(MyCircularQueue* obj) 
{
    //删除数据
    if(myCircularQueueIsEmpty(obj))//为空不能删除
    {
        return false;
    }

    ++obj->head;
    obj->head %= (obj->k+1);
    return true;
}

int myCircularQueueFront(MyCircularQueue* obj) 
{
    //取头的数据
    if(myCircularQueueIsEmpty(obj))
    {
        return -1;
    }
    else
    {
        return obj->a[obj->head];
    }
}

int myCircularQueueRear(MyCircularQueue* obj) 
{
    //取尾的数据
    if(myCircularQueueIsEmpty(obj))
    {
        return -1;
    }
    else
    {
        //两种写法:

        return obj->a[(obj->tail - 1 + obj->k + 1) % (obj->k + 1)];
        //return obj->tail == 0 ? obj->a[obj->k] : obj->a[obj->tail-1];
    }

}


void myCircularQueueFree(MyCircularQueue* obj) 
{
    //释放空间

    free(obj->a);
    free(obj);
}

2. 链表实现


总结:两种方法各有优劣,如果是高频、性能敏感的场景(如内核网络缓冲区、音视频环形缓冲)几乎清一色用数组。而如果当你明确需要动态容量,且可以接受额外的内存分配开销和代码复杂度时,再考虑链表。

栈和队列是计算机世界里最规矩的公民:一个讲究先来后到,一个偏爱后来居上。我们已经摸清了它们的脾气,也学会了用数组和链表去驾驭它们。但如果数据不想排成一列,而是要分岔、要分层、要像族谱一样展开呢?
这时候,就该请出下一位主角------二叉树。

下一篇,让我们走进树的领地,去看一看这种"非线性的美"

相关推荐
WL_Aurora1 小时前
Python 算法基础篇之查找算法(一):顺序查找、二分查找与插值查找
开发语言·python·算法
ChoSeitaku1 小时前
06_可变参数_递归_类和对象_封装
java·数据结构·算法
-To be number.wan1 小时前
算法日记 | 动态规划(初级)
算法·动态规划
_深海凉_1 小时前
LeetCode热题100-二叉搜索树中第 K 小的元素
算法·leetcode·职场和发展
图码1 小时前
文本两端对齐算法详解:从LeetCode到实际应用
数据结构·图像处理·算法·leetcode·生成对抗网络·面试·职场和发展
yoyo_zzm1 小时前
六大编程语言核心差异全解析
c语言·c++·spring boot·php
liu****1 小时前
第16届国赛蓝桥杯大赛C/C++大学C组
c语言·数据结构·c++·算法·蓝桥杯
码完就睡1 小时前
C语言——结构体的内存存储规则
c语言·开发语言
沈浩(种子思维作者)1 小时前
物理的本质是数学,还是数学只是描述物理的方便之语?
人工智能·python·算法