目录
前言
以前用c语言,都是直接安装devc++,昨天突发奇想,想用vscode来配置c语言环境。
结果按照教程到最后,发现报错了,于是我又重新找了几个教程。结果每次到测试的时候都会出现调用失败之类的问题,所以我还是老老实实地下了一个devc++。
一、顺序表的定义
顺序表是具有相同数据类型的n(n≥0)个数据元素的有限序列,其中n为表长,当n=0时顺序表是一个空表。若用L命名顺序表,则其一般表示为:
其中, 中的 表示位序(从1开始); 是表头元素,无前驱; 是表尾元素,无后继。
二、顺序表的基本操作
顺序表是一种数据结构,通用操作有——创销增删改查,有时也可加入判空、判长、判满、打印以及一些其他功能。
1、数据表的实现方式——静态分配与动态分配
1、静态分配
静态分配,数组的长度不可变,所以需要定义一个Maxsize来表示数组的最大长度。
我觉得一个数组本身就可以看作是一个顺序表,所以length这个结构变量是否是可有可无。但是我又转念一想,加上了这个变量,好像对于顺序来说也不是什么坏事,而且还能使结构体更加完整,看上去也更加好看。
以下是静态分配的实现方式。该顺序表被我命名为SqList,而ElemType则是默认使用int数据类型。
#define Maxsize 10 //数组最大长度
typedef int Elemtype;
typedef struct {
Elemtype data[Maxsize];
int length;
}SqList;
2、动态分配
动态分配,与静态分配有所不同的是,它的长度是可变的。因此需要对静态分配进行一定的修改。比如需要定义InitialSize用于表示顺序表初始长度。
最重要的一点,写函数的时候注意使用&地址符来对原顺序表进行更改。不然内存空间中会有顺序表L与顺序表List。
#include<stdio.h>
#include<stdlib.h>
#define InitialSize 10 //数组初始长度
typedef int ElemType;
typedef struct {
ElemType *data;
int Maxsize;
int length;
}SqList;
//初始化
void InitList(SqList &L){
L.data = (ElemType*)malloc(InitialSize*sizeof(ElemType));
L.Maxsize = InitialSize;
L.length = 0;
}
//增加顺序表长度
void IncreaseSize(SqList &L, int len){
ElemType *p = L.data; //先将L的数据存起来
L.data = (ElemType*)malloc((L.Maxsize + len)*sizeof(ElemType)); //再为L分配新空间
for(int i = 0; i<L.length; i++){
L.data[i] = p[i]; //转移数据
}
L.Maxsize += len; //L的最大长度进行变更
free(p); //释放原存储空间
}
int main() {
SqList list;
InitList(list);
IncreaseSize(list, 5);
return 0;
}
3、小结
顺序表具有随机存储的特点,能在O(1)时间复杂度内找到位序为 i 的元素,而且存储密度大。但是缺点也很明显,扩容(主要是转移数据费时)、插入、删除都很不方便。
2、插入、删除
顺序表的插入与删除十分麻烦,需要移动数组中的元素,开销很大。下面以动态分配时的情况为例。
1、插入
//插入,在L中的位序为i的位置插入元素elem
void Insert(SqList &L, int i, ElemType elem) {
for(int j = L.length; j>=i; j--){
L.data[j] = L.data[j-1]; //将数据元素后移一位
}
L.data[i-1] = elem; //插入元素
L.length++; //长度+1
}
以上是插入的一般思路,但是考虑到代码的健壮性,还需要考虑一些边界情况。
//插入,在L中的位序为i的位置插入元素elem
bool Insert(SqList &L, int i, ElemType elem) {
if(i<0 || i>length + 1) return false; //i值超边界,插入失败
if(L.lenth >= L.Maxsize) return false; //线性表已满,插入失败
for(int j = L.length; j>=i; j--) {
L.data[j] = L.data[j-1];
}
L.data[i-1] = elem;
L.length++;
return true; //插入成功
}
2、删除
//删除,删除在L中的位序为i的元素
void Delete(SqList &L, int i) {
for(int j = i; j<L.length; j++) {
L.data[j-1] = L.data[j]; //将数据元素后移一位
}
L.length--; //长度-1
}
这是最基本的删除方法,除此以外,还可以考虑边界情况;或者也可以增加功能获取被删除的元素。
//删除,删除在L中的位序为i的元素
bool Delete(SqList &L, int i, ElemType &elem) {
if(i<0 || i>L.length) return false; //i值超边界,删除失败
elem = L.data[i-1];
for(int j = i; j<L.length; j++) {
L.data[j-1] = L.data[j]; //将数据元素后移一位
}
L.length--; //长度-1
return true;
}
以下是测试
int main() {
SqList list;
InitList(list);
IncreaseSize(list, 5);
for(int i = 0; i<5; i++) {
ElemType temp = rand() % 10 + 1; //生成一个1~10的数
if(Insert(list, i+1, temp)) {
printf("数字%d插入成功,list长度为%d!\n", temp, list.length);
} else {
printf("插入失败!\n");
}
}
//打印结果,观察是否一致
for(int i = 0; i<list.length; i++) {
printf("%d ", list.data[i]);
}
puts("");
ElemType elem = -1;
if(Delete(list, 2, elem)) { //删除位序为2的元素,并用elem来获取
printf("删除成功,已删除数字%d\n", elem);
} else {
printf("删除失败!\n");
}
return 0;
}
3、小结
虽说增删在最好的情况下时间复杂度就只有O(1),但是在最差的情况下,其时间复杂度有O(n)
3、查找
1、按位查找
顾名思义,就是查找线性表L中位序为 i 的值
//按位查找,查找L中位序为i的元素的值,并获取该值
bool SearchByOrder(SqList L, int i, ElemType &result) {
if(i<1 || i>L.length) return false; //i值超边界,查找失败
result = L.data[i-1];
return true;
}
2、按值查找
//按值查找,找到第一个就返回其位序
bool SearchByElem(SqList L, int &order, ElemType elem) {
for(int i = 0; i<L.length; i++) {
if(L.data[i] == elem) {
order = i+1;
return true;
}
}
return false;
}
3、小结
由于查找过程中,不需要对原线性表进行操作,因此在传递参数时不需要加&
ElemType result = -1;
if(SearchByOrder(list, 3, result)) { //查找位序为3的元素
printf("查找成功,目标元素为%d\n", result);
} else {
printf("查找失败!\n");
}
int order = -1;
if(SearchByElem(list, order, 5)) { //查找第一个元素为5的位序
printf("查找成功,目标元素位序为%d\n", order);
} else {
printf("查找失败!\n");
}
按位查找时间复杂度为O(1),按值查找时间复杂度为O(n)
4、修改
1、按位修改
//按位修改,修改L中位序为i的元素的值为elem
bool UpdateByOrder(SqList &L, int i, ElemType elem) {
if(i<1 || i>L.length) return false; //i值超边界,修改失败
L.data[i-1] = elem;
return true;
}
2、按值修改
//按值修改,修改L中元素的值为elem的元素的值为change
bool UpdateByElem(SqList &L, ElemType elem, ElemType change) {
for(int i = 0; i<L.length; i++) {
if(L.data[i] == elem) L.data[i] = change;
}
return true;
}
3、小结
if(UpdateByOrder(list, 2, 5)) { //修改位序为2的元素为5
printf("修改成功!\n");
} else {
printf("修改失败!\n");
}
if(UpdateByElem(list, 5, 11)) { //修改元素为5的元素为11
printf("修改成功!\n");
} else {
printf("修改失败!\n");
}
修改也算是比较简单的,按位时间复杂度为O(1),按值时间复杂度为O(n)
总结
顺序表的实现还算比较简单。今天复习内容中最需要注意的是&的引用。
还有,因为ElemType默认为int数据类型,所以我写代码的时候一直用的是ElemType,在打印结果时也是用的%d格式。而且为了保持前后文风格一致,可能看上去会有点冗长。
总览一下
#include<stdio.h>
#include<stdlib.h>
#define InitialSize 10 //数组初始长度
typedef int ElemType;
typedef struct {
ElemType *data;
int Maxsize;
int length;
} SqList;
//初始化
void InitList(SqList &L) {
L.data = (ElemType*)malloc(InitialSize*sizeof(ElemType));
L.Maxsize = InitialSize;
L.length = 0;
}
//增加线性表长度
void IncreaseSize(SqList &L, int len) {
ElemType *p = L.data; //先将L的数据存起来
L.data = (ElemType*)malloc((L.Maxsize + len)*sizeof(ElemType)); //再为L分配新空间
for(int i = 0; i<L.length; i++) {
L.data[i] = p[i]; //转移数据
}
L.Maxsize += len; //L的最大长度进行变更
free(p); //释放原存储空间
}
//插入,在L中的位序为i的位置插入元素elem
bool Insert(SqList &L, int i, ElemType elem) {
if(i<1 || i>L.length + 1) return false; //i值超边界,插入失败
if(L.length >= L.Maxsize) return false; //线性表已满,插入失败
for(int j = L.length; j>=i; j--) {
L.data[j] = L.data[j-1]; //向后移动1位
}
L.data[i-1] = elem;
L.length++;
return true; //插入成功
}
//删除,删除在L中的位序为i的元素
bool Delete(SqList &L, int i, ElemType &elem) {
if(i<1 || i>L.length) return false; //i值超边界,删除失败
elem = L.data[i-1];
for(int j = i; j<L.length; j++) {
L.data[j-1] = L.data[j]; //将数据元素后移一位
}
L.length--; //长度-1
return true;
}
//按位查找,查找L中位序为i的元素的值,并获取该值
bool SearchByOrder(SqList L, int i, ElemType &result) {
if(i<1 || i>L.length) return false; //i值超边界,查找失败
result = L.data[i-1];
return true;
}
//按值查找,找到第一个就返回其位序
bool SearchByElem(SqList L, int &order, ElemType elem) {
for(int i = 0; i<L.length; i++) {
if(L.data[i] == elem) {
order = i+1;
return true;
}
}
return false;
}
//按位修改,修改L中位序为i的元素的值为elem
bool UpdateByOrder(SqList &L, int i, ElemType elem) {
if(i<1 || i>L.length) return false; //i值超边界,修改失败
L.data[i-1] = elem;
return true;
}
//按值修改,修改L中元素的值为elem的元素的值为change
bool UpdateByElem(SqList &L, ElemType elem, ElemType change) {
for(int i = 0; i<L.length; i++) {
if(L.data[i] == elem) L.data[i] = change;
}
return true;
}
int main() {
SqList list;
InitList(list);
IncreaseSize(list, 5);
for(int i = 0; i<6; i++) {
ElemType temp = rand() % 10 + 1; //生成一个1~10的数
if(Insert(list, i+1, temp)) {
printf("数字%d插入成功,list长度为%d!\n", temp, list.length);
} else {
printf("插入失败!\n");
}
}
for(int i = 0; i<list.length; i++) {
printf("%d ", list.data[i]);
}
puts("");
ElemType result = -1;
if(SearchByOrder(list, 3, result)) {
printf("查找成功,目标元素为%d\n", result);
} else {
printf("查找失败!\n");
}
int order = -1;
if(SearchByElem(list, order, 5)) {
printf("查找成功,目标元素位序为%d\n", order);
} else {
printf("查找失败!\n");
}
if(UpdateByOrder(list, 2, 5)) {
printf("修改成功!\n");
} else {
printf("修改失败!\n");
}
for(int i = 0; i<list.length; i++) {
printf("%d ", list.data[i]);
}
puts("");
if(UpdateByElem(list, 5, 11)) {
printf("修改成功!\n");
} else {
printf("修改失败!\n");
}
for(int i = 0; i<list.length; i++) {
printf("%d ", list.data[i]);
}
puts("");
ElemType e = -1;
if(Delete(list, 2, e)) {
printf("删除成功,已删除数字%d\n", e);
} else {
printf("删除失败!\n");
}
return 0;
}