数据结构前三章(自由回忆)

  1. 数据结构的基本概念
  2. 算法的基本概念
  3. 算法的时间复杂度
  4. 算法的空间复杂度
  5. 线性表(逻辑结构)的定义和基本操作
  6. 顺序表(存储结构:顺序存储)的定义和基本操作
  7. 单链表(存储结构:链式存储)的定义和基本操作
  8. 双链表的定义和基本操作
  9. 循环链表的定义和基本操作
  10. 静态链表的定义和基本操作
  11. 链表和顺序表的比较
  12. 栈的定义(逻辑结构:线性表)和基本操作
  13. 顺序栈(存储结构:顺序存储)的定义和基本操作
  14. 链栈(存储结构:链式存储)的定义和基本操作
  15. 队列的定义(逻辑结构:线性表)和基本操作
  16. 队列的顺序实现
  17. 队列的链式实现
  18. 双端队列(栈是只在一端进出的队列)
  19. 栈的应用
    - 括号匹配
    - 中缀表达式转后缀表达式
    - 中缀表达式求值、后缀表达式求值

概述

数据结构的基本概念

程序 = 算法 + 数据结构
数据结构 = 数据的逻辑结构、数据的物理结构(存储结构)、数据的运算

算法的基本概念

算法是数据运算的顺序和执行方式

算法的时间复杂度与空间复杂度

时间复杂度:算法的运行次数与数据规模n的关系
空间复杂度:算法所占用的内存单元与数据规模n的关系
用O表示法

线性表

定义:相同类型元素 的有限序列
操作:

//初始化
InitList(List &L);
//销毁列表
DestroyList(List &L);
//插入
ListInsert(List &L,ElemType x);
//删除i位元素
ListDelete(List &L,int i,ElemType &x);
//返回x地址
LocateElem(List L,ElemType x);
//返回i位元素x
GetElem(List L,ELemType &x);
//判空
IsEmpty(List L);
//判满
IsFull(List L);
//返回线性表长
ListLong(List L);

线性表的顺序存储

顺序表
顺序表的特点:
优点:随机读取,存储密度大
缺点:扩容不方便,插入、删除数据不方便
逻辑结构:线性表
存储结构:顺序存储
代码定义

静态分配

#define MaxSize 10
typedef struct{
	ElemType data[MaxSize];
	int length;
}SqList;

void InitList(SqList &L){
	for(int i = 0; i < MaxSize;i++)L.data[i] = 0;
	L.length = 0;
}

bool IsEmpty(SqList &L){
	if(L.length == 0)return true;
	return false;
}

bool InsertList(SqList &L,int i,ElemType e){
	if(i < 0|| i > length||L.length == MaxSize)return false;
	if(i == length)L.data[i] = e;
	else{
		for(int j = length - 1;j >= i - 1;j--){
			L.data[j+1] == L.data[j];
		}
		L.data[i - 1] = e;
	}
	L.length ++;
	return true;
}

动态分配

#define InitSize 10
typedef struct{
	ElemType *data;
	int maxSize;
	int length;
}SqList;

bool InitList(SqList &L){
	L.data = (int *)malloc(InitSIze*sizeof(int));
	if(L.data == NULL)return false;
	L.length = 0;
	L.maxSize = InitSize;
	return true;
}

bool IncreaseSize(SqList &L,int len){
	int *p = L.data;
	L.data = (int *)malloc((L.MaxSize + len)*sizeof(int));
	if(L.data == NULL)return false;
	for(int i = 0;i < L.length;i++)L.data[i] == p[i];
	L.maxSize += len;
	return true;
}

线性表的链式存储

单链表

定义:每个节点存储数据及指向下一个节点指针的线性表
链表的特点:
优点:扩容方便,不要求大片连续的空间
缺点:不可随机存取,存储密度小,要耗费一定空间存储指针

实现:

无头节点
typedef struct LNode{
	ElemType data;
	struct LNode *next;
}LNode, *LinkList;

void InitList(LinkList &L){
	L = NULL;
}

bool IsEmpty(LinkList L){
	if(L == NULL)return true;
	return false;
}

//指定节点后插入(非首个节点)
bool InsertNextNode(LNode *p,x){
	if(p == NULL)return false;
	LNode *s = (LNode *)malloc(sizeof(LNode));
	s -> data = x;
	s -> next = p -> next;
	p -> next = s;
	return true;
}

//首个节点插入
bool InsertFirstNode(LinkList &L,x){
	LNode *s = (LNode *)malloc(sizeof(LNode));
	if(s == NULL)return false;
	s -> data = x;
	s -> next = L;
	L = s;
	return true;
}

//获取第i位元素
LNode *GetElem(LinkList L,int i){
	if(i < 1)return NULL;
	if(i == 1)return L;
	for(int j = 1;j < i && L!= NULL;j++)L = L -> next;
	return L;     
}

有头节点
typedef struct LNode{
	ElemType data;
	struct LNode *next;
}LNode*LinkList;

//初始化
bool InitList(LinkList &L){
	L = (LNode *)malloc(sizeof(LNode));
	L -> next = NULL;
}

//判空
bool IsEmpty(LinkList L){
	if( L -> next == NULL)return true;
	return false;
}

//获取i位元素
LNode *GetElem(LinkList L,int i){
	if(i < 1)return NULL;
	for(int j = 0;j < i && L!= NULL;j++){
		L = L -> next;
	}
	return L;
}

//后插
bool InsertNextNode(LNode *p,ElemType e){
	if(p == NULL)return false;
	LNode *s == (LNode *)malloc(sizeof(LNode));
	if(s == NULL)return false;
	s -> data = e;
	s -> next = p -> next;
	p -> next = s;
	return true;
}

//前插
bool InsertPriorNode(LNode *p,ElemType e){
	if(p == NULL)return false;
	LNode *s = (LNode *)malloc(sizeof(LNode));
	s -> next = p -> next;
	s ->value = p -> value;
	p -> next = s;
	p -> value = e;
	return true;
}

//指定位置插入节点
bool ListInsert(LinkList L,int i,ElemType x){
	L = GetElem(L,i - 1);
	return InsertNextNode(L,x);
}

//删除指定位置节点
bool DeleteNode(LinkList L,int i,ElemType &e){
	L = GetElem(L,i - 1);
	if(L == NULL)return flase;
	if(L -> next == NULL)return flase;
	LNode *s = L -> next;
	e = s -> data;
	L -> next = s -> next;
	free(s);
	return true;
}

//删除节点
bool DeleteNode(LNode *p){
	if(p == NULL)return false;
	if(p -> next == NULL){
		free(p);
		return true;
	}
	LNode *s = p -> next;
	p -> data = s -> data;
	p ->next = s -> next;
	free(s);
	return true;
}

//后插法创建链表
LinkList ListTailInsert(LinkList &L){
	int x;
	InitList(&L);
	LNode *s= L;
	scanf("%d",&x);
	while(x != 9999){//输入9999时循环结束
		InsertNextNode(&s,x);
		s = s -> next;
		scanf("%d",&x);
	}
	p -> next = NULL;
	return L;
}

//前插法创建链表
LinkList ListHeadInsert(LinkList &L){
	int x;
	L = (LNode *)malloc(sizeof(LNode));
	L -> next = NULL;
	scanf("%d",&x);
	while(x != 9999){
		InsertNextNode(L,x);
		scanf("%d",&x);
	}
	return L;
}

LinkList ListInverse(LinkList &L){
	LNode *h = (LNode *)malloc(sizeof(LNode));
	h -> next = NULL;
	LNode *s = L;
	while(s != NULL){
		InsertNextNode(h,s -> data);
		s = s -> next;	
	}
	return h;
}

双向链表(循环链表)

//有头节点
typedef struct DNode{
	ElemType data;
	struct DNode *prior,*next;
}DNode,*DLinkList;

bool InitLinkList(DLinkList &L){
	L = (DNode *)malloc(sizeof(DNode));
	if(L == NULL)return false;
	L -> prior = L -> next = NULL;
	return true;
}

//Init循环
bool InitLinkList(DLinkList &L){
	L = (DNode *)malloc(sizeof(DNode));
	if(L == NULL)return false;
	L ->prior = L;
	L ->next = L;
	return true;
}

//在p节点后插入
bool InsertNextNode(DNode *p,ElemType x){
	LNode *s = (LNode *)malloc(sizeof(LNode));
	if( p == NULL||s == NULL)return false;
	s -> data = x;
	s -> next = p -> next;
	if(p -> next != NULL){
		p -> next -> prior = s;
	}
	s -> prior = p;
	p -> next = s;
	return true;
}

bool DeleteNextNode(DNode *p){
	if(p == NULL)return flase;
	DNode *q = p -> next;
	if(q ==	NULL)return false;
	p -> next = q -> next;
	if(q -> next != NULL)q -> next -> prior = p;
	free(q);
	return true;
}	

void DestroyDLL(DLinkList &L){
	while(L->next != NULL)DeleteNextNode(L);
	free(L);
	L = NULL;
}

静态链表

分配一整片连续的空间,每个节点由数据和游标(数组下标代替指针)组成
优点:增删操作不需要移动大量元素
缺点:不能随机存取,只能从头节点开始依次往后查找;固定容量不变

#define MaxSize 10
typedef struct {
	ElemType data;
	int next;
}SLinkList[MaxSize];

void InitList(SLinkList L){
	L[0] -> next = -1;
	for(int i = 1;i < MaxSize;i ++){
		L[i] -> next = -2;
	}
}
bool FirstEmptyLoc(SLinkList L,int &x){
	for(int i = 0;i < MaxSize;i ++){
		if(L[i] -> next == -2){
			x = i;
			return true;
		}
	}
	return false;
}

bool InsertList(SLinkList L,int i,ElemType x){
	int index = 0;
	int newIndex = 0;
	if(!FirstEmptyLoc(L,&newIndex))return false;	//队列满
	for(int j = 0;j < i - 2||index == -2; i++){
		if(L[index] == -2)return false;
		index = L[index] -> next;
	}
	L[newIndex] -> next = L[Index] -> next; 
	L[Index] -> next = newIndex;
	return true; 	 
}

bool DeleteNode(SLinkList L,int i){
	if(L == NULL || L[0] -> next = -1)return false;
	int index = 0;
	for(int j = 0;j < i - 1||index = -2; j++){
		index = L[index] -> next;
	}												//搜索链表第i-1个节点的游标
	if(index == -2)return false;					//i值非法
	int delIndex = L[index] -> next;				//第i个节点游标	
	if(L[delIndex] == -2)return flase;				//第i个节点不存才
	L[index] -> next = L[delIndex] -> next;			//第i-1个节点游标指向第i+1个节点
	L[delIndex] -> next = -2;						//第i个节点游标判空
	return true;
}

顺序表和链表的比较

逻辑结构

都是线性表,逻辑结构都是线性结构

物理结构(存储结构)

顺序表是顺序存储
优点:支持随机存取,存储密度高
缺点:大片连续空间的分配不方便,改变容量不方便
链表是链式存储
优点:离散的小空间分配方便,改变容量方便
缺点:不能随机存取,存储密度低

数据的运算/基本操作

  • 顺序表创建需要分配大量连续空间,若采用静态数组的方式存储,则无法改变容量,若采用动态数组的方式存储,则需要用malloc函数重新分配内存空间,将所有已存在的数据移动
  • 链表创建只需要分配一个头节点(或声明一个头指针),方便扩容
  • 顺序表增删元素可以直接定位到元素所在的位置,但需要移动所有后继元素,时间开销主要来源于后继元素的移动(时间复杂度O(n))
  • 链表增删元素需要从头指针遍历后续节点定位到目标节点,找到目标节点后只需要改变指针的指向,时间开销主要来源于定位目标节点(遍历前驱节点)(时间复杂度O(n))

栈的定义

线性表是相同类型元素的有限序列
栈是一种先进后出的线性表(FILO)
基本操作:

  • InitStack
  • Push
  • Pop
  • GetTop
  • IsEmpty
  • IsFull

栈的实现

顺序栈

#define MaxSize 10
typedef struct{
	ElemType data[MaxSize];
	int top;
}SqStack;

void InitStack(SqStack S){
	S.top = -1;
}

bool IsEmpty(SqStack S){
	if(S.top == -1)return true;
	return false;
}

bool IsFull(SqStack S){
	if(S.top == MaxSize -1)return ture;
	return false;
}

bool Push(SqStack &S,ElemType x){
	if(IsFull(S))return false;
	S.data[++top] == x;
	return true;
}

bool Pop(SqStack &s,ElemType &x){
	if(IsEmpty(s))return false;
	x = S.data[top--];
	retuern ture;
}

bool GetTop(SqStack &s,ElemType &x){
	if(isEmpty(s))return false;
	x = S.data[top];
	return true;	
}

共享栈

#define MaxSize 10
typedef struct{
	ElemType data[MaxSize];
	int top0;
	int top1;
}ShSqStack;

void InitStack(ShSqStack &s){
	s.top0 = -1;
	s.top1 = MaxSize;
}

bool IsFull(ShSqStack &s){
	if(s.top0 + 1 == S.top1)return ture;
	return false;
}

bool IsEmpty(ShSqStack &s){
	if(s.top1 - s.top0 == MaxSize + 1)return ture;
	return false; 
}

链栈

typedef struct LNode{
	ElemType data;
	struct LNode *next;
}LNode,*LinkStack;

void InitStack(ListStack &s){
	s = (LNode *)malloc(sizeof(LNode));
	s -> next = NULL;
}

bool IsEmpty(ListStack s){
	if(s -> next == NULL)return ture;
	return false;
}

bool Push(ListStack s,ElemType e){
	if(s == NULL)return false;
	LNode *p = (LNode *)malloc(sizeof(LNode));
	if(p == NULL)return false;
	p -> data = e;
	p -> next = s -> next;	
	s -> next = p;
	retuen ture;
}

boll Pop(ListStack s,ElemType &x){
	if(IsEmpty(s))return false;
	LNode *p = s -> next;
	x = p -> data;
	s -> next = p -> next;
	return true;
}

bool GetTop(ListStack s,ElemType &x){
	if(IsEmpty(s))return false;
	x = s -> next -> data;
	return ture;
}

队列

队列的定义

线性表是相同元素的有限序列
顺序表是顺序结构存储的线性表
链表是链式结构存储的线性表
栈是先进后出的线性表//FILO
顺序栈是顺序结构存储的先进后出的线性表
链栈是链式结构存储的先进后出的线性表
队列是先进先出的线性表//FIFO
队列可以用顺序结构实现(可以通过对数组游标取模实现循环队列)
队列可以用链式结构实现 考虑带头节点、不带头节点(不用循环队列)

基本操作

  • 初始化
  • 判空
  • 判满
  • 入队
  • 出队
  • 获取队头元素

队列的实现

顺序实现

#define MaxSize 10
typedef struct{
	ElemType data[MaxSize];
	int front,rear;
}SqQueue;

void InitQueue(SqQueue &q){
	q.front = 0;
	q.rear = 0;
}

bool IsEmpty(SqQqueue &q){
	if(q.front == q.rear)return ture;
	return false;
}

bool IsFull(SqQueue &q){
	if((q.rear + 1)%MaxSize == q.front)return ture;
	return false;
}

//入队
bool EnQueue(SqQueue &q,ElemType x){
	if(IsEmpty(q))return false;
	q.data[q.rear] = x;				//考察边界条件,当插入第一个节点时,rear = 0,要先给data[0]赋值
	q.rear = (q.rear + 1)%MaxSize;	//构成循环队列,当游标指向最后一个时,+1指回首节点
	return false;
}

//出队
bool DeQueue(SqQueue &q,ElemType &x){
	if(IsEmpty(&q))return flase;
	x = q.data[q.front];
	q.front = (q.front + 1)%MaxSize;
	return true;
}

//获取队首元素
bool GetFront(SqQueue &q,ElemType &x){
	if(IsEmpty(&q))return flase;
	x = q.data[q.front];
	return ture;
}


链式实现

带头节点
typedef struct LNode{		//定义一个链表节点类型
	ElemType data;
	struct LNode *next;
}LNode;
typedef struct{					//定义一个包含队首节点和队尾节点的链队列类型
	LinkNode *front,*rear;
}LinkQueue;
void InitQueue(LinkQueue &q){
	q.front = q.rear =(LinkNode *)malloc(sizeof(LinkNode));
	q.front -> next = NULL;
}

bool IsEmpty(LinkQueue &q){
	if(q.front -> next == NULL)return true;
	return false;
}

//带头节点
bool EnQueue(LinkQueue &q,ElemType e){
	LNode *s = (LNode *)malloc(sizeof(LNode));
	if(s == NULL)return false;
	s -> data = e;
	s -> next = NULL;
	q.rear -> next = s;
	q.rear = s;
	return true;
}

//带头结点
bool DeQueue(LinkQueue &q,ElemType &x){
	if(IsEmpty(q))return false;
	LNode *s = q.front -> next;
	x = s -> data;
	q.front -> next = s -> next;
	if(q.rear = s)						//如果s是最后一个节点,修改队尾节点的指向
		q.rear = q.front;				//队尾节点指向队首,队空
	free(s);						
	return true;
}

栈的应用

括号匹配

表达式求值

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值