数据结构是算法的基础。
常见的线性数据结构,包括 线性表,堆栈,队列。
基本特点:
- 线性数据结构中,数据项之间的顺序由添加或删除的顺序决定。 一旦一个数据项被添加, 它相对于前后元素的位置便一直保持不变。
- 线性数据结构有 两端, 有时被称为左右, 某些情况被称为前后, 也可以称为顶部和底部。
诸如此类的数据结构被称为线性数据结构。
线性表(Linear List)
有序 的 线性 结构。
- 表中 元素的个数 称为 线性表的**“长度”**
- 线性表 没有元素 时,称为**“空表”**
- 表的起始位置称为**“表头”,表的结束位置称为“表尾”**
线性表的应用
哪些地方用到了线性表?比如
- 多项式的表示
- 矩阵的表示
- 。。。
线性表的抽象数据结构
- 名称:线性表(List)
- 数据对象集(线性表中存储的数据对象):由 n (n ≥ 0)个元素构成的有序序列( a1, a2,…,an)
- 操作集(线性表支持的基本操作):
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。
线性表的具体实现
线性表的顺序存储(数组)实现
利用 数组 的 连续存储空间顺序 存放线性表的各元素。
顺序存储有什么优点?
- 索引更快
线性表的链式存储(链表)实现
不要求逻辑上相邻的两个元素物理上也相邻;通过“链”建立起数据元素之间的逻辑关系。
链式存储有什么优点?
- 空间使用上更加灵活。
- 插入、删除不需要移动数据元素,只需要修改“链”。
Python中的线性表实现
Python中的列表就是线性表。
广义表
- 广义表是线性表的推广
- 对于线性表而言, n个元素都是基本的单元素;
- 广义表中,这些元素不仅可以是单元素也可以是另一个广义表。
相关问题
堆栈(Stack)
具有一定操作约束 的 线性表 。
- 只能从一端(栈顶, Top)添加 和 取出 元素
- 添加元素的操作,我们叫做 入栈(Push)
- 取出元素的操作,我们叫做 出栈(Pop)
- 后进先出(LIFO, Last In First Out)
堆栈的应用
- 后缀表达式求值
- 函数调用及递归实现
- 深度优先搜索
- 回溯算法
- 。。。
堆栈的抽象数据结构
- 类型名称:堆栈(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 ):删除并返回栈顶元素(重要!!);
堆栈的顺序存储(数组)实现
栈的顺序存储结构通常由 一个一维数组 和 一个记录栈顶元素位置的变量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的空队列;
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 ):将队头数据元素从队列中删除并返回(重要!!)。
队列的顺序存储(数组)实现
队列的顺序存储结构通常由 一个一维数组 和 一个记录队列头元素位置的变量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
常用方法
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(慕课)