**单链表的抽象数据类型**
ADT 线性表(list)
Date
线性表数据对象集合{a1,a2,......an},每个元素的类型为DateType.
数据元素除了第一个没直接前驱最后一个没有直接后继,其余的都有
直接前驱和后继,数据元素之间一一对应.
Operation
InitList(*L);//初始化操作,建立一个空的线性表L
ListEmpty(L);//若线性表为空返回true,否则返回false
ClearList(*L);//将线性表清空
GetElem(L,i,*e);//将线性表L中的第i个元素返回给e
LocateElem(L,e);//在线性表L中查找与给定值e相等的元素,如果查找成功,返回该元素在表中序号表示成功否则返回表示失败
ListInsert(*L,i,e);//在线性表的L中的第I个位置插入新元素e
ListDelete(*L,i,*e);//删除线性表L中的i位置的元素,并用e返回其值
ListLenght(L);//返回线性表的元素个数
endADT
1.线性表的顺序存储结构
(1)线性表的顺序存储结构实例
#include<iostream>
#include<cstdio>
#include<string.h>
#define maxSize 100
#define Status int
#define ElemType int//这里的数据类型是存入的信息的统称,也可以是一个结构体
using namespace std;
typedef struct
{
ElemType data[maxSize];//这里可以是一个发杂的结构体数组
int length;
}SqList;
Status InitList(SqList &L)//初始化单链表
{
memset(L.data,0,sizeof(L.data));//此函数在string.h中,初始化数组
L.length=0;
return 0;
}
bool CreatList(SqList &L,int n)//创建顺序表函数,初始化前几个元素
{
if(n<0||n>maxSize)
return false;
for(int i=0;i<n;i++)
{
int temp;
scanf("%d",&L.data[i]);
L.length++;
}
return true;
}
bool InsertList(SqList &L,int i,ElemType e)//插入一个元素 这里的i是插入的位置如果i是1那么就在数组下标为0的地方,以此类推
{
if(i<1||i>L.length+1)
{
printf("位置无效\n");
return false;
}
if(L.length>=maxSize)
{
printf("当前储存空间已满\n");
return false;
}
for(int j=L.length;j>=i;j--)
{
L.data[j]=L.data[j-1];
}
L.data[i-1]=e;
L.length++;//线性表长度加1
return true;
}
bool ListDelete(SqList &L,int i)//删除指定位置的元素
{
if(i<1||i>L.length)
{
printf("位置无效\n");
return false;
}
for(int j=i;j<=L.length-1;j++)
{
L.data[j-1]=L.data[j];//把需要删除的元素后边的元素往前移动一位就完成删除的操作
}
L.length--;
return true;
}
int LocateElem(SqList L,ElemType e)
{
for(int i=0;i<L.length;i++)
{
if(L.data[i]==e)
return i+1;
}
return 0;
}
void printflist(SqList L)
{
printf("当前顺序表的元素有:\n");
for(int i=0;i<L.length;i++)
{
printf("%d\n",L.data[i]);
}
}
void Creat(SqList &L)
{
int n;
bool flag;
printf("请输入顺序表长度!\n");
scanf("%d",&n);
printf("请输入顺序表的元素:\n") ;
flag=CreatList(L,n);
if(flag)
{
printf("创建成功!\n");
printflist(L);
}
else
printf("创建失败!");
}
void Delete(SqList &L)
{
int place; bool flag;
printf("请输入要删除的位置(从1开始):\n");
scanf("%d", &place);
flag = ListDelete(L, place);
if (flag)
{
printf("删除成功!!!\n");
printflist(L);
}
}
//查找功能函数 调用LocateElem查找元素
void Insert(SqList &L)
{
int place ;
ElemType e;
bool flag;
printf("请输入插入的位置:\n");
scanf("%d",&place);
printf("请输入插入的元素:\n");
scanf("%d",&e);
flag=InsertList(L,place,e);
if(flag)
{
printf("插入成功\n");
printflist(L);
}
else
printf("插入失败\n");
}
void Search(SqList L)
{
ElemType e;
int flag;
printf("请输入要查找的值:");
scanf("%d",&e);
flag=LocateElem(L,e);
if(flag)
{
printf("该元素的位置是:%d\n",flag);
}
else
printf("未找到该元素\n");
}
void menu()
{
printf("********1.创建 2.插入*********\n");
printf("********3.删除 4.查找*********\n");
printf("********7.输出 8.退出*********\n");
}
int main()
{
SqList L;
int choice;
InitList(L);
while (1)
{
menu();
printf("请输入菜单序号:\n");
scanf("%d", &choice);
if (8 == choice) break;
switch (choice)
{
case 1:Creat(L); break;
case 2:Insert(L); break;
case 3:Delete(L); break;
case 4:Search(L); break;
case 7:printflist(L); break;
default:printf("输入错误!!!\n");
}
}
return 0;
}
(2)线性表顺序储存结构的时间复杂度
查询插入删除的空间复杂度为o(n)。拿查询来说因为最坏的情况就是插入的时候是插入第一个位置,然后后边所有的元素都要往后移动一位,虽然最好的情况是直接插在最后边,这时候空间复杂度为o(1).但是经过推导空间复杂度还是o(n);
(3)线性表顺序存储结构的优缺点
优点:1.可以快速存取任意位置的元素
2.无需为表示表中的元素之间的逻辑关系而额外增加储存空间
缺点:1.插入删除要移动大量的元素
2.当线性表长度变化较大时,难以确定存储空间的容量
3.造成空间的碎片
2:线性表的链式存储结构
(1)例子:
#include<bits/stdc++.h>
typedef struct Node{
int data;
struct Node *next;
}*node;
void creatlist(node *head,int n)//这里运用二级指针,*head接受指针的地址,这个功能是创建一个链表
{
//第一种方法是头插法
node p;
//for(int i=0;i<n;i++)
// {
// p=(node)malloc(sizeof(node));
// p->data=100+i;
// p->next=(*head)->next;
// (*head)->next=p;
// }
//第二种方法是尾插法,取其中一种就行了。
node q;
q=*head;
while(q->next!=NULL)
q=q->next;
for(int i=0;i<n;i++)
{
p=(node)malloc(sizeof(node));
p->data=100+i;
q->next=p;
p->next=NULL;
q=p;
}
}
void print(node *head)
{
node p;
p=(*head)->next;
if(p==NULL)
printf("111");
while(p!=NULL)
{
printf("%d\n",p->data);
p=p->next;
}
}
int Clearlist(node *head)
{
node p,q;
p=(*head)->next;
while(p)
{
q=p->next;
free(p);
p=q;
}
(*head)->next=NULL;
}
int main()
{
node head;
head=(node)malloc(sizeof(node));
head->next=NULL;
creatlist(&head,5);
print(&head);
Clearlist(&head);
print(&head);
}
(2)关于头指针和头结点
头指针,只有指针域没有信息域,头指针是指向第一个结点的指针,如果有头节点,那么头指针指向头结点。头指针是链表必须的
头结点,有信息域和指针域,但是信息域不放数据元素,而是存放线性表长度等公共数据或者不放任何东西,信息域无任何意义,有了头节点插入信息和删除信息就和其他结点一样了,头结点不是链表必须的
3.线性表顺序存储结构和链式存储结构的对比:
(1)存储分配方式:
顺序存储结构是用一段连续的储存空间
链式存储结构是用一组任意的存储单元存放元素
(2)时间性能:
1.查找方面(查找某个位置的元素):
顺序结构是o(1)
链式是o(n)
2.插入删除(找到插入或者删除的位置后)
顺序结构平均移动表长一半的元素为o(n)
链式结构为o(n)
(3)空间性能:
顺序结构要预支空间,分大了浪费,小了受限制。
链式结构不需要预支空间而且元素个数不受限制
(4)总结
频繁查找的时候顺序表优势,频繁删改链式好,
如果元素个数变化较大或者不知道有多少元素,那么链表较好。如果提前知道线性表的大致长度,用顺序表较好,二者的好坏根据实际情况而论。
4.静态链表 :
#include<stdio.h>
#include<windows.h>
#define max 10
#define ElemType int
typedef struct
{
ElemType data;
int cur;
}con,staticlist[max];
void initlist(staticlist space)
{
for(int i=0;i<max-1;i++)
space[i].cur=i+1;
space[max-1].cur=0;
}
int len(staticlist space)
{
int cnt=0;
int i=space[max-1].cur;
while(i!=0)
{
i=space[i].cur;
cnt++;
}
return cnt;
}
void add(staticlist space)
{
printf("请输入你想添加的数据成员的数量:");
int n;
scanf("%d",&n);
for(int i=1;i<=n;i++)
{
printf("请输入第%d个数据成员:",i);
scanf("%d",&space[i].data);
}
space[0].cur=n+1;
space[n].cur=0;
space[max-1].cur=1;
}
int malloc(staticlist space)
{
int i=space[0].cur;
if(i!=0)
{
space[0].cur=space[i].cur;
}
return i;
}
void trav(staticlist space)
{
int i=space[max-1].cur;
while(i)
{
printf("%d\n",space[i].data);
i=space[i].cur;
}
}
int insert(staticlist space,int i,ElemType a)
{
int j,k,l;
k=max-1;
if(i<1||i>len(space)+1)
{
printf("此位置不能进行插入\n");
return 0;
}
j=malloc(space);
if(j!=0)
{
space[j].data=a;
for(l=1;l<i;l++)
k=space[k].cur;
space[j].cur=space[k].cur;
space[k].cur=j;
return 1;
}
return 0;
}
int free(staticlist space,int i)
{
space[i].cur=space[0].cur;
space[0].cur=i;
}
int del(staticlist space,int i)
{
int k,j;
if(i<1||i>len(space))
return 0;
k=max-1;
for(j=1;j<i;j++)
k=space[k].cur;
j=space[k].cur;
space[k].cur=space[j].cur;
free(space,j);
return 1;
}
int main()
{
staticlist space;
printf("正在为你初始化静态链表..........!\n");
Sleep(2000);
initlist(space);
add(space);
trav(space);
insert(space,2,4);
printf("正在为你重新链接链表..........!\n");
Sleep(2000);
trav(space);
while(1)
{
printf("请输入你想删除的下标:");
int m;
scanf("%d",&m);
del(space,m);
printf("正在为你重新链接链表..........!\n");
Sleep(2000);
trav(space);
}
return 0;
}
静态链表的核心:
(1)下图为初始化的时候
静态链表头尾不存放数据成员,头存第一个没存数据的数组下标,尾存第一个放数据的数组下标
(2)下图为添加元素的时候
(3)
下图为插入元素的时候:
(4)
下图是删除的时候
静态链表的优缺点:
优点:插入和删除的时候只需修改游标,不需要移动元素,不像顺序表一样
缺点:没有解决长度不确定的问题,失去了顺序存储结构的随机存取的特性。
5.循环链表:
将单链表中终端结点的指针端由空指向指向头结点,使得整个链表变成一个环,简称循环链表
这样从任意的结点出发都能访问到全部结点
6.双向链表:
在单链表的基础上在设置一个指向前驱结点的指针,头结点的前驱指针指向尾,尾结点的后继