线性表的学习
1.线性表的顺序表示–顺序表
1.1、顺序表的定义–静态分配
#include <stdio.h>
#define MaxSize 10 //定义最大长度
typedef struct{
int data[MaxSize];//用数组存储数据元素
int length;//顺序表的当前长度
}SeqList;//顺序表的类型定义
//基本操作--初始化一个顺序表
void InitList(SeqList &L){
for(int i=0;i<MaxSize;i++){
L.data[i]=0;//将所有的数据元素设置为默认初始值
}
L.length=0;//顺序表的初始长度为0
}
int main(){
SeqList L;//声明一个顺序表
InitList(L);//初始化顺序表
return 0;
}
1.2、顺序表的定义–动态分配
#include <stdio.h>
#include <stdlib.h>
#define InitSize 100 //表长度的初始定义长度
typedef struct{
int *data;//指示动态分配数组的指针
int MaxSize,length;//数组的最大容量和当前个数
}SeqList;//顺序表的类型定义
//基本操作--初始化一个顺序表
void InitList(SeqList &L){
L.data=(int *)malloc(sizeof(int)* InitSize);
L.length=0;//顺序表的初始长度为0
L.MaxSize=InitSize;
}
void IncreaseSize(SeqList &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);
}
int main(){
SeqList L;//声明一个顺序表
InitList(L);//初始化顺序表
//测试数据
L.data[0]=1;
L.data[1]=2;
L.data[2]=3;
L.length=3;
//测试打印
printf("1:%d,2:%d,3:%d\n",L.data[0],L.data[1],L.data[2]);
//测试扩容
IncreaseSize(L,5);
//测试扩容后打印
printf("1:%d,2:%d,3:%d",L.data[0],L.data[1],L.data[2]);
return 0;
}
1.3、顺序表静态分配的插入、删除、按位查找和按值查找
#include <stdio.h>
#define MaxSize 10 //定义最大长度
typedef struct{
int data[MaxSize];//用数组存储数据元素
int length;//顺序表的当前长度
}SeqList;//顺序表的类型定义
//基本操作--初始化一个顺序表
void InitList(SeqList &L){
for(int i=0;i<MaxSize;i++){
L.data[i]=0;//将所有的数据元素设置为默认初始值
}
L.length=0;//顺序表的初始长度为0
}
//基本操作--在顺序表L的位序i处插入元素e
bool ListInsert(SeqList &L,int i,int e){
if(i<1||i>L.length+1)//判断i的范围是否有效
return false;
if(L.length>=MaxSize){//当前存储空间已满,不能插入
return false;
}
for(int j=L.length;j>=i;j--){//将第i个元素之后的元素后移
L.data[j]=L.data[j-1];
}
L.data[i-1]=e;//在位置i处放入e
L.length++;//长度加1
return true;
}
bool ListDelete(SeqList &L,int i,int &e){
if(i<1||i>L.length){//判断i的范围是否有效
return false;
}
e=L.data[i-1];//将被删除的元素赋值给e
for(int j=i;j<L.length;j++){
L.data[j-1]=L.data[j];
}
L.length--;
return true;
}
//按位查找
int GetElem(SeqList L,int i){
if(i<1||i>L.length){//判断查找位是否有效
return false;
}
return L.data[i-1];
}
//按值查找
int LocateElem(SeqList L,int e){
if(L.length<=0){
printf("查找的顺序表为空!");
return -1;
}
for(int j=0;j<L.length;j++){
if(L.data[j]==e){
return j+1;
}
}
return -1;
}
//打印链表中的数据
void PrintList(SeqList L){
for(int i=0;i<L.length;i++){
printf("%d:%d ",i,L.data[i]);
}
printf("\n");
}
int main(){
SeqList L;//声明一个顺序表
InitList(L);//初始化顺序表
//测试数据
L.data[0]=1;
L.data[1]=2;
L.data[2]=3;
L.data[3]=4;
L.data[4]=5;
L.data[5]=6;
L.length=6;
PrintList(L);//测试打印
//测试插入
ListInsert(L,3,3);
PrintList(L);//测试打印
//测试删除
int e=0;
ListDelete(L,3,e);
PrintList(L);//测试打印
printf("查找第五个元素:%d\n",GetElem(L,5));
printf("查找元素6所在的位置:%d\n",LocateElem(L,6));
printf("查找元素7所在的位置素:%d\n",LocateElem(L,7));
return 0;
}
2.线性表的链式表示–链表
2.1、单链表的定义及基本操作
#include<stdio.h>
#include<stdlib.h>
typedef struct LNode{//定义单链表结点类型
int data;//数据域
struct LNode *next;//指针域
}LNode,*LinkList;
//头插法建立单链表
LinkList List_HeadInsert(LinkList &L){//逆向建立单链表
LNode *s;int x;
L=(LinkList)malloc(sizeof(LNode));//创建头结点
L->next=NULL;//初始为空链表
scanf("%d",&x);//输入结点的值
while(x!=9999){//输入9999表示结束
s=(LNode*)malloc(sizeof(LNode));//创建新结点
s->data=x;
s->next=L->next;
L->next=s;//将新结点插入表中,L为头指针
scanf("%d",&x);
}
}
//尾插法建立单链表
LinkList List_TailInsert(LinkList &L){
LNode *s;int x;
LNode *cur;
L=(LinkList)malloc(sizeof(LNode));//创建头结点
L->next=NULL;//初始化头结点
cur=L;//记录当前指针位置,从头结点开始
scanf("%d",&x);//输入值
while(x!=9999){//当输入x为9999时结束创建链表
s=(LNode *)malloc(sizeof(LNode));
s->data=x;
cur->next=s;
cur=s;
scanf("%d",&x);
}
cur->next=NULL;//尾指针置为NULL
return L;
}
//打印链表的方法
void Print_LinkList(LinkList L){
if(L->next==NULL){
printf("空链表!!!");
return;
}
LinkList temp=L->next;
while(temp!=NULL){
printf("%d->",temp->data);
temp=temp->next;
}
printf("\n");
return;
}
//按序号查找结点值
LinkList GetElem(LinkList &L,int i){
LNode* temp=L->next;
if(temp==NULL){
printf("空链表!!!");
return temp;
}
if(i<1){
printf("位置i无效!!!");
return NULL;
}
while(temp!=NULL&&i-1>0){
temp=temp->next;
i--;
}
return temp;
}
//按值查找结点值
LinkList LocateElem(LinkList &L,int e){
LNode* temp=L->next;
if(temp==NULL){
printf("空链表!!!");
return temp;
}
while(temp!=NULL&&temp->data!=e){
temp=temp->next;
}
return temp;
}
//插入结点操作(后插法)
bool List_RearInsert(LinkList &L,int i,LinkList &e){
LinkList L1;//查找位置的结点
L1=GetElem(L,i-1);
if(L1!=NULL){
e->next=L1->next;
L1->next=e;
return true;
} else{
return false;
}
}
bool List_NodeDelete(LinkList &L,int i){
LinkList temp,LElem;//临时变量
if(L->next==NULL){//空链表不操作
printf("空链表!!!");
return false;
}
if(i==1){//删除头结点需要特殊处理
if(L->next->next==NULL){
L->next=NULL;
}else{
temp=L->next;
L->next=temp->next;
free(temp);
}
return true;
}
//删除头结点后的结点
LElem=GetElem(L,i-1);
if(LElem!=NULL){
temp=LElem->next;
LElem->next=temp->next;
free(temp);
return true;
} else{
return false;
}
}
int main(){
// 头插法测试
// LNode *L1;
// List_HeadInsert(L1);
// Print_LinkList(L1);
// 尾插法测试
LNode *L2;
LNode *item1,*item2;
List_TailInsert(L2);
Print_LinkList(L2);
//查询测试
// item1=GetElem(L2,3);
// printf("按次序查找到的值:%d\n",item1->data);
// item2=LocateElem(L2,4);
// printf("按值查找到的值:%d\n",item2->data);
//插入测试
// LinkList e=(LinkList)malloc(sizeof(LNode));
// e->data=999;
// List_RearInsert(L2,3,e);
// Print_LinkList(L2);
//删除测试
List_NodeDelete(L2,7);
Print_LinkList(L2);
}
2.2、双链表的定义及基本操作
#include<stdio.h>
#include<stdlib.h>
typedef struct DNode{
int data;//数据域
struct DNode *prior,*next;//前后指针
}DNode,*DLinkList;
//双链表初始化
void Init_DLinkList(DLinkList &L){
L=(DLinkList)malloc(sizeof(DNode));//分配头结点地址
L->next=NULL;
L->prior=NULL;
return;
}
//尾插法创建链表
DLinkList DList_HeadInsert(DLinkList &L){
DLinkList temp,LN;//临时变量、每个新结点变量
temp=L;
int x;//新结点数据变量
scanf("%d",&x);
while(x!=9999){
LN=(DLinkList)malloc(sizeof(DNode));
LN->data=x;
LN->next=NULL;
LN->prior=temp;
temp->next=LN;
temp=LN;
scanf("%d",&x);
}
return L;
}
//打印链表的方法
void Print_LinkList(DLinkList L){
if(L->next==NULL){
printf("空链表!!!");
return;
}
DLinkList temp=L->next;
while(temp!=NULL){
printf("%d->",temp->data);
temp=temp->next;
}
printf("\n");
return;
}
//获取双链表位序i的结点
DLinkList GetElem(DLinkList &L,int i){
if(L->next==NULL||i<1){//排除空表和i输入不可能的情况
return NULL;
}
DLinkList temp=L->next;//获取当前链表
while(temp!=NULL&&i-1>0){
temp=temp->next;
i--;
}
return temp;
}
//插入结点操作
bool Node_Insert(DLinkList &L,int i,DLinkList &e){
if(L->next==NULL||i<1){//排除空表和i输入不可能的情况
return false;
}
DLinkList elem=GetElem(L,i);//获取查找到的结点
if(elem!=NULL){
//查询到该结点后在该结点之前插入e结点
elem->prior->next=e;
e->prior=elem->prior;
e->next=elem;
elem->prior=e;
return true;
}else{
return false;
}
}
//删除结点操作
bool Node_Delete(DLinkList &L,int i){
if(L->next==NULL||i<1){//排除空表和i输入不可能的情况
return false;
}
DLinkList elem=GetElem(L,i);//获取查找到的结点
if(elem!=NULL){
//查询到该结点后删除
if(elem->next!=NULL){
elem->prior->next=elem->next;
elem->next->prior=elem->prior;
}else{
elem->prior->next=NULL;
}
free(elem);
return true;
}else{
return false;
}
}
int main(){
//定义双链表L1,e为插入的变量
DLinkList L1,e;
//初始化
Init_DLinkList(L1);
Init_DLinkList(e);
e->data=0;
DList_HeadInsert(L1);
Print_LinkList(L1);
//测试插入结点
// Node_Insert(L1,3,e);
// Print_LinkList(L1);
//测试删除结点
Node_Delete(L1,3);
Print_LinkList(L1);
Node_Delete(L1,7);
Print_LinkList(L1);
}
循环单链表、循环双链表只是在处理头结点指向和尾节点指向时有些不同,需要将首尾相连,但是操作大体一致
2.3、静态链表的定义
#include<stdio.h>
#include<stdlib.h>
#define MaxSize 50 //静态链表的最大长度
typedef struct StaticList{//静态链表结构类型的定义
int data;//存储数据元素,可以是其它类型数据
int next;//下一个元素的数组下标
}StaticList;
int main(){
//定义静态链表
StaticList a[MaxSize];
}
3.顺序表和链表的比较
3.1、存取(读写)方式
顺序表可以顺序存取,也可以随即存取,链表只能从表头顺序存取元素。例如在第i个位置上执行存或取的操作,顺序表仅需一次访问,而链表则需要从表头开始依次访问i次。
3.2、逻辑结构与物理结构
采取顺序存储时,逻辑上相邻的元素,对应的物理存储位置也是相邻的。而采取链式存储时,逻辑上相邻的元素,物理存储位置不一定相邻,对应的逻辑关系时通过指针来表示的。
3.3、查找、插入和删除操作
对于按值查找,顺序表无序时,两者的事件复杂度均为O(n);顺序表有序时,可采用折半查找,此时的时间复杂度为O(log2n)。
对于按序号查找,顺序表支持随机访问,时间复杂度仅为O(1),而链表的平均复杂度为O(n)。顺序表的插入、删除操作,平均需要移动半个表长的元素。链表的插入、删除操作,只需要修改相关结点的指针即可。由于链表的每个结点都带有指针域,故而存储密度不够大。
3.4、空间分配
顺序存储在静态存储分配的情况下,一旦存储空间装满就不能扩充,若再加入新元素,则会出现内存溢出,因此需要预先分配足够大的存储空间。预先分配过大,可能会导致顺序表后部大量闲置;预先分配过小,又会造成内存溢出。动态存储分配虽然存储空间可以扩充,但需要移动大量的元素,导致操作效率低,而且若内存中没有更大块的连续存储空间,则会导致分配失败。链式存储的结点空间只在需要时申请分配,只要内存有空间就可以分配,操作灵活、高效。