数据结构(第二章)

多项式表示问题的启示:
1.同一个问题可以有不同的表示。
2.有一类共性问题:有序线性序列的组织和管理。
线性表(Linear List): 由同类型数据元素构成有限序列的线形结构。
若将线性表记为(A1,A2…Ai-1,Ai,Ai+1,An),则表中Ai-1领先于Ai,Ai领先于Ai+1,称Ai-1是Ai的直接前驱元素,Ai+1是Ai的直接后继元素。当i=1,2…,n-1时,Ai有且仅有一个直接后继,当i=2,3,…,n时,Ai有且仅有一个直接前驱。
所以线性表元素的个数n(n>=0)定义为线性表的长度,当n=0时,称为空表。
表的起始位置称表头,表结束位置称表尾
线性表的抽象类型描述:
类型名称:线性表
数据对象集:线性表是n(n>=0)个元素构成的有序序列(A1,A2…An)
线性表的顺序存储
利用数组的连续存储空间顺序存放线性表的各元素
主要操作的实现:

//1.初始化(建立空的顺序表)
List *MakeEmpty()
{
	List *ptrL;
	ptrL=(List *)malloc(sizeof(List));
	ptrL->Last=-1;
	return ptrL;
}
//2.查找
int Find(ElementType X,List *ptrL)
{	
	int i=0;
	while(i<=ptrL->Last&&ptrL->data[i]!=X)
		i++;
	if(i>ptrL->last)
		return -1;
	else
		return i;
}
//3.插入
void Insert(ElementType X,int i,List ptrL)
{
	int j;
	if(ptrL->Last==MAXSIZE-1)//表空间已满,不能插入
	{
		printf("表满";
		return;
	}
	if(i<1||i>ptrL->Last+2)//检查插入位置的合法性
	{
		printf("插入位置不合理");
		return;
	}
	for(j=ptrL;j>=i-1;j--)
	{
		ptrL->data[j+1]=ptrL->data[j];//腾出地方
	}
	ptrL->data[i-1]=X;//插入
	ptrL->Last++;
	return;
}
//删除
void Delete(int i,List ptrL)
{
	int j;
	if(i<1||i>ptrL->Last+1)//检查删除位置的合法性
	{
		printf("不存在%d个元素",i);
		return;
	}
	for(j=i;j<=ptrL->Last;j++)
	{
		ptrL->data[j-1]=ptrL->data[j];
	}
	ptrL->last--;
	return;
}

为了表示每个数据元素ai与其直接后继数据元素ai+1之间的逻辑关系,对数据元素ai来说,除了存储其本身的信息之外,还需要存储一个指示其直接后继的信息(即直接后继的存储位置)。
我们把存储数据元素信息的域称为数据域,把存储直接后继位置的域称为指针域。指针域中存储的信息称作指针或链。这两部分信息组成数据元素ai的存储映像,称为结点(Node)。

n个结点(ai的存储映像)链成一个链表,即为线性表(a1,a2,…an)的链式存储结构,因为此链表的每个结点中只包含一个指针域,所以叫做单链表。
链表中第一个结点的存储位置叫做头指针。
线性表的链式存储:
不要求逻辑上相邻的两个元素物理上也相邻
通过“链”建立起数据元素之间的逻辑关系。
插入,删除不需要移动数据元素,只需要修改“链”

//1.初始化
typedef struct LNode *List;
struct LNode
{
	ElementType data;
	List next;
};
struct LNode L;
List ptrL;
//2.查找
List FindKth(int k,List ptrL)//按序号查找
{
	List p=ptrL;
	int i=1;
	while(p!=NULL&&i<k)
	{
		p=p->next;
		i++;
	}
	if(i==k)
		return p;
	else
		return NULL;
}

List Find(ElementType X,List ptrL)//按值查找
{
	List p=ptrL;
	while(p!=NULL&&p->data!=X)
	{
		p=p->next;
	}
	return p;
}
//3.插入
List Insert(ElementType X,int i,List ptrL)
{
	List p,s;
	if(i==1)//新结点插入在表头
	{
		s=(List)malloc(sizeof(struct LNode));//申请,填装结点
		s-data=X;
		s->next=ptrL;
		return s;//返回新表头指针
	}
	p=FindKth(i-1,ptrL);//查找第i-1个结点
	if(p=NULL)//第i-1个不存在,不能插入
	{
		printf("参数错误");
		return NULL;
	}
	else
	{
		s=(List)malloc(sizeof(struct LNode));//申请,填装结点
		s->data=X;
		s->next=p->next;//新结点插入在第i-1个结点的后面
		p->next=s;
		return ptrL;
	}
}
//4.删除
List Delete(int i,List ptrL)
{
	List p,s;
	if(i==1)//若要删除的是表的第一个结点
	{
		s=ptrL;//s指向第一个结点
		if(ptrL!=NULL)
			ptrL=ptrL->next;//从链表中删除
		else
			return NULL:
		free(s);//释放被删除结点
		return ptrL;
	}
	p=FindKth(i-1,ptrL);//查找第i-1个结点
	if(p==NULL)
	{
		printf("第%d个结点不存在",i-1);
		return NULL;
	}
	else if(p->next==NULL)
	{
		printf("第%d个结点不存在",i);
		return NULL:
	}
	else
	{
		s=p->next;//s指向第i个结点
		p->next=s->next;//从链表中删除
		free(s);//释放被删除结点
		return ptrL;
	}
}

广义表(Generalized List)
广义表是线性表的推广,对于线性表而言,n个元素都是基本的单元素;
广义表中,这些元素不应可以是元素也可以是说另一个广义表。
多重链表:链表中的结点可能同时隶属于多个链。
多重链表中的结点的指针域会有多个
但包含两个指针域的链表并不一定是多重链表,比如双向链表就不是多重链表。
多重链表有广泛的用途:基本上如这样相对复杂的数据结构都可以采用多重链表的方式实现存储。

栈(stack)
栈是限定仅在表尾进行插入和删除操作的线性表。
简单的来说就是先进后出(LIFO结构)
我们把允许插入和删除的一端称为栈顶,另一端称为栈底,不含任何数据元素的栈称为空栈
栈的插入操作也叫进栈,压栈,入栈。
栈的删除操作也叫出栈,弹栈。
栈的顺序存储实现
栈的顺序存储结构通常是由一个一维数组和一个记录栈顶元素位置的变量组成。

//初始化
#define MAXSIZE <存储数组的最大个数>
typedef struct SNode *Stack ;
struct SNode
{
	ElementType data[MAXSIZE];
	int Top;
};
//入栈
void Push(Stack ptrL,ElementType item)
{
	if(ptrL->Top==MAXSIZE-1)
		printf("栈已满");
	else
	{
		ptrL->data[++(ptrL->Top)]=item;
		return ;
	}
}
//出栈
ElementType Pop(stack ptrL)
{
	if(ptrL->Top==-1)
	{
		printf("栈空"):
		return ;
	}
	else
	return (ptrL->data[(ptrL->Top)--]);
}

两栈共享空间
两个栈分别从数组的两头开始向中间生长;当两个栈顶指针相遇时,表示两个栈都满了

//初始化
struct DStack
{
	ElementType data[MAXSIZE];
	int Top1;//栈1指针
	int Top2;//栈2指针
}s;
s.Top1=-1;
s.Top2=MAXSIZE;
//入栈
void Push(struct DStack *ptrS,ElementType item,int flag)//flag作为区分两个栈的标志,取值1和2
{
	if(ptrS->Top2-ptrS->Top1==1)
		{
			printf("栈满");
			return ;
		}
	if(flag==1)
		ptrS->data[++(ptrS->Top1)]=item;
	else
	   ptrS->data[++(ptrS->Top2)]=item;
}	
//出栈
ElementType Pop(struct DStack *ptrS,int flag)
{
	if(flag==1)
		{
			if(ptrS->Top1=-1)
			{
				printf("栈空");
				return NULL:
			}
			else
				return ptrS->data[--(ptrS->Top1)];
		}
	else
		{
			if(ptrS->Top2==MAXSIZE)
			{
				printf("栈空");
				return NULL;
			}
			else
				return ptrS->data[--(ptrS->Top2)];
		}
}

栈的链式存储
栈的链式存储结构实际上就是一个单链表,叫做链栈。插入和删除操作只能在链栈的栈顶进行。那么栈顶指针Top应该在链表的哪一头呢?
根据链表的特性可知,Top应该在首,因为如果在尾部的话,我们无法知道上一个元素的地址。

typedef struct SNode *Stack;
struct SNode
{
	ElementType data;
	struct SNode *next;
};
//建栈
Stack CreatStack()//构建一个栈的头结点,返回指针
{
	Stack S;
	S=(Satck)malloc(sizeof(struct SNode));
	S->next=NULL:
	return S;
}
//判断
int IsEmpty(Stack S)//判断栈S是否为空,若为空返回整数1,否则返回0
{
	return (S->next==NULL);
}
//入栈
void Push(ElementType item,Stack S)
{
	struct SNode *TmpCell;
	TmpCell=(struct SNode*)malloc(sizeof(struct SNode));
	TmpCell->data=item;
	TmpCell->next=S->next;
	S->next=TmpCell;
}
//出栈
ElementType Pop(Stack S)
{
	struct SNode *FirstCell;
	ElementType TopElem;
	if(IsEmpty)
	{
		printf("栈空");
		return NULL;
	}
	else
	{
		FirstCell=S->next;
		S->next=FirstCell->next;
		TopElem=FirstCell->data;
		free(FirstCell);
		return TopElem;
	}
}	

栈的应用————四则运算表达式求值
中缀表达式:运算符号位于两个运算数之间。如,a+bc-d/e;
后缀表达式(逆波兰):运算符号位于两个运算数之后。如,abc
+de/-;
后缀表达式求值策略
从左向右“扫描”,逐个处理运算数和运算符号,遇到数字就进栈,遇到符号,就将处于栈顶的两个数字出栈,进行运算,运算结果入栈,一直到最终获得结果。
中缀表达式转后缀表达式
规则
从头到尾读取中缀表达式的每个对象,对不同的对象按不同的情况处理。
1.运算数:直接输出;
2.左括号:压入栈中;
3.右括号:将栈顶的运算符弹出输出直到遇到左括号。(出栈,不输出)
4.运算符
优先级大于栈顶元素运算符时,入栈
优先级小于等于栈顶元素运算符时,将栈顶元素运算符弹出并输出;比较新的栈顶元素运算符,直到该运算符大于栈顶运算符优先级为止,然后将该运算符压栈
5.若对象处理完毕,则把栈中残留的运算符一并输出

队列(queue)
队列:是只允许在一端进行插入操作,而在另一端进行删除的线性表。
简单的来说:先进先出(FIFO)。
循环队列
为什么会出现循环队列呢?
是因为队列顺序存储的不足。
定义:我们把队列的这种头尾相接的顺序存储结构称为循环队列。

//初始化
typedef int QElementType //QElementType根据实际情况而定,这里假设为int
typedef struct
{
	QElementType data[MAXSIZE];
	int front;//头指针
	int rear;//尾指针,若队列不为空,指向队列尾元素的下一个元素
}SqQueue;
//循环队列的初始化
Status InitQueue(SqQueue *Q)
{
	Q->front=0;
	Q->rear=0;
	return OK;
}
//循环队列求长度
int Queuelength(SqQueue Q)
{
	return (Q.rear-Q.front+MAXSIZE)%MAXSIZE;
}
//循环队列入队
Status EnQueue(SqQueue *Q,QElemntType e)
{
	if((Q->rear+1)%MAXSIZE==Q->front)//队列已满
	return ERROR;
	Q->data[Q->rear]=e;
	Q->rear=(A->rear+1)%MAXSIZE;//指针后移
	return OK;
}
//循环队列出队
Status DeQueue(SqQueue *Q,QElementType *e)
{
	if(Q->front ==Q->rear)//队列为空
	return ERROR;
	*e=Q->data[Q->front];
	Q->front=(Q->front+1)%MAXSIZE;
	return OK;
}
发布了18 篇原创文章 · 获赞 0 · 访问量 446

猜你喜欢

转载自blog.csdn.net/JT518721/article/details/104287981