顺序表
今天是系统学习数据结构的第一天,从最简单的顺序表开始. 跟着课程把顺序表用C语言实现了一遍,然后再自己用C++再复现一遍.感觉这样子可以巩固一下基础语法.
C语言
#include<stdlib.h>
#define maxx 20
//结构体
typedef struct ArrayList
{
//int data[maxx];
int* data;
int s;//表长
}Array;
//初始化一个空顺序表
Array InitArray(Array a)
{
a.data = (int*)malloc(sizeof(int) * maxx);
/*if(a.data==NULL)
{
printf("空间申请失败\n");
return a;
}*/
a.s = 0;//初始是空表
return a;
}
//末尾添加一个数据k
void Add(Array* a, int k)
{
if (a->s == maxx)
{
printf("数组已满\n");
return;
}
a->data[a->s] = k;
a->s++;
}
//在下标i位置插入数据k
void Insert(Array* a, int i, int k)
{
if (a->s == maxx)
{
printf("数组已满\n");
return;
}
//把i位置空出来
//for(int j=i;j<=a->s-1;j++)//典型的错误
for (int j = a->s - 1; j >= i; j--)//倒着枚举移动
{
a->data[j + 1] = a->data[j];
}
a->data[i] = k;
a->s++;
}
int Find(Array* a, int k)
{
int i;
for (i = 0; i < a->s; i++)
{
if (a->data[i] == k)
{
return i;
}
}
return -1;//k不存在
}
void Delet(Array* a, int k)
{
//判空以及判断k是否存在,但是空表时k一定不存在
int i = Find(a, k);
if (i == -1)
{
printf("%d不存在,无法删除\n", k);
return;
}
//删除k i+1~~s-1挨个前移
for (int j = i + 1; j <= a->s - 1; j++)
{
a->data[j - 1] = a->data[j];
}
a->s--;
}
void Show(Array a)
{
if (a.s == 0)printf("空表\n");
else
{
for (int i = 0; i < a.s; i++)
{
printf("%d ", a.data[i]);
}
printf("\n");
}
}
int main()
{
Array a;//声明一个顺序表;
a = InitArray(a);
Add(&a, 1);
Add(&a, 8);
Add(&a, 2);
Add(&a, 7);
Add(&a, 0);
Show(a);
Insert(&a, 1, 3);
Insert(&a, 3, 5);
Show(a);
int i = Find(&a, 7);
if (i == -1)printf("7不存在\n");
else
printf("数据7的位置是:%d\n", i);
Delet(&a, 8);
Show(a);
return 0;
}
顺序表的功能比较简单,初始化 尾插 指定位置插入 查找 删除 遍历打印
这是第一遍用C语言的实现,主要是通过一个结构体来封装整个顺序表,由data和size组成.这里使用int* data可以动态开辟空间,实际上是找到了一块内存来给这个顺序表,这块内存多大是后续在初始化的时候自己决定的.
1. typedef这个关键字还比较常用这里用来给这个结构体取一个别名Array.
语法:`typedef 原类型 新类型名`;
2.初始化顺序表我们用c语言的malloc申请空间,头文件是`include` malloc后要用一个指针接收,这里就直接用我们前面的data.malloc后我们一般会用if语句来检查内存是否申请成功,如果失败直接return.
3.尾插时先要判断表是否已满,如果不满直接在表尾添加即可.
4.指定插入也须判满,移动元素时循环的方向非常容易错,我们应该`倒序后移`,如果从前往后移前面的元素会覆盖后面的元素导致后面元素全成了第一个移动的元素.插入操作后都需要让`size++`不然后面会影响到遍历打印 删除 查找.
5.查找/遍历打印就非常的常规,遍历整个表返回目标值下标/直接打印即可.(如果没有找到则返回`-1`)
6.删除操作须配合查找进行,找到要删除元素的下标.删除的操作不需要对内存进行操作,只需要把要删除的元素的后面的元素全部前移覆盖即可.删除操作后需要维护`size--`.
顺序表功能简单但是里面有许多小细节,如果不注意很容易导致代码逻辑出错!!!
C++
C++的实现我选择了用class对顺序表进行封装,也是对C++中面向对象的语法的复习
#include<iostream>
using namespace std;
#define maxx 20
class ArrayList {
private:
int* data;
int size;
public:
//初始化一个线性表
ArrayList() {
this->data = new int[maxx];
this->size = 0;
}
//释放内存
~ArrayList() {
delete[] data;
}
// 末尾添加
void Add(int k) {
if (this->size == maxx) {
cout << "线性表已满" << endl;
return;
}
this->data[this->size] = k;
this->size++;
return;
}
// 在下标 i 插入
void Insert(int i, int k) {
if (this->size == maxx) {
cout << "线性表已满" << endl;
return;
}
for (int j = this->size - 1; j >= i; j--) {
this->data[j + 1] = this->data[j];
}
this->data[i] = k;
this->size++;
return;
}
// 查找值 k,返回下标,没找到返回-1
int Find(int k) {
for (int i = 0; i < this->size; i++) {
if (this->data[i] == k) {
return i;
}
}
return -1;
}
// 删除值 k
void Delet(int k) {
int index = this->Find(k);
if (index == -1) {
cout << "目标不在表中" << endl;
return;
}
for (int i = index + 1; i < this->size; i++) {
this->data[i - 1] = this->data[i];
}
this->size--;
return;
}
// 打印
void Print() {
if (this->size == 0) {
cout << "空表" << endl;
return;
}
for (int i = 0; i < this->size; i++) {
cout << this->data[i] << " ";
}
cout << endl;
return;
}
};
int main() {
ArrayList a; // 直接定义对象,自动初始化
a.Add(1);
a.Add(8);
a.Add(2);
a.Add(7);
a.Add(0);
a.Print();
a.Insert(1, 3);
a.Insert(3, 5);
a.Print();
int i = a.Find(7);
if (i == -1)
cout << "7不存在" << endl;
else
cout << "数据7的位置是:" << i << endl;
a.Delet(8);
a.Print();
return 0;
}
C++实现中逻辑和C语言没有任何差别,只是在语法上有些许不同.
1.在C++中我们不用malloc选择new来申请空间.
2.在类中我们需要自己写构造函数以及析构函数,C语言中的初始化我们放到构造函数中,而析构函数我们用来释放空间.C++中释放空间用的是delete,因为我们申请的多个对象用的new[],所以delete时也应delete[].
作为第一个学习并记录的数据结构,顺序表虽然逻辑简单,代码简洁,但仍具有很重要的意义,让我能够了解在数据结构的学习中要注意的一些细节,同时在学习的过程中我会尽量的往内存操作的方向思考.