顺序表(顺序存储):
优点:可随机存取,存储密度高
缺点:要求大片连续空间,改变容量不方便
单链表(链式存储):
优点:不要求大片连续空间,改变容量方便
缺点:不可随机存取,要耗费一定空间存放指针
1.单链表
存储结构如下:
typedef struct Node{ //节点类型定义
ElemType data;
struct Node *next;
}Node,*LinkList;
建立单链表:
typedef struct LNode{
int data;
struct LNode *next;
}LNode,*LinkList;
//初始化一个空的单链表(不带头节点)
bool InitList(LinkList &L) {
L=NULL;
return true;
}
//初始化一个空的单链表(带头节点)
void InitList(LinkList &L) {
L=(LNode*)malloc(sizeof(LNode));//分配一个头节点
L->next=NULL;
}
int main(){
LinkList L; //声明一个指向单链表的指针
//初始化一个空的单链表
InitList(L);
.....
}
插入(带头节点):
//在第i个位置插入元素e
bool ListInsert(LinkList &L,int i,int e){
if(i<1)
return false;
LNode *p; //p指向当前扫描到的结点
int j=0; //当前p指向的是第几个结点
p=L;
while(p!=NULL && j<i-1){ //循环找到第i-1个结点
p=p->next;
j++;
}
if(p == NULL) //i值不合法
return false;
LNode *s=(LNode*)malloc(sizeof(LNode));
s->data=e;
s->next=p->next;
p->next=s;
return true;
}
插入(不带头节点):
//在第i个位置插入元素e (不带头节点)
bool ListInsert(LinkList &L,int i,int e){
if(i<1)
return false;
if(i == 1){
LNode *s=(LNode*)malloc(sizeof(LNode));
s->data=e;
s->next=L;
L=s;
return true;
}
LNode *p; //p指向当前扫描到的结点
int j=1; //当前p指向的是第几个结点
p=L;
while(p!=NULL && j<i-1){ //循环找到第i-1个结点
p=p->next;
j++;
}
if(p == NULL) //i值不合法
return false;
LNode *s=(LNode*)malloc(sizeof(LNode));
s->data=e;
s->next=p->next;
p->next=s;
return true;
}
指定结点后插操作:
//后插操作
bool InsertNextNode(LNode *p,int e){
if(p==NULL)
return false;
LNode *s=(LNode*)malloc(sizeof(LNode));
if(s==NULL) //内存分配失败
return false;
s->data=e;
s->next=p->next;
p->next=s;
return true;
}
指定结点前插操作:
//前插操作
bool InsertPriortNode(LNode *p,LNode *s){
if(p==NULL || s==NULL)
return false;
s->next=p->next;
p->next=s;
int temp = p->data;
p->data=s->data;
s->data=temp;
return true;
}
删除结点(带头节点):
//在第i个位置删除元素 (带头结点)
bool ListDelete(LinkList &L,int i,int &e){
if(i<1)
return false;
LNode *p; //p指向当前扫描到的结点
int j=0; //当前p指向的是第几个结点
p=L;
while(p!=NULL && j<i-1){ //循环找到第i-1个结点
p=p->next;
j++;
}
if(p == NULL) //i值不合法
return false;
if(p->next==NULL)//第i-1个结点之后无其他节点
return false;
LNode *q=p->next;//q指向被删除的结点
e=q->data; //删除元素的值
p->next=q->next;//将*q结点断开
free(q); //释放节点的存储空间
return true;
}
删除指定结点:
//删除指定结点p
bool DeleteNode(LNode *p){
if(p==NULL)
return false;
LNode *q=p->next;
p->data=p->next->data;
p->next=q->next;
free(q);
return true;
}
按位查找:
//按位查找
LNode * GetElem(LinkList L,int i){
int j=1;
LNode *p = L->next;
if(i==0)
return L;
if(i<1)
return NULL;
while(p!=NULL&&j<i){
p=p->next;
j++;
}
return p;
}
按值查找:
//按值查找
LNode * LocateElem(LinkList L,int e){
LNode *p=L->next;
while(p!=NULL && p->data!=e){
p=p->next;
}
return p;
}
表长(带头节点):
//求表长
int Length(LinkList L){
int len=0;
LNode *p=L;
while(p->next!=NULL){
p=p->next;
len++;
}
return len;
}
尾插法建立单链表:
#include<stdio.h>
#include<malloc.h>
typedef struct LNode{
int data;
struct LNode *next;
}LNode,*LinkList;
//初始化单链表(带头节点)
void InitList(LinkList &L) {
L=(LNode*)malloc(sizeof(LNode));//分配一个头节点
L->next=NULL;
}
//尾插法建立单链表
void ListTailInsert(LinkList &L){
int x;
L=(LinkList)malloc(sizeof(LNode));//建立头节点
LNode *s,*r=L;
scanf("%d",&x);
while(x!=0){
s=(LNode*)malloc(sizeof(LNode));
s->data=x;
r->next=s;
r=s;
scanf("%d",&x);
}
r->next=NULL;
}
int main(){
LinkList L; //声明一个指向单链表的指针
//初始化一个空的单链表
InitList(L);
ListTailInsert(L);
printf("number:");
while(L->next!=NULL){
L=L->next;
printf("%d\t",L->data);
}
}
结果:
12
13
14
15
0
number:12 13 14 15
头插法建立单链表:
#include<stdio.h>
#include<malloc.h>
typedef struct LNode{
int data;
struct LNode *next;
}LNode,*LinkList;
//初始化单链表(带头节点)
void InitList(LinkList &L) {
L=(LNode*)malloc(sizeof(LNode));//分配一个头节点
L->next=NULL;
}
//头插法建立单链表
void ListHeadInsert(LinkList &L){
LNode *s;
int x;
scanf("%d",&x);
while(x!=0){
s=(LNode*)malloc(sizeof(LNode));
s->data=x;
s->next=L->next;
L->next=s;
scanf("%d",&x);
}
}
int main(){
LinkList L; //声明一个指向单链表的指针
//初始化一个空的单链表
InitList(L);
ListHeadInsert(L);
printf("number:");
while(L->next!=NULL){
L=L->next;
printf("%d\t",L->data);
}
}
结果:
11
12
13
14
15
0
number:15 14 13 12 11
2.双链表
存储结构:
typedef struct DNode{
ElemType data; //数据域
struct DNode *prior,*next; //前驱和后继指针
}DNode,*DLinkList;
初始化:
//初始化双链表
bool InitDLinkList(DLinkList &L){
L=(DNode*)malloc(sizeof(DNode));
if(L == NULL)
return false;
L->prior = NULL;
L->next = NULL;
return true;
}
双链表的插入:
//双链表的插入
bool InsertNextDNode(DNode *p,DNode *s){
if(p==NULL||s==NULL)
return false;
s->next=p->next; //将结点*s插入到*p之后
if(p->next != NULL)
p->next->prior=s;
s->prior=p;
p->next=s;
return true;
}
双链表的删除:
//删除p结点的后继结点
bool DeleteNextNode(DNode *p){
if(p==NULL)
return false;
DNode *q=p->next;
if(q==NULL)
return false;
p->next=q->next;
if(q->next!=NULL)
q->next->prior=p;
free(q);
return true;
}
//销毁操作
void Destroy(DLinkList &L){
while(L->next!=NULL){
DeleteNextNode(L);
}
free(L); //释放头节点
L=NULL;
}
3.循环链表
循环单链表:
//循环单链表
typedef struct LNode{
ElemType data;
struct LNode *next;
}LNode,*LinkList;
//初始化一个循环单链表
bool InitList(LinkList &L){
L=(LNode*)malloc(sizeof(LNode));
if(L==NULL)
return false;
L->next=L;
return true;
}
//判断是否为空
bool isEmpty(LinkList L){
return L->next == L;
}
//判断结点p是否为循环单链表的表尾结点
bool isTail(LinkList L,LNode *p){
return p->next == L;
}
循环双链表:
//循环双链表
typedef struct DNode{
ElemType data;
struct DNode *prior,*next;
}DNode,*DLinkList;
//初始化空的循环双链表
bool InitDLinkList(DLinkList &L){
L=(DNode*)malloc(sizeof(DNode))
if(L==NULL)
return false;
L->prior=L;
L->next=L;
return true;
}
//判断是否为空
bool isEmpty(DLinkList L){
return L->next == L;
}
//判断结点p是否为循环双链表的表尾结点
bool isTail(DLinkList L,LNode *p){
return p->next == L;
}
4.静态链表
静态链表:定义一个较大的结构数组作为备用结点空间,当申请结点时,每个结点含有两个域,data域和next域,data域存放结点数据信息,next域不再是指针而是游标指示器
用数组的方式实现的链表
优点:增删操作不需要大量移动元素
缺点:不能随机存取,只能从头节点开始依次往后查找,容量固定不可变
//静态链表
#define MaxSize 10 //静态链表的最大长度
typedef struct {
ElemType data; //存储数据元素
int next; //下一个元素的数组下标
}SLinkList[MaxSize];
#define MaxSize 10 //静态链表的最大长度
struct Node{
ElemType data; //存储数据元素
int next; //下一个元素的数组下标
};
typedef struct Node SLinkList[MaxSize];
5.顺序表和链表比较
存储结构
顺序表:
优点:支持随机存取,存储密度高
缺点:大片连续空间分配不方便,改变容量不方便
链表:
优点:离散的小空间分配方便,改变容量方便
缺点:不可随机存取,存储密度低
表长难以预估,经常增加或删除元素-----链表
表长可以预估,查询操作较多----顺序表