数据结构--线性结构

时间秒杀一切 提交于 2020-01-31 00:46:17

线性结构

线性结构主要有以下四种:

  1. 线性表
  2. 队列

每种线性结构都可以用链表或者数组去实现,本身也是属于线性表,个人自学数据结构目前主要用于程序竞赛,所以主要以数组实现为主。

线性表(List)

数据对象集:n个元素构成的有序序列(a1,a2…an)

操作集:线性表L∈List,整数i表示位置,元素X∈ElementType,线性表的基本操作主要有:

1、List MakeEmpty():初始化一个空线性表L
2、ElementType FindKth( int K, List L ):根据位序K,返回相应元素
3、int Find( ElementType X, List L ):在线性表L中查找X的第一次出现位置
4、void Insert( ElementType X, int i, List L):在位序i前插入一个新元素X
5、void Delete( int i, List L ):删除指定位序i的元素
6、int Length( List L ):返回线性表L的长度n

线性表c++实现(顺序存储)

#include <bits/stdc++.h>
#define ElementType int 
#define Maxsize 10000
using namespace std ;

typedef struct Node{
	ElementType Date[Maxsize];
	int length = 0 ; // 记录当前存储了多少数据 
}List,*qList;

qList MakeEmpty()
{
	List  newlist ;
	return &newlist;
} 

void Insert(ElementType X , int i ,qList L) //在位序i前插入一个新元素 
{
	//1.把i位置开始的元素全部往后移动
	if (L->length +1 == Maxsize)
		{
			cout <<"位置已满,插入失败"<<endl;
			return ;
		}
	for (int j = L->length ; j>i;j--)
		L->Date[j] = L->Date[j-1];
	//2. 插入新元素
	L->Date[i] = X ;
	L->length ++ ; 
}

void TraversrList(List L ) //遍历输出 
{
	if (L.length == 0 )
	{
		cout <<"Empty!"<<endl;
		return ;
	}
	for (int i=0 ;i < L.length ;i++)
	cout <<L.Date[i]<<" ";
	cout << endl;
}

ElementType FindKth(int K ,List L)//返回第k个元素 
{
	if (K>=L.length || K<0)
		{
			cout <<"位置错误"<<endl;
			return -1 ;
		}
	return L.Date[K]; 
}

int Find (ElementType X , List L) //返回在线性表L中查找X的第一次出现位置
{
	for (int i= 0 ;i<L.length ;i++) 
		if (L.Date[i]==X)
			return i;
	return -1;
} 

void Delete(int i, qList L)
{
	if (L->length <i)	
	{
		cout <<"删除位置i无元素!"<<endl;
		return ; 
	}
	int date = L->Date[i];
	cout <<"位置"<<i<<"删除的元素是:"<<date <<endl; 
	i++;
	for (;i<L->length-1 ;i++)
		L->Date[i-1] = L->Date[i]; 
	L->length--;
} 

int Length(List L)
{
	return L.length;
}
int main ()
{
	List L = *MakeEmpty(); // 创建线性表
	for (int i=0;i<100;i++)
		Insert(rand(),i,&L);
	Insert(-1,100,&L);
	TraversrList(L);
	if (Find (-1,L)>=0)
	{
		cout <<"-1 的位置:"<< Find (-1,L)<<endl;
	}
	else 
	{
		cout <<"未找到!"<<endl; 
	}
	cout <<"位置10的元素为:"<< FindKth(10,L)<<endl;
	Delete(100,&L);
	TraversrList(L);
}

在这里插入图片描述

链式存储(有时间再补。。。)

连续存储和链式存储优缺点:

  1. 连续存储有利于元素的访问,链式存储需要访问某位置的元素需要从头开始寻找。
  2. 连续存储改变元素顺序时,需要改动很大部分的存储位置,链式结构改变元素只需改变周围结点指针的指向即可。
  3. 连续存储使用的时连续的空间,内存无法开很大,链式存储使用的空间时不连续的,不需要大片空闲的空间,因此可以大容量存储。

广义表
广义表时线性表的推广,广义表中的单个元素也可以是另一个广义表,但是广义表是非线性的。

多重链表
链表中的节点可能同时属于多个链

  • 多重链表中结点的指针域会有多个
  • 但包含两个指针域的链表并不一定是多重链表,比如在双向链表 不是多重链表
  • 多重链表有广泛的用途: 基本上如树、图这样相对 复杂的数据结构都可以采 用多重链表方式实现存储。

以多重链表实现矩阵为例子:
在这里插入图片描述

堆栈(Stack)

数据对象集:一个有0个或多个元素的又穷线性表
操作集:

1、Stack CreateStack( int MaxSize ): 生成空堆栈,其最大长度为MaxSize;
2、int IsFull( Stack S, int MaxSize ):判断堆栈S是否已满;
3、void Push( Stack S, ElementType item ):将元素item压入堆栈;
4、int IsEmpty ( Stack S ):判断堆栈S是否为空;
5、ElementType Pop( Stack S ):删除并返回栈顶元素

栈最大的特点:后进先出
在这里插入图片描述
应用

  • 函数调用及递归实现
  • 深度优先搜索
  • 回溯算法

顺序存储实现

#include <bits/stdc++.h>
#define ElementType int  
#define Maxsize 10000
using namespace std ;

typedef struct Node{
	ElementType Date[Maxsize];
	int Top= -1 ; // 记录当前存储了多少数据 
}Stack,*qStack;

qStack CreateStack( )//生成空堆栈,其最大长度为MaxSize
{
	Stack newstack ;
	return &newstack;
}

int IsFull( Stack S )//判断堆栈S是否已满
{
	if (S.Top == Maxsize-1)
		return 1 ;
	else 
		return 0 ;
}

void Push( qStack S, ElementType item )//将元素item压入堆栈
{
	if (IsFull(*S))
	{
		cout <<"栈满!"<<endl;
		return ; 
	}
	S->Date[S->Top+1] = item ;
	S->Top ++ ;
}

int IsEmpty ( Stack S )//判断堆栈S是否为空
{
	if (S.Top == -1)
		return 1 ;
	else 
		return 0 ;
}

ElementType Pop( qStack S )//删除并返回栈顶元素
{
	S->Top --;
	return S->Date[S->Top+1];
}

int main ()
{
	//将0 1 2 3 4 5 6压入栈 并以6 5 4 3 2 1 0输出
	Stack s= *CreateStack();
	
	for (int i=0;i<=6;i++)
		if (!IsFull(s))
			Push(&s , i);
	
	while (!IsEmpty(s))
	{
		cout <<Pop(&s)<<" ";
	}
}

在这里插入图片描述

值得一提的是,c++ STL库中已经有现成的栈供我们使用,在程序竞赛中经常会用到,下面会使用现成的Stack去实现表达式求值。

栈实现表达式求值

思路:将中缀表达式转换成后缀表达式,再计算。

  • 中缀表达式:运算符号位于两个运算数之间。如:a+b*c-d
  • 后缀表达式:运算符号位于两个运算数之后。如:abc*+d-

后缀表达式求值策略:从左向右“扫描”,逐个处理运算数和运算符号

  1. 运算数:入栈;
  2. 运算符:从堆栈中弹出适当数量的运算数,计算并结果入栈;
  3. 最后,堆栈顶上的元素就是表达式的结果值。

问题:括号会改变算数预算符的优先级
在这里插入图片描述
解决办法
在这里插入图片描述
不理解的话看下示例:
在这里插入图片描述
中缀转后缀

string change (string mid) // 中缀转后缀 
{
	string late ;
	for (int i = 0; i < mid.length(); i++)
	{

		if (mid[i] >= '0' && mid[i] <= '9') // 数字直接放入 
		{
			late.push_back(mid[i]);
		}
		else
		{
			if (s.empty() || mid[i] == '(') //符号栈 空或者为 ( 直接入栈 
			{
				s.push(mid[i]);
			}
			else
			{
				if (mid[i] == ')') //右括号 就把左括号之后的全部输出 
				{
					while (s.top() != '(')
					{
						late.push_back(s.top());
						s.pop();
					}
					s.pop();
				}
				else if (rule[s.top()] >= rule[mid[i]]&& s.top()!='(') //如果优先级比栈里的低 先将高优先级的输出(同优先级按从左到右顺序排) 注意别把(输出了 
				{
					while (!s.empty() && rule[s.top()] >= rule[mid[i]]&&s.top() != '(')
					{
						late.push_back(s.top());
						s.pop();
					}
					s.push(mid[i]);
				}
				else //其余情况就是优先级比栈头符号里高 
					s.push(mid[i]);
			}
		}
	}
	while (!s.empty()) //将剩下的符号输出 
	{
		late.push_back(s.top());
		s.pop();
	}
	return late;
} 

在这里插入图片描述
后缀式计算

double calculate (string late)
{
	for (int i=0;i<late.length();i++)
	{
		if (late[i]>='0' && late[i]<='9') //数字先放入栈中
			d.push(late[i]-'0');
		else 
		{   //计算设计除法,所以用double 
			double number1 , number2 ; //符号的话从栈中提取两个数字做运算
			number1= d.top();d.pop();
			number2= d.top();d.pop();
			switch (late[i])
			{
				case '+':  //number1 和 number2的次序千万别弄反了
					d.push(number2+number1); break ;
				case '-':
					d.push(number2-number1);break;
				case '*':
					d.push(number2*number1);break;
				case '/':
					d.push(number2/number1);break ; 
			}
		}
	}
	return d.top();
 } 

总代码:

#include <bits/stdc++.h>
#include <stack>
using namespace std ;
stack <char> s ; //储存符号 
stack <double> d ;//储存数字
map<char, int>rule ; //符号优先级按数字大小排 
//2*(9+6/3-5)+4
string change (string mid) // 中缀转后缀 
{
	string late ;
	for (int i = 0; i < mid.length(); i++)
	{

		if (mid[i] >= '0' && mid[i] <= '9') // 数字直接放入 
		{
			late.push_back(mid[i]);
		}
		else
		{
			if (s.empty() || mid[i] == '(') //符号栈 空或者为 ( 直接入栈 
			{
				s.push(mid[i]);
			}
			else
			{
				if (mid[i] == ')') //右括号 就把左括号之后的全部输出 
				{
					while (s.top() != '(')
					{
						late.push_back(s.top());
						s.pop();
					}
					s.pop();
				}
				else if (rule[s.top()] >= rule[mid[i]]&& s.top()!='(') //如果优先级比栈里的低 先将高优先级的输出 注意别把(输出了 
				{
					while (!s.empty() && rule[s.top()] >= rule[mid[i]]&&s.top() != '(')
					{
						late.push_back(s.top());
						s.pop();
					}
					s.push(mid[i]);
				}
				else //其余情况就是优先级比栈头符号里高 
					s.push(mid[i]);
			}
		}
	}
	while (!s.empty()) //将剩下的符号输出 
	{
		late.push_back(s.top());
		s.pop();
	}
	return late;
} 

double calculate (string late)
{
	for (int i=0;i<late.length();i++)
	{
		if (late[i]>='0' && late[i]<='9')
			d.push(late[i]-'0');
		else 
		{
			double number1 , number2 ;
			number1= d.top();d.pop();
			number2= d.top();d.pop();
			switch (late[i])
			{
				case '+':
					d.push(number2+number1); break ;
				case '-':
					d.push(number2-number1);break;
				case '*':
					d.push(number2*number1);break;
				case '/':
					d.push(number2/number1);break ; 
			}
		}
	}
	return d.top();
 } 

int main ()
{
	rule['+'] = 0 ; // 设立数字便于优先级比较 
	rule['-'] = 0 ; // 同优先级按从左到右顺序排 
	rule['*'] = 1 ;
	rule['/'] = 1 ;
	rule['('] = 2 ;
	string mid , late ; // 中缀 , 后缀 
	cin >> mid;
	late = change(mid);
	cout <<late<<endl ;
	cout <<calculate(late);
}

在这里插入图片描述

队列

数据对象集:一个有0个或多个元素的有穷线性表
操作集:长度为MaxSize的队列Q  Queue,队列元素item ∈ ElementType

1、Queue CreatQueue( int MaxSize ):生成长度为MaxSize的空队列;
2、int IsFullQ( Queue Q, int MaxSize ):判断队列Q是否已满;
3、void AddQ( Queue Q, ElementType item ): 将数据元素item插入队列Q中;
4、int IsEmptyQ( Queue Q ): 判断队列Q是否为空;
5、ElementType DeleteQ( Queue Q ):将队头数据元素从队列中删除并返回

特点:先进先出

顺序存储实现

#include <bits/stdc++.h>
#define ElementType int  
#define Maxsize 10000
using namespace std ;

typedef struct QUEUE {
	ElementType Date[Maxsize];
	int rear=0 ;
	int front=0;
}Queue;
Queue CreatQueue(  )//生成长度为MaxSize的空队列
{
	Queue q ;
	return q ;	
} 
int IsFullQ( Queue Q)//判断队列Q是否已满
{
	if 	(Q.rear +1 == Q.front)
		return 1 ;
	else 
		return 0;
}
void AddQ( Queue &Q, ElementType item )//将数据元素item插入队列Q中
{
	if (IsFullQ(Q))
	{
		cout <<"队满!"<<endl;
		return ; 
	}
	Q.Date[Q.rear]=item; 
	Q.rear = (Q.rear +1)%Maxsize;
}
int IsEmptyQ( Queue Q )//判断队列Q是否为空
{
	if (Q.front == Q.rear  )
		return 1;
	else 
		return 0; 
} 
ElementType DeleteQ( Queue &Q )//将队头数据元素从队列中删除并返回
{
	cout <<"删除的元素为:"<<Q.Date[Q.front]<<endl ;
	Q.front = (Q.front+1)%Maxsize ;
}
int main ()
{
	Queue q = CreatQueue ();
	AddQ(q,100);
	AddQ(q,55);
	AddQ(q,22);
	DeleteQ(q);
	DeleteQ(q);
	DeleteQ(q);
} 

在这里插入图片描述
STL 中也有现成队列可以使用,主要有两种,一种是普通队列:queue ,还有一种是优先队列:priority_queue,优先队列主要的优点是可以将队列中的元素自动排序,排序的规则可自定义。

队列再算法中的应用:

  • 宽度优先搜索
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!