线性表定义以及基本操作
定义
- 相同数据类型的n个数据元素组成的有限序列,n>=0
- 除第一个元素以外,每个元素有且只有一个直接前驱
- 除最后一个元素以外,每个元素有且只有一个直接后继
- 线性表属于逻辑结构
基本操作
- InitList ( &L ):初始化表,构造一个空的线性表
- Length ( L ):返回线性表L的长度
- LocateElem ( L , e ):按值查找
- GetElem ( L , i ):按位查找
- ListInsert( &L , i , e ):插入操作,在表L中第i个位置插入指定元素e
- ListDelete( &L , i , e ):删除操作,删除表L中第i个位置的元素,并用e返回删除元素的值
- PrintList( L ):输出操作,按照前后顺序输出线性表L中的元素
- Empty( L ):判空操作,空为true,非空为false
- DestroyLise( &L ):销毁操作。销毁线性表,并且释放空间
1 顺序表示
1.1 定义
- 顺序存储又称为顺序表
- 用一组地址连续的存储单元依次存储线性表中的数据元素
- 逻辑上相邻的两个元素物理位置上也相邻
- 逻辑顺序与物理顺序相同
- 最主要特点是随机访问
1.2 基本操作的实现
- 定义实现
- 静态分配
#define MAXSIZE 1000
typedef struct
{
ElemType data[MAXSIZE];
int length;
}SqList;
- 动态分配
#define MAXSIZE 1000
typedef struct
{
ElemType *data;
int MaxSize,length;
}SqList
//C的初始动态分配语句
L.data=(ElemType*)malloc(sizeof(ElemType)*InitSize);
//C++的初始动态分配语句
L.data=new ElemType[InitSize];
- 插入操作 O(n)
bool ListInsert(SqList &L,int i,ElemType e)
{
if(i<1||i>L.length+1) return false;//判定i的范围是否有效
if(L.length>=MaxSize) return false;//判断当前L的长度是否达到最大长度
for(int j=L.length-1;j>=i;j--)
{
L.data[j]=L.data[j-1];
}
L.data[i-1]=e;
L.length++;
return true;
}
- 删除操作 O(n)
bool ListDelete(SqList &L,int i,ElemType &e)
{
if(i<1||i>L.length) return false;
e=L.data[i-1];
for(int j=i;i<L.length;j++)
{
L.data[j-1]=L.data[j];
}
return true;
}
- 按值查找 O(n)
- 从前往后依次遍历即可
1.3 问题解决
- 设将n(n>1)个整数存放到一维数组中,将R中保存的序列循环左移P个位置,即将R中的数据由(1 2 3 4 5 6…n)变为(3 4 5 6…n 1 2)
- 算法思想:分成两段,分别颠倒,再将总序列颠倒
- 1 2 3 4 5 6 -> 2 1 6 5 4 3 -> 3 4 5 6 1 2
void Reverse(int R[],int from,int to)
{
int k;
for(int i=0;i<(to-from+1)/2;i++)
{
k=R[from+i];
R[from+i]=R[to-i];
R[to-i]=k;
}
}
void Converse(int R[],int n,int p)
{
Reverse(R,0,p-1);
Reverse(R,p,n-1);
Reverse(R,0,n-1);
}
- 给定一个长度为n一维数组,寻找出现次数最多的元素
- 算法思想:利用map<int,int>存不同元素出现次数
int Search(int a[],int length)
{
map<int,int>m;
for(int i=0;i<length;i++)
{
++m[a[i]];
}
int value=(m.begin())->first;
int max=(m.begin())->second;
for(map<int,int>::iterator iter=m.begin();iter!=m.end();iter++)
{
if(iter->second>max)
{
value=iter->first;
max=iter->second;
}
}
return value;
}
2 链式表示
2.1 单链表
typedef struct LNode
{
ElemType data;
struct LNode *next;
}LNode,*LinkList;
LinkList List_HeadInsert(LinkList &L)
{
LNode *s;
int k;
L=(LinkList)malloc(sizeof(LNode));//创建头节点,并且分配内存
L->next=NULL;
cin>>k;
while(k!=9999)//选择一个终止条件,这里用x=9999来代替
{
s=(LNode*)malloc(sizeof(LNode));
s->data=k;
s->next=L->next;
L->next=s;
cin>>k;
}
return L;
}
- 尾插法 O(n)
LinkList list_TailInsert(LinkList &L)
{
LNode *s;
LNode *r=L;
int k;
L=(LinkList)malloc(sizeof(LNode));//创建头节点,并且分配内存
L->next=NULL;
cin>>k;
while(k!=9999)//选择一个终止条件
{
s=(LNode*)malloc(sizeof(LNode));
s->data=k;
s->next=r->next;
r->next=s;
r=s;
cin>>k;
}
return L;
}
- 按位查找(查找的是节点)
LNode *GetELem(LinkList L,int i)
{
int j=1;
LNode *p=L->next;
if(i==0)
return L;
if(i<1)
return NULL;
while(j<i&&p)
{
p=p->next;
j++;
}
return p;
}
- 按值查找(从头遍历,和上面差不多,方法定义的时候也是寻找节点)
- 插入节点(随便想想啦,注意指针顺序)
- 删除节点(同上)
2.2 双链表
- 拥有两个指针,分别指向前驱节点和后驱节点
typedef struct DNode
{
Elemtype data;
struct Dnode *piror,*next;
}DNode.*Dlinklist;
2.3 循环链表
- 循环单链表
- 判空条件为:L->next==L
- 循环双链表
- 判空条件为:L->next == L && L->piror == L
2.4 静态链表
- 借助数组来描述线性表的链式存储结构
#define MaxSize 100
typedef struct
{
ElemType data; //保存数据
int next; //保存下一个节点的数组下标
}SLinkList[MaxSize];
+结束标志为:next==-1
顺序表与链表的比较
- 总结一下大概就是顺序表存取方便,链表操作方便