【数据结构与算法】线性数据结构(线性表、堆栈Stack、队列Queue、双端队列Deque)

数据结构是算法的基础。

常见的线性数据结构,包括 线性表,堆栈,队列。

基本特点:

  • 线性数据结构中,数据项之间的顺序由添加或删除的顺序决定。 一旦一个数据项被添加, 它相对于前后元素的位置便一直保持不变。
  • 线性数据结构有 两端, 有时被称为左右, 某些情况被称为前后, 也可以称为顶部和底部。

诸如此类的数据结构被称为线性数据结构。


线性表(Linear List)

有序线性 结构。

  • 表中 元素的个数 称为 线性表的**“长度”**
  • 线性表 没有元素 时,称为**“空表”**
  • 表的起始位置称为**“表头”,表的结束位置称为“表尾”**

线性表的应用

哪些地方用到了线性表?比如

  • 多项式的表示
  • 矩阵的表示
  • 。。。

线性表的抽象数据结构

  • 名称:线性表(List)
  • 数据对象集(线性表中存储的数据对象):由 n (n ≥ 0)个元素构成的有序序列( a1, a2,…,an)
  • 操作集(线性表支持的基本操作)
1、List MakeEmpty():初始化一个空线性表L;

2、ElementType FindKth( int K, List L ):根据位序K,返回相应元素 ;

3int Find( ElementType X, List L ):在线性表L中查找X的第一次出现位置;

4void Insert( ElementType X, int i, List L):在位序i前插入一个新元素X;

5void Delete( int i, List L ):删除指定位序i的元素;

6int Length( List L ):返回线性表L的长度n。

线性表的具体实现

线性表的顺序存储(数组)实现

利用 数组连续存储空间顺序 存放线性表的各元素。

顺序存储有什么优点?

  • 索引更快

线性表的链式存储(链表)实现

不要求逻辑上相邻的两个元素物理上也相邻通过“链”建立起数据元素之间的逻辑关系

链式存储有什么优点?

  • 空间使用上更加灵活。
  • 插入、删除不需要移动数据元素,只需要修改“链”。

Python中的线性表实现

Python中的列表就是线性表。

广义表

  • 广义表是线性表的推广
  • 对于线性表而言, n个元素都是基本的单元素;
  • 广义表中,这些元素不仅可以是单元素也可以是另一个广义表。

相关问题


堆栈(Stack)

具有一定操作约束线性表

  • 只能从一端(栈顶, Top)添加取出 元素
  • 添加元素的操作,我们叫做 入栈(Push)
  • 取出元素的操作,我们叫做 出栈(Pop)
  • 后进先出(LIFO, Last In First Out)

堆栈的应用

  • 后缀表达式求值
  • 函数调用及递归实现
  • 深度优先搜索
  • 回溯算法
  • 。。。

堆栈的抽象数据结构

  • 类型名称:堆栈(Stack)
  • 数据对象集:一个有0个或多个元素的有穷线性表
  • 操作集(堆栈支持的基本操作): :
1、 Stack CreateStack( int MaxSize ): 生成空堆栈,其最大长度为MaxSize;

2int IsFull( Stack S, int MaxSize ):判断堆栈S是否已满;

3void Push( Stack S, ElementType item ):将元素item压入堆栈(重要!!);

4int IsEmpty ( Stack S ):判断堆栈S是否为空;

5、 ElementType Pop( Stack S ):删除并返回栈顶元素(重要!!);

在这里插入图片描述

堆栈的顺序存储(数组)实现

栈的顺序存储结构通常由 一个一维数组一个记录栈顶元素位置的变量top 组成。

#define MaxSize <储存数据元素的最大个数>
typedef struct SNode *Stack;
struct SNode{
		ElementType Data[MaxSize];
		int Top;
};

堆栈的链式存储(链表)实现

栈的链式存储结构实际上就是一个 单链表,叫做 链栈。插入和删除操作只能在链栈的栈顶进行。栈顶指针Top在链表头(在链表头进行插入删除操作)。

typedef struct SNode *Stack;
struct SNode{
		ElementType Data;
		struct SNode *Next;
} ;

Python中的堆栈实现

可以基于列表进行实现

class Stack:
	def __init__(self):
		self.items = []
		
	def isEmpty(self):
		return self.items == []
		
	def push(self, item):
		self.items.append(item)
		
	def pop(self):
		return self.items.pop()
		
	def peek(self):
		return self.items[len(self.items)-1]
		
	def size(self):
		return len(self.items)

相关问题

① 栈的压入弹出序列

② 用一个数组实现两个堆栈(针对C++)


队列(Queue)

具有一定操作约束线性表

  • 只能从一端(队尾)添加元素,从另一端(队首)取出元素
  • 添加数据的操作,我们叫做 入队
  • 取出数据的操作,我们叫做 出队
  • 先到先得,先进先出(FIFO, First In First Out)

队列的应用

队列是 BFS 的核心数据结构

队列的抽象数据结构

  • 类型名称:队列(Queue)
  • 数据对象集:一个有0个或多个元素的有穷线性表
  • 操作集: 队列基本操作主要有:
1、 Queue CreatQueue( int MaxSize ):生成长度为MaxSize的空队列;

2int IsFullQ( Queue Q, int MaxSize ):判断队列Q是否已满;

3void AddQ( Queue Q, ElementType item ): 将数据元素item插入队列Q中(重要!!);

4int IsEmptyQ( Queue Q ): 判断队列Q是否为空;

5、 ElementType DeleteQ( Queue Q ):将队头数据元素从队列中删除并返回(重要!!)。

队列的顺序存储(数组)实现

队列的顺序存储结构通常由 一个一维数组一个记录队列头元素位置的变量front 以及 一个记录队列尾元素位置的变量rear 组成。

#define MaxSize <储存数据元素的最大个数>
struct QNode {
		ElementType Data[ MaxSize ];
		int rear;
		int front;
};
typedef struct QNode *Queue;

循环队列

队列的链式存储(链表)实现

队列的链式存储结构也可以用一个单链表实现。插入和删除操作
分别在链表的两头进行;需要注意的是,我们将front指向头结点,rear指向尾结点(因为链表头可以作插入和删除操作,链表尾只能作插入操作,不好做删除操作)。

(***感觉队列用链式存储更合适一些?***)

struct Node{
		ElementType Data;
		struct Node *Next;
};
struct QNode{ /* 链队列结构 */
		struct Node *rear; /* 指向队尾结点 */
		struct Node *front; /* 指向队头结点 */
};
typedef struct QNode *Queue;
Queue PtrQ;

Python中的队列实现

① 基于 list 实现

我们需要确定列表的哪一端作为队首, 哪一端作为队尾。

下面代码所示的实现假定队尾在列表中的位置为 0。这允许我们使用列表上的插入函数 insert 向队尾添加新元素。用弹出操作pop删除队首的元素(列表的最后一个元素)。

class Queue:
	def __init__(self):
		self.items = []
		
	def isEmpty(self):
		return self.items == []
		
	def enqueue(self, item):
		self.items.insert(0,item)
		
	def dequeue(self):
		return self.items.pop()
		
	def size(self):
		return len(self.items)

对于上面基于 list 的实现方式,入队的效率为 O(n), 出队的效率为 O(1)。入队的效率很低

Python 的内置模块 collections.deque 双端队列可以很高效的向两边追加和删除元素,入队出队的效率都为O(1),我们平时主要用这种实现

② 基于 collections.deque 实现

from collections import deque

myQueue = deque([1, 2])
myQueue = deque.append(3)
nums = deque.popleft()

相关问题


双端队列(Deque)

双端队列和栈,普通队列最大的不同在于,它允许我们在队列的头尾两端都能在 O(1)的时间内进行数据的查看、添加和删除。

Deque 同时拥有栈和队列的特性, 它不需要由那些数据结构强制的 LIFO 和 FIFO 排序。 取决于如何持续添加和删除操作
在这里插入图片描述

双端队列的应用

实现一个长度动态变化的窗口或者连续区间

双端队列的抽象数据结构

队列基本操作主要有:

  • Deque() 创建一个空的新 deque。 它不需要参数, 并返回空的 deque。
  • addFront(item) 将一个新项添加到 deque 的首部。 它需要 item 参数 并不返回任何内容。
  • addRear(item) 将一个新项添加到 deque 的尾部。 它需要 item 参数并不返回任何内容。
  • removeFront() 从 deque 中删除首项。 它不需要参数并返回 item。 deque 被修改。
  • removeRear() 从 deque 中删除尾项。 它不需要参数并返回 item。 deque 被修改。
  • isEmpty() 测试 deque 是否为空。 它不需要参数, 并返回布尔值。
  • size() 返回 deque 中的项数。 它不需要参数, 并返回一个整数

双端队列的实现

可以利用一个双链表实现双端队列。

Python中的双端队列实现

① 基于 List 实现

我们假定 deque 的尾部在列表中的位置为 0。

class Deque:

def __init__(self):
	self.items = []

def isEmpty(self):
	return self.items == []

def addFront(self, item):
	self.items.append(item)

def addRear(self, item):
	self.items.insert(0,item)

def removeFront(self):
	return self.items.pop()

def removeRear(self):
	return self.items.pop(0)

def size(self):
	return len(self.items)

对于上面基于 list 的实现方式, 从前面添加和删除项的效率是O(1), 而从后面添加和删除的效率是 O(n)。

② Python高级容器库 collections.deque

python collections 模块中的 deque

常用方法

d = collections.deque([])  # 初始化一个deque
d.append('a')  # 在最右边添加一个元素,此时 d=deque('a')
d.appendleft('b')  # 在最左边添加一个元素,此时 d=deque(['b', 'a'])
d.extend(['c','d'])  # 在最右边添加所有元素,此时 d=deque(['b', 'a', 'c', 'd'])
d.extendleft(['e','f'])  # 在最左边添加所有元素,此时 d=deque(['f', 'e', 'b', 'a', 'c', 'd'])
d.pop()  # 将最右边的元素取出,返回 'd',此时 d=deque(['f', 'e', 'b', 'a', 'c'])
d.popleft()  # 将最左边的元素取出,返回 'f',此时 d=deque(['e', 'b', 'a', 'c'])
d.rotate(-2)  # 向左旋转两个位置(正数则向右旋转),此时 d=deque(['a', 'c', 'e', 'b'])
d.count('a')  # 队列中'a'的个数,返回 1
d.remove('c')  # 从队列中将'c'删除,此时 d=deque(['a', 'e', 'b'])
d.reverse()  # 将队列倒序,此时 d=deque(['b', 'e', 'a'])

底层代码 (deque使用双向链表很好实现)


[1] 《Problem Solving with Algorithms and Data Structures using Python》
[2] 浙江大学 数据结构_中国大学MOOC(慕课)

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值