1.线性表
1.1概念
一对一的数据所组成的关系称为线性表,注意线性表是逻辑结构
线性表中的数据元素可以是各种各样的,但同一线性表中的元素必定 具有相同的特性,即同一数据对象(26个英文字母),相邻的数据之间 是一对一的关系,比如B字母节点(数据节点)前面是A字母节点,后面 是C字母节点,线性表的特性如下:
(1) 存在唯一的一个被称为"第一个"的数据元素
(2) 存在唯一的一个被称为"最后一个"的数据元素
(3) 处了第一个外,集合中每一个数据元素只有一个前驱节点
(4) 除了最后一个外,集合中每个数据元素均只有一个后驱节点
举例:生活中的线性表例子非常多,比如一个班级中的以学号编排的学生、一座图书馆中的以序号编排的图书、一条正常排队等候的队列、一摞从上到下堆叠的餐盘,这些都是线性表。他们的特点都是:除了首尾两个元素,其余任何一个元素前后都对应相邻的另一个元素。
注意:
1.线性表是一种数据内部的逻辑关系,与存储形式无关
2.线性表既可以采用连续的顺序存储(数组),也可以采用离散的 链式存储(链表)
3.顺序表和链表都称为线性表。
2.顺序表
2.1概念
顺序表:顺序存储的线性表
顺序存储就是将数据存储到一片连续的内存中,在C语言环境下,可以 是具名的栈数组,或者是匿名的堆数组。
存储方式不仅仅只是提供数据的存储空间,而是必须要能体现数据之间的逻辑关系。当采用顺序存储的方式来存放数据时,唯一能用来表达数据间本身的逻辑关系的就是存储位置。比如队列中的两个人,小明和小花,如果小明在逻辑上排在相邻的小花的前面,那么在存储位置上也必须把小明存放在相邻的小花的前面。
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdbool.h>
typedef struct
{
int cap; // 顺序表容量
int last; // 顺序表元素下标
int *data; // 指向顺序表的首元素地址
}sequenList;
//初始化顺序表
sequenList *init_seque(int cap)
{
if(cap <= 0)
return NULL;
//给顺序表管理结构体分配空间
sequenList *head = malloc(sizeof(sequenList));
if(head == NULL)
return NULL;
head->cap = cap;
head->last = -1;
head->data = calloc(cap,sizeof(int));
if(head->data == NULL)
return NULL;
return head;
}
//判断表是否已满
bool isFull(sequenList *head)
{
if(head->cap == head->last+1)
return true;
return false;
}
//判断正负
bool absd(int data)
{
if(data < 0)
return true;
return false;
}
//删除绝对值
bool deleted(sequenList *head,int data)
{
if(head == NULL)
return false;
int i = 0;
while(i <= head->last)
{
if(head->data[i] == data)
{
for(int j = i; j < head->last; j++)
{
head->data[j] = head->data[j+1];
}
head->last--;
//因为删除1位之后所有位置前移1,而下一步是i++,所以需要让i=-1,i++重新回归0位进行判断,否则可能漏掉一些相同的数字
i = -1;
}
i++;
}
if(i == head->last)
return true;
return true;
}
//冒泡排序
bool order(sequenList *head)
{
if(head == NULL)
return false;
for(int i = 0; i < head->last; i++)
{
for (int j = 0;head->data[j] < 0; j++)
{
head->data[j] = ~(head->data[j] -1);
deleted(head ,head->data[j]);
j--;
}
for(int j = 0; j < head->last -i; j++)
{
if(head->data[j] > head->data[j+1])
{
int temp = head->data[j];
head->data[j] = head->data[j+1];
head->data[j+1] = temp;
}
}
}
return true;
}
// 插入数据
bool insert(sequenList *head, int data)
{
if(head == NULL)
return false;
//判断数据是否已满
if(isFull(head))
return false;
head->data[++head->last] = data;
return true;
}
//判断如果错误则输出true,这样下面的错误判断才会接受运行,输出false
//也就是如果head正常则输出false,预防的程序不会运行
bool isEmpty(sequenList *head)
{
if(head->last == -1)
return true;
return false;
}
//显示插入的数据
void showList(sequenList *head)
{
if(isEmpty(head))
return;
for(int j = 0; j < (head->last+1); j++)
{
printf("%d ",head->data[j]);
}
printf("\n");
}
//销毁顺序表释放空间
void destroy(sequenList *head)
{
if(head == NULL)
return;
free(head->data);
head->data = NULL;
free(head);
head = NULL;
}
//主函数
int main(int argc, char const *argv[])
{
sequenList *head = init_seque(10);
if(head == NULL)
{
printf("init failed\n");
return -1;
}
//一次性输入,有个数限制(和挨个输入取其一)
for(int i = 0; i < 5; i++)
{
int data;
scanf("%d", &data);
//将输入插入到表
insert(head,data);
}
//挨个输入输出,无个数限制
while (1)
{
int data;
// 拿不到数据,表示缓冲区输入的是字符
if(scanf("%d",&data) == 0)
break;
if(data > 0)
{
bool ret = insert(head,data);
if(!ret)
{
printf("数据已满\n");
break;
}
showList(head);
}
else if(data < 0)
{
data = ~(data -1);
bool ret = deleted(head, data);
if(!ret)
{
printf("数据为空\n");
break;
}
showList(head);
}
}
//删除负数和绝对值的判断(如果是一次性输入需要,但是挨个输入输出就不需要了)
for(int i = 0; i < head->last; i++)
{
//如果使用if,当负数的绝对值在负数前面时,i需要减2,否则会漏掉负数后面紧挨着的负数
//if(head->data[i] < 0)
for(;head->data[i] < 0;)
{
head->data[i] = ~(head->data[i] -1);
deleted(head, head->data[i]);
//i -= 2;
i--; //for循环使用或者把删除放在排序后面;
}
}
//修改插入数据顺序
order(head);
//显示顺序表
showList(head);
//销毁,释放空间,否则在许多程序一起运行的时候此程序不用也会占用空间,可能会造成系统卡顿,需要进行释放,养成好习惯。
destroy(head);
return 0;
}