线性结构的特点是除第一个和最后一个数据元素外每个数据元素只有一个前驱数据元素和一个后继数据元素。线性表是一种最简单的线性结构。线性表的操作特点主要是可以在任意 位置插入和删除一个数据元素。线性表可以用顺序存储结构和链式存储结构存储。用顺序存储结构实现的线性表称作顺序表,用链式存储结构实现的线性表称作链表。链表主要有单链表、循环单链表和双向循环链表三种。顺序表和单链表各有优缺点,并且优缺点刚好相反。
线性表的顺序表示和实现
线性表的抽象数据类型瑶池示了线性表的数据元素、数据元素间的逻辑关系以及线性表的操作集合。任何需要计算机进行管理和处理的数据元素都必须首先按某种方式存储在计算机中。线性表的抽象数据类型有两种存储结构:一种是顺序存储结构;另一种是链式存储结构。一旦确定了线性表的存储结构,线性表操作集合中的所有操作就可以具体实现。
typedef struct {
DataType list[MaxSize];
int size;
}SeqList;
//顺序表操作实现
//1.初始化
void ListInitiate(SeqList*L)
{
L->size = 0;//定义初始数据元素个数
}
//2.求当前数据元素个数
int ListLength(SeqList L)
{
return L.size;//返回顺序表L的当前数据元素个数
}
//3.插入数据元素
int ListInsert(SeqList*L,int i,DataType x)
{
/*
在顺序表L的第i(0<=i<=size)个位置前插入数据元素值x
插入成功返回1,插入失败返回0
*/
int j;
if(L->size >= MaxSize)
{
printf("顺序表已满无法插入!\n");
return 0;
}
else if(i < 0 || i > L->size)
{
printf("参数i不合法!\n");
return 0;
}
else
{
/*为插入做准备*/
for(j=L->size;j > i; j--)
{
L->list[j] = L->list[j-1];
}
L->list[i] = x; /*插入元素x*/
L->size++; /*元素个数加1*/
return 1;
}
}
//4.删除数据元素
int ListDelete(SeqList*L,int i,DataType*x)
{
/*
删除顺序表L中位置为i(0<=i<=size-1)的数据元素并存放到x中
删除成功返回1,删除失败返回0
*/
int j;
if(L->size <= 0)
{
printf("顺序表已空无数据元素可删!\n");
return 0;
}
else if(i < 0 || i > L->size - 1)
{
printf("参数i不合法\n");
return 0;
}
else
{
*x = L->list[i]; /* 保存删除的元素到x中*/
for(j = i;j < L->size-1; j++)
{
L->list[j] = L->list[j+1];
}
L->size--; /*数据元素个数减1*/
return 1;
}
}
//5.取数据元素
int ListGet(SeqList L,int i,DataType* x)
{
/* 取顺序表L中第i个数据元素存于x中,成功返回1,失败返回0*/
if(i < 0 || i > L.size - 1)
{
printf("参数i不合法!\n");
return 0;
}
else
{
*x=L.list[i];
return 1;
}
}
/*
例:编程实现如下任务:建立一个线性表,首先依次输入数据元素1,2,3,...,10,然后删除数据元素5,最后依次显示当前线性表中的数据元素。假设该线性表的数据元素个数在最坏情况下不会超过100个。
设计:用顺序表实现建立线性表的任务。因该线性表数据元素最坏情况下不会超过100个,所以定义顺序表的MaxSize为100;因该设计任务中数据元素的数据类型是int类型,所以定义顺序表的DataType为int;该任务要求的插入、删除、取元素等操作可通过调用顺序表的插入、删除、取元素等函数来实现。
*/
#include<stdio.h> /* 该文件包含printf函数*/
#define MaxSize 100 /* 定义MaxSize为100*/
typedef int DataType; /* 定义DataType为int*/
#include "SeqList.h" /* 包含顺序表文件*/
int main(int argc, char* argv[])
{
SeqList myList;
int i,x;
ListInitiate(&myList); /* 初始化函数调用*/
for(i = 0;i < 10; i++) /* 插入10个数据元素*/
{
if(ListInsert(&myList,i,i+1) == 0) /*插入函数调用*/
{
printf("错误!\n");
return ;
}
}
/* 显示顺序表当前的数据元素*/
for(i = 0;i < ListLength(myList); i++)
{
if(ListGet(myList,i,&x) == 0) /* 取元素函数调用*/
{
printf("错误!\n");
return ;
}
else
{
printf("%d ",x);
}
}
printf("\n");
if(ListDelete(&myList,4,&x) == 0) /* 删除函数调用*/
{
printf("错误!\n");
return ;
}
/* 显示顺序表当前的数据元素*/
for(i = 0;i < ListLength(myList); i++)
{
if(ListGet(myList,i,&x) == 0) /* 取元素函数调用*/
{
printf("错误!\n");
return ;
}
else
{
printf("%d ",x);
}
}
printf("\n");
return 0;
}
/*
1 2 3 4 5 6 7 8 9 10
1 2 3 4 6 7 8 9 10
Press any key to continue
*/
/*
2.编程实现如下任务:建立一个如下所示的学生情况表,要求先依次输入数据元素然后依次显示当前表中的数据元素。假设该表元素个数在最坏情况下不会超过100个。要求使用顺序表。
学号 姓名 性别 年龄
2000001 张三 男 20
2000002 李四 男 21
2000003 王五 女 22
设计:
因该表数据元素个数在最坏情况下不会超过100个,所以我们定义顺序表的MaxSize为100;该设计任务的表元素为学生,从表2-1可知,每个学生数据元素包括学号、姓名、性别和年龄四个数据项。
*/
#include<stdio.h> /* 该文件包含printf函数*/
#define MaxSize 100 /* 定义MaxSize为100*/
typedef struct Student{ /* 定义Student类型*/
long number; /* 学号数据项*/
char name[10]; /* 姓名数据项*/
char sex[3]; /* 性别数据项*/
int age; /* 年龄数据项*/
}StudentType; /* 结构体StudentType*/
typedef StudentType DataType; /* 定义DataType为StudentType数据类型*/
#include "SeqList.h" /* 包含顺序表文件*/
int main(int argc, char* argv[])
{
SeqList myList;
int i;
StudentType x[3] = {
{
2000001,"张三","男",20
},
{
2000002,"李四","男",21
},
{
2000003,"王五","女",22
}
};
StudentType s;
ListInitiate(&myList); /* 初始化函数调用*/
for(i = 0;i < 3; i++)
{
if(ListInsert(&myList,i,x[i]) == 0) /*插入函数调用*/
{
printf("错误!\n");
return ;
}
}
/* 显示顺序表当前的数据元素*/
for(i = 0;i < ListLength(myList); i++)
{
if(ListGet(myList,i,&s) == 0) /* 取元素函数调用*/
{
printf("错误!\n");
return ;
}
else
{
printf("%d\t%s\t%s\t%d\n",s.number,s.name,s.sex,s.age);
}
}
printf("\n");
return 0;
}
/*
2000001 张三 男 20
2000002 李四 男 21
2000003 王五 女 22
Press any key to continue
*/
线性表的链式表示和实现
单向链表
链式存储结构存储线性表数据元素的方法,是把存放数据元素的结点用指针域构造成链。指针是指向物理存储单元地址的变量,由数据元素域和一个或若干个指针域组成的一个结构体称为一个结点。其中,数据域用来存放数据元素,指针域用来构造数据元素之间的关联关系。链式存储结构的特点是数据元素间的逻辑关系表现在结点的链接关系上。链式存储结构的线性表称为链表。根据指针域的不同和结点构造链的方法不同,链表主要有单链表、单循环链表和双向循环链表三种。
/************************************************************************/
/* 单链表应用举例:
建立一个表,首先依次输入数据元素1,2,3,...,10,然后删除 数据元素5,
最后依次显示当前表中的数据元素。要求使用链表
设计:
因要求数据元素是int类型,所以定义单链表的DataType为int.对表元素的插入,删除,
取元素等操作均可通过调用单链表的插入,删除,取元素等操作来实现。
*/
/************************************************************************/
#include <stdio.h> //包含printf()函数
#include <stdlib.h> //包含exit()函数
#include <malloc.h> //包含malloc()等函数
typedef int DataType; //定义DataType为int
#include "LinList.h" //包含单链表文件
int main(int argc, char* argv[])
{
SLNode *head; //定义头指针变量
int i,x;
ListInitiate(&head); //初始化
for(i = 0;i < 10; i++) //插入10个数据元素
{
if(ListInsert(head,i,i+1) == 0)
{
printf("错误!\n");
return ;
}
}
for(i = 0;i < ListLength(head); i++) //显示当前的数据元素中的值
{
if(ListGet(head,i,&x) == 0) //取元素值到x变量中
{
printf("错误!\n");
return ;
}
else
{
printf("%d ",x); //显示
}
}
printf("\n");
if(ListDelete(head,4,&x) == 0) //删除下标为四(值为5)的数据元素
{
printf("错误!\n");
return ;
}
for(i = 0;i < ListLength(head); i++) //显示当前的数据元素中的值
{
if(ListGet(head,i,&x) == 0) //取元素值到x变量中
{
printf("错误!\n");
return ;
}
else
{
printf("%d ",x); //显示
}
}
printf("\n");
Destroy(&head); //撤消单链表
return 0;
}
/*
1 2 3 4 5 6 7 8 9 10
1 2 3 4 6 7 8 9 10
Press any key to continue
*/
双向链表
双向链表是每个结点除后继指针域外还有一个前驱指针域。与单链表类同,双向链表也有带头结点结构和不带头结点结构2种,带头结点的双向链表更为常用。
在双向链表中,每个结点包括三个域,分别是data域,next域和prior域,其中data域为数据域,next域为指向后继结点的指针,prior域为指向前驱结点的指针。
//双向链表的实现
typedef struct Node
{
DataType data;
struct Node *next;
struct Node *prior;
}DLNode;
//1.初始化
void ListInitiate(DLNode** head)
{
if((*head=(DLNode*)malloc(sizeof(DLNode))) == NULL) exit(0);
(*head)->prior = *head; //构成前驱指针循环链
(*head)->next = *head; //构成后继指针循环链
}
//2.插入数据元素
int ListInsert(DLNode*head,int i,DataType x)
{
//在带头结点的双向循环链表head的第i(0<=i<=size)个结点前
//插入一个存放数据元素x的结点。插入成功时返回1,失败返回0
DLNode*p,*s;
int j;
p = head->next;
j = 0;
while(p != head && j < i)
{
p = p->next;
j++;
}
if(j != i)
{
printf("插入位置参数出错!");
return 0;
}
if((s = (DLNode*)malloc(sizeof(DLNode))) == NULL) exit(0);
s->data = x;
s->prior = p->prior; //修改新增结点 prior域指向
p->prior->next = s; //修改新增结点前一个结点的next域指向
s->next = p; //修改新增结点的next域
p->prior = s; //修改新增结点的下一个结点的prior域指向
return 1;
}
//3.删除数据元素
int ListDelete(DLNode*head,int i,DataType*x)
{
//删除带头结点双向循环链表head的第i(0<=i<=size-1)个结点
//被删除结点的数据元素域值由x带回。删除成功时返回1,失败返回0
DLNode*p;
int j;
p = head->next;
j = 0;
while(p->next != head && j < i) //寻找第i个结点
{
p = p->next;
j++;
}
if(j != i)
{
printf("删除位置参数出错!");
return 0;
}
p->prior->next = p->next;
p->next->prior = p->prior;
*x = p->data;
free(p);
return 1;
}
//4.求循环链表长度
int ListLength(SLNode*head)
{
SLNode*p = head;
int size = 0;
while(p->next != head)
{
p = p->next;
size++;
}
return size;
}
//5.撤消内存空间
void Destroy(DLNode**head)
{
DLNode *p,*p1;
int i,n = ListLength(*head); //为求长度函数
p = *head;
for(i = 0;i <= n; i++)
{
p1 = p;
p = p->next;
free(p1);
}
*head = NULL;
}
静态链表
在链式存储结构中,实现数据元素之间的次序关系依靠指针。我们也可以用数组来构造链表,方法是:在数组中增加一个(或两个)指针域,这些指针域用来存放下一个(或上一个)数据元素在数组中的下标,从而构成用数组构造的单链表(或双向链表)。由于相对于申请结点内存空间的动态性来说数组内存空间的申请方式是静态的,所以这种存储结构称作静态链表。由于静态链表中增加的指针仿真了链式存储结构中的指针,所以静态链表中的指针也称作仿真指针。
算法举例
顺序表算法举例
/************************************************************************/
/*1.构造一个顺序表的删除算法,把顺序表L中数据元素x删除。
算法思想:此删除问题可通过一个循环比较过程来实现
*/
/************************************************************************/
#include"SeqList.h"
int ListDataDelete(SeqList*L,DataType x)
{
int i,j;
for(i = 0;i < L->size; i++) //寻找元素x
if(x == L->list[i]) break;
if(i == L->size) return 0; //未寻找到元素x
else //寻找到元素x
{
for(j = i;j < L->size; j++) //元素依次前移
L->list[j] = L->list[j+1];
L->size--; //元素个数减1
return 1;
}
}
/*
2.构造一个顺序表的删除算法,把顺序表L中所有值相同的数据元素x全部删除
算法思想:删除算法的基础上,再增加一重循环来实现值相同数据元素x的全部删除。
*/
int ListMoreDataDelete(SeqList*L,DataType x)
{
int i,j;
int tag = 0; //初始删除标记置为0
for(i = 0;i < L->size; i++)
{
if(x == L->list[i]) //寻找到元素x
{
for(j = i;j < L->size; j++) //依次前移
L->list[j] = L->list[j+1];
L->size--; //元素个数减1
i--; //保证2个相邻 的元素均为x时正确删除
tag = 1; //设置删除成功标记
}
}
return tag;
}
单链表算法举例
/************************************************************************/
/* 1.设头指针为head,并设带头结点单链表中的数据元素递增有序,编写算法将数据
元素x插入到带头结点单链表的适当位置上,要求插入后保持单链表数据元素的递增有序。
算法思想:
从链表的第1个数据元素结点开始,逐个比较每个结点的data域值和x的值,当data小于等于x时,
进行下一个结点的比较;否则,就找到了插入结点的合适位置,此时申请新结点把x存入,
然后把新结点插入;当比较到最后一个结点仍有data小于等于x时,则把新结点插入单链表尾。
*/
/************************************************************************/
void LinListInsert(SLNode*head,DataType x)
{
SLNode*curr,*pre,*q;
//循环初始化
curr = head->next; //curr指向第一个数据元素结点
pre = head; //pre指向头结点
//循环定位插入位置
while(curr != NULL && curr->data <= x)
{
pre = curr;
curr = curr->next;
}
//申请一个结点并把x存入data域
if((q = (SLNode*)malloc(sizeof(SLNode))) == NULL) exit(1);
q->data = x;
//把新结点插入pre所指结点后
q->next = pre->next;
pre->next = q;
}
/*
2.设head为单链表的头指针,并设单链表带有头结点,编写
算法将单链表中的数据元素按照其值递增有序的顺序进行就地排序
*/
void LinListSort(SLNode *head)
{
SLNode*curr,*pre,*p,*q;
p = head->next;
head->next = NULL;
while(p != NULL)
{
curr = head->next;
pre = head;
while(curr != NULL && curr->data <= p->data)
{
pre = curr;
curr = curr->next;
}
q = p;
p = p->next;
q->next = pre->next;
pre->next = q;
}
}