王道数据结构源码实战ch2线性表
顺序表
顺序的表结构体定义
typedef struct SqList //和链表定义的单个节点不同,顺序表定义的结构体就是整个完整的表
{
ElemType data[MaxSize];
int length;
} SqList;
在顺序表L的第i个位置插入元素e
bool ListInsert(SqList &L,int i,ElemType e) //在第i个位置插入e
{
if(i<1||i>L.length+1) //插入位置越界,失败
return false;
if(L.length>=MaxSize) //长度已经达到上限,插入失败
return false;
//i已经用作参数,使用j作为循环变量
for(int j=L.length; j>=i; j--) //data[length]开始是空的,所以从最右边元素开始依次右移,直到原来第i个元素(下标为i-1)变为第i+1(下标为i)个元素
{
L.data[j]=L.data[j-1]; //每轮都是小的给大的
}
L.data[i-1]=e; //在现在空出来的第i个位置(下标为i-1)插入
L.length++; //插入后,长度+1
return true;
}
删除顺序表L第i个位置的元素,并用e取出
bool ListDelete(SqList &L,int i,ElemType &e) //删除第i个位置的元素,删之前用e保存
{
if(i<1||i>L.length) //删除位置越界,失败
return false;
if(L.length==0) //顺序表为空,无法删除,失败
return false;
e=L.data[i-1]; //取出第i个元素(下标为i-1)
for(int j=i; j<L.length; j++) //开始第i个位置(下标为i-1)是空的,所以从第i+1(下标为i)个位置开始向左填充
L.data[j-1]=L.data[j]; //每轮都是大的给小的
L.length--; //删除后,顺序表长度-1
return true;
}
在顺序表L中查找e是第几个元素
int ListLocate(SqList &L,ElemType e) //查找e是第几个元素
{
for(int i=0; i<L.length; i++)
if(L.data[i]==e)
return i+1; //下表为i代表第i+1个元素
return 0;
}
完整代码
#include<bits/stdc++.h>
using namespace std;
#define MaxSize 50
typedef int ElemType;
typedef struct SqList //和链表定义的单个节点不同,顺序表定义的结构体就是整个完整的表
{
ElemType data[MaxSize];
int length;
} SqList;
bool ListInsert(SqList &L,int i,ElemType e) //在第i个位置插入e
{
if(i<1||i>L.length+1) //插入位置越界,失败
return false;
if(L.length>=MaxSize) //长度已经达到上限,插入失败
return false;
//i已经用作参数,使用j作为循环变量
for(int j=L.length; j>=i; j--) //data[length]开始是空的,所以从最右边元素开始依次右移,直到原来第i个元素(下标为i-1)变为第i+1(下标为i)个元素
{
L.data[j]=L.data[j-1]; //每轮都是小的给大的
}
L.data[i-1]=e; //在现在空出来的第i个位置(下标为i-1)插入
L.length++; //插入后,长度+1
return true;
}
bool ListDelete(SqList &L,int i,ElemType &e) //删除第i个位置的元素,删之前用e保存
{
if(i<1||i>L.length) //删除位置越界,失败
return false;
if(L.length==0) //顺序表为空,无法删除,失败
return false;
e=L.data[i-1]; //取出第i个元素(下标为i-1)
for(int j=i; j<L.length; j++) //开始第i个位置(下标为i-1)是空的,所以从第i+1(下标为i)个位置开始向左填充
L.data[j-1]=L.data[j]; //每轮都是大的给小的
L.length--; //删除后,顺序表长度-1
return true;
}
int ListLocate(SqList &L,ElemType e) //查找e是第几元素
{
for(int i=0; i<L.length; i++)
if(L.data[i]==e)
return i+1; //下表为i代表第i+1个元素
return 0;
}
void PrintList(SqList L) //打印
{
for(int i=0; i<L.length; i++)
printf("%3d",L.data[i]); //每个数字占三个字符,右对齐,左边用空格占位
cout<<endl; //%md 表示:如果数据的位数小于 m,则左端补以空格,若大于 m,则按实际位数输出。
} //%-md 左对齐
//%0md 右对齐,左边补0
int main()
{
SqList L;
bool ret;
ElemType del;
L.data[0]=1;
L.data[1]=2;
L.data[2]=3;
L.length=3;
ret=ListInsert(L,2,60); //第二个位置插入60
if(ret)
{
cout<<"insert success!"<<endl;
PrintList(L);
}
else
{
cout<<"insert fail!"<<endl;
}
ret=ListDelete(L,1,del);
if(ret)
{
cout<<"delete success!"<<endl;
PrintList(L);
}
else
{
cout<<"delete fail!"<<endl;
}
int elem_pos; //用来接收元素的位置
elem_pos=ListLocate(L,60);
if(elem_pos)
{
cout<<"search success!"<<endl;
cout<<elem_pos<<endl;
}
else
{
cout<<"search fail!"<<endl;
}
return 0;
}
单链表
单链表节点的定义
typedef struct LNode //因为在定义内需要定义节点类型的指针,所以这里必须对类型命名
{
//结构体定义链表的节点,与顺序表的定义整个表不同
ElemType data;
struct LNode *next; //定义的递归,内部用到了结构体的类型
} LNode,*LinkList; //定义了结构体类型,与指向该结构体的指针,前者从名字上更强调的是一个节点,后者强调的是一个链表
头插法建立单链表L
LinkList List_Head_Insert(LinkList &L) //头插法一次插入多个
{
LNode*s; //临时变量,暂时指向待插入的节点
int x; //临时变量,暂存数据
L=(LinkList)malloc(sizeof(LNode)); //只有指针,要先生成头结点,L指向新生成的头结点
L->next=NULL; //至此,初始化完成,只有一个头结点
scanf("%d",&x); //第一次先读入x,才能进入while循环
while(x!=9999) //9999为约定的终止输入符号
{
s=(LNode*)malloc(sizeof(LNode)); //创建新的节点,用临时指针s指向
s->data=x; //给新节点放入数据
s->next=L->next; //挂右
L->next=s; //挂左
scanf("%d",&x);
}
return L;
}
尾插法建立单链表L
LinkList List_Tail_Insert(LinkList &L)//尾插法一次插入多个
{
int x; //临时变量,暂存数据
LNode*s,*r; //生成临时指针s,尾指针r
L=(LinkList)malloc(sizeof(LNode)); //只有指针,要先生成头结点
r=L; //r指向尾部节点(第一次写时写错了)
L->next=NULL; //初始化完成,此时有且仅有,一个头结点和一个尾指针
scanf("%d",&x);
while(x!=9999)
{
s=(LNode*)malloc(sizeof(LNode));
s->data=x;
//可省略挂右,s->next=NULL;,因为只有最后一个插入的节点需要置NULL
r->next=s; //只需挂左
r=s; //r=r->next更能表明指针后移一位
scanf("%d",&x);
}
r->next=NULL; //重要,最后一个节点 置NULL,否则会异常。
//不是NULL的话,默认指针的值为0xCDCDCDCD,不可访问,无法继续执行
return L;
}
获取单链表L第i个节点的指针
LNode* GetElem(LinkList L,int i) //获取第i个节点的指针
{
LNode*p=L->next; //临时指针指向头结点后第一个元素 ,作为遍历的开始
int j=1; //设定p现在的位置为第一个
if(i==0) //第0个节点是头结点
return L;
if(i<0) //越界,失败
return NULL;
while(p &&j<i) //当前节点不为NULL,最后停留在i前一个位置。
{
p=p->next;
j++;
}
return p; //最后一次循环中,p又后移一个位置,正好就是第i个。
}
获取单链表L中元素value第一次出现的位置
int LocateElem(LinkList L,ElemType value) //找到第一个相等的值的位置就结束,不考虑后面有相同的值
{
LNode*p=L->next; //从头结点后一个元素开始
int j=1;
while(p&&(p->data)!=value)
{
p=p->next;
j++;
}
return j;
}
在单链表L的第i个位置插入新节点(数据域为e)
bool ListFrontInsert(LinkList L,int i,ElemType e) //在第i个位置插入新节点(数据域为e)
{
LNode* p=GetElem(L,i-1); //p指向插入位置前一个节点的位置,作为开始
if(p==NULL)
return false;
LNode* s=(LNode*)malloc(sizeof(LNode)); //s临时指向新生成的节点
s->data=e;
s->next=p->next; //挂右
p->next=s; //挂左
return true;
}
删除单链表L的第i个元素
bool ListDelete(LinkList L,int i) //删除第i个元素
{
LNode* p=GetElem(L,i-1); //p指向删除位置前一个节点的位置
if(p==NULL) return false;
LNode* q=p->next; //必须暂存待删除节点的指针,因为后一歩改变了P的后继,找不到待删除节点了,无法free空间
if(q==NULL) return false;
p->next=q->next; //挂右
free(q); //暂存了待删除元素的位置,才能free
q==NULL; //处理野指针
return true;
}
完整代码
#include<bits/stdc++.h>
using namespace std;
typedef int ElemType;
typedef struct LNode //因为在定义内需要定义节点类型的指针,所以这里必须对类型命名
{
//结构体定义链表的节点,与顺序表的定义整个表不同
ElemType data;
struct LNode *next; //定义的递归,内部用到了结构体的类型
} LNode,*LinkList; //定义了结构体类型,与指向该结构体的指针,前者从名字上更强调的是一个节点,后者强调的是一个链表
LinkList List_Head_Insert(LinkList &L) //头插法一次插入多个
{
LNode*s; //临时变量,暂时指向待插入的节点
int x; //临时变量,暂存数据
L=(LinkList)malloc(sizeof(LNode)); //只有指针,要先生成头结点,L指向新生成的头结点
L->next=NULL; //至此,初始化完成,只有一个头结点
scanf("%d",&x); //第一次先读入x,才能进入while循环
while(x!=9999) //9999为约定的终止输入符号
{
s=(LNode*)malloc(sizeof(LNode)); //创建新的节点,用临时指针s指向
s->data=x; //给新节点放入数据
s->next=L->next; //挂右
L->next=s; //挂左
scanf("%d",&x);
}
return L;
}
LinkList List_Tail_Insert(LinkList &L)//尾插法一次插入多个
{
int x; //临时变量,暂存数据
LNode*s,*r; //生成临时指针s,尾指针r
L=(LinkList)malloc(sizeof(LNode)); //只有指针,要先生成头结点
r=L; //r指向尾部节点(第一次写时写错了)
L->next=NULL; //初始化完成,此时有且仅有,一个头结点和一个尾指针
scanf("%d",&x);
while(x!=9999)
{
s=(LNode*)malloc(sizeof(LNode));
s->data=x;
//可省略挂右,s->next=NULL;,因为只有最后一个插入的节点需要置NULL
r->next=s; //只需挂左
r=s; //r=r->next更能表明指针后移一位
scanf("%d",&x);
}
r->next=NULL; //重要,最后一个节点 置NULL,否则会异常。
//不是NULL的话,默认指针的值为0xCDCDCDCD,不可访问,无法继续执行
return L;
}
LNode* GetElem(LinkList L,int i) //得到第i个节点的指针
{
LNode*p=L->next; //临时指针指向头结点后第一个元素 ,作为遍历的开始
int j=1; //设定p现在的位置为第一个
if(i==0) //第0个节点是头结点
return L;
if(i<0) //越界,失败
return NULL;
while(p &&j<i) //当前节点不为NULL,最后停留在i前一个位置。
{
p=p->next;
j++;
}
return p; //最后一次循环中,p又后移一个位置,正好就是第i个。
}
int LocateElem(LinkList L,ElemType value) //找到第一个相等的值的位置就结束,不考虑后面有相同的值
{
LNode*p=L->next; //从头结点后一个元素开始
int j=1;
while(p&&(p->data)!=value)
{
p=p->next;
j++;
}
return j;
}
//不需要引用,因为头结点L不变
bool ListFrontInsert(LinkList L,int i,ElemType e) //在第i个位置插入新节点(数据域为e)
{
LNode* p=GetElem(L,i-1); //p指向插入位置前一个节点的位置,作为开始
if(p==NULL)
return false;
LNode* s=(LNode*)malloc(sizeof(LNode)); //s临时指向新生成的节点
s->data=e;
s->next=p->next; //挂右
p->next=s; //挂左
return true;
}
bool ListDelete(LinkList L,int i) //删除第i个元素
{
LNode* p=GetElem(L,i-1); //p指向删除位置前一个节点的位置
if(p==NULL) return false;
LNode* q=p->next; //必须暂存待删除节点的指针,因为后一歩改变了P的后继,找不到待删除节点了,无法free空间
if(q==NULL) return false;
p->next=q->next; //挂右
free(q); //暂存了待删除元素的位置,才能free
q==NULL; //处理野指针
return true;
}
void ShowList1(LinkList L) //打印链表
{
L=L->next; //先移动一次
while(L) //此后每次先打印再移动
{
cout<<L->data<<" ";
L=L->next;
}
cout<<endl;
}
void ShowList2(LinkList L)//打印链表方法二,自己想的
{
while(L->next!=NULL) //每次先移动,再打印
{
L=L->next;
cout<<L->data<<" ";
}
cout<<endl;
}
int main()
{
LinkList L; //定义单链表
LNode* searching; //用来保存指向第i个节点的指针,要操作的节点的前驱
//List_Head_Insert(L); //前插法建立单链表
//cout<<endl<<"------------linked list generated by head insertion:------------"<<endl;
//ShowList1(L);
List_Tail_Insert(L); //后插法建立单链表
cout<<endl<<"------------linked list generated by tail insertion:------------"<<endl;
ShowList1(L);
ListFrontInsert(L,3,666); //在第3个位置插入666
cout<<endl<<"------------Insert 666 in the 3rd position:------------"<<endl;
ShowList1(L);
ListDelete(L,2); //删除第2个元素
cout<<endl<<"------------delete the second element:------------"<<endl;
ShowList1(L);
searching=GetElem(L,2); //得到第2个位置的指针
cout<<endl<<"------------data element in 2nd position:------------"<<endl;
cout<<(searching->data)<<endl; //打印第二个位置的数据元素
int pos=LocateElem(L,5); //查找5第一次出现的位置
cout<<endl<<"------------The position 5 where it first appears:------------"<<endl;
cout<<pos<<endl; //打印5第一次出现的位置
//Test Data:
//5 6 96 85 65 9999
return 0;
}
双链表
双链表节点的定义
typedef struct DLNode
{
ElemType data;
struct DLNode *prior;
struct DLNode *next;
} DLNode,*DLinkList;
头插法建立双链表DL
DLinkList Dlist_Head_Insert(DLinkList &DL) //头插法
{
int x; //临时变量,暂存数据
DLNode * s;//临时变量,暂时指向待插入的节点
DL=(DLinkList)malloc(sizeof(DLNode));
DL->next=NULL;
DL->prior=NULL; //至此,初始化完成,只有一个头结点,两个指针域都为空
cin>>x; //先输入第一个元素,才能进入while循环
while(x!=9999) //9999为约定的终止输入符号
{
s=(DLNode*)malloc(sizeof(DLNode)); //创建新节点
s->data=x;
//插入过程为:先挂右,右返回;再挂左边,左返回
s->next=DL->next; //挂右
if(DL->next!=NULL) //右边没有元素时,不需要返回挂上
{
DL->next->prior=s; //右边有元素时,挂右(返回)
}
s->prior=DL; //挂左
DL->next=s; //挂左(返回)
cin>>x;
}
return DL;
}
尾插法建立单链表L
DLinkList Dlist_Tail_Insert(DLinkList &DL) //尾插法
{
int x; //临时变量,暂存数据
DLNode * s; //临时变量,暂时指向待插入的节点
DL=(DLinkList)malloc(sizeof(DLNode)); //先创建头结点,才能定义尾指针
DLNode*r=DL; //尾插法需要额外一个尾指针
DL->prior=NULL;
DL->next=NULL; //至此,初始化完成,只有一个头结点,两个指针域都为空
cin>>x;
while(x!=9999)
{
s=(DLNode*)malloc(sizeof(DLNode)); //创建新节点
s->data=x;
r->next=s; //挂左(返回)
s->prior=r; //挂左
r=s; //r=r->next; 尾指针后移
cin>>x;
}
r->next=NULL;
return DL;
}
获取双链表DL第i个节点的指针
DLNode* GetElem(DLinkList DL,int i) //获取第i个节点的指针
{
DLNode*p=DL->next; //临时指针指向头结点后第一个元素 ,作为遍历的开始
int j=1; //设定p现在的位置为第一个
if(i==0) //第0个元素,返回头结点
return DL;
if(i<0) //越界
return NULL;
while(p &&j<i) //当前节点不为NULL,最后停留在i前一个位置。
{
p=p->next;
j++;
}
return p; //最后一次循环中,p又后移一个位置,正好就是第i个。
}
在双链表DL的第i个位置插入新节点(数据域为e)
bool DList_Front_Insert(DLinkList &DL,int i,int e) //在第i个位置插入
{
DLNode*p=GetElem(DL,i-1); //找到待插入位置的前一个节点
if(p==NULL) return false;
DLNode*s=(DLNode*)malloc(sizeof(DLNode)); //创建新节点
s->data=e;
//第一种方式
s->next=p->next; //挂右
p->next->prior=s; //挂右(返回) //若在第一个位置插入,且此时只有头结点,需要特判这一句,此处忽略
s->prior=p; //挂左
p->next=s; //挂左(返回)
//另一种挂新节点的方式
/*
s->next=p->next; //挂右
s->prior=p; //挂左
p->next=s; //挂左(返回)
s->next->prior=s 挂右(返回)
*/
return true;
}
双链表的两种插入方式详解(王道和天勤):
删除单链表DL的第i个元素
bool DListDelete(DLinkList &DL,int i) //删除第i个位置的元素
{
DLNode*p =GetElem(DL,i-1); //找到待删除位置的前一个节点
if(p==NULL) return false;
DLNode*q=p->next; //保存待删除节点的指针,不然无法free空间
if(q==NULL) return false;
p->next=q->next; //挂右(可以指向NULL)
if(q->next!=NULL) //判断删除的是否最后一个节点
{
q->next->prior=p; //挂左
}
free(q);
q=NULL; //处理野指针
return true;
}
完整代码
#include<bits/stdc++.h>
using namespace std;
typedef int ElemType ;
typedef struct DLNode
{
ElemType data;
struct DLNode *prior;
struct DLNode *next;
} DLNode,*DLinkList;
DLinkList Dlist_Head_Insert(DLinkList &DL) //头插法
{
int x; //临时变量,暂存数据
DLNode * s;//临时变量,暂时指向待插入的节点
DL=(DLinkList)malloc(sizeof(DLNode));
DL->next=NULL;
DL->prior=NULL; //至此,初始化完成,只有一个头结点,两个指针域都为空
cin>>x; //先输入第一个元素,才能进入while循环
while(x!=9999) //9999为约定的终止输入符号
{
s=(DLNode*)malloc(sizeof(DLNode)); //创建新节点
s->data=x;
//插入过程为:先挂右,右返回;再挂左边,左返回
s->next=DL->next; //挂右
if(DL->next!=NULL) //右边没有元素时,不需要返回挂上
{
DL->next->prior=s; //右边有元素时,挂右(返回)
}
s->prior=DL; //挂左
DL->next=s; //挂左(返回)
cin>>x;
}
return DL;
}
DLinkList Dlist_Tail_Insert(DLinkList &DL) //尾插法
{
int x; //临时变量,暂存数据
DLNode * s; //临时变量,暂时指向待插入的节点
DL=(DLinkList)malloc(sizeof(DLNode)); //先创建头结点,才能定义尾指针
DLNode*r=DL; //尾插法需要额外一个尾指针
DL->prior=NULL;
DL->next=NULL; //至此,初始化完成,只有一个头结点,两个指针域都为空
cin>>x;
while(x!=9999)
{
s=(DLNode*)malloc(sizeof(DLNode)); //创建新节点
s->data=x;
r->next=s; //挂左(返回)
s->prior=r; //挂左
r=s; //r=r->next; 尾指针后移
cin>>x;
}
r->next=NULL;
return DL;
}
DLNode* GetElem(DLinkList DL,int i) //查找第i个节点的值
{
DLNode*p=DL->next; //临时指针指向头结点后第一个元素 ,作为遍历的开始
int j=1; //设定p现在的位置为第一个
if(i==0) //第0个元素,返回头结点
return DL;
if(i<0) //越界
return NULL;
while(p &&j<i) //当前节点不为NULL,最后停留在i前一个位置。
{
p=p->next;
j++;
}
return p; //最后一次循环中,p又后移一个位置,正好就是第i个。
}
bool DList_Front_Insert(DLinkList &DL,int i,int e) //在第i个位置插入
{
DLNode*p=GetElem(DL,i-1); //找到待插入位置的前一个节点
if(p==NULL) return false;
DLNode*s=(DLNode*)malloc(sizeof(DLNode)); //创建新节点
s->data=e;
//第一种方式
s->next=p->next; //挂右
p->next->prior=s; //挂右(返回) //若在第一个位置插入,且此时只有头结点,需要特判这一句,此处忽略
s->prior=p; //挂左
p->next=s; //挂左(返回)
//另一种挂新节点的方式
/*
s->next=p->next; //挂右
s->prior=p; //挂左
p->next=s; //挂左(返回)
s->next->prior=s 挂右(返回)
*/
return true;
}
bool DListDelete(DLinkList &DL,int i) //删除第i个位置的元素
{
DLNode*p =GetElem(DL,i-1); //找到待删除位置的前一个节点
if(p==NULL) return false;
DLNode*q=p->next; //保存待删除节点的指针,不然无法free空间
if(q==NULL) return false;
p->next=q->next; //挂右(可以指向NULL)
if(q->next!=NULL) //判断删除的是否最后一个节点
{
q->next->prior=p; //挂左
}
free(q);
q=NULL; //处理野指针
return true;
}
void ShowList2(DLinkList L)//自己想的
{
while(L->next!=NULL)
{
L=L->next;
cout<<L->data<<" ";
}
cout<<endl;
}
int main()
{
DLinkList DL; //定义双链表
DLNode* searching; //用来保存指向第i个节点的指针,要操作的节点的前驱
Dlist_Head_Insert(DL); //头插法
cout<<endl<<"------------Double linked list generated by head insertion:------------"<<endl;
ShowList2(DL);
//cout<<endl<<"-----------Double linked list generated by tail insertion:-----------"<<endl;
//Dlist_Tail_Insert(DL); //尾插法
//ShowList2(DL);
searching=GetElem(DL,3); //得到第3个位置的指针
cout<<endl<<"----------------The data element in the 3rd position is:----------------"<<endl;
cout<<(searching->data)<<endl; //打印第3个位置的数据元素
DList_Front_Insert(DL,2,666); //在第2个位置插入666
cout<<endl<<"--------------------Insert 666 in the 2nd position:--------------------"<<endl;
ShowList2(DL);
DListDelete(DL,666); //删除666
cout<<endl<<"-------------------------------Delete 666 :------------------------------"<<endl;
ShowList2(DL);
//Test Data:
//5 6 96 85 65 9999
return 0;
}
#define与typedef的区别
关键字typedef在编译阶段有效,由于是在编译阶段,因此typedef有类型检查的功能。
#define则是宏定义,发生在预处理阶段,也就是编译之前,它只进行简单而机械的字符串替换,而不进行任何检查