一,线性表的定义
线性表是最为基础的逻辑结构,是n个相同属性元素的有限序列,而数据元素在线性表中的位置取决于它自身的序号,它所对应的逻辑结构如图:
通常我们需要设置一系列连续的数据时使用线性表。
二,线性表的基本运算
线性表的运算方式复杂多样,种类也繁多,但对于初学者来说,线性表的常用运算我进行了一下总结:
(1)设置基础的线性表
(2)增添线性表中的数据元素
(3)展示线性表中的数据元素
(4)删除线性表中的数据元素
(5)查找线性表中的数据元素
(6)修改线性表中的数据元素
线性表虽然有不同的种类,但对于初学者的基本操作都基本归于以上几种类型
三,线性表中的顺序表
首先,我们先来介绍一下线性表中的顺序表。顺序表是用来存储线性表最简单,最常用的方式,是在计算机中开辟一系列连续的内存空间进行存储不同的数据元素。顺序表具有物理相邻的特点。我们之前设置的基础通讯录就运用的是顺序表。下面,我以C++中的通讯录设置的算法来跟大家演示一下顺序表中的基本运算:
(1)设置基础的线性表,在这里我以结构体的形式设置,也可用数组的形式,但设置起来比较麻烦,思路基本相同。
//首先,设置通用的数据元素,即线性表的数据元素
struct person
{
string name;
int sex;
int age;
string phone;
string number;
string address;
};
//然后,用数组的形式将一系列数据元素构成线性表
struct add
{
struct person arr[m];
int size;
}a;
(2)增添线性表中的数据元素,要考虑线性表是否满载的情况,具体方法如下
void addperson(struct add* a,int* p)//基础运算中的增添算法
{
if (a->size == m)//当线性表满了时候的情况
{
cout << "通讯录存放已满,无法添加" << endl;
return;
}
else//当线性表没有存储满时进行增加数据元素
{
//姓名
cin >> a->arr[*p].name;
//性别
cout << "1,男\n" << "2,女" << endl;
while (true)
{
int select;
cin >> select;
if (select == 1 || select == 2)
{
if (select == 1)
a->arr[*p].sex = 1;
else
a->arr[*p].sex = 2;
break;
}
}
//年龄
cin >> a->arr[*p].age;
//电话
cin >> a->arr[*p].phone;
//身份证号
cin >> a->arr[*p].number;
//地址
cin >> a->arr[*p].address;
}
}
(3)展示线性表中的数据元素,线性表具有物理相邻的特点,我们可用这一性质来查看线性表中的所有数据元素或数据项
void show(struct add* a)
{
if (a->size == 0)//当线性表没有任何数据元素时的情况cout << "当前没有任何记录" << endl;
else//当有元素时进行输出{
for (int i = 0; i < a->size; i++)//直接从物理上的开始位置起查找{
cout << "姓名: " << a->arr[i].name << " ";
cout << "性别: " << (a->arr[i].sex == 1 ? "男" : "女") << " ";
cout << "年龄: " << a->arr[i].age << " ";
cout << "手机号: " << a->arr[i].phone << " ";
cout << "身份证号: " << a->arr[i].number << " ";
cout << "家庭地址: " << a->arr[i].address << endl;
}
}
}
(4)删除线性表中的数据元素,在这里我们要删除指定的数据项,将其对应的数据元素一起删除
可以先设置一个算法,先查找要删除的位置,然后进行删除
int foundperson(add* a,string name1)//查找指定人的位置
{
for (int i = 0; i < a->size; i++)
{
if (a->arr[i].name == name1)
{
return i;
}
}
return -1;
}
void deleteperson(add* a)//要删除指定的人的姓名{
string name;
int number;
cin >> name;
number = foundperson(a, name);
if (number != -1)
{
for (int i = number; i < a->size; i++)
{
a->arr[i] = a->arr[i + 1];
}
a->size--;
cout << "删除成功" << endl;
}
else//当没有要删除指定的这个人时的情况{
cout << "没有此人" << endl;
}
}
(5)查找线性表中的数据元素,跟删除操作基本一致,先查找指定人的具体位置,然后根据这个线性表对应的物理位置直接查找
int foundperson(add* a,string name1)//查找指定人的位置,name1对应的是查找人的名字
{
for (int i = 0; i < a->size; i++)
{
if (a->arr[i].name == name1)
{
return i;
}
}
return -1;
}void found(add* a)
{
int i;
string name;
cin >> name;
i = foundperson(a, name);//指定查找人的位置if (i != -1)//查找人的基本信息
{
cout << "姓名: " << a->arr[i].name << " ";
cout << "性别: " << (a->arr[i].sex == 1 ? "男" : "女") << " ";
cout << "年龄: " << a->arr[i].age << " ";
cout << "手机号: " << a->arr[i].phone << " ";
cout << "身份证号: " << a->arr[i].number << " ";
cout << "家庭地址: " << a->arr[i].address << endl;
}
else//当没有查到的情况{
cout << "没有此人" << endl;
}
}
(6)修改线性表中的数据元素,先找到数据项的位置,然后根据物理位置进行修改
int foundperson(add* a,string name1)//指定人的位置算法
{
for (int i = 0; i < a->size; i++)
{
if (a->arr[i].name == name1)
{
return i;
}
}
return -1;
}
void fix(add* a)
{
int number;
string name;
cin >> name;
number = foundperson(a, name);//指定人的位置
if (number != -1)//进行修改
{
//姓名
cin >> a->arr[*p].name;
//性别
while (true)
{
int select;
cin >> select;
if (select == 1 || select == 2)
{
if (select == 1)
a->arr[*p].sex = 1;
else
a->arr[*p].sex = 2;
break;
}
}
//年龄
cin >> a->arr[*p].age;
//电话
cin >> a->arr[*p].phone;
//身份证号
cin >> a->arr[*p].number;
//地址
cin >> a->arr[*p].address;
}
}
else
{
cout << "没有此人" << endl;
}
}
由此可见,在基础操作中,查找,修改,删除三种方法基本思路一样,换种情其实也是一样的,你可以私下想象一下。
四,线性表中的链式表
链式表其实就是我们之前学的链表。链表具有相连的逻辑关系,它可以表示各种复杂的非线性结构式。链表的逻辑结构要比顺序表的情况稍微复杂一点点。常见的链表形式有单向链表,双向链表,循环链表。
1,单向链表
单向链表是最简单,最常用的链式结构之一,单链表中的每个结点只包含一个指针域,如图:
下面为单向链表的创建与输出:
#include<stdio.h>
#include<stdlib.h>
typedef int type;
typedef struct h
{
type a;
struct h* next;
}Lnode;
Lnode* Link()
{
type a, i = 0;
Lnode* head = NULL, * p, * pa;
head = p = (Lnode*)malloc(sizeof(Lnode));
fscanf(stdin, "%d", &a);
while (a != 0)
{
i++;
if (i == 1)
{
head->a = a;//因为位顺序结构式,要先确定头节点
}
p->a = a;//当满足式子时加入,数据加入表中
pa = (Lnode*)malloc(sizeof(Lnode));//建立结点
p->next = pa;//建立连接
p = pa;//顺序链接,建立相应的关系式子
fscanf(stdin, "%d", &a);
}
p->next = NULL;
return head;
}//以上已成功建立
type main()
{
Lnode * t;
for (t = Link(); t->next != NULL; t = t->next)
{
fprintf(stdout, "%d ", t->a);
}
return 0;
}
2,双向链表
单项链表中的每个结点只能指向前面的结点,如果再加上一个指针域指向后面的结点,这种链表称为双向链表。如图:
下面为双向链表的创建与双向输出
#include"linkc.h"//自己写的必要头文件
struct pa
{
type a;
link* next1, * next2;//next1指向前趋,next2指向后继
};
int main()
{
int n = 0;
link* p, * pp, *head;
p = head = (link*)malloc(sizeof(link));
scanf("%d", &p->a);
while (p->a)
{
pp=(link*)malloc(sizeof(link));
scanf("%d", &pp->a);
p->next1 = pp;
pp->next2 = p;//逆向建立连接,建立双向
p = pp;
}
p->next1 = 0;//正向的末尾
head->next2 = 0;//逆向的末尾
//双向链表正向输出
for (pp = head; pp->next1; pp = pp->next1)
{
printf("%d ", pp->a);
Sleep(500);
}
printf("\n");
//双向链表的逆向输出
for (pp = p; pp->next2; pp = pp->next2)
{
printf("%d ", pp->a);
Sleep(500);
}
return 0;
}
3,循环链表
循环链表是一种首尾相接的链表,具有无限循环结构。在单链表中,如果最后一个结点的指针域直接指向头节点,这就变成了循环链表。循环链表的结构式如图所示:
下面是循环链表的创建和输出的代码演示:
#include"linkc.h"//头文件,里面我们设置的一些简单的基础操作
struct pa
{
type a;
link* next;
};
int main()
{
link* p, * head, * pp;
p = head = (link*)malloc(sizeof(link));
scanf("%d", &p->a);
while (p->a)//终止条件为0
{
pp = (link*)malloc(sizeof(link));
scanf("%d", &pp->a);
p->next = pp;
p = pp;
}
p->next = head;//将最后结点指向头节点
for (pp = head;; pp = pp->next)//查看循环链表,不用设置条件
{
printf("%d\n", pp->a);
Sleep(500);//控制输出时间,因为循环链表是无限循环
}
return 0;
}
总:单向链表,双向链表,循环链表都是基础的链式结构,更为复杂的如双向循环链表,双向带头循环链表等,但需注意的是,虽然链式结构复杂多样,基本的运算规律都是一致的思想,只要在形式上稍微改动即可,初级的线性表一共大约也就这么多,顺序表比较简单,没有涉及复杂的结构和逻辑思想,至于跟为复杂的链式结构式以及线性表的使用逻辑和注意事项,后面的深入文章会给大家做出详细的讲解。