【数据结构严蔚敏】第二章-线性表【期末复习】

什么是线性表?

所要处理的元素在逻辑上是线性的,即除去头尾元素,其他的元素都有且只有一个直接前驱和后继。
物理结构:顺序结构、链式结构

顺序存储结构

存储结构
typedef int ElemType;	//不同需求下,ElemType可取不同类型
typedef struct SqList{
	ElemType *elem;	//elem[MAXSIZE]就定长了
	int length;	//当前长度
	int listsize;	//目前最大长度,
	//因为是动态分配的数组大小,当表的长度不够时可以通过realloc函数增长
}SqList;
运算(初始化、增、删、改、查)
  • 初始化
const int INIT_SIZE = 100;	//初始化长度
const int INCREAMENT_SIZE = 100;	//随着表长增加,表不够长时的增长量
void InitList(SqList *L){	//这里用指针否则只修改形参
	L->elem = (ElemType*)malloc(INIT_SIZE*sizeof(ElemType));
	L->length = 0;
	L->listsize = INIT_SIZE;
}
  • 增(插入), O ( n ) O(n) O(n)
void Insert(SqList *L,ELemType e,int index){
	//将元素插入到index处,index > -1
	if(index < 0 || index > L->length) //越界检查
		 PrintError(),return;
	//将第index个以及往后的所有元素右移
	for(int i = L->length - 1; i >= index; i--)
		L->elem[i+1] = L->elem[i];
	L->index = e;	//插入
	L->length ++; //长度记得+1
	/*这里可以加一个长度不够的处理逻辑
		if(check(*L)){
			L->elem = realloc(L->elem,sizeof(ELemType)*(L->listsize + INCREAMENT_SIZE));
			L->length ++;
			L->listsize += INCREAMENT_SIZE;
		}
	*/
}
  • 删(增的逆运算), O ( n ) O(n) O(n)
void Delete(SqList *L,ElemType &e,int index){//e是引用变量,带回删除的元素
	//删除index位置的元素
	if(index < 0 || index > L->length - 1) //越界检查
		PrintError(),return;
	e = L->elem[index];
	//将第index个元素后面的所有元素左移一个单位
	for(int i = index; i < L->length; i++)
		L->elem[i] = L->elem[i+1];
	L->length --;	//长度记得-1
}
  • 改和查 , O ( 1 ) O(1) O(1)
    没必要实现,利用顺序存储的性质,直接用[ ]访问就可以。
  • 其他典型操作
//删除顺序表中与x相同的元素 O(n)
void DeleteX(SqList *L,ElemType x){
	for(int i = 0,k = 0; i < L->length; i++){
		if(L->elem[i] == x)
			continue;
		else
			L->elem[k++] = L->elem[i];
	}
	L->length = k;
}

链式存储结构(单链表)

逻辑上连续,物理上不连续。一条链,故称其为单链。

存储结构
typedef struct LNode{
	ElemType data;
	struct LNode *next;
}LNode,*LinkList;
运算(初始化,创建、增、删、改、查)
  • 初始化(创建一个头结点)
void InitList(LinkList &L){	//用引用,不怕麻烦就用指针
	L = (ElemType)malloc(sizeof(LNode));
	L->data = 0;	//存放表长或者啥的,随你的便,无所谓
	L->next = NULL;
}
  • 创建(顺序表循环赋值就行了)
void CreateList_H(LinkList &L,int n){	//头插创建一个表长为n的链表
	//此时假设已经调用过InitList();
	LNode *p;
	while(n--){
		p = (ElemType*)malloc(sizeof(LNode));
		scanf("%d",&p->data);
		p->next = L->next;
		L->next = p;
	}
}
void CreateList_T(LinkList &L,int n){	//尾插创建一个表长为n的链表
	//此时假设已经调用过InitList();
	LNode *p,*t = L;
	while(n--){
		p = (ElemType*)malloc(sizeof(LNode));
		scanf("%d",&p->data);
		t->next = p;
		t = p;
	}
	t->next = NULL;	//尾指针记得置空
}
  • 增(插入)
    O ( i n d e x ) O(index) O(index)
void Insert(LinkList &L,ElemType e,int index){	//只要你不修改头结点,其实这里L可以不用引用的,因为操作的是头结点的指针,无所谓了......
	//这里index从1开始,顺序表从0,插入到第index个位置
	if(index > getLength(L) || index < 0)
		PrintError(),return;
	//理所当然的拿到index-1位置的指针
	int cnt = 0;
	LNode *p = L;
	while(cnt < index - 1){
		p = p->next;
		cnt ++;
	} //退出时cnt == index - 1
	LNode *t = (ElemType*)malloc(sizeof(LNode));
	t->data = e;
	t->next = p->next;
	p->next = t; 
}
  • 删(跟上面类似的思路,拿到前驱指针,退出循环后的处理操作不一样罢了)
    O ( i n d e x ) O(index) O(index)
void Insert(LinkList &L,ElemType e,int index){	//只要你不修改头结点,其实这里L可以不用引用的,因为操作的是头结点的指针,无所谓了......
	//这里index从1开始,顺序表从0,插入到第index个位置
	if(index > getLength(L) || index < 0)
		PrintError(),return;
	//理所当然的拿到index-1位置的指针
	int cnt = 0;
	LNode *p = L;
	while(cnt < index - 1){
		p = p->next;
		cnt ++;
	} //退出时cnt == index - 1
	LNode *t = p->next;
	p->next = t->next;
	free(t);
}
  • 改和查也跟上面差不多,没啥意思
其他链表
  • 循环链表
    把尾指针指向头结点就行了,(首元结点也行随你的便)。通常只设一个尾指针,便于在表尾的插入,又因为尾指针指向了头结点,从头遍历也方便。
  • 双向链表
    顾名思义双向的,每一个结点既有前驱指针pre又有后继结点next。
  • 双向循环链表
    前两者结合得到。把双向链表的头结点的前驱指向表尾,同时把表尾的后继指向表头。

总结对比

顺序表:表长相对固定,插入删除需要移动元素,但存取迅速。
链表:表长自由,插入删除操作 O ( 1 ) O(1) O(1),只需要找到前驱即可,缺点是存取不方便,需要从头扫。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值