数据结构之线性表

数据结构之线性表

一、线性表的基本概念

  • 线性表(Linear List)是线性结构中最常用而又最简单的一种数据结构,几乎所有的线性关系都可以用线性表表示。线性表是线性结构的抽象。

  • 线性结构的特点是一对一的线性关系,数据元素一个接一个的排列。

  • 线性表是具有相同数据类型的n(n>=0)个数据元素的有限序列,通常记为:(a1,a2,···,ai-1,ai,ai+1,···an)

  • 在线性表中 相邻元素之间存在着顺序关系,其中主要是直接前驱,直接后继。数据类型我们通常抽象为DataType。

二、线性表的顺序存储

  • 线性表的顺序存储是指用一组地址连续的存储单元依次存储线性表的数据元素,我们把这种存储形式存储的线性表称为顺序表。
  • 顺序表的特点:
    • 顺序表中的逻辑顺序和物理顺序是一致的。
    • 顺序表中任意一个数据元素都可以随机存取,所以顺序表是一种速记存取的存储结构。

1.顺序表基本功能代码

//sqlist.cpp
#include<stdio.h>
#include <malloc.h>
typedef int ElemType;
#define MaxSize 100

typedef struct{
	ElemType data[MaxSize];
	int length;
 } SqList;
 
// 1.顺序表的初始化 
void InitList(SqList *&L){
	L=(SqList *)malloc(sizeof(SqList));
	L->length=0;
}
//2.创建顺序表 
void CreateList(SqList *&L,ElemType a[],int n){
	int i;
	for (i=0;i<n;i++){
		L->data[i]=a[i];
	}
	L->length=n;
} 
//3.销毁顺序表
 void DestroyList(SqList *&L){
 	free(L);
 }
 //4.判断是否为空
 bool ListEmpty(SqList *L){
 	return (L->length==0);//!L->Length
  } 
//5.求顺序表的长度
int ListLength(SqList *L){
	return L->length;
 } 
//6.输出顺序表
void DispList(SqList *&L){
	if(ListEmpty(L))
		printf("顺序表为空");
	else{
		int i;
		for(i=0;i<L->length;i++)
			printf("%4d",L->data[i]);
		printf("\n");
	 } 
 } 
//7.求线性表指定位置的元素
void GetList(SqList *L,int i,ElemType &e){
	if(i<1 || i>L->length)
		e=-1;
	else
		e=L->data[i-1];
 } 
//8.按值查找元素位置
int  LocateElem(SqList *L,ElemType e){
	//要从顺序表的第一个元素开始比较 
	int i=0;
	while(i<L->length ){
		if(e==L->data[i])
			return i++;
		i++;
	}
	return -1;
}
//9插入元素;
//1定位,2移动元素   3,插入元素
bool ListInsert(SqList *&L ,int i, ElemType e){
	int j;
	if(i<0 || i>=L->length)
		return false;
	for(j=L->length;j>=i;j--){
		L->data[j]=L->data[j-1];
	}
	L->data[i-1]=e;
	L->length++;
	return true;
 } 
 //10.按照位置删除元素
 bool ListDelete1(SqList *&L,int i){
 	if(i<0 || i>=L->length) 
 		return false;
 	int j;
 	for(j=i-1;j<L->length;j++)
 		L->data[j]=L->data[j+1];
 	L->length--;
 	return true;
  } 
//11.按照元素值 删除元素
bool ListDelete2(SqList *&L,ElemType e){
	int i,j;
	for(i=0;i<L->length;i++){
		if(e==L->data[i]){
			for(j=i;j<L->length;j++){
				L->data[j]=L->data[j+1];
			}
			L->length--;
			return true;
		}
	}
	return false;
 } 

主函数

#include "sqlist.cpp"
int main(){
	int a[]={1,2,3,4,5,6,7,8,9,10};
	int n=10;
	int e;
	SqList *L;
	InitList(L);
	printf("一、初始化的表:\n") ;
	DispList(L);
	printf("\n");
	
	printf("二、建立好的表:\n") ;
	CreateList(L,a,n);
	DispList(L);
	
	printf("三、查找元素:\n") ;
	GetList(L,5,e);
	printf("第5个元素:%d\n ",e);
	
	printf("四、查找元素(测试):\n") ;
	GetList(L,15,e);
	if(e<0)
		printf("输入的数据有误\n");
	else
		printf("第15个元素:%d",e);
	
	printf("五、按值查找元素位置:\n");
	e=LocateElem(L,6);
	if(e<0)
		printf("未找到");
	else
		printf("你查找的元素在第%d个\n"); 
		
	printf("六、插入元素:\n");
	ListInsert(L,5,100);
	DispList(L);
	
	printf("七、按位置删除元素:\n");
	if(ListDelete1(L,5))
		DispList(L);
	else
		printf("位置有错\n"); 
	
	printf("八、按元素值,删除元素:\n");
	if(ListDelete2(L,6))
		DispList(L);
	else
		printf("位置有错\n"); 
}

2.小应用-划分顺序表

//划分顺序表 
#include "sqlist.cpp"
//thoerd1
void swap(ElemType &x,ElemType &y){
	ElemType t;
	t=x;x=y;y=t;
}
void move1(SqList *&L){ 
    int i=0,j=L->length-1;  
	ElemType x,y;
   	ElemType pivot=L->data[0];//以data[0]为基准
   	while (i<j){  
   		while (i<j && L->data[j]>pivot)
	 		j--;//从后向前扫描,找一个≤pivot的元素
    	while (i<j && L->data[i]<=pivot)
	 		i++;//从前向后扫描,找一个>pivot的元素
    	if (i<j)
    		swap(L->data[i],L->data[j]);
   	}
   	swap(L->data[0],L->data[j]);
}
//2
void move2(SqList *&L){ 
  int i=0,j=L->length-1;
  ElemType  pivot=L->data[0];	//以data[0]为基准
  while (i<j){
     while (j>i && L->data[j]>pivot)
		j--;       		//从右向左扫描,找≤pivot的data[j]
     L->data[i]=L->data[j];	//将其放入data[i]处
     while (i<j && L->data[i]<=pivot)
		i++;      		//从左向右扫描,找>pivot的记录data[i]
     L->data[j]=L->data[i];	//将其放入data[j]处
  }
  L->data[i]=pivot;    	//放置基准
}


int main(){
	ElemType a[]={4,6,1,11,3,1,3,2,9,8};
	int n=10;
	SqList *L;
	//1,初始化顺序表
	InitList(L);
	//2.建立顺序表
	CreateList(L,a,n);
	printf("L  :  ");
	DispList(L);
	move1(L);
	printf("L-1:  ");
	DispList(L);
	move2(L);
	printf("L-2:  ");
	DispList(L);
}

3.小应用-删除值为X的元素

//删除值为X的元素 
#include "sqlist.cpp"
// 方法1 
void delnode1(SqList *&L,ElemType x){
   int k=0,i;		//k记录值不等于x的元素个数
   for (i=0;i<L->length;i++)
      if (L->data[i]!=x){    	//若当前元素不为x,将其插入A中
          L->data[k]=L->data[i];
          k++;		 	//不等于x的元素增1
      }
   L->length=k;		//顺序表L的长度等于k
}
//方法2
void delnode2(SqList *&L,ElemType x){
   int k=0,i=0;	      	//k记录值等于x的元素个数
   while (i<L->length)
   {  if (L->data[i]==x)  	//当前元素值为x时k增1
	   k++;
      else			//当前元素不为x时将其前移k个位置
	   L->data[i-k] = L->data[i];
      i++;
   }
   L->length-=k;		//顺序表L的长度递减k
}


int main(){
	ElemType a[]={1,3,1,4,2};
	int n=5;
	ElemType x=1;
	SqList *L;
	//1,初始化顺序表
	InitList(L);
	//2.建立顺序表
	CreateList(L,a,n);
	printf("L:  ");
	DispList(L);
	delnode1(L,x);
	printf("L:  ");
	DispList(L);
	delnode2(L,x);
	printf("L:  ");
	DispList(L);
}

三、线性表的链式存储

  • 单链表的定义:线性表的链式存储结构是指用一组任意的存储单元存储线性表中的数据元素。为了反映数据元素之间的逻辑关系,对于每个数据元素不仅要表示它的具体内容,还要附加一个表示它的直接后继元素存储位置的信息。这样构成的链表成为线性单向链接表,简称单链表。

1.单链表基本功能代码

#include<stdio.h>
#include <malloc.h>
typedef int DataType;
#define MaxLen 100;
typedef struct LinkNode{
	DataType data;
	struct LinkNode *next;
 } LinkList;
 //初始化单链表
 void InitList(LinkList *&L){
 	L=(LinkList *)malloc (sizeof(LinkList));
 	L->next=NULL;
  } 
//头插法 
void CreateListF(LinkList *&L,DataType a[],int n){
	LinkList *s;
	int i;
	InitList(L);
	for(i=0;i<n;i++){
		s=(LinkList *)malloc(sizeof(LinkList));
		s->data=a[i];
		s->next=L->next;
		L->next=s;
	}
}
//尾插法
void CreateListR(LinkList *&L,DataType a[],int n){
	LinkList *s,*r=L;
	int i;
	for(i=0;i<n;i++){
		s=(LinkList *)malloc(sizeof(LinkList));
		s->data=a[i];
		r->next=s;
		r=s;//r=r->next;
	}
	r->next=NULL;
} 
//销毁单链表
 void DestroyList(LinkList *L){
	LinkList *pre,*p;
	pre=L,p=pre->next;
	while(p!=NULL){	
		free(pre);
		pre=p;
		p=pre->next;
	}
	free(pre);	//此时p为NULL,pre指向尾结点,释放它
}
//判空
bool EmptyList(LinkList *L){
	return L->next==NULL;
 } 
//求长度
 int LengthList(LinkList *L){
 	int n=0;
 	LinkList *p=L->next;
 	while(p!=NULL){
 		n++;
 		p=p->next;
	 }
	 return n;
  } 
//根据位置来查找元素
bool GetElem(LinkList *&L,int i, DataType &e){
	int j;
	LinkList *p=L;
	while(j<i && p!=NULL){
		j++;p=p->next;
	}
	if(p==NULL)
		return false;
	else{
		e=p->data;
		return true;
	}
 } 
//根据元素的值来寻找位置
int LocateElem(LinkList *L,DataType e){
	int i=1;
	LinkList *p=L->next;
	while(p->data!=e && p!=NULL){
		p=p->next;
		i++;
	}
	if(p==NULL)
		return false;
	else
		return i;
 } 
//插入元素
bool ListInsert(LinkList *&L,int i,DataType e){
	int j=0;
	LinkList *p=L,*s;
	while(j<i-1 && p!=NULL){
		p=p->next;
		j++;
	}
	if(p==NULL)
		return false;
	else{
		s=(LinkList *)malloc(sizeof(LinkList));
		s->data=e;
		s->next=p->next;
		p->next=s;
		return true;
	}
}
//删除元素(按照位置删除) 
 bool ListDelete(LinkList *&L,int i){
 	int j=0;
 	LinkList *p=L,*q;
	while(j<i-1 && p!=NULL){
		p=p->next;
		j++; 
	 } 
	 if(p==NULL){
	 	return false;
	 }else{
	 	q=p->next;
	 	if(q==NULL)
	 		return false;
	 	else{
	 		p->next=q->next;
	 		free(q);
	 		return true;
		 }
	 }
 }
//按照值删除
bool ElemDelete(LinkList *L,DataType e){
	LinkList *pre=L,*p=L->next;//定义姐妹指针 
	while(p!=NULL && p->data!=e){
		pre=p; 
		p=p->next;
	}
	if(p==NULL)
		return false;
	else{
		pre->next=p->next;
		free(p);
		return true;
	}
 } 
//删除最大值
void  delmaxnode(LinkList *&L){
	LinkList *p=L->next,*pre=L, *maxp=p,*maxpre=pre;
	while(p!=NULL){
		if(maxp->data<p->data){
            maxp=p;
            maxpre=pre;
        }
        pre=p;
        p=p->next;
	}
	maxpre->next=maxp->next;
	free(maxp);
} 
//排序
void Sort(LinkList *&L){
	LinkList *p,*pre,*q;
	p=L->next->next;   //p指向L的第二个数据结点
	L->next->next=NULL;
	while(p!=NULL){
        q=p->next;   //q保存p结点后继结点的指针 
		pre=L;
		while(pre->next!=NULL  && pre->next->data<p->data)
			pre=pre->next;  //在有序表中找插入p的前驱结点pre;
		p->next=pre->next; 
		pre->next=p;
		p=q;   //扫描原单链表余下的结点 
		}	  
}
//输出链表
void DispList(LinkList *L){
	LinkList *p;
	p=L->next;//指向第一元素;
	if(p==NULL)
		printf("List is Empty!\n");
	else{
	while(p!=NULL){
		printf("%4d",p->data) ;
		p=p->next;
	 } 
	 printf("\n");
	 }
 } 
 //逆序(运用头插法) 
void Revers(LinkList *&L){
	LinkList *p=L->next,*q;
	L->next=NULL;
	while(p!=NULL){
        q=p->next;
        p->next=L->next;
        L->next=p;
        p=q; 
	}
}
//分裂成两个链表 
void Split(LinkList *L,LinkList *&L1,LinkList *&L2){
	LinkList *p=L->next,*q,*r;
	L1=L;
	r=L1;
	L2=(LinkList *)malloc(sizeof(LinkList));
	L2->next=NULL;
	while(p!=NULL){
		r->next=p;
		r=p;
		p=p->next;
		q=p->next;
		p->next=L2->next;
		L2->next=p;
		p=q;
	}
	r->next=NULL;
}

主函数

#include"linklist.cpp"
int main(){
	int a[]={1,2,3,4,5,6,7,8,9,10};
	int n=10;
	int i=6;
	DataType e; 
	LinkList *L;
	InitList(L);
	DispList(L);
	CreateListR(L,a,n);
	DispList(L);
	if(GetElem(L,i,e))
		printf("第%3d  个元素的数据为:%4d\n",i,e);
	else
		printf("查无此位置!\n");
	e=5;
	int val=LocateElem(L,e);
	if(val)
		printf("值为 %d 的元素在链表的第 %d 位置!\n",e,val);
	else
		printf("值为 %d 的元素不存在!\n",e);
	if(ListInsert(L,i,e)){
			printf("插入成功!\n");
		DispList(L);
	 }else
	 	printf("插入位置错误!\n");
	if(ElemDelete(L,1)){
		printf("删除成功\n");
		DispList(L);
	}else
		printf("删除失败,值溢出!\n");
	LinkList *L1,*L2;
	Split(L,L1,L2);
	printf("\n");
	DispList(L1); 
	printf("\n");
	DispList(L2); 
	DestroyList(L);
}

2.双链表

/*双链表*/
#include <stdio.h>
#include <malloc.h>
typedef int DataType;
#define MaxLen 100;
typedef struct DLinkNode{
	DataType data;
	struct DLinkNode *prior;
	struct DLinkNode *next;
}DLinkNode;
//头插法 
void CreateListF(DLinkNode *&L,DataType a[],int n){
	DLinkNode *s;int i;
	L=(DLinkNode *)malloc(sizeof(DLinkNode));//创建头结点
	L->prior=L->next=NULL;
	for(i=0;i<n;i++){
		s=(DLinkNode *)malloc(sizeof(DLinkNode));
		s->data=a[i];
		s->next=L->next;
		if(L->next!=NULL)
			L->next->prior=s;
		L->next=s;
		s->prior=L; 
	 } 
}
//尾插法 
void CreateListR(DLinkNode *&L,DataType a[],int n){
	DLinkNode *s,*r;
	int i;
	L=(DLinkNode *)malloc(sizeof(DLinkNode));
	r=L;
	for(i=0;i<n;i++){
		s=(DLinkNode *)malloc(sizeof(DLinkNode));
		s->data=a[i];
		r->next=s;
		s->prior=r;
		r=s;
	}
}
//初始化
void InitList(DLinkNode *&L){
	L=(DLinkNode *)malloc(sizeof(DLinkNode));
	L->next=L->prior=NULL;
 } 
//销毁双链表
void DestroyList(DLinkNode *L){
	DLinkNode *pre,*p;
	pre=L,p=pre->next;
	while(p!=NULL){
		free(pre);
		pre=p;
		p=pre->next;
	}
	free(pre);
}
//判空
bool EmptyList(DLinkNode *L){
	return L->next==NULL;
 } 
//求长度
int LengthList(DLinkNode *L){
	int n=0;
	DLinkNode *p=L->next;
	while(p!=NULL){
		n++;
		p=p->next;
	}
	return n;
 } 
//按照位置查找
bool GetElem(DLinkNode *&L,int i,DataType &e){
	int j;
	DLinkNode *p=L;
	while(j<i && p!=NULL){
		j++;p=p->next;
	}
	if(p==NULL)
		return false;
	else{
		e=p->data;
		return true;
	}
}
//按照元素查找 
int LocateElem(DLinkNode *L,DataType e){
	int i=1;
	DLinkNode *p;
	p=p->next;
	while(p->data!=e && p!=NULL){
		p=p->next;
		i++;
	}
	if(p==NULL)
		return false;
	else
		return i;
}
//输出 
void DispList(DLinkNode *&L){
	DLinkNode *p;
	p=L->next;
	if(p->data==NULL)
		printf("表空");
	while(p!=NULL)
	{printf("%3d",p->data);
	p=p->next;
	}
	printf("\n");
}
//插入
bool ListInsert(DLinkNode *&L,int i,DataType e){
	int j=0;
	DLinkNode *p=L,*s;
	while(j<i-1  && p!=NULL){
		j++;
		p=p->next;
	}
	if(p==NULL)
		return false;
	else{
		s=(DLinkNode *)malloc(sizeof(DLinkNode));
		s->next=p->next;
		if(p->next!=NULL)
		 	p->next->prior=s;
		p->next=s;
		s->prior=p;
		return true;
	}
}
//按照值删除
bool ElemDelete(DLinkNode *L,DataType e){
	DLinkNode *p=L->next;
	while(p!=NULL && p->data!=e){
		p=p->next;
	}
	if(p==NULL)
		return false;
	else{
		p=p->prior;
		p->next=p->next->next;
		p->next->next->prior=p;
		free(p);
		return true;
	}
} 
#include"dlinklist.cpp"
int main(){
	DLinkNode *L;
	int n=10;
	DataType e; 
	DataType a[10]={1,2,3,4,5,6,7,8,9,10};
	printf("头插法建立双链表:\n");
	CreateListF(L,a,n);
	DispList(L);
	printf("尾插法建立双链表:\n");
	CreateListR(L,a,n);
	DispList(L);
	printf("链表的长度是%d.\n",LengthList(L));
	if(GetElem(L,5,e))
		printf("The Element is:%4d\n",e);
	else
		printf("The Element is not exist!\n");
} 

3.循环单链表

在循环单链表中,可以从表中的任意一个节点p出发找到它的直接前驱,而不必从头指针head出发,其算法如下:

LinkList *prior(LinkList *p){
    //循环单链表查找结点前驱函数
    LinkList *q;
    q=p->next;
    while(q->next!=p)
        q=q->next;
    return(q);
}

4.线性表的应用

//线性表的应用:两个表的简单自然连接的算法
#include <stdio.h>
#include <malloc.h>
#define MaxCol  10			//最大列数
typedef int ElemType;
typedef struct Node1{	//数据结点类型	
	ElemType data[MaxCol];
	struct Node1 *next;		//指向后继数据结点
} DList;
typedef struct Node2{		//头结点类型
	int Row,Col;			//行数和列数
	DList *next;			//指向第一个数据结点
} HList;
void CreateTable(HList *&h){
	int i,j;
	DList *r,*s;
	h=(HList *)malloc(sizeof(HList));		//创建头结点
	h->next=NULL;
	printf("表的行数,列数:");
	scanf("%d%d",&h->Row,&h->Col);
	for (i=0;i<h->Row;i++){
        printf("  第%d行:",i+1);
		s=(DList *)malloc(sizeof(DList));	//创建数据结点
		for (j=0;j<h->Col;j++)				//输入一行的数据初步统计
			scanf("%d",&s->data[j]);
		if (h->next==NULL)					//插入第一个数据结点
			h->next=s;
		else								//插入其他数据结点
			r->next=s;						//将结点s插入到结点r结点之后
		r=s;								//r始终指向最后一个数据结点
	}
	r->next=NULL;							//表尾结点next域置空
}
void DispTable(HList *h){
	int j;
	DList *p=h->next;
	while (p!=NULL){	
        for (j=0;j<h->Col;j++)
			printf("%4d",p->data[j]);
		printf("\n");
		p=p->next;
	}
}
void LinkTable(HList *h1,HList *h2,HList *&h){
	int f1,f2,i;
	DList *p=h1->next,*q,*s,*r;
	printf("连接字段是:第1个表位序,第2个表位序:");
	scanf("%d%d",&f1,&f2);
	h=(HList *)malloc(sizeof(HList));
	h->Row=0;
	h->Col=h1->Col+h2->Col;
	h->next=NULL;
	while (p!=NULL){
        q=h2->next;
		while (q!=NULL){
            if (p->data[f1-1]==q->data[f2-1]){		//对应字段值相等
				s=(DList *)malloc(sizeof(DList));	//创建一个数据结点
				for (i=0;i<h1->Col;i++)				//复制表1的当前行
					s->data[i]=p->data[i];
				for (i=0;i<h2->Col;i++)
					s->data[h1->Col+i]=q->data[i];	//复制表2的当前行
				if (h->next==NULL)				//插入第一个数据结点
					h->next=s;
				else							//插入其他数据结点
					r->next=s;
				r=s;							//r始终指向最后数据结点
				h->Row++;						//表行数增1
			}
			q=q->next;							//表2下移一个记录
		}
		p=p->next;								//表1下移一个记录
	}
	r->next=NULL;								//表尾结点next域置空
}
int main()
{
	HList *h1,*h2,*h;
	printf("表1:\n");	
	CreateTable(h1);			//创建表1
	printf("表2:\n");  
	CreateTable(h2);			//创建表2
	LinkTable(h1,h2,h);			//连接两个表
	printf("连接结果表:\n");	
	DispTable(h);				//输出连接结果
	return 1;
}

四、顺序表与链表的比较

顺序存储的优点:

  1. 方法简单,高级语言都提供数组,实现容易;
  2. 无需为表示线性表 中元素的逻辑关系而额外增加存储开销;
  3. 具有按元素序号随机访问的特点。

顺序存储的缺点:

  1. 在顺序表中进行插入、删除操作时,需要顺序移动元素,平均移动次数是线性表长度的一半,因此对长度较大的线性表操作效率较低;
  2. 要预先分配足够大的存储空间,空间分配过大会造成浪费,过小又有可能导致一些操作失败。

顺序表的优点就是链表的缺点,其缺点就是链表的优点。

实际使用时,采用哪种存储结构,考虑三方面:

  • 基于空间考虑

  • 基于时间考虑

  • 基于语言考虑

posted @ 2022-05-10 19:30  涂勇军  阅读(105)  评论(0编辑  收藏  举报