第一次学习数据结构,具体学习内容为顺序表。
顺序表是顺序存储结构的线性表。
1.class.h 文件---创建顺序表ADT
这里我使用的是动态指针的方式定义顺序表SeqList:
//顺序表第二种定义-----------动态分配:指针
#define InitSize 10
typedef struct {
int* data; //指针
int maxsize; //顺序表的最大容量
int length; //顺序表的当前长度
}SeqList; //顺序表的数据元素类型
2.first.c 文件,实现顺序表的插入删除操作
初始化顺序表
#include<stdio.h>
#include"class.h"
#include<stdlib.h>
//指针的方式初始化顺序表
void InitList(SeqList*L) {
L->data = (int*)malloc(InitSize * sizeof(int));
L->length = 0;
L->maxsize = InitSize;
}
class.h为自定义的头文件,这里引用需要双引号。初始化函数InitList。初始化顺序表的长度和最大存储空间。
L->data = (int*)malloc(InitSize * sizeof(int));
上面这句是核心代码,malloc实现动态的申请一片连续的内存空间。malloc执行完成后会return指向这一整片内存空间开始地址的指针。
1.(int*)将malloc函数返回的指针强制转换为我所定义的数据元素的类型指针,这里我定义的是int型指针,赋给L->data这个指针变量,这个指针变量就回指向这片内存空间的起始地址。
2.(sizeof(int)*InitSize) malloc函数需要申请多大的内存空间?int类型所占的大小为4个字节,乘以顺序表初始长度--->存放10个Int型变量所需要的内存空间。
增加动态数组的长度:
void IncreaseSize(SeqList* L, int len) { //增加动态数组的长度
int* p = L->data;
L->data = (int*)malloc((L->maxsize + len) * sizeof(int));
for (int i = 0; i < L->length; i++)
{
L->data[i] = p[i]; // 将数据复制到新的区域
}
L->maxsize = L->maxsize + len; //顺序表最大长度增加len
free(p);
} //释放原来的内存空间
- 顺序表的算法操作
顺序表的插入操作 tip:顺序表的位序是从1开始。
int ListInsert(SeqList* L, int i, int e) //插入操作
{
int j; //位置索引 1<=i<=L->length+1
if (i<1 || i>L->length + 1) return 0; //i值不合法
if (L->length == L->maxsize) return 0; //无空间
for (j = L->length; j >= i; j--) {
L->data[j] = L->data[j - 1]; // L为指针变量,所以访问的时候用->x
}
L->data[i - 1] = e;
L->length++;
return 1;
}
顺序表的删除操作
int ListDelete(SeqList *L, int i, int *e) //删除
{
if (i<1 || i>L->length + 1) return 0;
if (L->length == L->maxsize) return 0;
e = L->data[i - 1];
for (int j = i; j < L->length; j++)
{
L->data[j-1] = L->data[j];
}
L->length--;
return 1;
}
顺序表的查找操作
int GetElem(SeqList L, int i) // 按位查找
{
return L.data[i - 1];
}
int LocateElem(SeqList L, int m) // 按值查找
{
for (int i = 0; i < L.length; i++)
{
if (L.data[i] == m)
return i + 1;
}
return 0;
}
主函数实现
int main()
{
SeqList L;
InitList(&L); //int *L=&L 表示将变量 L 的地址赋给指针 L
ListInsert(&L, 1, 1);
ListInsert(&L, 2, 2);
ListInsert(&L, 3, 3);
ListInsert(&L, 4, 4);
ListInsert(&L, 5, 5);
IncreaseSize(&L, 5);
for (int i = 0; i < L.length; i++)
{
printf("data[%d]=%d\n", i, L.data[i]);
}
int e = -2;
if (ListDelete(&L, 1, &e))
{
printf("已删除第1个元素\n");
}
else
printf("第i个位置不合法,删除操作失败\n");
int i = 3;
printf("第%d个位置的元素为%d\n", i,GetElem(L, i));
int m = 4;
printf("元素值为%d的元素的位置为%d\n", m, LocateElem(L, m));
for (int i = 0; i < L.length; i++)
{
printf("data[%d]=%d\n", i, L.data[i]);
}
printf("%d\n", L.maxsize); // 由原本的长度10增加为15
return 0;
}
接下来实现相关算法题
1.已知一个线性表L中的数据元素按升序排列,编写一个算法,实现在线性表中插入一个数据元素item,使得线性表仍然保持升序排列。
void Insert(SeqList* L, int item)
{
int i = 0;
if (item >= L->data[L->length - 1])
L->data[L->length] = item;
else
{
while (item >= L->data[i])
{
i++;
}
ListInsert(L, i, item); // 参数 SeqList * L 已经是一个指针类型,传递给函数时已经是地址的形式,
//因此在函数内部直接使用 L 就可以修改传入的指针指向的内容,无需再使用取地址符号&
}
//L->length++;
}
因为在ListInsert函数中已经存在length++,所有不需要再使用。注意的是当调用ListInsert函数时不需要使用&符,因为此时L已经是一个地址,运行结果如下图:
2.已知一个线性表L中的数据元素按升序排列,编写一个算法,实现删除线性表中重复元素。
void Delete(SeqList* L)
{
int i = 0;
int j = 0;
for (i = 0; i < L->length; i++)
{
for (j = i+1; j < L->length; j++)
if (L->data[i] == L->data[j])
{
printf("--%d,%d\n", i, j);
ListDelete(L, j, L->data[j]);
}
}
}
上面这个算法的执行效率并不高,并且不安全,下面是参考书上的代码
void Delete(SeqList* L)
{
int i = 0;
while (i < L->length - 1)
{
if (L->data[i] != L->data[i + 1])
i++;
else
ListDelete(L, i+1,L->data[i]);
}
}
运行结果:
3.已知两个线性表LA和LB,编写一个算法,实现将存在于线性表LB中而不存在线性表LA中的数据元素插入到LA末尾,得到新的线性表LA。
void Union(SqList& L, SqList& S)
{
for (int i = 1; i <= S.length; i++)
{
if (!LocateElem(L, GetElem(S, i))) {
printf("%d\n", GetElem(S, i));
ListInsert(L, L.length + 1, GetElem(S, i));
//L.length++;
}
}
}
两个查找方式-----按位查找和按值查找。