数据结构-线性表

线性表

  线性表(List):由零个或多个数据元素组成的有限序列。
  优点:1.无需为表示表中元素之间的逻辑关系而增加额外存储空间。2.可以快速存取表中任意位置元素。
  缺点:1.插入和删除操作需要移动大量元素。2.当线性表长度变化较大时,难以确定存储空间的容量。3.容易造成存储空间的碎片。

线性表顺序存储结构

#define MAXSIZE 20
typedef int ElemType;
typedef struct
{
	ElemType data[MAXSIZE];
	int length;   //线性表当前长度
}SqList;

线性表的插入操作

  最好情况O(1),最坏情况O(n)
ListInsert(SqList *L,int i,ElemType e)
{
int k;

	if (L->length==MAXSIZE)return -1;
	
	if (i<1 || i>L->length+1)return -1;
	
	if (i<=L->length)
	{
		//要插入位置后的每个元素向后移
		for(k=L->length-1;k>=i-1;k--)
		{
			L->data[k+1]=L->data[k];
		}
	}
	
	//插入新元素
	L-data[i-1]=e;
	L->length++;
}

线性表的删除操作

  最好情况O(1),最坏情况O(n)
ListDelete(SqList *L,int i,ElemType e)
{
int k;

	if (L->length==0)return -1;
	
	if (i<1 || i>L->length+1)return -1;
	
	*e=L->data[i-1];
	
	if (i<=L->length)
	{
		for(k=i;k <= L->length-1 ; k++)
		{
			L->data[k-1]=L->data[k];
		}
	}

	L->length--;
}

  线性表的顺序存储结构,在存、读数据时,不管哪个位置,时间复杂度O(1),在查找、删除操作时,为O(n).

链式存储结构

  头指针:指向链表第一个结点的指针,链表若存在头结点,则指向头结点,头指针具有标识作用,常把头指针命名为链表的名字。无论链表是否为空,头指针不为空。
  头结点:头结点是为了操作的统一和方便而设立,放在第一个元素结点之前,其数据域一般存储链表长度。有了头结点,在第一个元素结点前插入和删除变得和其他结点一样。
  当频繁的插入或删除数据时,单链表效率优势明显。

typedef struct Node
{
	ElemType data;
	struct Node * Next;//指向结点类型的指针
}Node;
typedef struct Node * LinkList;


//GetElem.c 核心思想工作指针后移
int GetElem(LinkList L,int i,ElemType *e)
{
	int j;
	LinkList p;
	p=L->next;
	for(int j=1;j<i;j++)
	{
		p=p->next;
		if(P==NULL)
		{
			return ERROR;
		}
	}
	*e=p->next->data;
	return *e
}

//用while更好
while(p && j<i)
{
	p=p->next;
	++j;
}
if(!P||j>i)
{
	return ERROR;
}
	
 *e=p->data;


//单链表的插入和删除 在**第i个位置之前**插入元素e
InsertElem(LinkList L,int i,ElemType *e)
{
	int j=1;
	LinkList p=L->next;
	while(p&&j<i)
	{
		p=p->next;
		j++;
	}
	if(!p||j>i)
	{
		return ERROR;
	}
		
		s=(LinkList)malloc(sizeof(Node));
		s->next=p->next;
		p->next=s;
		s->data=e;
}

//单链表的删除操作
	DeleteElem(LinkList L,int i,ElemType *e)
{
	int j=1;
	LinkList p=L->next,q;
	while(p&&j<i)
	{
		p=p->next;
		j++;
	}
	if(!p||j>i)
	{
		return ERROR;
	}
	q=p->next;
	*e=q->data;
	p->next=q-next;
	free q;
}

//单链表的整表创建 头插法:让新结点的next指向头结点之后,然后让头结点的next指向新结点

void CreateListHead(LinkList *L,int n)
{
	LinkList p;
	int i;
	
	srand(time(0));  //初始随机数种子

	*L=(LinkList)malloc(sizeof(Node));  //生成空链表
	(*L)->next=NULL;

	for(i=0;i<n;i++)
	{
		p=(LinkList)malloc(sizeof(Node));
		p->data=rand()%100+1; //生成1-100的随机数
		p->next=(*L)->next;
		(*L)->next=p;
	}
}

//尾插法:需要一个结点时刻保存尾结点的位置
void CreateListTail(LinkList *L,int n)
{
	LinkList p,r; //结点p是中间结点,记录每个新生成的结点,结点r用来记录尾结点位置
	int i;
	
	srand(time(0));  //初始随机数种子

	*L=(LinkList)malloc(sizeof(Node));  //生成空链表
	r=*L;

	for(i=0;i<n;i++)
	{
		p=(Node *)malloc(sizeof(Node));
		p->data=rand()%100+1; //生成1-100的随机数
		r->next=p;
		r=p;
	}
	r->next = NULL;
}


//单链表的整表删除 从头部开始循环释放结点

ClearList(LinkList *L)
{
	LinkList p,q;
	p=(*L)->next;
	
	while(p)
	{
		q=p->next;
		free(p);
		p=q;
	}
	(*L)->next=NULL;
}

单链表结构与顺序存储结构总结

  存储分配方式:1.顺序存储结构用一段连续的存储单元依次存储线性表的数据元素。
2.单链表采用链式存储结构,用一组任意的存储单元存放线性表元素
  查找:1.顺序存储结构0(1) 2.单链表O(n)
  插入和删除:1.顺序存储结构O(n) 2.单链表O(1)

  空间性能:1.顺序存储结构需要预先分配存储空间,分大了造成浪费,分小了容易溢出。2.单链表不需要分配存储空间,只要有就可以分配,元素个数也不受限制。

  当需要频繁查找,很少进行插入删除操作时,宜采用顺序存储结构。若需要频繁插入和删除是,宜采用单链表结构。若事先不知道线性表长度,使用单链表。

  例如:对于用户注册的个人信息,大部分是读取操作,宜使用顺序存储结构。

静态链表

  用数组存放链表,包括游标位,数据位。下标为0的位和下标为MAX_SIZE-1位,不存放数据。下标为MAX_SIZE-1位的游标指向第一个有数字的元素。下标为0的位的游标指向第一个没有存放数据的下标。其他每个元素的游标指向下一个元素的下标地址。把未使用的数组元素称为备用链表。数组第一个元素,即下标为0的那个元素的游标存放备用链表的第一个结点的下标。最后一个元素的游标为0。

##面试题
  快速找到单链表的中间结点
  1.遍历两次单链表。第一遍获得链表长度L,第二遍获得L/2的结点。时间复杂度O(3n/2)
  2.使用快慢指针,第二个指针为第一个指针速度的两倍

GetMiNode(LinkList L,ElemType *e)
{
	LinkList search,mid;
	mid=search=L;
	
	while(search->next!=NULL)
	{
		if(search->next->next != NULL)
		{
			search = search->next->next;
			mid = mid ->next;
		}
		else
		{
			search = search -> next;
		}
	}

	*e= mid ->data;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值