2.1 线性表及逻辑结构
2.1.1 线性表的定义
- 线性表是具有相同特性的数据元素的一个有限序列
线性表有如下几条性质:
- 有穷性:一个线性表中的元素是有限的
- 一致性:一个线性表中的所有元素性质相同,从现实角度看,每个元素具有相同的数据类型
- 序列性:一个线性表中所有元素之间的相对位置是线性的,即存在唯一的开始元素和终端元素,除此之外,每个元素之有唯一的前驱元素和后继元素。各元素在线性表中的位置只取决于它们的序号,所以一个线性表中可以存在两个值相同的元素。
2.1.2 线性表的抽象数据类型描述
ADT List{
数据对象: D={ai| ai(-ElemSet,i=1,2,…,n,n>=0}
数据关系: R1={<ai-1,ai>| ai-1,ai(- D,i=2,…,n}
基本操作:
1.InitList(&L)
操作结果:构造一个空的线性表
2.DestroyList(&L)
初始条件:线性表L已存在
操作结果:销毁线性表L
3.ClearList(&L)
初始条件:线性表L已存在
操作结果:将L重置为空表
4.ListEmpty(L)
初始条件:线性表L已存在
操作结果:若L为空表,则返回TRUE,否则返回FALSE
5.ListLength(L)
初始条件:线性表L已存在
操作结果:返回L中数据元素个数
6.GetElem(L,i,&e)
初始条件:线性表L已存在,1≤i≤LIST_SIZE
操作结果:用e返回L中第i个数据元素的值
7.LocatElem(L,e,compare())
初始条件:线性表L已存在,compaer()是数据元素判断函数
操作结果:返回L中第1个与e满足关系compaer()的数据元素的位序。若这样的数据元素不存在,则返回0
8.ListInsert(&L,i,e)
初始条件:线性表L已存在
操作结果:在L中第i个位置之前插入新的数据元素e,L的长度加1
9.ListDelete(&L,i,&e)
初始条件:线性表L已存在
操作结果:删除L的第i个数据元素,并用e返回其值,L的长度减1
}ADT List
2.2 线性表的顺序存储结构
2.2.1 线性表的顺序存储结构—顺序表
线性表的顺序存储结构称为顺序表
2.2.2 顺序表基本算法实现
#include <iostream>
using namespace std;
const int maxsize = 100;
typedef int ElemType;
typedef struct{
ElemType data[maxsize];
int length;
}Sqlist;
void CreateList(Sqlist *& L,ElemType a[],int n){
int i;
L = new Sqlist;
for(i = 0;i < n;i++)
L->data[i] = a[i];
L->length = n;
}
void InitList(Sqlist *& L){
L = new Sqlist;
L->length = 0;
}
void DestroyList(Sqlist * &L){
L -> length = 0;
delete [] L;
}
bool ListEmpty(Sqlist *L){
return (L -> length == 0);
}
int ListLength(Sqlist *L){
return L -> length;
}
void DispList(Sqlist *L){
int i;
for(i = 0;i < ListLength(L);i++)
cout << L -> data[i] << " ";
cout << endl;
}
bool GetElem(Sqlist *L,int i,ElemType &e){
if(i < 0||i > L->length)
return false;
e = L -> data[i-1];
return true;
}
int LocateElem(Sqlist *L,ElemType e){
int i;
for(i = 0;i < L->length;i++){
if(L->data[i] == e) return i+1;
}
return -1;
}
bool ListInsert(Sqlist *L,int i,ElemType e){
if(i < 1||i > L->length+1) return false;
else
{
i--;
int j;
for(j = L->length;j > i;j--){
L->data[j] = L->data[j-1];
}
L->data[i] = e;
L->length++;
return true;
}
}
bool ListDelete(Sqlist *L,int i,int &e){
if(i < 1 || i > L->length) return false;
else
{
i--;
e = L->data[i];
for(int j = i;j < L->length - 1;j++)
L->data[j] = L->data[j+1];
L->length--;
return true;
}
}
int main(){
Sqlist * node;
ElemType data[maxsize];
int n;
cout << "输入初始化顺序表长度" << endl;
cin >> n;cout << "输入顺序表值" << endl;
for(int i = 0;i < n;i++)
cin >> data[i];
CreateList(node,data,n);
cout << "顺序表元素为 " << endl;
DispList(node);
cout << "顺序表长为 " << ListLength(node) << endl;
cout << "输入插入元素位置和数值" << endl;
int p,e;cin >> p >> e;
if(ListInsert(node,p,e)){
cout << "插入后为" << endl;
DispList(node);
}
else
{
cout << "输入不合法" << endl;
}
cout << "删除插入位置后顺序表为" << endl;
ListDelete(node,p,e);
DispList(node);
cout << "现在销毁顺序表" << endl;
DestroyList(node);
if(ListEmpty(node)) cout << "现在为空表" << endl;
else
{
cout << "现在仍存在顺序表" << endl;
}
return 0;
}
3 顺序表的应用示例
例2.3 假设一个线性表采用顺序表表示,设计一个算法,删除其中所有值等于x的元素,要求算法的时间复杂度是O(n),空间复杂度是O(1)
代码实现:
解法一
void delnode1(Sqlist * & L,ElemType x){
int i,k = 0;
for(i = 0;i < L->length;i++){
if(L->data[i] == x)
k++;
else{
L->data[i-k] = L->data[i];
}
}
L->length -= k;
}
解法二
void delnode2(Sqlist *L,ElemType x){
int i,k = 0;
for(i = 0;i < L->length;i++){
if(L->data[i] != x){
L->data[k] = L->data[i];
k++;
}
}
L->length = k;
}
例2.4 有一个顺序表L,假设元素类型ElemType为整型,设计一个尽可能高效的算法,以第一个元素为分界线,将所有小于等于它的元素移动到他的元素该基准的后面,将所有大于它的元素移动到该基准的前面。
基本思路是以第一个元素为基准,从右向左找一个小于等于基准的元素x,从左向右找到一个大于基准的元素y,然后将两者交换,直到全部找完.
代码实现:
解法一
void partition1(Sqlist * & L){
int i = 0,j = L->length-1;
ElemType pivot = L->data[0];
while(i < j){
while(i < j&&L->data[j] > pivot)
j--;
while(i < j&&L->data[i] <= pivot)
i++;
if(i < j) swap(L->data[i],L->data[j]);
}
swap(L->data[0],L->data[i]);
}
解法二
void partition2(Sqlist *&L){
int i = 0,j = L->length-1;
ElemType pivot = L->data[0];
while(i < j){
while(i < j && L->data[j] > pivot)
j--;
L->data[i] = L->data[j];
while(i < j && L->data[i] <= pivot)
i++;
L->data[j] = L->data[i];
}
L->data[i] = pivot;
}
尽管对一个同一个数据序列,这两个算法的执行结果不完全相同,但是都能满足题目要求,而且它们的时间复杂度都是O(n),空间复杂度为O(1),都属于高效算法
但是比较而言,第二个算法移动元素的次数更少,所以算法更优.
2.3 线性表的链式存储结构
2.3.1 线性表的链式存储结构–链表
线性表的链式存储结构称为链表(linked list),其中每个存储节点不仅包含元素本身信息(称为数据域),而且包含表示元素之间逻辑关系,在c/c++通过指针来实现,这称为指针域
2.3.2 单链表
1 . 结点声明
typedef struct LNode{
ElemType data;
struct LNode *next;
}LinkNode;
为了整体替换int数据类型增加如下表达
typedef int ElemType;
2.单链表的一些基本操作
2.1 建立单链表
头插法:在头结点后依次插入元素(若输出数据会呈现出输入的倒叙)
void CreatListF(LinkNode *& L,ElemType a[],int n) //头插法 逆序输入
{
LinkNode *s;
L = new LinkNode;
L->data = n;L->next = NULL;
for(int i = 0;i < n;i++){
s = new LinkNode;
s->next = L->next;
L->next = s;
s->data = a[i];
}
}
尾插法:在尾部依次插入元素(输出则为正常顺序)
void CreatListR(LinkNode *& L,ElemType a[],int n) //尾插法
{
LinkNode *s,*r;
L = new LinkNode;
L->data = n;L->next = NULL;
r = L;//r永远指向尾节点
for(int i = 0;i < n;i++){
s = new LinkNode;
r->next = s;
r = s;
s->data = a[i];
}
r->next = NULL;
}
2.2 初始化单链表
void InitList(LinkNode *& L)
{
L = new LinkNode;
L->next = NULL;
}
2.3 销毁单链表
void DestroyList(LinkNode *& L)
{
LinkNode *pre = L,*p = L->next;
while(p != NULL)
{
delete pre;
pre = p;
p = p->next;
}
}
2.4 判断是否为空表
bool ListEmpty(LinkNode *L){
return (L->next == NULL);
}
2.5 求链表长度
int ListLength(LinkNode *L){
LinkNode *p = L->next;
int n = 0;
while(p){
n++;
p = p->next;
}
return n;
}
2.6 输出线性表
void DispList(LinkNode *L){
LinkNode *p = L->next;
while(p){
cout << p->data << " ";
p = p->next;
}
cout << endl;
}
2.7 求单链表中某个逻辑位置上数据元素的值
bool GetElem(LinkNode *L,int i,int &e){
if(i < 1) return false;
else
{
int j = 0;
LinkNode *p = L->next;
i--;
while(i > j && p!= NULL){
j++;
p = p->next;
}
if(p == NULL) return false;
else
{
e = p->data;
return true;
}
}
}
2.8 按元素e查找,返回元素逻辑序号
int LocateElem(LinkNode * L,ElemType e){
LinkNode *p = L->next;
int k = 1;
while(p != NULL && p->data != e){
p = p->next;
k++;
}
if(p == NULL) return -1;
else
return k;
}
2.9 插入数据元素 在第i个位置上 插入元素e
bool ListInsert(LinkNode *& L,int i,ElemType e){
if(i < 1) return false;
LinkNode *p = L,*s;
for(int j = 1;j < i;j++){
if(!p) return false;
p = p->next;
}
s = new LinkNode;
s->data = e;
s->next = p->next;
p->next = s;
return true;
}
2.10 删除第i个位置上的元素 并返回删除元素的值
bool DeleteList(LinkNode *&L,int i,ElemType &e){
if(i < 1) return false;
else
{
LinkNode *p = L,*q;
for(int j = 1;j < i;j++){
if(!p) return false;
else
p = p->next;
}
q = p->next;
e = q->data;
p->next = q->next;
delete q;
}
return true;
}
讲完单链表的基本操作,现在我们来看单链表的应用示例
例子