首页 > 其他分享 >数据结构顺序表实现通讯录

数据结构顺序表实现通讯录

时间:2024-05-24 17:24:55浏览次数:22  
标签:ps arr 顺序 联系人 通讯录 printf 数据结构 con

目录

1. 前言:

2.通讯录项目的创建

3. 通讯录的实现

3.1 通讯录的初始化

3.2 通讯录的销毁

3.3 通讯录添加数据

3.4 通讯录查找数据

3.5 通讯录展示数据 

 3.6 通讯录删除数据

 3.7 通讯录修改数据

 4. 通讯录完整代码

4.1 test.c

4.2 SeqList.h

4.3 SeqList.c

 4.4 Contact.h

4.5 Contact.c


1. 前言:

想必大家也都用过手机上面的通讯录吧,手机上面的通讯录有着许多的功能,可以新建联系人,删除联系人,查找联系人,修改联系人等等一系列的功能,我们其实也可以使用数据结构中的顺序表来模拟实现一下这样功能。

本章主要讲述利用顺序表来实现一个简单的通讯录项目

2.通讯录项目的创建

通讯录项目在以顺序表的结构上面会多增加两个文件

通讯录头文件Contact.h和通讯录源文件Contact.h

我们之前使用顺序表数据是用的整型,但是我们想要实现一个通讯录,那么单单一个整型的数据肯定是不能完成的,通讯录里面的数据要有联系人姓名,联系人性别,联系人年龄,联系人电话,联系人住址,一想到需要这么多数据,我们不免就会用到结构体了。

Contact.h 创建结构体

//这里我们用#define来定义大小,方便后续修改
#define NAME_MAX  20
#define GENDER_MAX   20
#define TEL_MAX 20
#define ADDR_MAX 100

typedef struct SeqList son;//这里我们把顺序表重新命名为son

//创建一个存放联系人数据的结构体
typedef struct personinfo
{
	char name[NAME_MAX];//姓名
	char gender[GENDER_MAX];//性别
	int age;//年龄
	char tel[TEL_MAX];//电话
	char addr[ADDR_MAX];//住址
}person;

我们在SeqList.h里面包含通讯录头文件,并把int类型修改为struct类型

这里我们的test.c里面也包含通讯录头文件

 这里我们把int类型修改成struct类型之后,在SeqList.c里面我们的查找方法和打印方法就会报错,这时我们只需要注释掉这两个方法就好了。

最后在Contact.c里面包含通讯录头文件和顺序表头文件

3. 通讯录的实现

3.1 通讯录的初始化

思路:

我们在之前写顺序表的时候,写过初始化的方法,我们可以直接调用这个方法为我们的通讯录进行初始化

Contact.c

void ConInit(son* con)
{
	assert(con);//判断con是否为空指针
	//这里我们在顺序表里面就写过初始化的方法,我们直接调用,这个就是初始化方法的复用
	SLInit(con);
}

通讯录初始化的测试

3.2 通讯录的销毁

思路:

我们写顺序表时也写过销毁方法,我们这里直接调用方法就可以了

 Contact.c

void ConDestroy(son* con)
{
	assert(con);//判断con是否为空指针
	SLDestroy(con);//顺序表销毁方法复用
}

通讯录的销毁测试

3.3 通讯录添加数据

思路:

在我们对通讯录添加数据之前,我们需要检查一下空间大小是否足够,不够申请空间,调用我们之前顺序表的空间检查,增容函数,然后,我们创建一个结构体,把我们想要添加的联系人数据都写上,最后我们在写顺序表时有三种插入方法,我们这里选择尾插,调用尾插方法,将创建的结构体变量传过去,这样通讯录添加数据就完成了

Contact.h

void ConAdd(son* con)
{
	assert(con);//判断con是否为空指针
	//在我们添加联系人数据之前,我们需要检查空间大小
	SLCheckCapacity(con);
	//这里我们创建一个结构体变量
	person p;
	//我们在这里将我们添加联系人的数据写入到p结构体里面
    //这里姓名,性别,电话,住址都是数组,数组名是首元素的地址,所以不需要取地址符号
	printf("请输入你要添加联系人的姓名\n");
	scanf("%s", p.name);
	printf("请输入你要添加联系人的性别\n");
	scanf("%s", p.gender);
	printf("请输入你要添加联系人的年龄\n");
	scanf("%d", &(p.age));//age是整型,需要取地址
	printf("请输入你要添加联系人的电话\n");
	scanf("%s", p.tel);
	printf("请输入你要添加联系人的住址\n");
	scanf("%s", p.addr);
	//在这里我们写了3中插入数据的方法,随便调用一种即可,我们选择尾插
    printf("联系人新建成功!\n");
	SLPushBack(con, p);//我们将p结构体尾插进去
}

通讯录添加数据测试

3.4 通讯录查找数据

思路:

我们一般想要在通讯录里面查找联系人一般直接就是查找联系人的姓名,这里我们也是这样思考的,我们直接创建一个字符数组来接收要查找的联系人姓名,然后再通讯录里面利用循环的方法进行查找,然后找到联系人了,我们就把它的数据都打印出来,这样通讯录查找数据就完成了。

Contact.c

int Confind(son* con,char name[])
{
	int i = 0;
	//循环查找
	for (i = 0; i < con->size; i++)
	{
		if (strcmp(con->arr[i].name,name) == 0)
		{
			return i;
		}
	}
	return -1;
}
void ConFind(son* con)
{
    assert(con);//判断con是否为空指针
	//在这里我们创建一个字符数组用来接收输入要查找的联系人姓名
	char name[NAME_MAX];
	printf("请输入你要查找的联系人姓名\n");
	scanf("%s",name);
	//写一个查找函数,在通讯录里面循环查找
	int find =Confind(con,name);
	//查找到了返回下标,如果没有查找到返回不是下标的数
	if (find < 0)
	{
		printf("你要查找的联系人不存在!\n");
	}
	else
	{   //当我们查找到了,就把它打印出来
		printf("姓名 性别 年龄 电话 住址\n");
		printf("%4s %4s %4d %4s %4s\n",
			con->arr[find].name,
			con->arr[find].gender,
			con->arr[find].age,
			con->arr[find].tel,
			con->arr[find].addr);
	}
}

 通讯录查找数据测试:

3.5 通讯录展示数据 

思路:

我们直接利用循环来打印通讯里里面的所有联系人数据

Contact.c

void ConShow(son* con)
{
	assert(con);//判断con是否为空指针
	printf("姓名 性别 年龄 电话 住址\n");
	int i = 0;
	for (i = 0; i < con->size; i++)
	{
		printf("%4s %4s %4d %4s %4s\n",
			con->arr[i].name,
			con->arr[i].gender,
			con->arr[i].age,
			con->arr[i].tel,
			con->arr[i].addr);
	}
}

通讯录展示数据测试

 3.6 通讯录删除数据

思路:

这个删除数据,首先我们肯定要考虑到通过联系人姓名进行删除联系人数据,我们要先创建一个字符数据来接收我们要删除的联系人姓名,然后利用前面写过的查找函数,来判断联系人是否在通讯录里面,最后,我们利用在前面顺序表里面写过的指定位置删除的方法进行删除,这样通讯录删除数据就完成了

Contact.c

void ConDel(son* con)
{
	assert(con);//判断con是否为空指针
	char name[NAME_MAX];//创建变量来接收要删除的联系人姓名
	printf("请输入你要删除的联系人姓名\n");
	scanf("%s", name);
	//这里先调用一下查找函数,看看联系人是否存在
	int find = Confind(con,name);
	if (find < 0)
	{
		printf("你要删除的联系人不存在\n");
	}
	else
	{
        printf("联系人删除成功!\n");
		//这里我们在前面顺序表里面写过在指定位置删除,进行复用
		SLEarse(con,find);
	}
}

通讯录删除数据测试

 

 3.7 通讯录修改数据

思路:

这里,我们是要对在通讯录里面的联系人数据进行修改,那我们肯定先查找要修改的联系人姓名在不在通讯录里面,利用查找函数,如果存在,直接进行修改

Contact.

void ConModify(son* con)
{
	assert(con);//判断con是否为空指针
	char name[NAME_MAX];//创建变量来接收要修改的联系人姓名
	printf("请输入你要修改的联系人姓名\n");
	scanf("%s",name);
	//这里先调用一下查找函数,看看联系人是否存在
	int find = Confind(con, name);
	if (find < 0)
	{
		printf("你要修改的联系人不存在\n");
	}
	else
	{   //我们直接在这里进行修改
		printf("请输入你要新建联系人的姓名\n");
		scanf("%s", con->arr[find].name);
		printf("请输入你要新建联系人的性别\n");
		scanf("%s", con->arr[find].gender);
		printf("请输入你要新建联系人的年龄\n");
		scanf("%d", &(con->arr[find].age));//age是整型,需要取地址
		printf("请输入你要新建联系人的电话\n");
		scanf("%s", con->arr[find].tel);
		printf("请输入你要新建联系人的住址\n");
		scanf("%s", con->arr[find].addr);
	}
    printf("联系人修改成功!\n");
}

通讯录修改数据测试

 

 4. 通讯录完整代码

4.1 test.c

#include"SeqList.h"//包含顺序表头文件
#include"Contact.h"//包含通讯录头文件

void menu()
{
	printf("----------通讯录----------\n");
	printf("-------1.新建联系人-------\n");
	printf("-------2.删除联系人-------\n");
	printf("-------3.展示联系人-------\n");
	printf("-------4.查找联系人-------\n");
	printf("-------5.修改联系人-------\n");
	printf("-------0.退出通讯录-------\n");

}
int main()
{
	int input = 0;
	son con;
	ConInit(&con);
	do
	{
		menu();
		printf("请选择你的操作!\n");
		scanf("%d", &input);
		switch (input)
		{
		case 1:
			ConAdd(&con);
			break;
		case 2:
			ConDel(&con);
			break;
		case 3:
			ConShow(&con);
			break;
		case 4:
			ConFind(&con);
			break;
		case 5:
			ConModify(&con);
			break;
		default:
			printf("选择错误,请重新选择!\n");
			break;
		}
	} while (input);
	return 0;
}

4.2 SeqList.h

#include"Contact.h"

//下面是需要使用到的库函数的头文件
#include<stdio.h>
#include<stdlib.h>
#include<assert.h>
#include<string.h>

//typedef int SLData;//类型重命名,为了方便后续替换类型
//我们把int类型替换成strcut 类型
typedef person SLData;

typedef struct SeqList //类型重命名
{
	SLData *arr;//定长数组
	int size;//顺序表当前有效的数据个数
	int capacity;//容量大小
}sl;

//顺序表增容
void SLCheckCapacity(sl* ps);
//顺序表初始化
void SLInit(sl* ps);
//顺序表打印
void SLPaint(sl* ps);
//顺序表尾插
void SLPushBack(sl* ps, SLData x);
//顺序表头插
void SLPushFront(sl* ps, SLData x);
//顺序表尾删
void SLPopBack(sl* ps);
//顺序表头删
void SLPopFront(sl* ps);
//顺序表指定位置之前插入
void SLInsert(sl* ps, int pos, SLData x);
//顺序表指定位置删除
void SLEarse(sl* ps, int pos);
//顺序表修改指定位置数据
void SLModify(sl* ps,int pos, SLData x);
//顺序表数据查找
int SLFind(sl* ps, SLData x);
//顺序表销毁
void SLDestroy(sl* ps);

4.3 SeqList.c

#include"SeqList.h"//包含顺序表的头文件

void SLInit(sl* ps)
{
	assert(ps);//判断ps是否为空指针
	//这里我们不知道这个顺序表需求的大小
	ps->arr = NULL;//我们就先把指针置为空
	ps->size = ps->capacity = 0;//这里有效数据个数和可存储数据个数都置为0;
}

void SLDestroy(sl* ps)
{
	assert(ps);//判断ps是否为空指针
	free(ps->arr);//释放动态内存开辟的空间
	ps->arr = NULL;//防止ps->arr变成野指针
	ps->size = ps->capacity = 0;//将有效数据和空间大小置0
}

void SLCheckCapacity(sl* ps)
{
	assert(ps);//判断ps是否为空指针
	if (ps->size == ps->capacity)//判断有效数据个数和可存储数据个数,这里如果有效数据个数==可存储数据个数,说明空间不够了,需要增容
	{
		//这里利用三目操作符,判断可用数据个数的大小,并创建相同变量来接收
		int tmp = ps->capacity == 0 ? 4 : 2 * ps->capacity;
		//这里我们用于同类型指针来接收realloc开辟空间返回的地址
		SLData* ptr = (SLData*)realloc(ps->arr, tmp * sizeof(SLData));
		//判断是否开辟成功
		if (ptr == NULL)
		{
			//开辟失败,报错
			perror("realloc");
			exit(1);//退出程序
		}
		//开辟成功,接收
		ps->arr = ptr;
		ps->capacity = tmp;//接收可用空间数据
	}
}

void SLPushBack(sl* ps, SLData x)
{
	assert(ps);//断言ps是否为空指针
	//检查空间大小,不够进行增容
	SLCheckCapacity(ps);
	//这里我们在size位置进行尾插,之后让size++(后置++,先使用在++),用来记录顺序表中有效数据的个数
	ps->arr[ps->size++] = x;
}

//void SLPaint(sl* ps)
//{
//	assert(ps);//判断ps是否为空指针
//	int i = 0;
//	//循环打印
//	for (i = 0; i < ps->size; i++)
//	{
//		printf("%d ", ps->arr[i]);
//	}
//	//换行
//	printf("\n");
//}

void SLPushFront(sl* ps, SLData x)
{
	assert(ps);//判断ps是否为空指针
	//判断空间大小
	SLCheckCapacity(ps);
	int i = 0;
	//循环移动数据
	for (i = ps->size; i > 0; i--)
	{
		ps->arr[i] = ps->arr[i - 1];//将前面的数据复制到后一位
	}
	//这里i == 0,跳出循环
	ps->arr[i] = x;
	//让顺序表有效数据个数++
	ps->size++;
}

void SLPopBack(sl* ps)
{
	assert(ps);//判断ps是否为空指针
	assert(ps->size);//判断顺序表里面的有效数据个数
	ps->size--;
}

void SLPopFront(sl* ps)
{
	assert(ps);//判断ps是否为空指针
	assert(ps->size);//判断顺序表里面的有效数据个数
	int i = 0;
	//循环移动
	for (i = 0; i < ps->size; i++)
	{
		ps->arr[i] = ps->arr[i + 1];//将后面值赋值给前一位
	}
	ps->size--;
}

void SLInsert(sl* ps, int pos, SLData x)
{
	assert(ps);//判断ps是否为空指针
	assert(pos >= 0 && pos < ps->size);//判断删除数据的范围
	SLCheckCapacity(ps);//判断空间大小
	int i = 0;
	for (i = ps->size; i > pos; i--)
	{
		ps->arr[i] = ps->arr[i - 1];//将数据整体往后移动一位
	}
	//i == pos 跳出循环
	ps->arr[i] = x;
	ps->size++;
}

void SLEarse(sl* ps, int pos)
{
	assert(ps);//判断ps是否为空指针
	assert(pos >= 0 && pos < ps->size);//判断指定位置的范围
	int i = 0;
	for (i = pos; i < ps->size; i++)
	{
		ps->arr[i] = ps->arr[i + 1];//将指定位置之后的数据整体往前移动一位
	}
	ps->size--;
}

void SLModify(sl* ps, int pos, SLData x)
{
	assert(ps);//判断ps是否为空指针
	assert(pos >= 0 && pos < ps->size);//判断指定位置是否合法
	ps->arr[pos] = x;
}

 4.4 Contact.h

//这里我们用#define来定义大小,方便后续修改
#define NAME_MAX  20
#define GENDER_MAX   20
#define TEL_MAX 20
#define ADDR_MAX 100

typedef struct SeqList son;//这里我们把顺序表重新命名为son

//创建一个存放联系人数据的结构体
typedef struct personinfo
{
	char name[NAME_MAX];//姓名
	char gender[GENDER_MAX];//性别
	int age;//年龄
	char tel[TEL_MAX];//电话
	char addr[ADDR_MAX];//住址
}person;

//通讯录初始化
void ConInit(son* con);
//通讯录销毁
void ConDestroy(son* con);
//通讯录添加数据
void ConAdd(son* con);
//通讯录查找数据
void ConFind(son* con);
//通讯录展示数据
void ConShow(son* con);
//通讯录删除数据
void ConDel(son* con);
//通讯录修改数据
void ConModify(son* con);

4.5 Contact.c

#include"Contact.h"
#include"SeqList.h"

void ConInit(son* con)
{
	assert(con);//判断con是否为空指针
	//这里我们在顺序表里面就写过初始化的方法,我们直接调用,这个就是初始化方法的复用
	SLInit(con);
}

void ConDestroy(son* con)
{
	assert(con);//判断con是否为空指针
	SLDestroy(con);//顺序表销毁方法复用
}

void ConAdd(son* con)
{
	assert(con);//判断con是否为空指针
	//在我们添加联系人数据之前,我们需要检查空间大小
	SLCheckCapacity(con);
	//这里我们创建一个结构体变量
	person p;
	//我们在这里将我们添加联系人的数据写入到p结构体里面
    //这里姓名,性别,电话,住址都是数组,数组名是首元素的地址,所以不需要取地址符号
	printf("请输入你要添加联系人的姓名\n");
	scanf("%s", p.name);
	printf("请输入你要添加联系人的性别\n");
	scanf("%s", p.gender);
	printf("请输入你要添加联系人的年龄\n");
	scanf("%d", &(p.age));//age是整型,需要取地址
	printf("请输入你要添加联系人的电话\n");
	scanf("%s", p.tel);
	printf("请输入你要添加联系人的住址\n");
	scanf("%s", p.addr);
	printf("联系人新建成功!\n");
	printf("--------------------------\n");
	//在这里我们写了3中插入数据的方法,随便调用一种即可,我们选择尾插
	SLPushBack(con, p);//我们将p结构体尾插进去
}


int Confind(son* con,char name[])
{
	int i = 0;
	//循环查找
	for (i = 0; i < con->size; i++)
	{
		if (strcmp(con->arr[i].name,name) == 0)
		{
			return i;
		}
	}
	return -1;
}
void ConFind(son* con)
{
	assert(con);//判断con是否为空指针
	//在这里我们创建一个字符数组用来接收输入要查找的联系人姓名
	char name[NAME_MAX];
	printf("请输入你要查找的联系人姓名\n");
	scanf("%s",name);
	//写一个查找函数,在通讯录里面循环查找
	int find =Confind(con,name);
	//查找到了返回下标,如果没有查找到返回不是下标的数
	if (find < 0)
	{
		printf("你要查找的联系人不存在!\n");
		return;//提前返回
	}
	else
	{   //当我们查找到了,就把它打印出来
		printf("姓名 性别 年龄 电话 住址\n");
		printf("%4s %4s %4d %4s %4s\n",
			con->arr[find].name,
			con->arr[find].gender,
			con->arr[find].age,
			con->arr[find].tel,
			con->arr[find].addr);
	}
}

void ConShow(son* con)
{
	assert(con);//判断con是否为空指针
	printf("姓名 性别 年龄 电话 住址\n");
	int i = 0;
	for (i = 0; i < con->size; i++)
	{
		printf("%4s %4s %4d %4s %4s\n",
			con->arr[i].name,
			con->arr[i].gender,
			con->arr[i].age,
			con->arr[i].tel,
			con->arr[i].addr);
	}
}

void ConDel(son* con)
{
	assert(con);//判断con是否为空指针
	char name[NAME_MAX];//创建变量来接收要删除的联系人姓名
	printf("请输入你要删除的联系人姓名\n");
	scanf("%s", name);
	//这里先调用一下查找函数,看看联系人是否存在
	int find = Confind(con,name);
	if (find < 0)
	{
		printf("你要删除的联系人不存在\n");
		return;//提前返回
	}
	else
	{
		//这里我们在前面顺序表里面写过在指定位置删除,进行复用
		printf("联系人删除成功!\n");
		SLEarse(con,find);
	}
}

void ConModify(son* con)
{
	assert(con);//判断con是否为空指针
	char name[NAME_MAX];//创建变量来接收要修改的联系人姓名
	printf("请输入你要修改的联系人姓名\n");
	scanf("%s",name);
	//这里先调用一下查找函数,看看联系人是否存在
	int find = Confind(con, name);
	if (find < 0)
	{
		printf("你要修改的联系人不存在\n");
	}
	else
	{   //我们直接在这里进行修改
		printf("请输入你要新建联系人的姓名\n");
		scanf("%s", con->arr[find].name);
		printf("请输入你要新建联系人的性别\n");
		scanf("%s", con->arr[find].gender);
		printf("请输入你要新建联系人的年龄\n");
		scanf("%d", &(con->arr[find].age));//age是整型,需要取地址
		printf("请输入你要新建联系人的电话\n");
		scanf("%s", con->arr[find].tel);
		printf("请输入你要新建联系人的住址\n");
		scanf("%s", con->arr[find].addr);
	}
	printf("联系人修改成功!\n");
}

标签:ps,arr,顺序,联系人,通讯录,printf,数据结构,con
From: https://blog.csdn.net/2301_81291674/article/details/139159717

相关文章

  • 温州-顺序-2
    第一部分。中央及部委关于数据要素的政策。政策1。《中华人民共和国国民经济和社会发展第十三个五年规划纲要》。该政策由国务院于2016年03月发布。主要内容包括。第27章明确提出“实施国家大数据战略”,包括加快政府数据开放共享,促进大数据产业健康发展。政策2。《关于坚持和......
  • 数据结构与算法-快速排序
    快速排序特点:快思路:  1.取第一个元素p,使元素p归位;  2.列表被p分成两部分,左边都比p小,右边都比p大;  3.递归完成排序.快速排序的效率:O(nlogn) 代码实现:defpartition(li,left,right):tmp=li[left]whileleft<right:whileleft<righ......
  • 温州-顺序
    第一部分。中央及部委关于数据要素的政策。政策1。《中华人民共和国国民经济和社会发展第十三个五年规划纲要》。该政策由国务院于2016年03月发布。主要内容包括。第27章明确提出“实施国家大数据战略”,包括加快政府数据开放共享,促进大数据产业健康发展。政策2。《关于坚持和......
  • 栈和队列1 顺序栈及基本操作实例(进制转换)
    #include<stdio.h>#include<stdlib.h>#defineINITSIZE100#defineINCREAMENT10 typedefstructSqStack{   int*data;   int*top;   intstacksize;}SqStack;voidInitStack(SqStack*L){   L->data=(int*)malloc(INITSIZE*siz......
  • 【老鼠看不懂的数据结构】FHQTreap 初识
    Treap弱平衡的随机性很强的老鼠看不懂的平衡树Q:为什么叫Treap?A:看看二叉搜索树(BST)和堆(Heap),组合起来就是Treap其中,二叉搜索树的性质是:左子节点的值(val)比父节点小右子节点的值(val)比父节点大如果这些节点的值都一样,这棵树就会退化成一颗(?)链。对,我知道你在想......
  • 关于async/await、promise和setTimeout执行顺序
    前段时间领导给我们出了一道题,关于async/await、promise和setTimeout的执行顺序,网上查了查资料,这是头条的一道笔试题,记录一下,加深理解。题目如下:asyncfunctionasync1(){console.log('async1start');awaitasync2();console.log('asnyc1end');}asyncfunc......
  • RocketMq如何实现顺序消息?
    RocketMq是一款金融级别的消息中间件,作为高可靠的中间件,在需要保证消息顺序性的场景下,可不能掉链子!那么,RocketMq是如何实现顺序消息的呢?RocketMq并不保证Topic维度的消息顺序性,而是在Queue维度保证了消息顺序性。RocketMq的消费进程会同步消费Queue中的消息,且消费......
  • 数据结构与算法学习——动态规划
    动态规划动态规划(英语:Dynamicprogramming,简称DP)是一种在数学、管理科学、计算机科学、经济学和生物信息学中使用的,通过把原问题分解为相对简单的子问题的方式求解复杂问题的方法。动态规划常常适用于有重叠子问题[1]和最优子结构性质的问题,动态规划方法所耗时间往往远少于朴素......
  • 在数据结构上,后端要求前端在一个对象中添加一个类型字段,并且对该对象的某些属性中都加
    这个需求的合理性取决于具体的应用场景和目的。让我们分析一下:合理性的一面:简化逻辑处理:如果这个类型字段是为了在后端快速区分或过滤不同类型的对象属性,那么在前端就做好标记,可以简化后端处理逻辑,减少在后端进行类型判断的需要。一致性保证:在前端加入类型字段并确保它与对象......
  • redis数据结构:RedisObject,SkipList,SortedSet
    1.RedisObject对象redis中任何KV都会被封装为RedisObject对象,也叫做Redis对象 2.SkipList跳表元素按照升序排列存储,是有序的双向链表节点可以有多个指针,并且跨度不同。指针个数根据节点数自动生成,1~32性能和红黑树;二分查找差不多。实现简单,但是空间复杂度高样例:1——2......