学会线性表,一篇就够了

线性表是最常用最典型的线性结构。

简易目录:

线性表:

   逻辑特征

   线性表的类型定义

   存储结构

         顺序存储表示

             元素存储位置的计算

             顺序表的基本操作实现

             顺序表各算法时间复杂度的计算

             C++中的参数传递

        链式存储表示

             单链表

             双向链表

             循坏链表

       单链表,循坏链表和双向链表的时间效率的比较

       顺序表和链表的比较

       线性表的合并

           用顺序表实现

           用链表实现

 

可以看到,线性表是最基础,也是最简单的了。所以我们从线性表开始学习。

定义:具有相同特性的数据元素的一个有限序列,例如:

逻辑特征

线性表的类型抽象数据类型,也称数据类型)定义

上述的基本操作是逻辑结构上定义的运算,那么如何实现这些基本运算,就要先确定其存储结构。

线性表的存储以及在存储结构上各操作的实现

线性表的基本操作

操作算法中用到的预定义变量和类型

线性表的存储结构有四种,其中最基本两种的存储结构:顺序存储结构和链式存储结构

线性表的顺序存储表示(也称为顺序映像):

定义:把逻辑上相邻的数据元素存储在物理上相邻的存储单元中。

也就是说线性表顺序存储我们可以用一维数组来表示,但由于数组长度不可动态定义

即:

//错误写法 
int n;
scanf("%d",&n);
int a[n];

这样是不可行的,所以要想用一维数组来表示,就必须再定义一个长度,像以上一样用结构体,在里面定义一个长度就好了。

元素存储位置的计算

我们一般用的都是第二个公式,因为一般第一个元素的地址(即基地址)我们都知道。

顺序表的基本操作实现:

#include<stdio.h>
#include<iostream>
using namespace std;
//1.malloc(m):开辟m字节长度的地址空间,并返回这段空间的首地址
//2.sizeof(x):计算变量x的长度
//3.free(p)函数:释放指针p所指变量的存储空间,即彻底删除一个变量
//以上3个函数都需用到下面的这个头文件 
#include<stdlib.h> 
//函数的结果状态代码 
#define OVERFLOW -2   
#define OK 1   
#define TRUE   1
#define FALSE  0
#define ERROR  0   
//定义 
#define MAXSIZE 100
typedef struct{
	int *elem; //(数组动态分配)//int elem[MAXSIZE];(数组静态分配)  
	int length;
}SqList;
int InitList(SqList &L);//初始化操作,建立一个空的线性表L 
void DestroyList(SqList &L);//销毁线性表L 
void ClearList(SqList &L);//清空线性表L 
int ListInsert(SqList &L,int i,int e);//在线性表L中第i个位置插入新元素e 
int ListDelete(SqList &L,int i,int &e);// 删除L中第i个位置的元素,并用e返回其值
bool ListEmpty(SqList L);//判断线性表是否为空,若为空,返回true,否则,返回false 
int ListLength(SqList L);//返回线性表L中的元素个数 
int LocateElem(SqList L,int e);//L中查找与给定值e相等的元素,若成功,则返回该元素在表中的序号,否则返回0 
int GetElem(SqList L,int i,int &e);//将线性表L中第i个位置的元素返回给e 
int main()
{
	SqList L;//创建线性表L 
	int m=InitList(L);
	if(m)
	cout<<"成功创建空表L"<<endl;
	ListInsert(L,1,3);
	cout<<"已成功插入"<<endl;
	cout<<"插入后的长度:"<<L.length<<endl;
	ListInsert(L,2,6);
	cout<<"已成功插入"<<endl;
	cout<<"插入后的长度:"<<L.length<<endl;
	int e; 
	ListDelete(L,1,e);
	cout<<"被删除的元素:"<<e<<endl; 
	cout<<"删除后的长度:"<<L.length<<endl;
	ListEmpty(L);
	if(ListEmpty(L)) 
	cout<<"线性表L为空"<<endl;
	else
	cout<<"线性表L非空"<<endl;
	int n; 
	ListLength(L);
	cout<<"此时线性表的长度:"<<ListLength(L)<<endl;
	int a=6;
	LocateElem(L,a);
	cout<<"该元素的序号为:"<<LocateElem(L,a)<<endl;
	GetElem(L,1,e);
	cout<<"线性表L的第1个元素:"<<e<<endl; 
	ClearList(L);
	cout<<"线性表L已清空"<<endl; 
	DestroyList(L);
	cout<<"线性表L已销毁"<<endl; 
	return 0;
}
//初始化(即建立一个空的顺序表) 
int InitList(SqList &L)
{
 
    L.elem=new int[MAXSIZE];                       // 用C++来为顺序表分配空间
	// L.elem=(int*)malloc(sizeof(int)*MAXSIZE);   //用C语言来为顺序表分配空间 
	if(!L.elem) exit(OVERFLOW);                    //exit表程序终止  OVERFLOW可看作“溢出” 
	L.length=0;
	return OK;
}
//销毁线性表L
void DestroyList(SqList &L)
{
	if(L.elem) delete L.elem;
	 
}
//清空线性表L 
void ClearList(SqList &L)
{
	L.length=0;
}
//在线性表L中第i个位置插入新元素e 
int ListInsert(SqList &L,int i,int e)
{
	if(i<1||i>L.length+1) return ERROR;//插入位置不合法,可以插入在第1到n个位置,也可以插入到表最后(即第n+1个位置) 
 	if(L.length==MAXSIZE) return ERROR;//当前存储空间已满 
	for(int j=L.length-1;j>=i-1;j--)
 	{
 	    L.elem[j+1]=L.elem[j];	      //插入位置及以后的元素后移 
	 }
	 L.elem[i-1]=e;                   //将新元素放到第i个位置上,即下标为i-1的位置上 
	 L.length++;
//平均移动次数n/2,时间复杂度O(n) 
}
//删除L中第i个位置的元素,并用e返回其值
int ListDelete(SqList &L,int i,int &e)
{
     if( (i<1) || (i>L.length) )       return ERROR;
     e = L.elem[i-1];
     for(int j=i ;j<=L.length-1 ;j++)
        L.elem[j-1]=L.elem[j];        //被删除元素之后的元素都前移
     --L.length;                      
     return OK;
//平均移动次数(n-1)/2,时间复杂度O(n) 
 }
//判断线性表是否为空
bool ListEmpty(SqList L)
{
	if(L.length==0) return true;
	else
	return false;
}
//返回线性表L中的元素个数 
int ListLength(SqList L)
{
	return(L.length);
}
//在L中查找与e相等的元素,并返回该元素在表中的序号
int LocateElem(SqList L,int a)
{
	for(int i;i<L.length;i++)
	if(L.elem[i]==a) return i+1;  //查找成功,返回序号 
	return 0;                    //查找失败,返回0 
//平均查找次数(n+1)/2,时间复杂度O(n) 
}
//将线性表L中第i个位置的元素返回给e
int GetElem(SqList L,int i,int &e)
{
   	if(i < 1 || i > L.length)  return ERROR;  //i值不合理
   	e=L.elem[i-1];                           
    return OK;
//时间复杂度O(1) 
}

运行结果

顺序表各算法时间复杂度的计算:

1.插入:

所以时间复杂度就为O(n)。(如果不懂,可以参考第一章绪论关于时间复杂度的分析)

2.删除:

所以时间复杂度为O(n)

3.查找:

时间复杂度O(n)

同理:

若查找的是第一个元素,则查找1次就行了

若查找的是第n个元素或查找不成功,则都需要查找n次

平均查找次数:(1/n)*((1+n)*n)/2)=(n+1)/2

 C++中的参数传递:(主要解释代码实现中&的含义)

 1.指针变量做参数:

#include<iostream>
using namespace std;
void swap(int *m,int *n);
void swap2(int *x,int *y);
int main()
{
	int a=3,c=3;
	int b=5,d=5;
	swap(&a,&b);
	swap2(&c,&d);
	cout<<a<<" "<<b<<endl;
	cout<<c<<" "<<d<<endl; 
	return 0;
}
//形参变化影响实参 
void swap(int *m,int *n)
{
	int t;
	t=*m;
	*m=*n;
	*n=t;
}
//形参变化不影响实参 
void swap2(int *x,int *y) 
{
	int *t;
	t=x;
	x=y;
	y=t;
}

运行结果:

2.数组名作参数:

#include<iostream>
using namespace std;
void sub(char b[]);
int main()
{
   char a[10]="hello";
   sub(a);
   cout<<a<<endl; 	
}
void sub(char b[])
{
	b[0]='w';
}

 运行结果:

 3.引用类型做参数:

#include<iostream>
using namespace std;
void swap(int &m,int &n);
int main()
{
	int a=3;
	int b=5;
	swap(a,b);
	cout<<a<<" "<<b<<endl;
}
void swap(int &m,int &n)
{
	int t;
	t=m;
	m=n;
	n=t;
}

运行结果:

可见,我们要修改原来的值时,可用& ,就如顺序表代码实现时,用&L和L的区别就在于L是否被修改。

线性表的链式存储表示

定义:

单链表:

头结点的作用:

头结点的数据域:

单链表的特点:

 单链表的基本操作以及代码实现:(带头结点)

#include<iostream>
using namespace std;
//函数的结果状态代码 
#define OK 1
#define ERROR  0   
typedef int ElemType ;//ElemType就相当于int 
//定义
typedef struct LNode{    //声明结点的类型和指向结点的指针类型 
	ElemType data;            //结点的数据域 
	struct LNode *next;  //结点的指针域 ,指向的仍然是这样的一个结构体 
}LNode,*LinkList;        //LinkList为指向结构体Lnode的指针类型 
int InitList(LinkList &L);//单链表的初始化,即构造一个空表 
void CreatList(LinkList &L,int n);//头插法建立单链表
void CreatList2(LinkList &L,int n);//尾插法建立单链表
int DestroyList(LinkList &L);//销毁单链表L 
int ClearList(LinkList &L);//清空单链表L 
int ListInsert(LinkList &L,int i,int e);//在单链表L中第i个元素之前插入新元素e 
int ListDelete(LinkList &L,int i,int &e);// 删除L中第i个数据元素,并用e返回其值
bool ListEmpty(LinkList L);//判断单链表是否为空,若为空,返回true,否则,返回false 
int ListLength(LinkList L);//求单链表的表长 
int LocateElem(LinkList L,int e);//L中查找与给定值e相等的元素,若成功,则返回该元素在表中的序号,否则返回0 
LNode *LocateElem2(LinkList L,int e);//L中查找与给定值e相等的元素,若成功,则返回该元素的地址,否则返回NULL 
int GetElem(LinkList L,int i,int &e);//将单链表L中第i个位置的元素返回给e 
int main()
{
	LinkList L; //或LNode *L; 
	int m=InitList(L);
	if(m)
	cout<<"成功创建空表L"<<endl;
	CreatList(L,2);
	cout<<"头插法创建单链表成功"<<endl; 
	CreatList2(L,2);
	cout<<"尾插法创建单链表成功"<<endl; 
	ListInsert(L,1,6);
	cout<<"已成功插入"<<endl;
	ListInsert(L,2,9);
	cout<<"已成功插入"<<endl;
	int e; 
	ListDelete(L,1,e);
	cout<<"被删除的元素:"<<e<<endl; 
	ListEmpty(L);
	if(ListEmpty(L)) 
	cout<<"单链表L为空"<<endl;
	else
	cout<<"单链表L非空"<<endl;
	ListLength(L);
	cout<<"此时单链表的长度:"<<ListLength(L)<<endl;
	int a=9;
	LocateElem(L,a);
	cout<<"该元素的位置为:"<<LocateElem(L,a)<<endl;
	int b=9;
	LocateElem2(L,b);
	cout<<"该元素的地址为:"<<LocateElem2(L,b)<<endl;
	GetElem(L,1,e);
	cout<<"单链表L的第1个元素:"<<e<<endl;
	ClearList(L);
	cout<<"单链表L已清空"<<endl; 
	DestroyList(L);
	cout<<"单链表L已销毁"<<endl; 
	return 0;
}
//单链表的初始化,即构造一个空表 
int InitList(LinkList &L)
{
   L=new LNode;  //或L=(LinkList)malloc(sizeof(LNode));	
   L->next=NULL;
   return OK;
} 
//头插法建立单链表
void CreatList(LinkList &L,int n)
{
   	for(int i=n;i>0;--i)
   	{
   		LinkList p;
   		p=new LNode;
   		cin>>p->data;
   		p->next=L->next;
   		L->next=p;
	}
} 
//尾插法建立单链表
void CreatList2(LinkList &L,int n)
{
	LinkList r;//尾指针
	r=L;//尾指针指向头结点
	for(int i=0;i<n;++i)
	{
		LinkList p;
		p=new LNode;
		cin>>p->data;
		p->next=NULL;
		r->next=p;
		r=p;
	 } 
} 
//判断链表是否为空 ,若L为空表,则返回1,否则返回0
bool ListEmpty(LinkList L)
{
	if(L->next) //非空 
	return 0;
	else
	return 1;
}
//销毁单链表L 
int DestroyList(LinkList &L)//(从头指针开始,依次释放所有结点) 
{
   LinkList p;//或LNode *p;
   while(L)
   {
     p=L;
     L=L->next;
	 delete p;
   }	
   return OK;
} 
//清空单链表L (链表依然存在,但无元素,头指针和头结点仍然存在) 
int ClearList(LinkList &L)//(依次释放所有结点,并将头结点指针域设置为空) 
{
	LNode *p,*q;
	p=L->next;//从首元结点开始 
	while(p)
	{
		q=p->next;
		delete p;
		p=q;
	}
	L->next=NULL;
	return OK;
}
//在单链表L中第i个元素之前插入新元素e 
int ListInsert(LinkList &L,int i,int e)
{
  LinkList p;
  p=L;
  int j=0;
  while(p&&j<i-1)
  {
  	p=p->next;
  	++j;
  }	
  if(!p||j>i-1) return ERROR;
  LinkList s;
  s=new LNode;
  s->data=e;
  s->next=p->next;
  p->next=s;
  return OK;
} 
// 删除L中第i个数据元素,并用e返回其值
int ListDelete(LinkList &L,int i,int &e)
{
  LinkList p,q;
  p=L;
  int j=0;
  while(p->next&&j<i-1) //寻找第i个结点,并令p指向其前驱 
  {
  	p=p->next;
	++j;
  }	
  if(!p->next||j>i-1) return ERROR;
  q=p->next;//保存被删结点的地址以备释放 
  p->next=q->next;//改变被删节点前驱结点的指针域 
  e=q->data;
  delete q;
  return OK;
} 
//求单链表的表长 
int ListLength(LinkList L)
{
  LinkList p;
  p=L->next;
  int i=0;
  while(p)
  {
  	i++;
  	p=p->next;//遍历单链表,统计结点数 
  }	
  return i;
} 
//L中查找与给定值e相等的元素,若成功,则返回该元素在表中的序号,否则返回0 
int LocateElem(LinkList L,int e)
{
	LinkList p;
	int j=1;
	p=L->next;
	while(p&&p->data!=e)//循环结束条件:p为空时或找到时 
	{
		p=p->next;
		j++;
	}
    if(p) return j;//若找到了,返回其位置
    else
    return 0;//若没找到,返回0
}
//L中查找与给定值e相等的元素,若成功,则返回该元素的地址,否则返回NULL 
LNode *LocateElem2(LinkList L,int e)
{
	LinkList p;
	p=L->next;
	while(p&&p->data!=e)//循环结束条件:p为空时或找到时 
	{
		p=p->next;
	}
	return p;//若找到了,则直接返回地址,若没找到,则此时p为空了已经,返回空就行 
}
//将单链表L中第i个位置的元素返回给e
int GetElem(LinkList L,int i,int &e)
{
  LinkList p;
  p=L->next;
  int j=1;
  while(p&&j<i)//p不为空,j还没到i 
  {
  	p=p->next;
  	++j;
  }	
  if(!p||j>i) return ERROR;//没有找到,元素不存在 
  e=p->data;
  return OK;
} 

运行结果:

算法时间复杂度分析:

 

双向链表

定义

 双向链表的操作以及实现:(除插入和删除操作,其他的操作与上述单链表的操作基本一致)

双向链表的插入:

s->prior=p->prior  //将p的前驱结点地址存到s的前指针域

p->prior->next=s  //将p的前驱结点的指针域指向s

s->next=p  //给s的next赋值p

p->prior=s  //将p的前指针域指向s

双向链表的删除: 

p->prior->next=p->next  //让指针变量p的前驱结点的指针域指向其后继结点

p->next->prior=p->prior  //将指针变量p的前驱结点的地址存到其后继结点的前指针域

循坏链表:

定义:

循坏链表的合并:

带尾指针循坏链表的合并的操作:

单链表,循坏链表和双向链表的时间效率的比较:

顺序表和链表的比较:

线性表的合并:

有序表合并--用顺序表实现:

有序表的合并--用链表实现:

 

 

  • 12
    点赞
  • 60
    收藏
    觉得还不错? 一键收藏
  • 3
    评论
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值