详解顺序表功能

前言

随着我们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;
}

总结

大家可以对比一下静态和动态的区别,动态只是在静态的基础上稍稍的进行一下改进,增加一个扩容的函数,其他的一模一样,我们下一个将会用一个小项目来加深我们顺序表印象,这是我们的第一个数据结构,我们还要用它和后面的链表进行对比。学习每个结构的优缺点,是我们在不同问题上选择最优的方案。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/17438.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

使用WebMvcConfigurationSupport后导致原来返回的json数据变为了xml的解决方法

问题 未使用WebMvcConfigurationSupport拦截时返回的数据都是JSON格式&#xff0c;使用WebMvcConfigurationSupport做拦截后数据的返回变为了XML的格式。 原因 在Spring框架中&#xff0c;WebMvcConfigurationSupport 是一个类&#xff0c;它可以用于自定义Spring MVC的配置…

【模型预测控制MPC】使用离散、连续、线性或非线性模型对预测控制进行建模(Matlab代码实现)

&#x1f4a5;&#x1f4a5;&#x1f49e;&#x1f49e;欢迎来到本博客❤️❤️&#x1f4a5;&#x1f4a5; &#x1f3c6;博主优势&#xff1a;&#x1f31e;&#x1f31e;&#x1f31e;博客内容尽量做到思维缜密&#xff0c;逻辑清晰&#xff0c;为了方便读者。 ⛳️座右铭&a…

LLM微调 | Adapter: Parameter-Efficient Transfer Learning for NLP

目的:大模型预训练+微调范式,微调成本高。adapter只只微调新增的小部分参数【但adapter增加了模型层数,引入了额外的推理延迟。】 Adapters最初来源于CV领域的《Learning multiple visual domains with residual adapters》一文,其核心思想是在神经网络模块基础上添加一些残…

【Python机器学习】实验04(1) 多分类(基于逻辑回归)实践

文章目录 多分类以及机器学习实践如何对多个类别进行分类1.1 数据的预处理1.2 训练数据的准备1.3 定义假设函数&#xff0c;代价函数&#xff0c;梯度下降算法&#xff08;从实验3复制过来&#xff09;1.4 调用梯度下降算法来学习三个分类模型的参数1.5 利用模型进行预测1.6 评…

CS162 13-17 虚拟内存

起源 为啥我们需要虚拟内存-----------需求是啥&#xff1f; 可以给程序提供一个统一的视图&#xff0c;比如多个程序运行同一个代码段的话&#xff0c;同一个kernel&#xff0c;就可以直接共享 cpu眼里的虚拟内存 无限内存的假象 设计迭代过程 为啥这样设计&#xff1f; 一…

安装vite-plugin-svg-icons

找不到合适的图标&#xff0c;如何使用其他的svg图标&#xff1f; 安装vite-plugin-svg-icons 使用svg-icon&#xff0c;即可使用iconfont等svg图标库 安装及使用过程 一、安装依赖二、在src/assets新建svg目录三、vite.config.js中进行配置四、在main.js中导入文件五、在compo…

Redis篇

文章目录 Redis-使用场景1、缓存穿透2、缓存击穿3、缓存雪崩4、双写一致5、Redis持久化6、数据过期策略7、数据淘汰策略 Redis-分布式锁1、redis分布式锁&#xff0c;是如何实现的&#xff1f;2、redisson实现的分布式锁执行流程3、redisson实现的分布式锁-可重入4、redisson实…

技术复盘(5)--git

技术复盘--git 资料地址原理图安装配置基本命令分支命令对接gitee练习:远程仓库操作 资料地址 学习地址-B站黑马&#xff1a;https://www.bilibili.com/video/BV1MU4y1Y7h5 git官方&#xff1a;https://git-scm.com/ gitee官网&#xff1a;https://gitee.com/ 原理图 说明&am…

Twitter 劲敌 Threads,“魔改”了哪些 Python 技术栈?

Meta 创始人 Mark Zuckerberg 昨天在 Threads 上宣布&#xff0c;周三正式上线的 Threads 注册量已突破三千万。 Threads 是一个基本文本的社交应用&#xff0c;由 Instagram 团队开发。虽然它在功能上还无法真正取代 Twitter&#xff0c;但目前看来事实上已是 Twitter 的替代方…

耗时3个月,线下访谈30+ csdn大佬,规划出了我的云原生学习路线

前言 大家好&#xff0c;我是沐风晓月&#xff0c;最近线下拜访不少云原生方向的大佬和csdn其他方向的大佬&#xff0c;受益匪浅。 于是在 5月23日&#xff0c;我定下来自己的目标&#xff1a; 我的目标&#xff1a; 可以说&#xff0c;这个世代给予的机遇&#xff0c;让我…

对外接口签名生成方式

接口签名生成方式 前言 当某个系统对外部系统提供接口访问时&#xff0c;为提高接口请求安全性&#xff0c;往往会在接口访问时添加签名&#xff0c;当外部系统访问本系统签名验证成功时才能正常返回数据&#xff0c;一般接口提供方会与外部系统提前约定好&#xff0c;不同外…

基于飞桨paddle的极简方案构建手写数字识别模型测试代码

基于飞桨paddle的极简方案构建手写数字识别模型测试代码 原始测试图片为255X252的图片 因为是极简方案采用的是线性回归模型&#xff0c;所以预测结果数字不一致 本次预测的数字是 [[3]] 测试结果&#xff1a; PS E:\project\python> & D:/Python39/python.exe e:/pro…

你知道HTTP与HTTPS有什么区别吗?

作者&#xff1a;Insist-- 个人主页&#xff1a;insist--个人主页 作者会持续更新网络知识和python基础知识&#xff0c;期待你的关注 目录 一、什么是HTTP&#xff1f; 二、什么是HTTPS&#xff1f; 三、HTTPS 的工作原理 1、客户端发起 HTTPS 请求 2、服务端的配置 3、…

Android如何用系统签名打包应用

前言 应用使用系统签名可以在用户不需要手动授权的情况下自动获取权限。适合一些定制系统中集成apk的方案商。 步骤 需要在AndroidManifest.xml中添加共享系统进程属性&#xff1a; android:sharedUserId"android.uid.system"如下图所示&#xff1a; 找到系统定制…

windows环境安装elasticsearch+kibana并完成JAVA客户端查询

下载elasticsearch和kibana安装包 原文连接&#xff1a;https://juejin.cn/post/7261262567304298554 elasticsearch官网下载比较慢&#xff0c;有时还打不开&#xff0c;可以通过https://elasticsearch.cn/download/下载&#xff0c;先找到对应的版本&#xff0c;最好使用迅…

LeetCode每日一题——1331.数组序号转换

题目传送门 题目描述 给你一个整数数组 arr &#xff0c;请你将数组中的每个元素替换为它们排序后的序号。 序号代表了一个元素有多大。序号编号的规则如下&#xff1a; 序号从 1 开始编号。一个元素越大&#xff0c;那么序号越大。如果两个元素相等&#xff0c;那么它们的…

集团MySQL的酒店管理系统

酒店管理系统 概述 基于Spring Spring MVC MyBatis的酒店管理系统&#xff0c;主要实现酒店客房的预定、入住以及结账等功能。使用Maven进行包管理。 用户端主要功能包括&#xff1a; 登录注册、客房预订、客房评论&#xff08;编写评论和查看评论&#xff09; 后台管理主要…

Java maven的下载解压配置(保姆级教学)

mamen基本概念 Maven项目对象模型(POM)&#xff0c;可以通过一小段描述信息来管理项目的构建&#xff0c;报告和文档的项目管理工具软件。 Maven 除了以程序构建能力为特色之外&#xff0c;还提供高级项目管理工具。由于 Maven 的缺省构建规则有较高的可重用性&#xff0c;所以…

【已解决】windows7添加打印机报错:加载Tcp Mib库时的错误,无法加载标准TCP/IP端口的向导页

windows7 添加打印机的时候&#xff0c;输入完打印机的IP地址后&#xff0c;点击下一步&#xff0c;报错&#xff1a; 加载Tcp Mib库时的错误&#xff0c;无法加载标准TCP/IP端口的向导页 解决办法&#xff1a; 复制以下的代码到新建文本文档.txt中&#xff0c;然后修改文本文…

【机器学习】 奇异值分解 (SVD) 和主成分分析 (PCA)

一、说明 在机器学习 &#xff08;ML&#xff09; 中&#xff0c;一些最重要的线性代数概念是奇异值分解 &#xff08;SVD&#xff09; 和主成分分析 &#xff08;PCA&#xff09;。收集到所有原始数据后&#xff0c;我们如何发现结构&#xff1f;例如&#xff0c;通过过去 6 天…