数据结构与算法


播放链接: https://www.bilibili.com/video/BV1K7411q7he?p=2.

数据结构定义

数据结构就是帮我们解决如何组织和存储数据的方式。

数据结构主要是研究非数值计算问题的程序中操作对象以及他们之间的关系,不是研究复杂的算法的。

算法的基本概念

算法是特定问题求解步骤的描述
对于算法而言,最重要的是思想。

算法与数据结构的关系

算法是为了解决实际问题而设计的。
数据结构是算法需要处理的问题载体。
一个好的算法是需要依赖于数据结构的。

算法效率的度量

原文链接: https://blog.csdn.net/weixin_40136018/article/details/81365198.

时间复杂度的分类

1、分析算法时,存在几种可能的考虑:

算法完成工作最少需要多少基本操作,即最优时间复杂度

算法完成工作最多需要多少基本操作,即最坏时间复杂度

算法完成工作平均需要多少基本操作,即平均时间复杂度

对于最优时间复杂度,其价值不大,因为它没有提供什么有用信息,其反映的只是最乐观最理想的情况,没有参考价值。

对于平均时间复杂度,是对算法的一个全面评价,因此它完整全面的反映了这个算法的性质。但另一方面,这种衡量并没有保证,不是每个计算都能在这个基本操作内完成。而且,对于平均情况的计算,也会因为应用算法的实例分布可能并不均匀而难以计算。

对于最坏时间复杂度,提供了一种保证,表明算法在此种程度的基本操作中一定能完成工作。

因此,我们主要关注算法的最坏情况,亦即最坏时间复杂度。

利用大O表示法计算算法的时间复杂度

常见的时间复杂度

在这里插入图片描述
注意,log2n 换算成 logn
分析如下:
根据换底公式,logan 可换算成 logcn / logca。
再根据计算时间复杂度有一个原则:“如果算法复杂度的最高次项的乘数不是1,直接舍去。”,因此,又可换算成 logcn,又c为任意常数,直接取为1。所以最后换算为logn。

常见的时间复杂度之间的关系

在这里插入图片描述
所消耗的时间从小到大
O(1) < O(logn) < O(n) < O(nlogn) < O(n2) < O(n3) < O(2n) < O(n!) < O(nn)

时间复杂度的几条基本计算规则

1、判断一个算法的效率时,往往只需要关注操作数量的最高次项,其他次要项和常数项可以忽略。
2、只有常数项,记作1。
3、log2n 换算成 logn
4、如果算法复杂度的最高次项的乘数不是1,直接舍去。

大O表示法练习题

在这里插入图片描述
O(3) ==》O(1)
在这里插入图片描述
O(n)
在这里插入图片描述
int count = 1,(count * 2 * 2 … *2中的count直接带入1)
假设

第 1 次 :21 < n
第 2 次 :22 < n
第 3 次 :23 < n


第 x 次 :2x > n
则 x = log2n
因此,O( log2n) ==》O( logn)
在这里插入图片描述
O( n2)
在这里插入图片描述
i = 0,for (j=i; j<n; j++) 执行 n 次
i = 1,for (j=i; j<n; j++) 执行 n - 1次
i = 2,for (j=i; j<n; j++) 执行 n - 2 次

i = n,for (j=i; j<n; j++) 执行 0 次

因此,
n + (n-1) + … + 1 + 0 = n (n + 1) / 2
从而时间复杂度为 O( n2)

线性表

数据结构包括数据元素的逻辑结构、存储结构和运算。
线性表是一种逻辑结构。而非存储结构。

线性表的定义

在这里插入图片描述

线性表的特点

在这里插入图片描述

线性表的性质

在这里插入图片描述

线性表的九种基本操作

在这里插入图片描述

线性表的两种存储方式 — 顺序存储和链式存储

顺序存储—顺序表

线性表的顺序存储又称顺序表

顺序表的定义

用一段地址连续的存储单元依次存储线性表的数据元素。
在这里插入图片描述

如何用程序设计语言来描述顺序表?

顺序表可以用数组来实现,而描述数组有两种方式,从而可确定两种分配方式,也就可以有两种方式来描述顺序表。即数组动态分配和数组静态分配。
在这里插入图片描述
对于数组动态分配,只要根据第一个数据元素的地址和类型,就可以计算出任意数据元素的位置。也就是说只要定义了第一个数据元素的指针,就可以描述整个顺序表。而对于ElemType *data;仅仅只是一个地址,而没有确切的空间,因此在使用时还需要进行动态申请空间。

使用数组来描述顺序表时需要注意:
在这里插入图片描述
顺序表的标号是从1开始的,而数组下标则是从0开始的。

线性表顺序存储案例
动态数组

拿到一块数据空间的首地址,就相当于拿到这块数据空间中的数据。

My_Dynamic_Array.c

#include <stdio.h>
#include <stdlib.h> //system("pause");需要用到
#include <string.h>

#include "DynamicArray.h"

void test01()
{
	//初始化一个动态数组
	Dynamic_Array* myArray = Init_Dynamic_Array();
	//打印容量
	printf("数组容量:%d\n", GetCapacity_Array(myArray));
	//打印数组大小
	printf("数组大小:%d\n", GetSize_Array(myArray));

	//插入元素
	for (int i = 0; i < 30; i++)
	{
		PushBack_Array(myArray, i);
	}

	//删除--根据指定位置删除数据
	RemoveByPos_Array(myArray, 5);

	//打印容量
	printf("数组容量:%d\n", GetCapacity_Array(myArray));
	//打印数组大小
	printf("数组大小:%d\n", GetSize_Array(myArray));

	//打印
	Print_Array(myArray);

	//释放动态数组的内存
	FreeSpace_Array(myArray);
}

int main(void)
{
	test01();
	system("pause");
	return 0;
}

Dynamic_Array.h

#ifndef DYNAMIC_ARRAY_H
#define DYNAMIC_ARRAY_H

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

//动态增长内存,策略是将存放数据的内存放到堆上。
//现有一个动态数组,如果只能存放5个元素,现在想存放6个,就需要执行“申请内存 -> 拷贝数据 -> 释放内存”
//那现在我又想存放7个元素,那就必须再次执行“申请内存 -> 拷贝数据 -> 释放内存”这三步,因此效率很低。
//容量capacity,表示我的这块内存空间一共可以存放多少元素。
//size概念,记录当前数组中具体的元素个数。 

//动态数组结构体定义
typedef struct DYNAMICARRAY
{
	int* pAddr;//存放数据的地址
	int size;//当前有多少个元素
	int capacity;//容量,我容器当前最大能容纳多少元素
}Dynamic_Array;

//写一系列对DYNAMICARRAY结构体操作的函数

//写API时,要通过与其他函数通过后缀进行区分,比如这里都是以Array结尾

//初始化动态数组
Dynamic_Array* Init_Dynamic_Array();

//插入--因为默认是从尾部插入,所以不需要指定位置,因此也就不需要传指定位置的参数。
void PushBack_Array(Dynamic_Array* arr,int value);

//删除--根据指定位置删除数据
void RemoveByPos_Array(Dynamic_Array* arr, int pos);

//删除--删除指定数据
void RemoveByValue_Array(Dynamic_Array* arr, int value);

//查找
int Find_Array(Dynamic_Array* arr, int value);

//打印
void Print_Array(Dynamic_Array* arr);

//清空数组
void Clear_Array(Dynamic_Array* arr);

//获得动态数组容量
int GetCapacity_Array(Dynamic_Array* arr);

//获得当前动态数组元素的个数
int GetSize_Array(Dynamic_Array* arr);

//根据位置获得某个位置元素
int GetByPos_Array(Dynamic_Array* arr, int pos);

//释放动态数组的内存
void FreeSpace_Array(Dynamic_Array* arr);

#endif // !DYDAMIC_ARRAY

Dynamic_Array.c

#include "DynamicArray.h"

Dynamic_Array* Init_Dynamic_Array() {

	//申请内存
	Dynamic_Array* myArray = (Dynamic_Array*)malloc(sizeof(Dynamic_Array));
	if (myArray == NULL)
	{
		return -1;
	}
	//初始化
	myArray->size = 0;
	myArray->capacity = 20;
	myArray->pAddr = (int* )malloc(sizeof(int)*(myArray->capacity));
	//等价于定义了int myArray->pAddr[myArray->capacity];
	if (myArray->pAddr == NULL)
	{
		return -2;
	}

	return myArray;
}
//插入--因为默认是从尾部插入,所以不需要指定位置,因此也就不需要传指定位置的参数。
void PushBack_Array(Dynamic_Array* arr, int value) 
{
	if (arr == NULL)
	{
		return NULL;
	}

	//判断空间是否足够
	if (arr->size == arr->capacity)
	{
		//第一步:申请一块更多的内存空间,这里设定新空间是旧空间的两倍
		int* newSpace = (int* )malloc(sizeof(int) * arr->capacity * 2);

		if (newSpace == NULL)
		{
			return -2;
		}
		//第二步:拷贝数据到新的空间
		//一个字节一个字节的拷贝
		memcpy(newSpace,arr->pAddr,arr->capacity*sizeof(int)); 
		
		//第三步:释放旧空间的内存
		free(arr->pAddr);
		//第四步:更新容量
		arr->capacity = arr->capacity * 2;
		arr->pAddr = newSpace;
	}

	//插入新元素--数组从零开始,所以不需要加1。
	arr->pAddr[arr->size] = value;
	arr->size++;
}
//删除--根据指定位置删除数据
void RemoveByPos_Array(Dynamic_Array* arr, int pos) 
{
	if (arr == NULL)
	{
		return NULL;
	}

	//判断位置是否有效
	if (pos < 0 || pos >= arr->size)
	{
		printf("位置错误.\n");
		return;
	}

	//删除元素
	for (int i = pos; i < arr->size - 1; i++)
	{
		arr->pAddr[i] = arr->pAddr[i+1];
	}
	 
	arr->size--;
		
}
//删除--删除指定数据
void RemoveByValue_Array(Dynamic_Array* arr, int value) 
{
	//找到值的位置,遍历数组
	//只删除第一次寻找到的值,如果顺序表中有多个相同值的元素,只需多次调用即可。
	int pos = Find_Array(arr, value);
	//根据位置删除数据
	RemoveByPos_Array(arr, pos);
}

//查找
int Find_Array(Dynamic_Array* arr, int value) 
{
	if (arr == NULL)
	{
		return NULL;
	}

	//找到值的位置,遍历数组
	int pos = -1;
	for (int i = 0; i < arr->size; i++)
	{
		if (arr->pAddr[i] == value)
		{
			pos = i;
			break;
		}
	}
 
	return pos;
}
//打印
void Print_Array(Dynamic_Array* arr) {

	if (arr == NULL)
	{
		return NULL;
	}

	for (int i = 0; i < arr->size; i++)
	{
		printf("%d ", arr->pAddr[i]);
	}
	printf("\n");
}

//清空数组
void Clear_Array(Dynamic_Array* arr) {
	if (arr == NULL)
	{
		return NULL;
	}

	//我们往pAddr所指向的空间插入值是根据size插入的,以前的值直接覆盖掉。
	arr->size = 0;
}

//获得动态数组容量
int GetCapacity_Array(Dynamic_Array* arr) {
	if (arr == NULL)
	{
		return NULL;
	}
	return arr->capacity;
}

//获得当前动态数组元素的个数
int GetSize_Array(Dynamic_Array* arr) {
	if (arr == NULL)
	{
		return NULL;
	}

	return arr->size;
}

//根据位置获得某个位置元素
int GetByPos_Array(Dynamic_Array* arr, int pos) {
	if (arr == NULL)
	{
		return NULL;
	}

	return arr->pAddr[pos];
}

//释放动态数组的内存
void FreeSpace_Array(Dynamic_Array* arr) {
	
	if (arr == NULL)
	{
		return NULL;
	}

	if (arr->pAddr != NULL)
	{
		arr->pAddr == NULL;
	}

	if (arr != NULL)
	{
		free(arr);
		arr = NULL;
	}
}
动态数组如何进行删除

在这里插入图片描述

链式存储—链表

在这里插入图片描述

针对链表操作的API的框架建立

新建SListAPI.h文件

#ifndef SLISTAPI_H
#define SLISTAPI_H

#include <stdio.h>
#include <stdlib.h> //system("pause");需要用到
#include <string.h>

//定义链表结点
typedef struct SListNode
{	
	void* data;
	struct SListNode* next;
}SListNode;

//定义链表结构体
typedef struct SLinkList
{
	SListNode* head;
	int size;
	//需要容量的变量吗?
	//根据所需去申请内存,不需要提前分配好一块连续的内存。
}SLinkList;

//打印的函数指针
typedef void(*PRINTLINKNODE)(void*);

//1、初始化链表
SLinkList* Init_SLinkList();

//2、在指定位置插入结点
void Insert_SLinkList(SLinkList* list,int pos, void* data);

//3、删除指定位置的结点
void RemoveByPos_SLinkList(SLinkList* list,int pos);

//4、获得链表的长度
int Size_SLinkList(SLinkList* list);

//5、查找并返回链表中数据为data的首个结点的下标。
int Find_SLinkList(SLinkList* list,void* data);

//6、打印链表结点中的数据
void Print_SLinkList(SLinkList* list, PRINTLINKNODE print);

//7、返回第一个有效数据结点
void* Front_SLinkList(SLinkList* list);

//8、释放链表内存
void FreeSpace_SLinkList(SLinkList* list);

#endif
针对链表操作的API的框架实现

定义链表结点

无类型指针可以指向任何类型的数据。

typedef struct SListNode
{	
	void* data;
	struct SListNode* next;
}SListNode;

1、初始化链表

我们只需要维护这个链表结构体,对这个结构体进行操作即可。

//初始化链表
//我们新建了一个链表结构体,只需要维护这个链表结构体,对这个结构体进行操作即可。
SLinkList* Init_SLinkList()
{
	SLinkList* list = (SLinkList*)malloc(sizeof(SLinkList));
	//初始化链表

	//1、首先需要给它一个头结点,头结点不保存数据信息
	list->head = (SListNode *)malloc(sizeof(SListNode));
	list->head->data = NULL;
	list->head->next = NULL;

	//2、size初始值为0
	list->size = 0;

	return list;
}

2、在链表的指定位置插入结点

友好的处理,pos越界或者小于0,直接插入到尾部。
链表的位置从开始。
注意:找节点怎么找?
插入新结点后,size别忘了加1

画图分析:
在这里插入图片描述

//在指定位置插入结点
void Insert_SLinkList(SLinkList* list,int pos, void* data)
{
	if (list == NULL)
	{
		return;
	}
	if (data == NULL)
	{
		return;
	}

	//友好的处理,pos越界或者小于0,直接插入到尾部
	if (pos <0|| pos >list->size)
	{
		pos = list->size;
	}

	//创建新的结点
	SListNode* pNewNode = (SListNode* )malloc(sizeof(SListNode));
	//给新结点初始化
	pNewNode->data = data;
	pNewNode->next = NULL;

	//找结点
	//定义辅助指针变量
	SListNode* pCurPos = list->head;
	for(int i=0;i<pos;i++)
	{
		pCurPos = pCurPos->next;
	}
	
	//定义两个辅助变量并设置初始值
	SListNode* pCurNode = pCurPos;

	//将新结点插入链表,顺序不能乱
	pNewNode->next = pCurNode->next;
	pCurNode->next = pNewNode ;
	
	list->size++;
}

3、在链表的指定位置删除结点

pos越界(pos >= size,因为等于size表示指向NULL)或者小于0,直接返回。

删除结点后,size别忘了减1

画图分析:
在这里插入图片描述

//删除指定位置的结点
void RemoveByPos_SLinkList(SLinkList* list,int pos)
{
	if (list == NULL)
	{
		return;
	}

	if (pos <=0 || pos >=list->size)
	{
		return;
	}

	//定位到要删除结点的前一个结点
	//定义辅助指针变量
	SListNode* pPrePos = list->head;
	for (int i = 1; i < pos; i++)
	{
		pPrePos = pPrePos->next;
	}
	//定义两个辅助变量并设置初始值
	SListNode* pPreNode = pPrePos;
	SListNode* pDelNode = pPreNode->next;

	//删除结点
	pPreNode->next = pDelNode->next;
	//释放被删除结点的内存
	free(pDelNode);

	list->size--;
}

4、获得链表的长度(有效数据的个数)

//获得链表的长度(有效数据的个数)
int Size_SLinkList(SLinkList* list)
{
	if (list == NULL)
	{
		return;
	}
	return list->size;
}

5、查找并返回链表中数据为data的首个结点的下标。

链表中有效数据的结点是head结点的下一个结点

//查找并返回链表中数据为data的首个结点的下标。
int Find_SLinkList(SLinkList* list, void* data)
{
	if (list == NULL)
	{
		return;
	}
	if (data == NULL)
	{
		return;
	}

	SListNode* pCurNode = list->head->next;
	int pos = 1;

	//遍历查找
	while (pCurNode != NULL)
	{
		if (pCurNode->data == data)
		{
			break;
		}
		pos++;
		pCurNode = pCurNode->next;
	}
	return pos;
}

6、打印链表结点中的数据

//打印链表结点中的数据
void Print_SLinkList(SLinkList* list, PRINTLINKNODE print) 
{
	if (list == NULL)
	{
		return;
	}
	printf("head -> ");
	//辅助指针变量
	SListNode* pCurNode = list->head->next;
	while (pCurNode!=NULL)
	{
		print(pCurNode->data);
		pCurNode = pCurNode->next;
	}
	printf("NULL.\n");
}

7、返回第一个有效数据结点中的数据

//返回第一个有效数据结点中的数据
void* Front_SLinkList(SLinkList* list)
{
	return list->head->next->data;
}

8、释放链表内存

先释放各个结点的内存,再释放整个链表的内存。

//释放链表内存
void FreeSpace_SLinkList(SLinkList* list)
{
	if (list == NULL)
	{
		return;
	}
	//定义辅助指针结点变量来标识每次要释放的结点
	SListNode* pCurNode = list->head;
	//缓存下一个要释放的结点
	SListNode* pNextNode = NULL;
	//记录释放结点次数
	int i = 0;

	while (pCurNode != NULL)
	{
		i++;
		//缓存下一个要释放的结点
		pNextNode = pCurNode->next;
		free(pCurNode);
		pCurNode = pNextNode;
	}
	printf("释放结点个数为:%d.\n", i);

	//释放链表内存
	list->size = 0;
	free(list);
}
针对链表操作的API的框架测试
#include <stdio.h>
#include <stdlib.h> //system("pause");需要用到
#include <string.h>

#include "SListAPI.h"

//自定义数据类型
typedef struct PERSON
{
	char name[64];
	int age;
	int score;
}PERSON;

//打印函数
void MyPrint(void* data)
{
	PERSON* p = (PERSON*)data;
	printf("Name:%s, Age = %3d , score = %3d. ->\n", p->name, p->age, p->score);
}

int main(void)
{
	//1、创建链表
	SLinkList* mylist = Init_SLinkList();

	//自定义数据类型
	//静态数组	
	PERSON person[5] = {
		{ "aaa",18,100},
		{ "bbb",19,99 },
		{ "ccc",20,87 },
		{ "ddd",21,59 },
		{ "eee",22,68 }
	};

	//2、向链表中插入数据
	for (int i = 0; i < 5; i++)
	{
		Insert_SLinkList(mylist, i, &person[i]);
	}

	Print_SLinkList(mylist, MyPrint);

	//3、删除指定位置的结点
	RemoveByPos_SLinkList(mylist, 1);
	printf("删除后:\n");
	Print_SLinkList(mylist, MyPrint);

	//4、获得链表的长度(有效数据的个数)
	printf("链表的长度为:%d.\n", Size_SLinkList(mylist));

	//5、查找并返回链表中数据为data的首个结点的下标。
	//不能这么查找,查不到。
	/*
	PERSON a = { "aaa",18,100 };
	或
	PERSON a = person[1];
	int pos = Find_SLinkList(mylist, &a);
	printf("pos为:%d.\n", pos);
	*/

	//只能这样查
	//int pos = Find_SLinkList(mylist, &person[1]);
	//printf("pos为:%d.\n", pos);
	//或
	PERSON* a = &person[1];
	int pos = Find_SLinkList(mylist, a);
	printf("pos为:%d.\n", pos);

	//7、返回第一个有效数据结点中的数据
	PERSON* ret = (PERSON*) Front_SLinkList(mylist);
	printf("第一个有效数据结点中的数据为:Name:%s, Age = %3d , score = %3d.\n", ret->name, ret->age, ret->score);

	//8、释放链表内存
	FreeSpace_SLinkList(mylist);

	mylist = NULL;
	
	printf("\n");
	system("pause");
	return 0;
}
12篇学通csharp网络编程——第四篇 TCP应用编程 12篇学通csharp网络编程——第三篇 HTTP应用编程(下) 12篇学通csharp网络编程——第二篇 HTTP应用编程(上) 12篇学通csharp网络编程——第一篇 基础之进程线程 Lucene(1)lucene,你也会(7篇)——第一篇 快速入门 MongoDB(8)8天学通MongoDB——第八天 驱动实践 8天学通MongoDB——第七天 运维技术 8天学通MongoDB——第六天 分片技术 8天学通MongoDB——第五天 主从复制 8天学通MongoDB——第四天 索引操作 8天学通MongoDB——第三天 细说高级操作 8天学通MongoDB——第二天 细说增删查改 8天学通MongoDB——第一天 基础入门 UML系列(4)团队沟通利器之UML——类图 团队沟通利器之UML—— 序列图 团队沟通利器之UML——用例图 团队沟通利器之UML——活动图 wcf系列(5)wcf系列学习5天速成——第五天 服务托管 wcf系列学习5天速成——第四天 wcf之分布式架构 wcf系列学习5天速成——第三天 事务的使用 wcf系列5天速成——第二天 binding的使用(2) wcf系列5天速成——第一天 binding的使用(1) wpf系列(8)8天入门wpf—— 第八天 最后的补充 8天入门wpf—— 第七天 画刷 8天入门wpf—— 第六天 细说控件 8天入门wpf—— 第五天 数据绑定 8天入门wpf—— 第四天 模板 8天入门wpf—— 第三天 样式 8天入门wpf—— 第二天 xaml详解 8天入门wpf—— 第一天 基础概念介绍 并行开发(8)8天玩转并行开发——第八天 用VS性能向导解剖你的程序 8天玩转并行开发——第七天 简要分析任务与线程池 8天玩转并行开发——第六天 异步编程模型 8天玩转并行开发——第五天 同步机制(下) 8天玩转并行开发——第四天 同步机制(上) 8天玩转并行开发——第三天 plinq的使用 8天玩转并行开发——第二天 Task的使用 8天玩转并行开发——第一天 Parallel的使用 多线程系列(5)5天不再惧怕多线程——第五天 线程池 5天不再惧怕多线程——第四天 信号量 5天不再惧怕多线程——第三天 互斥体 5天不再惧怕多线程——第二天 锁机制 5天不再惧怕多线程——第一天 尝试Thread 经典算法专题(21)经典算法题每日演练——第二十一题 十字链表 经典算法题每日演练——第二十题 三元组 经典算法题每日演练——第十九题 双端队列 经典算法题每日演练——第十八题 外排序 经典算法题每日演练——第十七题 Dijkstra算法 经典算法题每日演练——第十六题 Kruskal算法 经典算法题每日演练——第十五题 并查集 经典算法题每日演练——第十四题 Prim算法 经典算法题每日演练——第十三题 赫夫曼树 经典算法题每日演练——第十二题 线段树 经典算法题每日演练——第十一题 Bitmap算法 经典算法题每日演练——第十题 树状数组 经典算法题每日演练——第九题 优先队列 经典算法题每日演练——第八题 AC自动机 经典算法题每日演练——第七题 KMP算法 经典算法题每日演练——第六题 协同推荐SlopeOne 算法 经典算法题每日演练——第五题 字符串相似度 经典算法题每日演练——第四题 最长公共子序列 经典算法题每日演练——第三题 猴子吃桃 经典算法题每日演练——第二题 五家共井 经典算法题每日演练——第一题 百钱买百鸡 开发利器系列(1)介绍一个小工具 Linqer 那点所谓的分布式(2)那点所谓的分布式——memcache 那点所谓的分布式——redis 树结构专题(5)6天通吃树结构—— 第五天 Trie树 6天通吃树结构—— 第四天 伸展树 6天通吃树结构—— 第三天 Treap树 6天通吃树结构—— 第二天 平衡二叉树 6天通吃树结构—— 第一天 二叉查找树 算法速成系列(15)算法系列15天速成——第十五天 图【下】(大结局) 算法系列15天速成——第十四天 图【上】 算法系列15天速成——第十三天 树操作【下】 算法系列15天速成——第十二天 树操作【中】 算法系列15天速成——第十一天 树操作(上) 算法系列15天速成——第十天 栈 算法系列15天速成——第九天 队列 算法系列15天速成——第八天 线性表【下】 算法系列15天速成——第七天 线性表【上】 算法系列15天速成——第六天 五大经典查找【下】 算法系列15天速成——第五天 五大经典查找【中】 算法系列15天速成——第四天 五大经典查找【上】 算法系列15天速成——第三天 七大经典排序【下】 算法系列15天速成——第二天 七大经典排序【中】 算法系列15天速成——第一天 七大经典排序【上】 算法洗脑系列(8)算法洗脑系列(8篇)——第八篇 概率思想 算法洗脑系列(8篇)——第七篇 动态规划 算法洗脑系列(8篇)——第六篇 回溯思想 算法洗脑系列(8篇)——第五篇 分治思想 算法洗脑系列(8篇)——第四篇 枚举思想 算法洗脑系列(8篇)——第三篇 贪心思想 算法洗脑系列(8篇)——第二篇 递归思想 算法洗脑系列(8篇)——第一篇 递推思想 天籁数学(3)天籁数学——数列篇(3) 天籁数学——数列篇(2) 天籁数学——数列篇(1) 图形图像(1)玩玩图形图像——第一篇:图片灰度化 小爬虫系列(4)玩玩小爬虫——抓取时的几个小细节 玩玩小爬虫——抓取动态页面 玩玩小爬虫——试搭小架构 玩玩小爬虫——入门
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

xuechanba

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值