学习Python中的数据结构

77 阅读8分钟

编程算法需要与数据打交道,而这些数据往往包含在特定的格式或数据结构中。现在是了解这些创建各种算法时使用的常见数据结构的好时机。数据结构的目的是为了组织信息,使其易于被算法操作。你可能有一个股票观察清单,你可能希望能够按照股息率或市盈率对它们进行排序。另一个例子是一个代表文件夹和文件集合的树状结构,你想在所有这些文件夹中找到一个特定的文件。每个场景都有不同数据结构的相关数据。最常见的需要精通的数据结构是数组、链表、堆栈、队列、树和哈希表。不同的应用将需要不同类型的数据结构来保存算法需要工作的信息。在本教程中,我们将进一步探讨这些主题。


数组

数组是一组项目,每个项目的位置由一个索引或一个键值来确定。一维数组是最基本的数组类型,下图显示了它可能是什么样子。

One Dimensional Array

元素的位置可以用数学表达式来计算,这使得数组元素可以用随机访问的方式直接访问。这意味着,由于每个元素的位置可以直接计算出来,所以不需要为了访问一个项目而导航或遍历数据结构。一个数组的第一个索引元素总是在0的位置。 下面是一个Python中简单的一维数组的例子。

import array

one_dimensional = array.array('i', [3, 6, 9, 12, 15])
for i in range(0, len(one_dimensional)):
    print(one_dimensional[i])
3
6
9
12
15

访问数组中的其他每个元素

import array

one_dimensional = array.array('i', [3, 6, 9, 12, 15])
for i in range(0, len(one_dimensional), 2):
    print(one_dimensional[i])
3
9
15

直接访问一个元素

import array

one_dimensional = array.array('i', [3, 6, 9, 12, 15])

print(one_dimensional[4])
15

数组可以有多个维度。要创建一个二维数组,第一维本身就可以包含数组。访问一个二维数组中的一个项目需要提供两个索引。这里是一个二维数组的示意图,索引为2,1,突出显示。

Multi Dimensional Array

在Python中,你可能更常使用List数据结构,它是一个类似数组的数据类型。在Python中,List和数组的行为方式相似,你可以对它们进行迭代,并在一个特定的索引上存储项目。两者之间的区别在于你可以对它们执行的函数。在Python中使用真正的数组是比较麻烦的,因为你必须导入数组模块并声明一个数组。列表只是Python语法的一部分,所以它们的使用频率更高,并且涵盖了你所需要的大多数用例。真正的数组对于数学函数以及处理大量的数据来说会更好。大多数时候,你可以简单地使用Lists。下面是一些 Python 中的列表的例子。

empty_list = []

list_of_ints = [3, 6, 9]

mixed_list = [2, 'Boo', 3.14]

two_dimensional_list = [[3, 6, 9], [2, 'Boo', 3.14]]

链接列表

链接列表的数据结构是数据元素的线性集合,通常被称为节点。它们类似于数组,但是每个节点都有一个指向列表中下一个元素的字段,与数组不同。有单链表和双链表。这里有几张图显示了这一点。


单链表

一个链接列表的第一项被称为头。每个元素都包含一个字段,指向列表中的下一个项目。链接列表中的最后一个项目指向null,这意味着它是列表的结束。
single linked list


双链表

在一个双链接列表中,每个数据项都有对上一个和下一个项目的引用。
double linked list

Python中的链接列表 (单链接)

下面是一个在 Python 中实现的链表。它使用了两个类。一个用来表示列表的节点,一个用来表示链表本身。这个 **Node**类实现了将被存储在链表中的节点类型。它有一个单一的 **next**字段,表示这是一个单链表。该 **LinkedList**类的字段是 **head**以及一个 **count**字段,用于跟踪列表中的节点数量。

class Node(object):
    def __init__(self, val):
        self.val = val
        self.next = None

    def get_data(self):
        return self.val

    def set_data(self, val):
        self.val = val

    def get_next(self):
        return self.next

    def set_next(self, next):
        self.next = next


class LinkedList(object):
    def __init__(self, head=None):
        self.head = head
        self.count = 0

    def get_count(self):
        return self.count

    def insert(self, data):
        new_node = Node(data)
        new_node.set_next(self.head)
        self.head = new_node
        self.count += 1

    def find(self, val):
        item = self.head
        while (item != None):
            if item.get_data() == val:
                return item
            else:
                item = item.get_next()
        return None

    def delete(self, index):
        if index > self.count:
            return
        if self.head == None:
            return
        else:
            tempIndex = 0
            node = self.head
            while tempIndex < index - 1:
                node = node.get_next()
                tempIndex += 1
            node.set_next(node.get_next().get_next())
            self.count -= 1

    def print_list(self):
        tempnode = self.head
        while (tempnode != None):
            print('Node: ', tempnode.get_data())
            tempnode = tempnode.get_next()

初始化一个链接列表并存储一些值

linkedlist = LinkedList()
linkedlist.insert(3)
linkedlist.insert(6)
linkedlist.insert(9)
linkedlist.insert(12)
linkedlist.insert(15)
linkedlist.print_list()
Node:  15
Node:  12
Node:  9
Node:  6
Node:  3

打印关联列表的计数

print('Number of items in List: ', linkedlist.get_count())
Number of items in List:  5

在链接列表中找到两个Node对象

print('Finding item: ', linkedlist.find(6))
print('Finding item: ', linkedlist.find(9))
Finding item:  <__main__.Node object at 0x03512FD0>
Finding item:  <__main__.Node object at 0x03538028>

删除关联列表中的一个节点

linkedlist.delete(3)
print('Number of items in List: ', linkedlist.get_count())
print('Finding item: ', linkedlist.find(12))
linkedlist.print_list()
Number of items in List:  4
Finding item:  <__main__.Node object at 0x031A8058>
Node:  15
Node:  12
Node:  9
Node:  3


堆栈数据结构

堆栈数据结构是一个元素的集合,它有两个基本的操作,即推和弹。堆栈是后进先出(LIFO)的数据结构,或称后进先出。推入堆栈的最后一个项目就是弹出的第一个项目。堆栈的一个例子是当你在浏览器中使用返回按钮时。当你上网时,浏览器会将每个链接添加到一个堆栈中,以保持它们被访问的顺序。当你点击后退按钮时,最近添加的URL会从堆栈中弹出,然后再重新访问。

Python中的堆栈数据结构

你可以通过使用一个列表来获得在Python中使用堆栈数据结构的特点。

初始化一个堆栈

stack = []

将项目推送(Append)到栈上

stack.append('Tom')
stack.append('Dick')
stack.append('Harry')
stack.append('Bosch')

打印出堆栈

print(stack)
['Tom', 'Dick', 'Harry', 'Bosch']

从堆栈中弹出一个项目

popped = stack.pop()
print(popped)
print(stack)
Bosch
['Tom', 'Dick', 'Harry']

作为一个类的堆栈

你也可以像下面这样做,使用一个用户定义的类来提供堆栈功能。这仍然只是一个使用列表类型的封装器,但现在你有一个实际的推送()方法可以使用。

class Stack:
    def __init__(self):
        self.stack = []

    def __bool__(self):
        return bool(self.stack)

    def __str__(self):
        return str(self.stack)

    def push(self, data):
        self.stack.append(data)

    def pop(self):
        if self.stack:
            return self.stack.pop()
        else:
            raise IndexError('Stack is empty')

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


stack = Stack()
for i in range(5):
    stack.push(i)

print('Initial stack: ' + str(stack))
print('pop(): ' + str(stack.pop()))
print('After pop(), the stack is now: ' + str(stack))
stack.push(7)
print('After push(7), the stack is now: ' + str(stack))
print('The size is: ' + str(stack.size()))
Initial stack: [0, 1, 2, 3, 4]
pop(): 4
After pop(), the stack is now: [0, 1, 2, 3]
After push(7), the stack is now: [0, 1, 2, 3, 7]
The size is: 5

队列数据结构

队列数据结构也支持添加和删除项目,但它使用 FIFO 方法。FIFO 是一种先入先出的方法。一个空的队列如果被添加了一个项目,就会成为列表中的第一个项目。排入更多的项目只是增加了列表的长度。队列在编程中非常常见,因为它们模仿了现实生活中发生的很多事情。你曾经去过机动车管理部门吗?那么你很清楚什么是队列。你走到队伍的末尾(排队),等待大量的时间(排队处理),然后在所有在你前面的人都得到服务后,最终得到服务。在软件中,消息处理是队列的一个常见用途。

Python中的队列数据结构

初始化一个空队列

from collections import deque

queue = deque()

向队列中添加一些项目

queue.append('Monday')
queue.append('Tuesday')
queue.append('Wednesday')
queue.append('Thursday')
queue.append('Friday')

打印出队列

print(queue)
deque(['Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday'])

从队列中弹出项目

popped = queue.popleft()
print(popped)
print(queue)
Monday
deque(['Tuesday', 'Wednesday', 'Thursday', 'Friday'])

哈希表数据结构

一个哈希表基本上是一个关联数组。许多其他编程语言都有关联数组,Python通过字典实现了这种数据结构。这种数据结构使用一个哈希函数将键映射到值。哈希函数使用键来计算槽的索引,并将键映射到一个值。将一个给定的键唯一地映射到一个特定的值的能力是哈希表的一大好处。这使得使用计数器和过滤器的工作变得快速而简单。哈希表的速度也相当快,使它们适合于大型数据集。哈希表不以任何特定的方式排列它们的项目,所以如果需要的话,你需要添加一个排序机制。

Python中的哈希表数据结构

初始化一个新的哈希表

hashone = dict({'firstkey': 1, 'secondkey': 2, 'thirdkey': 'three'})
print(hashone)
{'firstkey': 1, 'secondkey': 2, 'thirdkey': 'three'}

用迭代法创建第二个哈希表

hashtwo = {}
hashtwo['firstkey'] = 1
hashtwo['secondkey'] = 2
hashtwo['thirdkey'] = 3
print(hashtwo)
{'firstkey': 1, 'secondkey': 2, 'thirdkey': 3}

替换哈希表中的一个项目

hashtwo['secondkey'] = 'two'
print(hashtwo)
{'firstkey': 1, 'secondkey': 'two', 'thirdkey': 3}

遍历哈希表以打印键值对

for key, value in hashtwo.items():
    print('key: ', key, ' value: ', value)
key:  firstkey  value:  1
key:  secondkey  value:  two
key:  thirdkey  value:  3

Python数据结构概要

  • 真正的Python数组是对C数组的包装,适合处理相同类型的项目。它们不像Lists那样方便用户。
  • Lists是一种更灵活的数组风格,可以容纳任何类型的数据组合。如果你需要不费吹灰之力地缩小和增加你的列表,它们是更好的选择。
  • 链接列表可能比数组更受欢迎,因为它们更容易和更快地进行重组。
  • 堆栈向右增长,向左收缩,适合于最后进先出的操作。
  • 队列使用先进先出的方法,适合于消息传递、日志记录和其它应用。
  • 哈希表在Python中使用字典来实现,是一种具有明显键值对的关联数组形式。