数据结构线性表

线性表

概念

线性表是一种逻辑结构,表示元素直接一对一的相邻关系。
顺序表和链表是存储结构,两者属于不同层面的概念,不要混淆。

如何实现线性表

用顺序存储实现(顺序表)

特点:逻辑上相邻的两个元素在物理位置上也相邻
定义:

#define Maxsize 50;  //定义线性表的长度
// typedef 的 作用 就是 起别名
typedef struct{
	ElemType data[Maxsize];  //顺序表的元素
	int len;                 //顺序表的当前长度
}SqlList;//顺序表的类型定义

优点

  • 可以随机存取(根据表头元素地址和元素序号)表中任意一个元素。
  • 存储密度高,每个结点只存储数据元素。

缺点

  • 插入与删除操作需要移动大量元素。
  • 线性表变化较大时,难以确定存储空间的容量。
  • 存储分配需要一整段的存储空间,不够灵活。

插入操作

  • 最好情况:在表尾插入元素,不需要移动元素,时间复杂度为O(1)
  • 最坏情况:在表头插入元素,所有元素依次后移,时间复杂度为O(n)
  • 平均情况:在插入位置概率均等的情况下,平均移动元素的次数为n/2,时间复杂度为O(n)
//i代表插入的位置,从1开始,e要插入的元素
bool ListInsit(SqlList& L, int i, ElemType e) {  
	//判断要插入的位置是否合法
	if (i > L.len + 1 || i < 1) {
		return false;
	}
	//元素存储满了,不能再插入元素
	if (L.len >= MaxSize) {
		return false;
	}
	for (int j = L.len;j>=i; j--) { //移动顺序表中的元素,依次往后移
		L.data[j] = L.data[j-1];
	}
	L.data[i - 1] = e; //数组下标从0开始,插入第一个位置,访问的下标为0
	L.len++;
	return true; //走到这里代表插入成功,返回true
}

删除操作

  • 最好情况:删除表尾元素,不需要移动元素,时间复杂度为O(1)
  • 最坏情况:删除表头元素,所有元素依次前移,时间复杂度为O(n)
  • 平均情况:在删除位置概率均等的情况下,平均移动元素的次数为(n-1)/2,时间复杂度为O(n)
//删除元素使用元素e引用的目的是拿出对应的值
bool ListDelete(SqlList &L, int i, ElemType &e) {
	if (i < 1 || i > L.len + 1) {
		return false;
	}
	//顺序表没有元素,无需删除
	if (L.len == MaxSize) {
		return false;
	}
	e = L.data[i - 1];//获取顺序表中对应的元素,赋值给e
	for (int j = i;j<L.len; j++) {//从i的位置依次把元素往前覆盖
		L.data[j - 1] = L.data[j];
	}
	L.len--;//删除一个元素,顺序表长度减一
	return true;
}

动态分配的数组还属于顺序存储结构吗?
动态分配并不是链式存储,同样还是属于顺序存储结构,其物理结构没有变化,依次是随机存取的方式,只是分配的空间可以在运行时决定。

顺序表增删查操作代码(用c++实现)

#include <stdio.h>
#include <stdlib.h>
#define MaxSize 50  //定义线性表的长度
typedef int ElemType; //顺序表中元素的元素
//静态分配;定义结构体
typedef struct{
	ElemType data[MaxSize];  //定义的数组,用来存元素
	int len;                 //当前顺序表有多少个
}SqlList;//顺序表的类型定义
//输出元素
void PrintList(SqlList L) {
	for (int i = 0; i < L.len; i++) {
		printf("%4d", L.data[i]);//并排输出
	}
	printf("\n");
}
//i代表插入的位置,从1开始,e要插入的元素
bool ListInsit(SqlList& L, int i, ElemType e) {  
	//判断要插入的位置是否合法
	if (i > L.len + 1 || i < 1) {
		return false;
	}
	//元素存储满了,不能再插入元素
	if (L.len >= MaxSize) {
		return false;
	}
	for (int j = L.len;j>=i; j--) { //移动顺序表中的元素,依次往后移
		L.data[j] = L.data[j-1];
	}
	L.data[i - 1] = e; //数组下标从0开始,插入第一个位置,访问的下标为0
	L.len++;
	return true; //走到这里代表插入成功,返回true
}
//删除元素使用元素e引用的目的是拿出对应的值
bool ListDelete(SqlList &L, int i, ElemType &e) {
	if (i < 1 || i > L.len + 1) {
		return false;
	}
	//顺序表没有元素,无需删除
	if (L.len == MaxSize) {
		return false;
	}
	e = L.data[i - 1];//获取顺序表中对应的元素,赋值给e
	for (int j = i;j<L.len; j++) {//从i的位置依次把元素往前覆盖
		L.data[j - 1] = L.data[j];
	}
	L.len--;//删除一个元素,顺序表长度减一
	return true;
}
//查找成功,返回位置,位置从一开始,查找失败,返回0
int LocateElem(SqlList L, ElemType e) {
	for (int i = 0; i < L.len; i++) {
		if (L.data[i] == e) {
			return i + 1;//加一就是元素在顺序表中的位置
		}
	}
	return 0;
}
int main() {
	SqlList L;//顺序表名称
	bool ret;//查看返回值,布尔型是True,或者False
	ElemType del;//要来存要删除的元素
	//首先手动在顺序表中赋值
	L.data[0] = 1;
	L.data[1] = 2;
	L.data[2] = 3;
	L.len = 3;//总计三个元素
	//插入元素
	ret = ListInsit(L,2,60);  // 往第二个位置插入60这个元素
	if (ret) {
		printf("插入成功\n");
		PrintList(L);
	}
	else {
		printf("插入失败\n");
	}
	//删除元素
	ret = ListDelete(L, 2, del);  // 删除第二个位置的元素,并把元素值输出
	if (ret) {
		printf("删除成功\n");
		printf("删除元素值为%d\n",del);
		PrintList(L);
	}
	else {
		printf("删除失败\n");
	}
	//查找元素
	int ret1 = LocateElem(L,3);  // 查找元素3的位置,并把位置值输出
	if (ret1) {
		printf("查找成功\n");
		printf("元素位置为%d\n", ret1);
		PrintList(L);
	}
	else {
		printf("查找失败\n");
	}
	return 0;
}

线性表的链式表示

单链表

单链表结点的定义:

typedef struct LNode{ //单链表结点类型
	ElemType data;    //数据域
	struct LNode *next; //指针域
}LNode,*LinkList;

头指针:链表中第一个结点的存储位置,用来标识单链表。(链表为空时,L也不是空指针)
头结点:在单链表第一个结点之前附加的一个结点,为了操作上的方便
(头结点里无数据,数据在后面一个。)

若链表有头结点,则头指针永远指向头结点,不论链表是否为空,头指针均不为空,头指针是链表的必须元素,他标识一个链表。
头结点是为了操作的方便而设立的,其数据域一般为空,或者存放链表的长度。有头结点后,对第一结点前插入和删除第一结点的操作就统一了,不需要频繁重置头指针。但头结点不是必须的。

单链表的头插法与尾插法,以及增删查找

#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <stdlib.h>
typedef int ElemType;
typedef struct LNode { //单链表结点类型
	ElemType data;    //数据域
	struct LNode* next; //指针域
}LNode, * LinkList; //LinkList等价于struct LNode *

//头插法新建链表
LinkList GreatList1(LinkList& L) {
	LNode* s; int x;
	L = (LinkList)malloc(sizeof(LNode));//带头结点的链表
	L->next = NULL;//L->data里面不放东西
	scanf("%d", &x);//从标准输入读取数据
	// 3 4 5 6 7 999
	while(x != 999) {
		s = (LinkList)malloc(sizeof(LNode)); //申请一个新空间给s,强制类型转换
		s->data = x; // 把读取到的值,给新空间的data成员
		s->next = L->next;//让新结点的next指针指向链表的第一个元素(第一个元素放我们数据的元素)
		L->next = s; //让s作为第一个元素
		scanf("%d", &x);
	}
	return L;
}

//尾插法新建链表
LinkList GreatList2(LinkList& L) {
	int x;
	L = (LinkList)malloc(sizeof(LNode));//带头结点的链表
	//r代表链表表尾结点,指向链表尾部
	LNode* r=L, * s;//LinkList s,r=L也可以
	scanf("%d", &x);
	while (x != 999) {
		s = (LinkList)malloc(sizeof(LNode));
		s->data = x;
		r->next = s;//让尾部结点指向新结点
		r=s;//r指向新的表尾结点
		scanf("%d", &x);
	}
	r->next = NULL;//尾结点的next指针赋值为NULL
	return  L;
} 
//打印链表
void PrintList(LinkList L) {
	L = L->next;
	while (L != NULL) {
		printf("%3d", L->data);//打印当前结点数据
		L = L->next;//指向下一个结点
	}
	printf("\n");
}
//查找第几个结点的值
LinkList getElem(LinkList L,int i) {
	LNode *p = L->next;
	int j = 1;
	if (i == 0) {
		return L; // i是0就返回头结点
	}
	if (i < 1) {
		return NULL; //i是负值就返回空
	}
	while (p&&j<i) {
		p = p->next;// 让指向下一个结点
		j++;
	}
	return p;
}
// 按值查找
LinkList LocateElem(LinkList L, ElemType e) {
	LNode* p = L->next;
	while (p != NULL && p->data != e) 
		p = p->next;
	return p;
}

//往第i个位置插入元素
bool ListFrontInsert(LinkList &L,int i, ElemType e) {
	LNode* p = getElem(L,i-1);//拿到要插入位置的前一个位置的地址值
	if (p == NULL) {
		return false;// i不对
	}
	LinkList s = (LNode*)malloc(sizeof(LNode));
	s->data = e;//要插入的值放入对应空间
	s->next = p->next;
	p->next = s;
	return true;
}

//删除第i个位置的元素
bool ListDelete(LinkList& L, int i) {
	LinkList p = getElem(L, i - 1);//查找删除位置的前驱元素
	if (p == NULL) {
		return false;
	}
	LinkList q = p->next;
	if (q == NULL) {
		return false;//要删除的位置不存在
	}
	p->next = q->next;//断链
	free(q);//释放对应结点的空间
	q = NULL;//为了避免野指针
	return true;
}
int main() {
	LinkList L;//链接头,是结构体指针类型
	LinkList search; //用来存储拿到的某一个节点
	//头插法插入元素
	//GreatList1(L);//输入数据可以为3 4 5 6 7 9999代表结束 
	
	//尾插法插入元素
	GreatList2(L);//输入数据可以为3 4 5 6 7 9999代表结束
	PrintList(L);
	//查找元素值
	search = getElem(L, 2);//查找链表第二个位置的元素
	if (search != NULL) {
		printf("按序号查找成功\n");
		printf("%d\n", search->data);
	}
	//按值查询
	search = LocateElem(L, 6);
	if (search != NULL) {
		printf("按值查找成功\n");
		printf("%d\n", search->data);
	}

	//新结点插入第i个位置
	ListFrontInsert(L,2,99);
	PrintList(L);

	//删除第四个结点
	ListDelete(L, 4);
	PrintList(L);
	return 0;
}

双链表的头插法与尾插法,以及增删查找

#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <stdlib.h>
typedef int ElemType;
//定义双链表结点类型
typedef struct DNode {
	ElemType data; //数据域
	struct DNode* prior,*next;//前驱和后驱指针
}DNode,*DLinkList;
//打印双向链表
void PrintDList(DLinkList DL) {
	DL = DL->next;
	while (DL != NULL) {
		printf("%3d", DL->data);
		DL = DL->next;
	}
	printf("\n");
}
//双向链表头插法
DLinkList Dlist_head_insert(DLinkList& DL) {
	DL = (DLinkList)malloc(sizeof(DNode));//带头结点的链表,DL是头结点
	DL->prior = NULL;//前驱指针和后驱指针为NULL
	DL->next = NULL;
	int x;
	DNode* s;
	scanf("%d", &x);//从标准输入读取数据
	while (x!=9999)
	{
		s = (DLinkList)malloc(sizeof(DNode));
		s->data = x;
		s->next = DL->next;
		if (DL->next != NULL) {
			DL->next->prior = s;
		}
		s->prior = DL; //要插入的结点指向头结点
		DL->next = s;
		scanf("%d", &x);
	}
	return DL;
}

//双向链表尾插法
DLinkList Dlist_tail_insert(DLinkList& DL) {
	DL = (DLinkList)malloc(sizeof(DNode));//带头结点的链表,DL是头结点
	DL->prior = NULL;//前驱指针为NULL
	int x;
	DNode* s,*r=DL;// r代表尾指针
	scanf("%d", &x);//从标准输入读取数据
	while (x != 9999) {
		s = (DLinkList)malloc(sizeof(DNode));
		s->data = x;
		r->next = s;
		s->prior = r;
		r = s;//r指向新的表尾结点
		scanf("%d", &x);
	}
	r->next = NULL; //尾结点的next指针赋值为0
	return DL;
}

//查找第几个结点的值
DLinkList getElem(DLinkList DL, int i) {
	DNode* p = DL->next;
	int j = 1;
	if (i == 0) {
		return DL; // i是0就返回头结点
	}
	if (i < 1) {
		return NULL; //i是负值就返回空
	}
	while (p && j < i) {
		p = p->next;// 让指向下一个结点
		j++;
	}
	return p;
}

//往第i个位置插入元素
bool DListFrontInsert(DLinkList& DL, int i, ElemType e) {
	DNode* p = getElem(DL, i - 1);//拿到要插入位置的前一个位置的地址值
	if (p == NULL) {
		return false;// i不对
	}
	DLinkList s = (DNode*)malloc(sizeof(DNode));
	s->data = e;//要插入的值放入对应空间
	s->next = p->next;
	p->next->prior = s;
	s->prior = p;
	p->next = s;
	return true;
}

//删除结点
bool DListDelete(DLinkList& DL, int i) {
	DLinkList p = getElem(DL, i - 1);
	if (NULL == p) {
		return false;
	}
	DNode* q;
	q = p->next;
	if (q == NULL) {
		return false;//要删除的位置不存在
	}
	p->next = q->next;//断链
	if (q->next != NULL) {//q->next为NULL删除的是最后一个结点
		q->next->prior = p;
	}
	free(q);
	return true;
}

int main() {
	DLinkList DL;//数据 3 4 5 6 7 8 9999
	DLinkList search; //用来存储拿到的某一个节点
	Dlist_head_insert(DL);//头部插入
	PrintDList(DL);
	Dlist_tail_insert(DL);//尾部插入
	PrintDList(DL);
    
	//查找元素值
	search = getElem(DL, 2);//查找链表第二个位置的元素
	if (search != NULL) {
		printf("按序号查找成功\n");
		printf("%d\n", search->data);
	}

	//新结点插入第i个位置
	DListFrontInsert(DL, 2, 99);
	PrintDList(DL);

	//删除结点
	DListDelete(DL, 2);
	PrintDList(DL);
	return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值