一、线性表介绍
1、线性结构
在数据元素的非空有限集中:
-
存在唯一的一个被称做“第一个”的数据元素
-
存在唯一的一个被称做“最后一个”的数据元素
-
除第一个之外,集合中的每个数据元素均只有一个前驱
-
除最后一个之外,集合中的每个数据元素均只有一个后继
2、线性表
线性表是n个数据元素的有限序列,同一线性表中的元素必定具有相同特性,相阾的数据元素之间存在着序偶关系。
线性表中元素的个数n(n>=0)定义为线性表的长度,0==n时称为空表,在非空表中每个数据元素都有一个确定的位置(下标)。
线性表是一个相当灵活的数据结构,它的长度可根据需要增长或缩短。
3、数据结构的运算
数据结构是计算机科学中的重要概念,常见的数据结构包括数组、链表、栈、队列、树、图等。这些数据结构支持许多常见的运算,以下是其中一些常见的运算:
-
插入(Insertion):将一个元素插入到数据结构的特定位置,例如在数组中插入一个元素或在链表中插入一个节点。
-
删除(Deletion):从数据结构中删除一个元素,例如从数组或链表中删除一个元素,或者从树或图中删除一个节点。
-
查找(Searching):在数据结构中查找特定的元素,例如在数组或链表中查找一个元素,或者在树或图中查找一个节点。
-
遍历(Traversal):按照一定的顺序访问数据结构中的所有元素,例如按顺序遍历数组或链表中的元素,或者按照深度优先搜索或广度优先搜索遍历树或图中的节点。
-
排序(Sorting):将数据结构中的元素按照一定的顺序重新排列,例如对数组或链表进行排序,常见的排序算法包括冒泡排序、插入排序、选择排序、快速排序、归并排序等。
-
合并(Merging):将两个或多个数据结构合并成一个,例如合并两个有序数组或链表,或者合并两个树或图。
-
查找最小或最大值(Minimum/Maximum):在数据结构中查找最小或最大的元素,例如在数组或链表中查找最小或最大的元素,或者在树或图中查找最小或最大的节点。
-
修改(Modification):修改数据结构中的一个或多个元素的值,例如在数组或链表中修改特定位置的元素值,或者在树或图中修改特定节点的值。
前面这些看不看都无所谓的,咱这中文博大精深,看它叫啥名词其实一般就知道干啥了。像“插入”是吧,就是没学过计算机也知道,插入不就是插个东西进去嘛。
二、线性表的顺序表示和实现数据结构的运算:
线性表的顺序表示指的是用一组地址连续的存储单元依次存储线性表的数据元素,支持元素的随机访问,在数组上完成数据的增删查改。
1. 静态顺序表示:
使用定长数组存储。使用结构体变量来维护每一张顺序表,结构体的第一个成员是存储顺序表的数组,第二个成员则是存储顺序表内的元素个数。
// 数据元素
typedef struct Student
{
char name[20];
char sex;
float score;
}Student;
//宏定义,方便后期修改
#define N 10;
// 数据结构
typedef struct Array
{
Student stu[N];
//元素个数
int size;
}Array;
2. 动态顺序表示:
使用动态开辟的数组存储,同样使用结构体变量来维护每一张顺序表,不同的是,由于数组是动态开辟的,所以需要增加一个成员来表示每个顺序表当前的容量。
// 数据元素
typedef struct Student
{
char name[20];
char sex;
float score;
}Student;
// 数据结构
typedef struct Array
{
Student* ptr;
size_t cap;//容量
size_t cnt;//元素个数
}Array;
这之前,需要说明的是:静态顺序表只适用于确定知道需要存多少数据的场景。导致N如果定大了,空间开多了浪费,开少了不够用,就会显的我们非常的呆。所以现实中基本都是使用动态顺序表,根据需要动态地分配空间大小,诶!就显的我们很鸡贼,很聪明。所以聪明的你们应该知道选什么了吧😂,诶!我们就用动态顺序表实现各种数据结构的运算。
3.动态顺序表实现各种数据结构的运算
1.创建和销毁表:
// 创建
Array* create_array(size_t cap)
{
// 给数据结构申请堆内存
Array* arr = malloc(sizeof(Array));
// 给数据元素申请堆内存
arr->ptr = malloc(sizeof(Student)*cap);
// 备份数据结构的容量
arr->cap = cap;
// 初始化元素个数
arr->cnt = 0;
// 返回一个空的数据结构
return arr;
}
// 销毁
void destroy_array(Array* arr)
{
//释放一定是从里到外,先把数据元素(节点)的内存释放了
free(arr->ptr);
//再把数据结构的内存释放了
free(arr);
}
2.任意位置的插入和删除
插入和删除非常的类似,一个是找到要删除的index的下标后全部后移一位,然后把要插入的值插入到index下标处,而删除则是找到向前移动一位。(插入和删除其实还分头插和尾插,头删和尾删,这里顺序表来实现感觉有点sb,尾删不久把总数减一就好咯,头删不就是从第二个元素起向前覆盖,头插尾插也是类似,写这个显的我们这个文章B格不够高,虽然本来也不高😂,头插尾插头删尾删在链式实现的时候会写)
// 插入
bool insert_array(Array* arr,int index,const Student* stu)
{
//当元素个数大于容量(表满了)或 要插入的下标index非法以及大于容量时,返回false插入失败
if(arr->cnt >= arr->cap || index < 0 || index >= arr->cap)
return false;
//全部向后移动一位,直到移动到index(要插入的下标)
for(int i=arr->cnt; i>index; i--)
{
arr->ptr[i] = arr->ptr[i-1];
}
//插入
arr->ptr[index] = *stu;
//个数加一
arr->cnt++;
//返回true 插入成功
return true;
}
// 删除
bool delete_array(Array* arr,int index)
{
//非法下标返回false
if(0 > index || index >= arr->cnt)
return false;
//从要删除的下标开始后面全部往前挪一位
for(int i=index; i<arr->cnt; i++)
{
arr->ptr[i] = arr->ptr[i+1];
}
//数量减一
arr->cnt--;
return true;
}
3.修改和查询
作用是将用户指定位置的元素修改为指定元素,通过下标直接进行修改即可。同样的,需要先检查指定的位置是否合法,查询也是类似就不多说了。
// 修改
bool modify_array(Array* arr,int index,Student* stu)
{
if(0 > index || index >= arr->cnt)
return false;
arr->ptr[index] = *stu;
return true;
}
// 查询
int query_array(Array* arr,Student* key)
{
for(int i=0; i<arr->cnt; i++)
{
if(!strcmp(arr->ptr[i].name,key->name) &&
arr->ptr[i].sex == key->sex &&
arr->ptr[i].score == key->score)
return i;
}
return -1;
}
注意点:可能看的同学会对strcmp这个函数有点忘记,给你们看看他的介绍,它相等时返回的是0哦(贴心吧,记得点赞😊)
函数原型:int strcmp(const char* str1, const char* str2)
头 文 件:#include <string.h>
返 回 值:str1 = str2 则返回0,
str1 > str2 则返回大于0的值,
str1 < str2 则返回小于0的值
4.排序以及最大值和最小值
排序时定义一个m初值等于i 然后从i+1个开始遍历一遍,当找到最大或最小值时把j值赋给m,当m的值和i不相等说明找到了最小的值之后进行交换。函数 min_array
的作用是在给定的数组 arr
中找到分数最低的学生,并返回该学生的指针。函数的参数是一个指向 Array 结构体的指针 arr
。函数内部使用了一个变量 min
来记录当前最低分数的学生的下标。在循环中,通过遍历数组中的每个学生,比较其分数与当前最低分数的学生的分数,如果找到更低的分数,则更新 min
的值为当前下标。最后,函数返回数组中分数最低的学生的指针,通过 arr->ptr+min
来获取该学生的地址。最大值也是类似。
// 排序
int sort_array(Array* arr)
{
for(int i=0; i<arr->cnt-1; i++)
{
int m = i;
for(int j=i+1; j<arr->cnt; j++)
{
if(arr->ptr[m].score > arr->ptr[j].score)
m = j;
}
if(m != i)
{
Student tmp = arr->ptr[m];
arr->ptr[m] = arr->ptr[i];
arr->ptr[i] = tmp;
}
}
}
// 最大值
Student* max_array(Array* arr)
{
int max = 0;
for(int i=1; i<arr->cnt; i++)
{
if(arr->ptr[max].score < arr->ptr[i].score)
max = i;
}
return arr->ptr+max;
}
// 最小值
Student* min_array(Array* arr)
{
int min = 0;
for(int i=1; i<arr->cnt; i++)
{
if(arr->ptr[min].score > arr->ptr[i].score)
min = i;
}
return arr->ptr+min;
}
5.遍历 合并
合并将arr1 和arr2两个数组和为同一条数组。
// 合并
void merge_array(Array* arr1,Array* arr2)
{
//将arr1容量扩等于自身的容量加上arr2的容量
arr1->cap += arr2->cap;
//开辟堆内存
arr1->ptr = realloc(arr1->ptr,sizeof(Student)*arr1->cap);
//合并,把arr2接到arr1上
for(int i=0; i<arr2->cnt; i++)
{
arr1->ptr[arr1->cnt++] = arr2->ptr[i];
}
}
// 遍历
void show_array(Array* arr)
{
for(int i=0; i<arr->cnt; i++)
{
printf("%s %s %g\n",
arr->ptr[i].name,
arr->ptr[i].sex=='w'?"女":"男",
arr->ptr[i].score);
}
printf("\n");
}
6.完整代码
#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>
#include <string.h>
// 数据元素
typedef struct Student
{
char name[20];
char sex;
float score;
}Student;
// 数据结构
typedef struct Array
{
Student* ptr;
size_t cap;
size_t cnt;
}Array;
// 创建
Array* create_array(size_t cap)
{
// 给数据结构申请堆内存
Array* arr = malloc(sizeof(Array));
// 给数据元素申请堆内存
arr->ptr = malloc(sizeof(Student)*cap);
// 备份数据结构的容量
arr->cap = cap;
// 初始化元素个数
arr->cnt = 0;
// 返回一个空的数据结构
return arr;
}
// 销毁
void destroy_array(Array* arr)
{
free(arr->ptr);
free(arr);
}
// 插入
bool insert_array(Array* arr,int index,const Student* stu)
{
if(arr->cnt >= arr->cap || index < 0 || index >= arr->cap)
return false;
for(int i=arr->cnt; i>index; i--)
{
arr->ptr[i] = arr->ptr[i-1];
}
arr->ptr[index] = *stu;
arr->cnt++;
return true;
}
// 删除
bool delete_array(Array* arr,int index)
{
if(0 > index || index >= arr->cnt)
return false;
for(int i=index; i<arr->cnt; i++)
{
arr->ptr[i] = arr->ptr[i+1];
}
arr->cnt--;
return true;
}
// 修改
bool modify_array(Array* arr,int index,Student* stu)
{
if(0 > index || index >= arr->cnt)
return false;
arr->ptr[index] = *stu;
return true;
}
// 查询
int query_array(Array* arr,Student* key)
{
for(int i=0; i<arr->cnt; i++)
{
if(!strcmp(arr->ptr[i].name,key->name) &&
arr->ptr[i].sex == key->sex &&
arr->ptr[i].score == key->score)
return i;
}
return -1;
}
// 排序
int sort_array(Array* arr)
{
for(int i=0; i<arr->cnt-1; i++)
{
int m = i;
for(int j=i+1; j<arr->cnt; j++)
{
if(arr->ptr[m].score > arr->ptr[j].score)
m = j;
}
if(m != i)
{
Student tmp = arr->ptr[m];
arr->ptr[m] = arr->ptr[i];
arr->ptr[i] = tmp;
}
}
}
// 最大值
Student* max_array(Array* arr)
{
int max = 0;
for(int i=1; i<arr->cnt; i++)
{
if(arr->ptr[max].score < arr->ptr[i].score)
max = i;
}
return arr->ptr+max;
}
// 最小值
Student* min_array(Array* arr)
{
int min = 0;
for(int i=1; i<arr->cnt; i++)
{
if(arr->ptr[min].score > arr->ptr[i].score)
min = i;
}
return arr->ptr+min;
}
// 合并
void merge_array(Array* arr1,Array* arr2)
{
arr1->cap += arr2->cap;
arr1->ptr = realloc(arr1->ptr,sizeof(Student)*arr1->cap);
for(int i=0; i<arr2->cnt; i++)
{
arr1->ptr[arr1->cnt++] = arr2->ptr[i];
}
}
// 遍历
void show_array(Array* arr)
{
for(int i=0; i<arr->cnt; i++)
{
printf("%s %s %g\n",
arr->ptr[i].name,
arr->ptr[i].sex=='w'?"女":"男",
arr->ptr[i].score);
}
printf("\n");
}
int main(int argc,const char* argv[])
{
Array* arr = create_array(10);
Student stu = {"heheA"};
for(int i=0; i<10; i++)
{
stu.name[4] += 1;
stu.sex = rand()%2 ? 'w' : 'm';
stu.score = (rand()%9000+1000)/100.0;
insert_array(arr,i,&stu);
}
//delete_array(arr,5);
//modify_array(arr,0,&stu);
show_array(arr);
printf("%d\n",query_array(arr,&stu));
Student* p = min_array(arr);
printf("max stu = %s %s %g\n",
p->name,
p->sex == 'w' ? "女" : "男",
p->score);
sort_array(arr);
show_array(arr);
Array* arr2 = create_array(5);
Student stus[5] = {
{"xixi1",'w',99},
{"xixi2",'m',98},
{"xixi3",'w',97},
{"xixi4",'m',96},
{"xixi5",'w',95},
};
insert_array(arr2,0,stus+0);
insert_array(arr2,0,stus+1);
insert_array(arr2,0,stus+2);
insert_array(arr2,0,stus+3);
insert_array(arr2,0,stus+4);
show_array(arr2);
merge_array(arr,arr2);
show_array(arr);
return 0;
}
三:总结
对于线性表的顺序表示,我只能说简单的一,数据结构不过如此嘛,来的难的打击一下我啊