前言
随着我们C语言的不断深入学习,我们要开始学习一点数据结构来增加我们的内功了,虽说现在很多高级语言的顺序表,链表等可以不用自己实现,但在C语言中是需要我们自己来实现的,这并不能说明C语言和其他语言比C语言很拉跨,我们通过C语言模拟实现一下这种数据结构可以让我们更加深入理解一个其他语言中我们经常使用的一些内容。当然,我们所要学习的不仅仅是它们的实现,更重要的是它们的思想。
一、顺序表
顺序表是在计算机内存中以数组的形式保存的线性表,线性表的顺序存储是指用一组地址连续的存储单元依次存储线性表中的各个元素、使得线性表中在逻辑结构上相邻的数据元素存储在相邻的物理存储单元中。顺序表是将表中的结点依次存放在计算机内存中一组地址连续的存储单元中。
在内存中的存储情况如下:
二、顺序表的功能
常见的顺序表的功能有增加,删除,查找,修改。还有一些其他的功能:排序,合并,返回元素个数等。
增加:增加可以分为头部增加和尾部增加和指定位置增加,指定位置的增加又可以分为在指定元素左边或右边进行增加。
删除:删除可以分为头部删除和尾部删除和指定位置删除。
查找:顾名思义,查找指定元素或位置。
修改:把指定元素或位置的元素修改为其他元素。
排序:把结构中的数据进行排序。
合并:把多个结构合并为一个。
返回元素个数:把顺序表中的具体的元素个数显示出来。
三、顺序表的实现
顺序表的实现又有静态和动态之分。
静态的顺序表的大小已经固定,而动态顺序表的大小是可以发生变化的,静态的顺序表使用局限性太大,会出现空间浪费和空间不够的尴尬局面。而动态顺序表则解决静态顺序表的问题。但我们还是要学习一下静态顺序表,观察动态和静态的异同点。我们的函数实现都是通过传址进行的,不用传值是避免进行大规模的拷贝,进而造成时间和空间的浪费。
1.顺序表的静态实现
顺序表的创建
#define MAX 10 //存放元素的最大个数
typedef int datatype;//把类型重新命名,方便后面的参数类型的修改,增加代码的鲁棒性
typedef struct SList//定义顺序表的结构体
{
int position;//表中元素的个数和位置
datatype arr[MAX];//用来存放元素
}SL;//把结构体类型重新命名为SL
我们实现的是一个简单的顺序表,存储类型是int类型的数据,放在的int类型的数组中。
顺序表的初始化,打印和销毁
void Initialization(SL *sl)//初始话顺序表
{
assert(sl);//进行断言,防止传入的指针或者地址不合法而造成的错误
sl->position = 0;//开始没有元素,所以位置为0
}
SL* InitializationList()
{
SL* sl = (SL*)malloc(sizeof(SL));
sl->position = 0;
return sl;
}
void print(const SL *sl)//打印顺序表
{
assert(sl);//进行断言
for (int i = 0; i < sl->position; i++)
{
printf("%d ", sl->arr[i]);
}
printf("\n");
}
void Destroy1(SL* sl)//销毁顺序表
{
assert(sl);
sl->position = 0;//把元素个数置为0
}
void Destroy2(SL* sl)//销毁顺序表
{
assert(sl);
free(sl);//把空间进行释放
sl->position = 0;
sl = NULL;//把指针置空
}
顺序表的初始化有两种,一种是在主函数中创建完成,通过第一个函数进行初始化,一种是在函数中进行创建并且通过返回类型返回给主函数。销毁函数也是针对上面的两种初始化函数而进行设计的,第一个不是动态开辟的,所以不可以用free,第二个是在函数中动态开辟的空间,所以需要free进行释放。防止造成内存泄漏。
这里我们不是静态实现吗?为什么还会用动态开辟呢?
这里的静态是指数据的储存大小是不变的,我们动态开辟的一块结构体所需要的内存是固定不变的,结构体里面数组的大小是固定的,他的存储元素个数和我们结构体是否是动态开辟的无关。和数组大小有关。
我们进行的是传址调用,当我们不需要对元素进行修改时,我们可以加上const进行修饰,防止我们进行修改。
顺序表的增加和删除
我们先看代码的实现:
void Popback(SL* sl, datatype n)//头插
{
assert(sl);//进行断言
if (sl->position < MAX )//判断数组中是否还有空间
{
int sum = sl->position;//创建一个变量等于数组中插入元素后的位置
for (; sum > 0; sum--)
{
sl->arr[sum] = sl->arr[sum - 1];//把插入元素后的前一个元素向后移动
}
sl->arr[sum] = n;//把数组中的一个元素设置为传入的值
sl->position++;//元素位置向后移动
}
else
{
printf("空间不足\n");
return;
}
}
void Popfornt(SL* sl)//头删
{
assert(sl);//进行断言
int sum = 1;
for (; sum < sl->position; sum++)
{
sl->arr[sum - 1] = sl->arr[sum];//把数组中元素进行前移进行覆盖
}
sl->position--; //元素位置向前移动
}
void Pushback(SL* sl, datatype n)//尾插
{
assert(sl);
if (sl->position < MAX)//判断数组中是否还有空间
{
sl->arr[sl->position] = n;//直接在数组后插入元素
sl->position++;//元素位置向后移动
}
else
{
printf("空间不足\n");
return;
}
}
void Pushfornt(SL* sl)//尾删
{
assert(sl);
sl->position--;//直接进行个数减一,表明这个位置可以进行覆盖
}
头插和头删:把元素插入在数组的第一个位置,所以需要第一个位置后面的所有元素向后进行移动,头删则需要把第一个位置后面的所有元素向前覆盖。
尾插和尾删:直接把元素插入在最后位置或者直接对元素个数进行减一达到尾插和尾删的效果。
void test1()//进行头删,尾删,头插,尾插。
{
SL sl;
Initialization(&sl);//初始话顺序表
Popback(&sl, 1);//进行头插
Popback(&sl, 2);
Popback(&sl, 3);
Popback(&sl, 4);
Popback(&sl, 5);
printf("头插后:");
print(&sl);
Pushback(&sl, 6);//进行尾插
Pushback(&sl, 7);
Pushback(&sl, 8);
Pushback(&sl, 9);
Pushback(&sl, 10);
Pushback(&sl, 11);//超过数组中的大小,空间不足
printf("尾插后:");
print(&sl);//打印顺序表的元素
Pushfornt(&sl);//进行尾删
printf("尾删后:");
print(&sl);
Popfornt(&sl);//进行头删
printf("头删后:");
print(&sl);
Destroy1(&sl);
}
int main()
{
test1();
return 0;
}
顺序表的插入,查询和修改
顺序表的插入和修改都依赖于查询功能,所以我们要先实现顺序表的查询,修改我们也可以直接修改我们想修改的下标的元素,但是我们一般不知道我们要修改的下标的元素是什么,所以需要查找功能,如即将要实现的通讯录,这个我们可以通过姓名查找到该人物的下标,进而修改我们的数值。
下面是我们的实现代码:
int Seeklift(const SL *sl, datatype n)//左查找
{
assert(sl);
int sum = 0;
for (sum = 0; sum < sl->position; sum++)
{
if (sl->arr[sum] == n)//判断改下标的元素是否和我们所需的元素相同。
{
return sum+1;//数组下标从0开始,所以返回位置进行加1
}
}
return -1;
}
int Seekright(const SL *sl, datatype n)//右查找
{
assert(sl);
int sum = sl->position-1;
for (; sum >0; sum--)
{
if (sl->arr[sum] == n)
{
return sum+1;
}
}
return -1;
}
void Modify(SL *sl, int n, datatype num)//修改
{
assert(sl);
sl->arr[n - 1] = num;//把坐标还原为数组的下标
}
void Insertpos(SL* sl, int n, datatype num)//按数组位置插入,在元素左插入
{
assert(sl);
//判断数组中是否还有空间
if (sl->position < MAX)
{
int sum = sl->position;//创建一个变量等于数组中插入元素后的位置
for (; sum > n-1; sum--)//把坐标还原为数组的下标
{
sl->arr[sum] = sl->arr[sum - 1];//把插入元素后的前一个元素向后移动
}
sl->arr[n-1] = num;//把数组中的一个元素设置为传入的值
sl->position++;//元素位置向后移动
}
else
{
printf("空间不足\n");
return;
}
}
void Insertright(SL* sl, int n, datatype num)//在元素右插入
{
assert(sl);
//判断数组中是否还有空间
if (sl->position < MAX)
{
int sum = sl->position;//创建一个变量等于数组中插入元素后的位置
for (; sum > n; sum--)//把坐标还原为数组的下标
{
sl->arr[sum] = sl->arr[sum - 1];//把插入元素后的前一个元素向后移动
}
sl->arr[n] = num;//把数组中的一个元素设置为传入的值
sl->position++;//元素位置向后移动
}
else
{
printf("空间不足\n");
return;
}
}
查找:我们把查找分为从左查找和从右查找,分别为左边第一个找到我们所需要的数和右边第一个找到我们所需要的数。我们把返回值设为int类型,如果找不到我们返回 -1。只有找到我们所需要的元素时才可以进行修改。
插入:我们分为左边插入和右边插入,左插入和正常插入逻辑一样,所以默认也是左插入,右插入只是边界和左插入不同,代码实现逻辑一模一样。
修改:修改则是通过我们查找到的元素的下标来进行修改的。如果找不到则不能进行修改。
下面是我们的测试函数:
void test2()//进行插入,查询,修改
{
SL sl;
Initialization(&sl);//初始话顺序表
Popback(&sl, 1);//进行头插
Popback(&sl, 2);
Popback(&sl, 2);
Popback(&sl, 2);
Popback(&sl, 3);
printf("修改前:");
print(&sl);
if (-1 != Seeklift(&sl, 2))//左查找
{
printf("左查找查到的下标:%d\n", Seeklift(&sl, 2));
Modify(&sl, Seeklift(&sl, 2), 7);//修改
printf("修改后:");
print(&sl);
}
if (-1 != Seekright(&sl, 2))//右查找
{
printf("右查找查到的下标:%d\n", Seekright(&sl, 2));
Modify(&sl, Seekright(&sl, 2), 9);
printf("修改后:");
print(&sl);
}
Insertpos(&sl, 3, 8);//按数组位置插入
printf("按数组位置插入后:");
print(&sl);
Insertpos(&sl, Seeklift(&sl, 2), 3);//在元素左插入
printf("在元素左插入后:");
print(&sl);
Insertright(&sl, Seeklift(&sl, 2), 4);//在元素右插入
printf("在元素右插入后:");
print(&sl);
Destroy1(&sl);
}
int main()
{
test2();
return 0;
}
我们测试的逻辑是先进行头插5个元素,分别进行左右查找并且进行修改,最后在进行左右插入。
顺序表的升降排序和元素个数的返回
这里的排序我们都是使用的冒泡排序,等到排序章节会改造为算法更优的排序方法。这里的升排序和降排序唯一的区别就是我们判断大小方式的不同。而我们返回元素个数则是直接返回我们结构体元素下标的总位置,这个也是我们元素的总个数。
void Sortmax(SL* sl)//升排序
{
assert(sl);
//冒泡排序
int i, j, temp;
for (i = 1; i < sl->position; i++)
{
for (j = 0; j < sl->position - i; j++)
{
if (sl->arr[j] > sl->arr[j + 1])
{
temp = sl->arr[j];
sl->arr[j] = sl->arr[j + 1];
sl->arr[j + 1] = temp;
}
}
}
}
void Sortmin(SL* sl)//降排序
{
assert(sl);
//冒泡排序
int i, j, temp;
for (i = 1; i < sl->position; i++)
{
for (j = 0; j < sl->position - i; j++)
{
if (sl->arr[j] < sl->arr[j + 1])
{
temp = sl->arr[j];
sl->arr[j] = sl->arr[j + 1];
sl->arr[j + 1] = temp;
}
}
}
}
int Size(const SL *sl)//返回顺序表中元素个数
{
assert(sl);
return sl->position;
}
下面是我们的测试代码:
void test3()//进行升降排序
{
SL* sl;
sl = InitializationList();//初始话顺序表
Popback(sl, 1);
Popback(sl, 5);
Popback(sl, 3);
Popback(sl, 4);
Popback(sl, 2);
printf("排序前:");
print(sl);
Sortmax(sl);//升序排列
printf("升序后:");
print(sl);
Sortmin(sl);//降序排列
printf("降序后:");
print(sl);
printf("元素总个数:%d", Size(sl));
Destroy2(sl);
}
int main()
{
test3();
return 0;
}
这里我们用的第二种初始化方式,所以进行传参是不需要在加取地址符了。销毁函数也是用的第二个,防止内存泄漏。
顺序表的合并
顺序表的合并我们是进行升序合并的,而且也是两个升序的顺序表进行合并的。
下面是我们的思路:
SL* Merge(const SL* sl1,const SL* sl2)//合并两个有序的顺序表
{
assert(sl1 && sl2);
SL* sl = (SL*)malloc(sizeof(SL));
sl->position = 0;
int i = 0;//用来访问数组中的元素
int j = 0;
while (i < sl1->position && j < sl2->position)//当任意一个数组中的元素到达尾部时结束循环
{
if (sl1->arr[i] <= sl2->arr[j])//判断数组中的元素大小,谁小谁进行尾插
{
Pushback(sl, sl1->arr[i]);
i++;
}
else
{
Pushback(sl, sl2->arr[j]);
j++;
}
}
if (i >= sl1->position)//判断哪一个数组到达了尾部,把没到达尾部的数组元素进行尾插
{
while(j < sl2->position)
{
Pushback(sl, sl2->arr[j]);
j++;
}
}
else
{
while (i < sl1->position)
{
Pushback(sl, sl1->arr[i]);
i++;
}
}
return sl;
}
合并的思路是先创建一个结构体,然后两个结构体中的数组进行比较,小的一方插入到我们创建的结构体中。然后当一个顺序表的元素用完时,把另一个顺序表中的元素全部插入到我们创建的结构体中。
下面是我们的测试代码:
void test4()//合并两个有序的顺序表
{
SL *sl;
SL sl1 = { 3,{1,6,7}};
SL sl2 = { 3,{2,3,5}};
printf("s1:");
print(&sl1);
printf("s2:");
print(&sl2);
sl = Merge(&sl1, &sl2);//合并两个有序的顺序表
printf("合并后:");
print(sl);
Destroy2(sl);
Destroy1(&sl1);
Destroy1(&sl2);
}
int main()
{
test4();
return 0;
}
1.顺序表的动态实现
顺序表的创建
顺序表的动态实现本质是通过指针来实现的。
typedef int datatype;
typedef struct SList//定义顺序表的结构体
{
datatype* num;//数据类型的指针
int sz;//数据元素的个数
int max;//数据元素的最大个数
}TSL;
这里我们把之前的数组改为相应的指针类型,但是额外的加入了一个元素的最大个数,元素的最大个数是用来判断我们当前的存储是否已满,存储满的话就要进行扩容了。
顺序表的初始化,打印和销毁
void InitIalization(TSL* sl)//初始话顺序表
{
sl->max = 4;//数据初始元素最大为4个
sl->sz = 0;//数据的起始元素为0个
sl->num = (datatype*)malloc(sizeof(datatype) * sl->max);
}
这里我们把开始的容量设置为4,当我们元素的个数和我们的容量大小相同时,我们就要进行扩容了。这里我们只写了初始化的代码,打印和销毁代码和静态的实现方式一模一样。
顺序表的增加和删除
void Expansion(TSL* sl)//进行扩容
{
datatype* sl1 = (datatype*)realloc(sl->num, sizeof(datatype) * (sl->max * 2));
{
if (sl1 == NULL)//判断是否开辟空间成功
{
perror("sl1 err\n");//开辟失败进行报错
return;
}
else
{
sl->num = sl1;
sl->max *= 2;//更改容量的大小
}
}
}
void PopBack(TSL* sl, datatype n)//头插
{
assert(sl);
if (sl->sz == sl->max)//判断空间是否已满,满则扩容
{
Expansion(&sl);//传递的是sl的地址,不是sl存放的地址
}
int i = 0;
for (i = sl->sz; i > 0; i--)
{
sl->num[i] = sl->num[i - 1];//把插入元素后的前一个元素向后移动
}
sl->num[0] = n;//把数组中的一个元素设置为传入的值
sl->sz++;//元素位置向后移动
}
这里我们可以看到,进行增加的代码逻辑和静态的一模一样,只是多了一个扩容的操作。
注意:我们要注意增容可能会失败,所以不能直接用我们的地址,而是先创建一个结构体,创建成功后在把我们的创建赋给我们原结构体。
我们这里只演示了头插的代码,尾插的逻辑和静态一样,只是把判断代码换成是否要扩容的判断。
下面是演示代码:
void test5()
{
TSL sl;
InitIalization(&sl);//初始话顺序表
PopBack(&sl, 4);
PopBack(&sl, 3);
PopBack(&sl, 2);
PopBack(&sl, 1);
Print(&sl);
PushBack(&sl, 5);
PushBack(&sl, 6);
PushBack(&sl, 7);
PushBack(&sl, 8);
Print(&sl);
PopFornt(&sl);
Print(&sl);
PushFornt(&sl);
Print(&sl);
DEstroy(&sl);
}
int main()
{
test5();
}
动态顺序表的总结
动态顺序表只是在判断是否已满的地方多加一个扩容的函数,其他的代码和静态一模一样,思路也一模一样,我们把静态中的数组换为相应的指针,可以通过动态开辟的方式来进行增容。我们的增容是对结构体中的指针进行增容,而非对结构体本身进行增容。
1.顺序表的动态代码
动态测试代码
//main1.c 动态测试代码
#include"main1.h"
void test5()
{
TSL sl;
InitIalization(&sl);//初始话顺序表
PopBack(&sl, 4);
PopBack(&sl, 3);
PopBack(&sl, 2);
PopBack(&sl, 1);
Print(&sl);
PushBack(&sl, 5);
PushBack(&sl, 6);
PushBack(&sl, 7);
PushBack(&sl, 8);
Print(&sl);
PopFornt(&sl);
Print(&sl);
PushFornt(&sl);
Print(&sl);
DEstroy(&sl);
}
void test6()
{
TSL sl;
InitIalization(&sl);//初始话顺序表
PopBack(&sl, 3);
PopBack(&sl, 2);
PopBack(&sl, 2);
PopBack(&sl, 2);
PopBack(&sl, 1);
Print(&sl);
if (-1 != SeekLift(&sl, 2))//左查找
{
printf("%d\n", SeekLift(&sl, 2));
MODify(&sl, SeekLift(&sl, 2), 20);//修改
Print(&sl);
}
if (-1 != SeekRight(&sl, 2))//右查找
{
printf("%d\n", SeekRight(&sl, 2));
MODify(&sl, SeekRight(&sl, 2), 200);
Print(&sl);
}
InsertPos(&sl, 3, 8);//按数组位置插入
Print(&sl);
InsertPos(&sl, SeekLift(&sl, 2), 33);//在元素左插入
Print(&sl);
InsertRight(&sl, SeekLift(&sl, 2), 44);//在元素右插入
Print(&sl);
printf("%d\n", SIze(&sl));
DEstroy(&sl);
}
void test7()
{
TSL sl;
InitIalization(&sl);//初始话顺序表
PopBack(&sl, 1);
PopBack(&sl, 5);
PopBack(&sl, 3);
PopBack(&sl, 4);
PopBack(&sl, 2);
Print(&sl);
SortMax(&sl);//升序排列
Print(&sl);
SortMin(&sl);//降序排列
Print(&sl);
DEstroy(&sl);
}
void test8()
{
TSL* sl;
TSL sl1;
InitIalization(&sl1);//初始话顺序表
TSL sl2;
InitIalization(&sl2);//初始话顺序表
PushBack(&sl1, 1);
PushBack(&sl1, 6);
PushBack(&sl1, 7);
PushBack(&sl2, 2);
PushBack(&sl2, 3);
PushBack(&sl2, 5);
Print(&sl1);
Print(&sl2);
sl = MErge(&sl1, &sl2);//合并两个有序的顺序表
Print(sl);
DEstroy(sl);
DEstroy(&sl1);
DEstroy(&sl2);
}
int main()
{
test5();
test6();
test7();
test8();
return 0;
}
动态实现头文件
//main1.h 动态头文件
#pragma once
#define _CRT_SECURE_NO_WARNINGS
#include<stdio.h>
#include<stdlib.h>
#include<assert.h>
#define MAX 10
typedef int datatype;
typedef struct SList//定义顺序表的结构体
{
datatype* num;//数据类型的指针
int sz;//数据元素的个数
int max;//数据元素的最大个数
}TSL;
void InitIalization(TSL* sl);//初始话顺序表
void PopBack(TSL* sl, datatype n);//头插
void PopFornt(TSL* sl);//头删
void PushBack(TSL* sl, datatype n);//尾插
void PushFornt(TSL* sl);//尾删
int SeekLift(const TSL* sl, datatype n);//左查找
int SeekRight(const TSL* sl, datatype n);//右查找
void MODify(TSL* sl, int n, datatype num);//修改
void InsertPos(TSL* sl, int n, datatype num);//按数组位置插入,在元素左插入
void InsertRight(TSL* sl, int n, datatype num);//在元素右插入
void SortMax(TSL* sl);//升排序
void SortMin(TSL* sl);//降排序
int SIze(const TSL* sl);//返回顺序表中元素个数
TSL* MErge(TSL* sl1, TSL* sl2);//合并两个有序的顺序表
void Print(const TSL* sl);//打印顺序表
void DEstroy(TSL* sl);//销毁顺序表
动态具体函数的实现
//test1.c 动态具体函数的实现
#include"main1.h"
void InitIalization(TSL* sl)//初始话顺序表
{
sl->max = 4;//数据初始元素最大为4个
sl->sz = 0;//数据的起始元素为0个
sl->num = (datatype*)malloc(sizeof(datatype) * sl->max);
}
void Expansion(TSL* sl)//进行扩容
{
datatype* sl1 = (datatype*)realloc(sl->num, sizeof(datatype) * (sl->max * 2));
{
if (sl1 == NULL)//判断是否开辟空间成功
{
perror("sl1 err\n");//开辟失败进行报错
return;
}
else
{
sl->num = sl1;
sl->max *= 2;//更改容量的大小
}
}
}
void PopBack(TSL* sl, datatype n)//头插
{
assert(sl);
if (sl->sz == sl->max)//判断空间是否已满,满则扩容
{
Expansion(sl);
}
int i = 0;
for (i = sl->sz; i > 0; i--)
{
sl->num[i] = sl->num[i - 1];//把插入元素后的前一个元素向后移动
}
sl->num[0] = n;//把数组中的一个元素设置为传入的值
sl->sz++;//元素位置向后移动
}
void PopFornt(TSL* sl)//头删
{
assert(sl);
int i = 0;
for (i = 0; i < sl->sz; i++)
{
sl->num[i] = sl->num[i + 1];
}
sl->sz--;
}
void PushBack(TSL* sl, datatype n)//尾插
{
assert(sl);
if (sl->sz == sl->max)
{
Expansion(sl);
}
sl->num[sl->sz] = n;
sl->sz++;
}
void PushFornt(TSL* sl)//尾删
{
assert(sl);
sl->sz--;
}
int SeekLift(const TSL* sl, datatype n)//左查找
{
assert(sl);
int i = 0;
for (i = 0; i < sl->sz; i++)
{
if (sl->num[i] == n)
{
return i + 1;//数组下标从0开始,所以返回位置进行加1
}
}
return -1;
}
int SeekRight(const TSL* sl, datatype n)//右查找
{
assert(sl);
int i = sl->sz - 1;
for (; i > 0; i--)
{
if (sl->num[i] == n)
{
return i + 1;
}
}
return -1;
}
void MODify(TSL* sl, int n, datatype num)//修改
{
assert(sl);
sl->num[n - 1] = num;
}
void InsertPos(TSL* sl, int n, datatype num)//按数组位置插入,在元素左插入
{
assert(sl);
//判断数组中是否还有空间
if (sl->sz == sl->max)
{
Expansion(sl);
}
int sum = sl->sz;//创建一个变量等于数组中插入元素后的位置
for (; sum > n - 1; sum--)//把坐标还原为数组的下标
{
sl->num[sum] = sl->num[sum - 1];//把插入元素后的前一个元素向后移动
}
sl->num[n - 1] = num;//把数组中的一个元素设置为传入的值
sl->sz++;//元素位置向后移动
}
void InsertRight(TSL* sl, int n, datatype num)//在元素右插入
{
assert(sl);
//判断数组中是否还有空间
if (sl->sz == sl->max)
{
Expansion(sl);
}
int sum = sl->sz;//创建一个变量等于数组中插入元素后的位置
for (; sum > n ; sum--)//把坐标还原为数组的下标
{
sl->num[sum] = sl->num[sum - 1];//把插入元素后的前一个元素向后移动
}
sl->num[n] = num;//把数组中的一个元素设置为传入的值
sl->sz++;//元素位置向后移动
}
void SortMax(TSL* sl)//升排序
{
assert(sl);
//冒泡排序
int i, j, temp;
for (i = 1; i < sl->sz; i++)
{
for (j = 0; j < sl->sz - i; j++)
{
if (sl->num[j] > sl->num[j + 1])
{
temp = sl->num[j];
sl->num[j] = sl->num[j + 1];
sl->num[j + 1] = temp;
}
}
}
}
void SortMin(TSL* sl)//降排序
{
assert(sl);
//冒泡排序
int i, j, temp;
for (i = 1; i < sl->sz; i++)
{
for (j = 0; j < sl->sz - i; j++)
{
if (sl->num[j] < sl->num[j + 1])
{
temp = sl->num[j];
sl->num[j] = sl->num[j + 1];
sl->num[j + 1] = temp;
}
}
}
}
int SIze(const TSL* sl)//返回顺序表中元素个数
{
assert(sl);
return sl->sz;
}
TSL* MErge(TSL* sl1, TSL* sl2)//合并两个有序的顺序表
{
assert(sl1 && sl2);
TSL* sl = (TSL*)malloc(sizeof(TSL));
if (sl == NULL)//判断是否为空
{
perror("malloc: ");//报错且退出
return;
}
InitIalization(sl);
int i = 0;//用来访问数组中的元素
int j = 0;
while (i < sl1->sz && j < sl2->sz)//当任意一个数组中的元素到达尾部时结束循环
{
if (sl1->num[i] <= sl2->num[j])//判断数组中的元素大小,谁小谁进行尾插
{
if (sl->sz == sl->max)
{
Expansion(sl);
}
PushBack(sl, sl1->num[i]);
i++;
}
else
{
if (sl->sz == sl->max)
{
Expansion(sl);
}
PushBack(sl, sl2->num[j]);
j++;
}
}
if (i >= sl1->sz)//判断哪一个数组到达了尾部,把没到达尾部的数组元素进行尾插
{
while (j < sl2->sz)
{
if (sl->sz == sl->max)
{
Expansion(sl);
}
PushBack(sl, sl2->num[j]);
j++;
}
}
else
{
while (i < sl1->sz)
{
if (sl->sz == sl->max)
{
Expansion(sl);
}
PushBack(sl, sl1->num[i]);
i++;
}
}
return sl;
}
void Print(const TSL* sl)//打印顺序表
{
int i = 0;
for (i = 0; i < sl->sz; i++)
{
printf("%d ", sl->num[i]);
}
printf("\n");
}
void DEstroy(TSL* sl)//销毁顺序表
{
assert(sl);
free(sl->num);
sl->num = NULL;
sl->max = 0;
sl->sz = 0;
}
总结
大家可以对比一下静态和动态的区别,动态只是在静态的基础上稍稍的进行一下改进,增加一个扩容的函数,其他的一模一样,我们下一个将会用一个小项目来加深我们顺序表印象,这是我们的第一个数据结构,我们还要用它和后面的链表进行对比。学习每个结构的优缺点,是我们在不同问题上选择最优的方案。