数据结构-通讯录逻辑实现

目录

1.0:定义一个动态的顺序表

2.0:各个功能实现

2.1:初始化

2.2:销毁

2.3:测试查看

2.4:检查是否需要扩容

2.5:头部插入

2.6:尾部插入

2.7:头部删除

2.8:尾部删除

2.9:寻找元素

2.11:在指定位置删除

2.12:修改指定位置的值

3.0:优化

3.1:优化头插

3.2:优化尾插

3.3:优化头删

3.4:优化尾删

4.0:代码整体

4.1:test.c

4.2:SeqList.h

4.3:SeqList.c


1.0:定义一个动态的顺序表

//动态顺序表
typedef int SLDataType;
typedef struct SeqList {
	SLDataType* a;  //指向首元素地址
	int size;       //存储有效数据个数
	int capacity   //空间大小
}SL;

 

 a指针指向首元素地址,方便对其进行访问和修改

size为有效个数

capacity为容量

2.0:各个功能实现

2.1:初始化

//初始化
void SLInit(SL*ps) {
	assert(ps);
	ps->a = (SLDataType*)malloc(sizeof(SLDataType)*4);
	if (ps->a == NULL) {
		perror("malloc failed");
		exit(-1);
	}
	ps->size = 0;
	ps->capacity = 4;
}

首先开辟了4个字节的空间,用于存放数据,由于开辟空间有可能会开辟失败,开辟失败pa->a就是一个空指针,所以要对其检验。此时,容量为4  有效个数为0

2.2:销毁

//销毁
void SLDestroy(SL* ps) {
	assert(ps);
	free(ps->a);
	ps->a = NULL;
	ps->capacity = 0;
	ps->size = 0;
}

我们最终要对其进行销毁,防止数据泄露或数据丢失。

释放掉开辟好的空间后,将指针定义为空指针,并使其他参量为0

2.3:测试查看

//测试查看
void SLPrint(SL* ps) {
	assert(ps);
	for (int i = 0; i < ps->size; i++) {
		printf("%d  ",ps->a[i]);
	}
	printf("\n");
}

对目前所存放的数据进行打印,ps->a[i]语句表示通过ps指针找到结构体中的a(首元素地址),a[i]

表示*(a+i)找到对应的值并且打印出来。

2.4:检查是否需要扩容

//检查是否需要扩容
void SLCheckCapacity(SL* ps) {
	assert(ps);
	//满了要扩容
	if (ps->size == ps->capacity) {
		SLDataType* tmp = realloc(ps->a, ps->capacity * 2 * (sizeof(SLDataType)));
		if (tmp == NULL) {
			perror("realloc failed");
			exit(-1);
		}
		ps->a = tmp;
		ps->capacity *= 2;
	}
}

 如果首先开辟的四个字节的空间满了,我们就要对其进行扩容,继续开辟一块容量为原来容量两倍的空间,并且检查是否开辟失败,然后将容量扩大2倍。

这里要强调的是,需要定义一个指针来接收这块空间的起始地址,由于考虑到开辟失败的因素,所以这里不能继续使用原来的指针a来接受。

使用malloc函数需要引用头文件stdlib.h

异地扩容:

 原地扩容:

 强调一下,不论是原地扩容还是异地扩容,都需要创建一个新的指针来接收,这样做的好处是,如果创建失败,原来的数据不会丢失,还可以通过a指针找回。如果创建成功,那么将原指针(a)指向新指针(tmp)即可

2.5:头部插入

//头插
void SLPushFront(SL* ps, SLDataType x) {
	assert(ps);
	void SLCheckCapacity(ps);
	//挪动数据
	int end = ps->size - 1;
	while (end >= 0) {
		ps->a[end + 1] = ps->a[end];
		--end;
	}
	ps->a[0] = x;
	ps->size++;
	
}

 头部插入的思想就是将新的元素放到a[0]的位置,我们先检查是否需要扩容,再将原数据依次向后挪动,将a[0]的位置空出来,再赋予它新的元素

假设我们现在有4个元素,要实现头插,后面是有空间的不需要扩容,体会一下挪动的思想

  挪动有两种方式,从前往后或者从后往前,我们来一一实现以下

从前往后挪:

 从后往前挪:

 最终,我们发现从前往后挪会篡改原数据的值,所以要从后往前挪。

最后通过ps->a[0] = x和ps->size++使头部插入我们需要的元素。

2.6:尾部插入

//尾插
void SLPushBack(SL* ps, SLDataType x) {
	assert(ps);
	SLCheckCapacity(ps);
	
	ps->a[ps->size] = x;
	ps->size++;
	
}

 尾插我们还是先检查是否需要扩容,然后直接将需要的元素插入到最后一个位置即可

2.7:头部删除

//头删
void SLPopFront(SL* ps) {
	assert(ps);
	assert(ps->size > 0);
	int begin = 1;
	while (begin < ps->size) {
		ps->a[begin - 1] = ps->a[begin];
		++begin;
	}
	ps->size--;
	
}

 头删我们直接将后面的数据依次向前挪动即可,从后面开始依次挪动会造成数据篡改,如下:

所以需要从前面开始挪

 最后通过size--将最后一位减去即可

2.8:尾部删除

//尾删
void SLPopBack(SL* ps) {
	assert(ps);
	//普通检查
	/*if (ps->size == 0) {
		return;
	}*/
	//暴力检查
	assert(ps->size > 0);
	ps->size--;
	}

 尾删之前我们需要对有效个数进行检查,只有最少有1个元素的时候我们才可以对其尾删,尾删过程直接将有效个数-1即可。

对于检查,这里使用暴力检查,通过断言,如果断言为真,才会进行下面的操作,断言为假,直接报错退出。当然,也可以使用普通检查,两者都是要确保至少存有一个元素,不过暴力检查比较实用一些。

2.9:寻找元素

//通过值寻找元素,返回下标,没找到返回-1
int SLFind(SL* ps, SLDataType x) {
	assert(ps);
	for (int i = 0; i < ps->size; i++) {
		if (ps->a[i] == x) {
			return i;
		}
	}
	return(-1);
}

 我们把要寻找的元素的值传入,通过循环遍历的方式去寻找这个值,如果找到了就返回这个元素的下标,没找到就返回-1即可。

2.10:在指定位置插入

//在pos位置插入x
void SLInsert(SL* ps, int pos, SLDataType x) {
	assert(ps);
	assert(pos >= 0 && pos <= ps->size);
	SLCheckCapacity(ps);
	int end = ps->size - 1;
	while (end >= pos) {
		ps->a[end + 1] = ps->a[end];
		--end;
	}
	ps->a[pos] = x;
	ps->size++;
}

 例如,我们要在3的位置插入我们需要的值,首先要检查是否需要扩容,接着将3后面的元素依次向后挪动一位,从前面开始挪动同样会造成数据篡改,如下图示:

 

 所以,我们需要的是从后面开始挪动。

 最后,插入我们所需的值即可。

2.11:在指定位置删除

//删除pos位置的值
void SLErase(SL* ps, int pos) {
	assert(ps);
	assert(pos >= 0 && pos < ps->size);
	int begin = pos + 1;
	while (begin < ps->size) {
		ps->a[begin - 1] = ps->a[begin];
		begin++;
	}
	ps->size--;
}

 假设我们要删除3这个位置的元素,只需要将3后面的元素依次向前即可。

 这里我们依旧需要从前开始挪动,从后开始会篡改数据,如下所示:

 所以,需要从后面开始挪动。

 

 最后,将size-1即可。

2.12:修改指定位置的值

//修改
void SLModify(SL* ps, int pos, SLDataType x) {
	assert(ps);
	assert(pos >= 0 && pos < ps->size);
	ps->a[pos] = x;
}

修改指定位置时,我们直接利用下标,将下标索引的元素进行覆盖即可。

3.0:优化

3.1:优化头插

头插就相当于在第一个位置(下标为0处)插入

//头插
void SLPushFront(SL* ps, SLDataType x) {
	assert(ps);
	SLInsert(ps, 0, x);

}

3.2:优化尾插

尾插就相当于在最后一个有效位置(size-1处)插入 

//尾插
void SLPushBack(SL* ps, SLDataType x) {
	assert(ps);
	SLInsert(ps, ps->size,x);
}

3.3:优化头删

头删就相当于在第一个位置(下标为0处)删除

//头删
void SLPopFront(SL* ps) {
	assert(ps);
	SLErase(ps, 0);
}

3.4:优化尾删

尾删就相当于在最后一个有效位置(size-1处)删除 

//尾删
void SLPopBack(SL* ps) {
	assert(ps);
	SLErase(ps, ps->size - 1);
}

4.0:代码整体

4.1:test.c

# define _CRT_SECURE_NO_WARNINGS 1
#include<stdio.h>
#include"SeqList.h"
int main() {
	//创建
	SL s1;
	//初始化
	SLInit(&s1);
	
    //进行指令操作.......

	//销毁
	SLDestroy(&s1);
	return 0;
}

4.2:SeqList.h

#pragma once
#include<assert.h>
#include<stdio.h>
#include<stdlib.h>
//动态顺序表
typedef int SLDataType;
typedef struct SeqList {
	SLDataType* a;
	int size;      //存储有效数据个数
	int capacity;  //空间大小
}SL;
//初始化
void SLInit(SL*ps);
//销毁
void SLDestroy(SL*ps);
//测试
void SLPrint(SL*ps);
//检查容量够不够
void SLCheckCapacity(SL*ps);
//头插头删 尾插尾删 

//尾插
void SLPushBack(SL* ps, SLDataType x);
//尾删
void SLPopBack(SL* ps);
//头插
void SLPushFront(SL* ps, SLDataType x);
//头删
void SLPopFront(SL* ps);

//返回下标,没找到返回-1
int SLFind(SL* ps, SLDataType x);

//在pos位置插入x
void SLInsert(SL* ps, int pos, SLDataType x);

//删除pos位置的值
void SLErase(SL* ps, int pos);

//修改
void SLModify(SL* ps, int pos, SLDataType x);

4.3:SeqList.c

#include"SeqList.h"
//初始化
void SLInit(SL*ps) {
	assert(ps);
	ps->a = (SLDataType*)malloc(sizeof(SLDataType)*4);
	if (ps->a == NULL) {
		perror("malloc failed");
		exit(-1);
	}
	ps->size = 0;
	ps->capacity = 4;
}
//销毁
void SLDestroy(SL* ps) {
	assert(ps);
	free(ps->a);
	ps->a = NULL;
	ps->capacity = 0;
	ps->size = 0;
}
//测试查看
void SLPrint(SL* ps) {
	assert(ps);
	for (int i = 0; i < ps->size; i++) {
		printf("%d  ",ps->a[i]);
	}
	printf("\n");
}
//检查是否需要扩容
void SLCheckCapacity(SL* ps) {
	assert(ps);
	//满了要扩容
	if (ps->size == ps->capacity) {
		SLDataType* tmp = realloc(ps->a, ps->capacity * 2 * (sizeof(SLDataType)));
		if (tmp == NULL) {
			perror("realloc failed");
			exit(-1);
		}
		ps->a = tmp;
		ps->capacity *= 2;
	}
}
//尾插
void SLPushBack(SL* ps, SLDataType x) {
	assert(ps);
	/*SLCheckCapacity(ps);
	
	ps->a[ps->size] = x;
	ps->size++;*/
	SLInsert(ps, ps->size,x);
}
//尾删
void SLPopBack(SL* ps) {
	assert(ps);
	//普通检查
	/*if (ps->size == 0) {
		return;
	}*/
	//暴力检查
	/*assert(ps->size > 0);
	ps->size--;*/
	SLErase(ps, ps->size - 1);
}
//头插
void SLPushFront(SL* ps, SLDataType x) {
	assert(ps);
	//void SLCheckCapacity(ps);
	挪动数据
	//int end = ps->size - 1;
	//while (end >= 0) {
	//	ps->a[end + 1] = ps->a[end];
	//	--end;
	//}
	//ps->a[0] = x;
	//ps->size++;
	SLInsert(ps, 0, x);

}
//头删
void SLPopFront(SL* ps) {
	assert(ps);
	/*assert(ps->size > 0);
	int begin = 1;
	while (begin < ps->size) {
		ps->a[begin - 1] = ps->a[begin];
		++begin;
	}
	ps->size--;*/
	SLErase(ps, 0);
}
//通过值寻找元素,返回下标,没找到返回-1
int SLFind(SL* ps, SLDataType x) {
	assert(ps);
	for (int i = 0; i < ps->size; i++) {
		if (ps->a[i] == x) {
			return i;
		}
	}
	return(-1);
}
//在pos位置插入x
void SLInsert(SL* ps, int pos, SLDataType x) {
	assert(ps);
	assert(pos >= 0 && pos <= ps->size);
	SLCheckCapacity(ps);
	int end = ps->size - 1;
	while (end >= pos) {
		ps->a[end + 1] = ps->a[end];
		--end;
	}
	ps->a[pos] = x;
	ps->size++;
}
//删除pos位置的值
void SLErase(SL* ps, int pos) {
	assert(ps);
	assert(pos >= 0 && pos < ps->size);
	int begin = pos + 1;
	while (begin < ps->size) {
		ps->a[begin - 1] = ps->a[begin];
		begin++;
	}
	ps->size--;
}
//修改
void SLModify(SL* ps, int pos, SLDataType x) {
	assert(ps);
	assert(pos >= 0 && pos < ps->size);
	ps->a[pos] = x;
}

最后,如有不足,请大家指教!!!

  • 3
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
数据结构 课程设计 void main() { list *head=NULL;//头指针初始化 FILE *fp;//定义文件指针 int m;//功能代号吗 do{ printf("\n #############################请选择################################\n"); printf(" # 1、信息输入 "); printf(" 2、显示信息 #\n"); printf(" # 3、查找姓名 "); printf(" 4、删除信息 #\n"); printf(" # 5、修改信息 "); printf(" 6、打开通讯录 #\n"); printf(" # 7、添加信息 "); printf(" 8、 保存 #\n"); printf(" # 9、清屏 "); printf(" 0、退出 #"); printf("\n ###################################################################\n"); printf(" 提示:信息输入和添加信息时最好存入字符,否则,在打开存储文件时,可能会出现乱码!\n"); printf("==输入要执行的功能代号:_"); scanf("%d",&m); switch(m) { case 1:enter(head);break; case 2:display(head); break; case 3:search(head); break; case 4:delete_name(head);break; case 5:change(head);break; case 6:load(head);break; case 7:insert(head);break; case 8:save(head,fp);printf("==保存成功==\n");break; case 9:system("cls");break; } } while(m!=0); } 2、信息输入: void enter(list *&head) { list *p0,*p1,*p2;//定义指针 int m;//作为判断是否继续新建的条件 FILE *fp; p0=(list *)malloc(LEN); //开辟一个新单元 p0->next=NULL;//结点指针域为空 head=p0;//第一个结点,即为头结点 printf("请输入信息建立通讯录:\n"); getchar(); scanfdata(p0);//输入数据 p2=p0; printf("是否继续按1输入,按0结束_"); scanf("%d",&m); while(m) { getchar(); p1=(list *)malloc(LEN); //开辟一个新单元 p1->next=NULL; p2->next=p1; scanfdata(p1);//输入数据 p2=p1; head->n = head->n+1;//表长 printf("是否继续按1输入,按0结束_"); scanf("%d",&m); } if(m==0) { save(head,fp);//把信息存到相应文件 } } 3、显示信息 void display(list *head) { list *p; //定义移动指针 int i; char *menu[]={"姓名","城市","电话", "QQ"}; p=head; printf("--------------------------------------------------------------------\n"); for(i=0;i<4;i++) printf("%-20s",menu[i]); printf("\n"); if (head!=N

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值