线性表的顺序定义
静态分配的顺序表
2.2.1
//王道第二章
//2.2线性表的顺序定义
//静态分配的顺序表
#include <iostream>
#include <stdio.h>
using namespace std;
#define MaxSize 10
typedef struct{
int data[MaxSize];
int length;
}SqList;
//初始化线性表
void InitList(SqList &L){
for(int i=0;i<MaxSize;i++){
L.data[i]=0;
}
L.length=0;
}
//销毁线性表
void DestoryList(SqList &L){
for(int i=0;i<MaxSize;i++){
L.data[i]=0;
}
L.length=0;
}
//插入操作
//在线性表L的第i个位置处插入指定元素e
//i是位序,从1开始
bool InsertList(SqList &L, int i, int e){
i=i-1;//将位序的i转化为角标
//判断请求是否合理
if(i<0 || i>L.length || L.length>=MaxSize) return false;
//请求合理,执行插入操作
for(int j=L.length;j>i;j--){
L.data[j]=L.data[j-1];
}
L.data[i]=e;
L.length++;
return true;
}
//删除操作
//删除线性表L中第i个位置的元素,返回删除元素的值
bool DeleteList(SqList &L, int i, int &e){
i--;
if(i<0 || i>=L.length) return false;
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 GetValue(SqList L, int e){
for(int i=0;i<MaxSize;i++){
if(L.data[i]==e) return i+1; //找得到返回对应的位序
}
return -1; //找不到返回-1
}
//按位查找
int GetElem(SqList L, int i){
return L.data[i-1];
}
//求表长
int LengthList(SqList L){
return L.length;
}
//输出操作
void PrintList(SqList L){
for(int i=0;i<L.length;i++){
printf("%d ",L.data[i]);
}
printf("\n");
}
//判空操作. 空返回true,不空返回false
bool Empty(SqList L){
if(L.length) return false;
return true;
}
int main(){
SqList L;
InitList(L);
/*
if(Empty(L)) cout<<"L为空表"<<endl;//printf("L为空表\n");
else printf("L不为空表\n");
int e=-1;
InsertList(L, 1, 88);
InsertList(L, 2, 77);
InsertList(L, 3, 66);
InsertList(L, 4, 55);
InsertList(L, 5, 44);
PrintList(L);
if(Empty(L)) printf("L为空表\n");
else printf("L不为空表\n");
DeleteList(L,3,e);
cout<<"删除的是e(66):"<<e<<endl;
PrintList(L);
DestoryList(L);
if(Empty(L)) printf("L为空表\n");
else printf("L不为空表\n");
*/
return 0;
}
线性表的顺序定义:
C++用new和delete
C语言中的malloc和free实现了动态的申请和释放空间(需要调用 <stdlib.h>头文件)
L.data=(ElemType *)malloc(sizeof(Elem type)*Initsize)
(ElemType *)用于强制转换为你所定义的数据类型的指针
malloc函数会返回申请的一片空间的开始地址的指针
动态顺序表
2.2.2
//王道第二章
//2.2线性表的顺序定义
//动态顺序表
#include <iostream>
#include <stdio.h>
#include <stdlib.h>
//malloc和free包含在<stdlib.h>的头文件中
using namespace std;
//动态分配的顺序表
//线性表的容量可变
#define InitSize 10
typedef struct{
int *data; //指示动态分配数组的指针
int MaxSize; //顺序表的最大容量
int length; //顺序表的当前长度
}SqList;
//初始化线性表
void InitList(SqList &L){
L.data=(int *)malloc(InitSize*sizeof(int));
L.length=0;
L.MaxSize=InitSize;
}
//增加动态数组的长度
void IncreaseList(SqList &L, int len){
int *p=L.data;
L.data=(int *)malloc((L.MaxSize+len)*sizeof(int));
for(int i=0;i<L.length;i++){
L.data[i]=p[i]; //将数据复制到新区域
}
L.MaxSize=L.MaxSize+len; //顺序表最大长度增加len
free(p); //释放原来的内存空间
}
//销毁线性表
void DestoryList(SqList &L){
for(int i=0;i<L.MaxSize;i++){
L.data[i]=0;
}
L.length=0;
}
//插入操作
//在线性表L的第i个位置处插入指定元素e
bool InsertList(SqList &L, int i, int e){
i=i-1;//将位序的i转化为角标
//判断请求是否合理
if(i<0 || i>L.length || L.length>=L.MaxSize) return false;
//请求合理,执行插入操作
for(int j=L.length;j>i;j--){
L.data[j]=L.data[j-1];
}
L.data[i]=e;
L.length++;
return true;
}
//删除操作
//删除线性表L中第i个位置的元素,返回删除元素的值
bool DeleteList(SqList &L, int i, int &e){
i--;
if(i<0 || i>=L.length) return false;
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 GetValue(SqList L, int e){
for(int i=0;i<L.MaxSize;i++){
if(L.data[i]==e) return i+1; //找得到返回对应的位序
}
return -1; //找不到返回-1
}
//按位查找
int GetElem(SqList L, int i){
return L.data[i-1];
}
//求表长
int LengthList(SqList L){
return L.length;
}
//输出操作
void PrintList(SqList L){
for(int i=0;i<L.length;i++){
printf("%d ",L.data[i]);
}
printf("\n");
}
//判空操作. 空返回true,不空返回false
bool Empty(SqList L){
if(L.length) return false;
return true;
}
int main(){
SqList L;
InitList(L);
if(Empty(L)) cout<<"L为空表"<<endl;//printf("L为空表\n");
else printf("L不为空表\n");
int e=-1;
InsertList(L, 1, 88);
InsertList(L, 2, 77);
InsertList(L, 3, 66);
InsertList(L, 4, 55);
InsertList(L, 5, 44);
PrintList(L);
if(Empty(L)) printf("L为空表\n");
else printf("L不为空表\n");
DeleteList(L,3,e);
cout<<"删除的是e(66):"<<e<<endl;
PrintList(L);
DestoryList(L);
if(Empty(L)) printf("L为空表\n");
else printf("L不为空表\n");
PrintList(L);
return 0;
}
不带头节点的单链表
2.2.3
//王道第二章
//不带头节点的单链表
#include <iostream>
#include <stdio.h>
#include <stdlib.h>
//malloc和free包含在<stdlib.h>的头文件中
using namespace std;
//动态分配的顺序表
//线性表的容量可变
typedef struct LNode{ //定义单链表的结点
int data; //数据域
struct LNode *next; //指针域
}LNode,*LinkList;
//初始化一个空的单链表(不带头节点)
bool InitList(LinkList &L){ //&L因为需要修改L
L=NULL; //空表,暂时还没有任何节点
return true;
}
//判断单链表是否为空(不带头节点)
bool Empty(LinkList L){
if(L==NULL) return true;
else return false;
//等价于
//return (L==NULL);
}
//尾插法
LinkList List_Tailnsert(LinkList &L){ //正向建立单链表
int x; //设ElemType为整型
L=(LinkList)malloc(sizeof(LNode));//建立头节点
LNode *s,*r; //r为表尾指针
r=L;
scanf("%d",&x); //输入结点的值储存在x中
while(x!=9999){ //设置特殊值9999,输入9999表示输入结束
s=(LNode *)malloc(sizeof(LNode));
//在r结点之后插入s结点
s->data=x;
r->next=s;
r=s; //r指向新的表尾结点
scanf("%d",&x);
}
r->next=NULL; //尾结点指针置空
return L;
}
//头插法
LinkList List_HeadInsert(LinkList &L){//逆向建立单链表
LNode *s,*head;
head=NULL;
int x;
scanf("%d",&x);
while(x!=9999){
s=(LNode *)malloc(sizeof(LNode));
s->data=x;
s->next=head;
head=s;
scanf("%d",&x);
}
return head;
}
//链表的原地逆置
LinkList List_HeadInsert_turn(LinkList &L){//逆向建立单链表
if(L == NULL || L->next == NULL) return L; //判定返回
LNode *head,*p,*q;
//head是新的头节点
//p是用来往后遍历链表L的指针
//q是用来存储p的备份的
head=NULL;
p=L;
while(p!=NULL){
q = p;
//向后遍历
p = p->next;
//与头插法类似
q->next = head;
head=q;
}
return head;
}
//转置后存入新的链表
LinkList List_HeadInsert_turn2(LinkList &L){//逆向建立单链表
if(L == NULL || L->next == NULL) return L; //判定返回
LNode * first_node=(LNode *)malloc(sizeof(LNode));
LNode *p;
//first_node是每一次通过单链表插入之后的最开始的结点
//一开始建立的链表是不带头结点的单链表,在最后完成链表创建后再加上头节点
//p是用来往后遍历链表L的指针
first_node=NULL;
p=L;
while(p!=NULL){
LNode *s=(LNode *)malloc(sizeof(LNode));
//与头插法类似
s->data=p->data;
s->next = first_node;
first_node=s;
//向后遍历
p = p->next;
}
return first_node;
}
//按位序插入(不带头结点)
//在表L的第i个位置上插入指定元素e,就是在第i-1的后面插入新元素
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; //将结点s连接到p之后
return true;
}
LNode *p; //指针p指向当前扫描到的结点
p=L; //L指向当前节点,头节点视为第1个节点
int j=1; //j存当前p指向的是第几个节点
while(p->next!=NULL && j<i-1){//循环找到第i-1个结点
p=p->next;
j++;
}
if(p==NULL) return false; //i值不合法
LNode *s=(LNode *)malloc(sizeof(LNode));
s->data=e;
s->next=p->next;
p->next=s; //将结点s连接到p之后
return true; //插入成功
}
//打印列表
void PrintList(LinkList L){
LNode *p;
p=L;
while(p!=NULL){
printf("%d ",p->data);
p=p->next;
}
puts("");
return;
}
int main(){
LinkList L;
L=List_HeadInsert(L);
PrintList(L);
L=List_HeadInsert_turn(L);
PrintList(L);
return 0;
}
带头节点的单链表
2.2.4
//王道第二章
//带头节点的单链表
#include <iostream>
#include <stdio.h>
#include <stdlib.h>
//malloc和free包含在<stdlib.h>的头文件中
using namespace std;
typedef struct LNode{ //定义单链表的结点
int data; //数据域
struct LNode *next; //指针域
}LNode,*LinkList;
//初始化一个空的单链表(带头节点)
bool InitList(LinkList &L){ //&L因为需要修改L
L=(LNode *)malloc(sizeof(LNode)); //分配一个头结点
if(L==NULL) return false; //空间不足,分配失败
L->next=NULL; //头节点之后暂时还没有节点
return true;
}
//判断单链表是否为空(带头节点)
bool Empty(LinkList L){
if(L->next==NULL) return true;
else return false;
}
//尾插法建立单链表:函数内包含链表的初始化
LinkList List_Tailnsert(LinkList &L){ //正向建立单链表
int x; //设ElemType为整型
L=(LinkList)malloc(sizeof(LNode));//建立头节点
LNode *s,*r=L; //r为表尾指针
scanf("%d",&x); //输入结点的值储存在x中
while(x!=9999){ //设置特殊值9999,输入9999表示输入结束
s=(LNode *)malloc(sizeof(LNode));
//在r结点之后插入s结点
s->data=x;
r->next=s;
r=s; //r指向新的表尾结点
scanf("%d",&x);
}
r->next=NULL; //尾结点指针置空
return L;
}
//头插法
LinkList List_HeadInsert(LinkList &L){//逆向建立单链表
LNode *s;
int x;
L=(LinkList)malloc(sizeof(LNode));//创建头节点
L->next=NULL; //初始化为空链表
scanf("%d",&x);
while(x!=9999){
s=(LNode *)malloc(sizeof(LNode));
s->data=x;
s->next=L->next;
L->next=s;
scanf("%d",&x);
}
return L;
}
//按位序插入(带头结点)
//在表L的第i个位置上插入指定元素e,就是在第i-1的后面插入新元素
bool ListInsert(LinkList &L,int i,int e){
if(i<1) return false; //判断传入的位置是否符合定义区间
LNode *p; //指针p指向当前扫描到的结点
p=L; //L指向当前节点,头节点视为第0个节点
int j=0; //j存当前p指向的是第几个节点
while(p!=NULL && j<i-1){//循环找到第i-1个结点
p=p->next;
j++;
}
//下面的这部分可以直接通过调用InsertNextNode(LNode *p,int e)函数实现
if(p==NULL) return false; //i值不合法
LNode *s=(LNode *)malloc(sizeof(LNode));
s->data=e;
s->next=p->next;
p->next=s; //将结点s连接到p之后
return true; //插入成功
}
//后插操作:在p结点之后插入元素e
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;
}
//前插操作:在p结点之前插入元素e
//相当于新开一个结点插在了p结点的后面,但是把所存储的data交换一下
//王道网课里面的写法
bool InsertPriorNode(LNode *p,int e){
if(p==NULL) return false;
LNode *s=(LNode *)malloc(sizeof(LNode));
if(s==NULL) return false; //说明内存分配失败
//交换数据
s->data=p->data;
p->data=e;
//连接链表
s->next=p->next;
p->next=s;
return true;
}
//前插操作:在p结点之前插入元素e
//相当于新开一个结点插在了p结点的后面,但是把所存储的data交换一下
//王道书里面的写法,直接传了要插入的结点
bool InsertPriorNode2(LNode *p,LNode *s){
if(p==NULL || s==NULL) return false;
//交换数据
int temp=s->data;
s->data=p->data;
p->data=temp;
//连接链表
s->next=p->next;
p->next=s;
return true;
}
//删除操作:删除表L中第i个位置的元素,并用e返回删除元素的值
bool ListDelete(LinkList &L,int i,int &e){
if(i<1) return false;
LNode *p=L;
int j=0; //标记p中所存储的结点的位置
while(p!=NULL && j<i-1){
p=p->next;
j++;
}
if(p==NULL || p->next==NULL) return false;
LNode *q=p->next;
e=q->data;
p->next=q->next;
free(q); //释放结点的存储空间
return true;
}
//删除指定结点p
bool DeleteNode(LNode *p){
if(p==NULL) return false;
//这里有bug
//如果p指向的是NULL,P是结尾处的话
//必须传入&L,然后从前往后便利找到前一个结点
LNode *q=p->next;
p->data=q->data;
p->next=q->next;
free(q);
return true;
}
//按位查找,返回第i个元素(带头结点)
LNode * GetElem(LinkList L,int i){
if(i<0) return NULL;
LNode *p;
p=L;
int j=0;
while(p!=NULL && j<i){
p=p->next;
j++;
}
return p;
}
//按位查找,返回第i个元素(带头结点)
//王道书里面的版本
LNode * GetElem2(LinkList L,int i){
if(i==0) return L;
if(i<0) return NULL;
int j=1;
LNode *p;
p=L->next;
while(p!=NULL && j<i){
p=p->next;
j++;
}
return p;
}
//按值查找:找到“数据域==e”的结点
LNode * LocateElem(LinkList L,int e){
LNode *p=L;
int j=0;
while(p->data!=e && p!=NULL){
p=p->next;
j++;
}
return p;
}
//求表的长度
int Length(LinkList L){
int len=0;
LNode *p=L;
while(p!=NULL){
p=p->next;
len++;
}
return len;
}
//打印列表
void PrintList(LinkList L){
LNode *p;
p=L->next;
while(p!=NULL){
printf("%d ",p->data);
p=p->next;
}
printf("\n");
return;
}
//链表的原地逆置:利用头插法的性质原地逆置
//LinkList List_HeadInsert(LinkList &L){//逆向建立单链表
LinkList List_HeadInsert_turn(LinkList &L){//逆向建立单链表
if(L == NULL || L->next == NULL) //判定返回
return L;
LNode *head,*p,*q;
//head是新的头节点
//p是用来往后遍历链表L的指针
//q是用来存储p的备份的
head=NULL;
p=L->next;
while(p!=NULL){
q = p;
//向后遍历
p = p->next;
//与头插法类似
q->next = head;
head=q;
}
return head;
}
//转置后存入新的链表
LinkList List_HeadInsert_turn2(LinkList &L){//逆向建立单链表
if(L == NULL || L->next == NULL) //判定返回
return L;
LNode * first_node=(LNode *)malloc(sizeof(LNode));
LNode *p;
//first_node是每一次通过单链表插入之后的最开始的结点
//一开始建立的链表是不带头结点的单链表,在最后完成链表创建后再加上头节点
//p是用来往后遍历链表L的指针
first_node=NULL;
p=L->next;
while(p!=NULL){
LNode *s=(LNode *)malloc(sizeof(LNode));
//与头插法类似
s->data=p->data;
s->next = first_node;
first_node=s;
//向后遍历
p = p->next;
}
//完成链表创建后加上头节点
LinkList realhead=(LinkList)malloc(sizeof(LinkList));
realhead->next=first_node;
return realhead;
}
int main(){
LinkList L,NewL;
L=List_HeadInsert(L);
//L=List_Tailnsert(L);
PrintList(L);
NewL=List_HeadInsert_turn2(L);
printf("NewList:");
PrintList(NewL);
return 0;
}
双链表
2.2.5
//王道第二章
//双链表
#include <iostream>
#include <stdio.h>
#include <stdlib.h>
using namespace std;
//DNode D是double的意思,两个
typedef struct DNode{ //定义双链表的结点
int data; //数据域
struct DNode *prior,*next; //指针域
}DNode,*DLinkList;
//初始化一个空的双链表
bool InitDList(DLinkList &L){ //&L因为需要修改L
L=(DNode *)malloc(sizeof(DNode)); //分配一个头结点
if(L==NULL) return false; //空间不足,分配失败
L->prior=NULL; //头节点之后的prior永远指向NULL
L->next=NULL; //头节点之后暂时还没有节点
return true;
}
//判断双链表是否为空
bool Empty(DLinkList L){
if(L->next==NULL) return true;
else return false;
}
//后插操作:在p结点之后插入s结点
//注意修改指针的顺序
bool InsertNextNode(DNode *p,DNode *s){
if(p==NULL || s==NULL) return false;
s->next=p->next;
if(p->next!=NULL) p->next->prior=s; //如果p结点有后继结点
//如果p结点没有后级结点,就不需要后级结点的前向指针
s->prior=p;
p->next=s;
return true;
}
//删除指定结点p的后继结点
bool DeleteDNode(DNode *p){
if(p==NULL || p->next==NULL) return false;
DNode *q=p->next;
p->next=q->next;
//判断q结点有无后继结点,如果有需要修改前向指针
if(q->next != NULL) q->next->prior=p;
free(q);
return true;
}
//销毁双链表的操作
void DestoryDList(DLinkList &L){
while(L->next!=NULL){
DeleteDNode(L);
}
free(L);
L=NULL;
return;
}
//正序打印双列表
//正向遍历双链表
void PrintList(DLinkList L){
DNode *p;
p=L;
while(p!=NULL){
printf("%d ",p->data);
p=p->next;
}
printf("\n");
return;
}
//逆序打印双列表
//逆向遍历双链表
void De_PrintList(DLinkList tail){
DNode *p;
p=tail;
while(p!=NULL){
printf("%d ",p->data);
p=p->prior;
}
printf("\n");
return;
}
int main(){
DLinkList L;
InitDList(L);
return 0;
}
循环单链表(带头结点)
2.2.6
//王道第二章
//循环单链表(带头结点)
#include <iostream>
#include <stdio.h>
#include <stdlib.h>
using namespace std;
typedef struct LNode{ //定义循环单链表的结点
int data; //数据域
struct LNode *next; //指针域
}LNode,*LinkList;
//初始化一个循环单链表
bool InitList(LinkList &L){ //&L因为需要修改L
L=(LNode *)malloc(sizeof(LNode)); //分配一个头结点
if(L==NULL) return false; //空间不足,分配失败
L->next=L; //单链表为空的状态下,头节点next指向头节点自己
return true;
}
//判断循环单链表表是否为空
bool Empty(LinkList L){
//为空的条件是头节点指向头节点自己
if(L->next==L) return true;
else return false;
}
//判断结点p是否为循环单链表的表尾结点
bool isTail(LinkList L,LNode *p){
if(p->next==L) return true;
else return false;
}
int main(){
LinkList L;
InitList(L);
return 0;
}
循环双链表
2.2.7
//王道第二章
//循环双链表
#include <iostream>
#include <stdio.h>
#include <stdlib.h>
using namespace std;
typedef struct DNode{ //定义双链表的结点
int data; //数据域
struct DNode *prior,*next; //指针域
}DNode,*DLinkList;
//初始化一个空的循环双链表
bool InitDList(DLinkList &L){ //&L因为需要修改L
L=(DNode *)malloc(sizeof(DNode)); //分配一个头结点
if(L==NULL) return false; //空间不足,分配失败
L->prior=L; //头节点之后的prior永远指向NULL
L->next=L; //头节点之后暂时还没有节点
return true;
}
//判断双链表是否为空
bool Empty(DLinkList L){
if(L->next==L) return true;
else return false;
}
//判断结点p是否为循环双链表的表尾结点
bool isTail(DLinkList L,DNode *p){
if(p->next==L) return true;
else return false;
}
//在p结点之后插入s结点
bool InsertNextDNode(DNode *p,DNode *s){
s->next=p->next;
s->prior=p;
//用循环双链表的时候
//不用判断if(p->next!=NULL),因为p->next一定非空
//下面对于删除p结点的后继结点s也是相同的
p->next->prior=s;
p->next=s;
return true;
}
//删除p结点的后继结点
bool InsertNextDNode(DNode *p){
DNode *s=p->next;
p->next=s->next;
s->prior=p;
free(s);
return true;
}
int main(){
DLinkList L;
InitDList(L);
return 0;
}
静态链表
2.2.8
方法一:我们常常能想到定义一个静态链表的方法
//王道第二章
//静态链表
//我们常常能想到定义一个静态链表的方法
#include <iostream>
#include <stdio.h>
#include <stdlib.h>
using namespace std;
#define MaxSize 10 //静态链表的最大长度
typedef struct Node{ //静态链表结构类型的定义
int data; //存储的数据元素
int next; //下一个元素的下标
}Node;
int main(){
//定义一个静态链表
Node a[MaxSize];
return 0;
}
静态链表
方法二:王道书给出的定义方法
#define MaxSize 10 //静态链表的最大长度
typedef struct{ //静态链表结构类型的定义
int data; //存储的数据元素
int next; //下一个元素的下标
}SLinkList[MaxSize];
//等价于
//typedef struct Node SLinkList[MaxSize]
//就是可以直接用SLinkList定义一个长度为MaxSize的Node型数组
int main(){
//定义一个静态链表
SLinkList a;
//等价于Node a[MaxSize];
return 0;
}
游标为-1代表已经指向了静态链表的表尾
初始化静态链表:将a[0]设置为-1
查找(查找某个位序的结点):从头结点出发,通过游标依次往后遍历结点,直至找到我们想要的结点,时间复杂度O(n)。(注意我们这里说的是某一个位序的结点而不是某一个数组下标的结点,位序指的是各个结点在逻辑上的顺序,而这里的数组下表是实际物理存储空间中的位置顺序)
插入位序为i的结点(就是在i-1的后面插入一个结点):
1.找到一个空的结点,用来存放新节点
2.将为位序为i-1结点所存储的游标next改为i;将原来i-1中所存储的游标next存入i,让表重新连起来
如何判断结点为空? 可以在初始化静态链表的时候将空结点的游标next设置为特殊值,如-2,因此如果判断结点的游标next为-2时,就知道该节点是空闲的,可以用来存储新的数据元素
因此删除数据的时候也要记得将删除结点的游标next设置为-2,表示该点被回收了
关于顺序表和链表的对比