机器学习1000_Python基础_数据结构与算法_简单数据结构

我们先来看四种简单但是重要的数据结构:栈(stack)、队列(queue)、双端队列(deque)、 列表(list)。它们的区别在于元素如何加入集合中和从集合中移除。这些数据结构,也称为线性数据结构。
一、 栈
1. 排序原则:后进先出(LIFO)
从图中可以看出,第一个被加入栈中的元素[4]处于栈的底端,需要上面的元素[8.4]、[True]、['dog']先出去,[4]才能出去。
2. 实现抽象的栈数据结构
一个栈stack应该要有以下的函数实现其功能
1)Stack() 创建一个空的栈。不需要参数、并返回一个空的栈。
2)Push(item) 增添一个新的元素到栈的顶端。需要增添的新元素作为参数,无返回值。
3)Pop( ) 从栈的顶端移除一个元素。不需要参数,并返回被移除的元素。
4) Peek( ) 返回栈的顶端的元素,但不移除它。不需要参数。
5) is_empty( ) 查看栈是否为空。不需要参数,返回布尔值。
6)size( ) 返回栈中元素的数目。不需要参数,返回一个整数。
class Stack:
    def __init__(self):
        self.items = []    # 使用Python里的List作为栈容器
    def is_empty(self):
        return self.items == []
    def push(self,item):
        self.items.append(item)    # list.append(x) 添加元素到列表末端 Add an item to the end of the list
    def pop(self):
        return self.items.pop()    # list.pop( ) 移除和返回列表中最后一个元素 Removes and returns the last item in the list
    def peek(self):
        return self.items[len(self.items)-1]
    def size(self):
        return len(self.items)
下面是对这个类的测试效果

3. 例子:十进制整数转换为二进制至十六进制整数
我们首先来看十进制转换为二进制的原理,如将10转换为二进制则是1010,转换过程为
10//2 = 5 rem(余数)= 0
5//2 = 2 rem = 1
2//2 = 1 rem = 0
1//2 = 0 rem = 1
取反则为1010
这个过程和我们栈的结构相近,我们可用栈按照0101的顺序储存余数,然后按后进先出取出,则为1010.
 
import stack

def base_converter(dec_number,base):
    digits="0123456789ABCDEF"    # 用来查找替代的数值
    rem_stack = stack.Stack()    # 新建栈

    while dec_number>0:
        rem = dec_number % base    # 求余,如5%2=1
        rem_stack.push(rem)    # 储存余数
        dec_number = dec_number // base    # 除法向下取整,如5//2=2

    new_string = ""
    while not rem_stack.is_empty():
        # 按后进先出的顺序取出栈中的元素,如取出的元素为15,则替代为十六进制中的F
        new_string = new_string +digits[rem_stack.pop()]
    return new_string

print(base_converter(10,2))
print(base_converter(25,16))

二、 队列(Queue)
1. 排序原则:先进先出(FIFO)

从图中可以看出,队列就像是我们现实生活中的等车排队一样,第一个来排队的人第一个上车。
2. 实现抽象的队列数据结构
一个队列Queue应该要有以下的函数实现其功能:
1) Queue( ) 创建一个空的队列。不需要参数、并返回一个空的队列。
2)enqueue(item) 增添一个新的元素到队列后面。需要增添的新元素作为参数,无返回值。
3)dequeue( ) 移除队列前面的元素。不需要参数,并返回被移除的元素。
4) is_empty( ) 查看队列是否为空。不需要参数,返回布尔值。
5)size( ) 返回队列中元素的数目。不需要参数,返回一个整数。
我们现在用Python来完善一个具备上述功能的Queue类:
 
class Queue:
    def __init__(self):
        self.items = []
    def is_empty(self):
        return self.items == []
    def enqueue(self,item):
        # list.insert(i, x) Insert an item at a given position.
        self.items.insert(0,item)    # 添加元素到列表前端
    def dequeue(self):
        return self.items.pop()
    def size(self):
        return len(self.items)
下面是对这个类的测试效果
3. 例子:击鼓传花
我们小时候想必都玩过一种叫做“击鼓传花”的游戏:所有人围成一个圈,鼓点开始的时候,花从其中一个人手里传到下一个人手里,当鼓点停的时候,手里有花的玩家会被淘汰,然后剩下的人继续比赛。我们就使用队列的结构来模拟这样的游戏。
游戏:我们有六个人,每计数1次,队列最前面的人排到队列最后面,鼓点(计数7次)停止时,新队列最前面的人被淘汰。一直到剩下一个人为止。
 
import queue

def hot_potato(name_list,num):
    sim_queue = queue.Queue()
    for name in name_list:
        sim_queue.enqueue(name)
    while sim_queue.size()>1:
        for i in range(num):
            sim_queue.enqueue(sim_queue.dequeue())    # 传递7次以后组成新的队列
        print(sim_queue.dequeue())    # 手里有花的人被淘汰
    return  sim_queue.dequeue()    #返回剩下的人

print(hot_potato(['Bill','David','Susan','Jane','Kent','Brad'],7))

三、 双端队列(Deque)
1. 排序原则:两端进出

从图中可以看出,双端队列比队列要灵活,可以从前面也可以从后面进行添加、删除等操作。

2. 实现抽象的双端队列数据结构
一个队列Deque应该要有以下的函数实现其功能:
1)Deque( ) 创建一个空的双端队列。不需要参数,并返回一个空的双端队列。
2)add_front(item) 增添一个新的元素到双端队列前面。需要增添的新元素作为参数,无返回值。
3)remove_front ( ) 移除双端队列前面的元素。不需要参数,并返回被移除的元素。
4)remove_rear ( ) 移除双端队列后面的元素。不需要参数,并返回被移除的元素。
5)is_empty( ) 查看双端队列是否为空。不需要参数,返回布尔值。
6)size( ) 返回双端队列中元素的数目。不需要参数,返回一个整数。
我们现在用Python来完善一个具备上述功能的Dueue类:
 
class Deque:
    def __init__(self):
        self.items = []
    def is_empty(self):
        return  self.items == []
    def add_front(self,item):
        self.items.append(item)
    def add_rear(self,item):
        self.items.insert(0,item)
    def remove_front(self):
        return self.items.pop()
    def remove_rear(self):
        return self.items.pop(0)
    def size(self):
        return len(self.items)
下面是对这个类的测试效果
3. 例子:回文(指顺读和倒读都一样的词语)
有些英文单词如radar,toot,madam无论顺读还是倒读都一样,正如一些汉语词汇如“我为人人,人人为我”一样。我们使用双端队列来实现回文的检测。
 
import deque

def pal_checker(a_string):
    char_deque = deque.Deque()
    for ch in a_string:
        char_deque.add_rear(ch)    # 拆分字符串放入双端队列中

    still_equal = True

    while char_deque.size()>1 and still_equal:
        first = char_deque.remove_front()    # 去掉双端队列前面的一个元素
        last = char_deque.remove_rear()    # 去掉双端队列后面的一个元素
        if first != last:    # 判断去掉的前后元素是否相同
            still_equal = False
    return  still_equal

print(pal_checker('helloworld'))
print(pal_checker('radar'))
print(pal_checker('少生孩子多种树'))
print(pal_checker('我为人人,人人为我'))

四、 无序列表——链表(Linked Lists)
1. 排序原则:无序,但是有相对位置
从图中可以看出,无序列表具有相对位置,[54]是head,[31]是end, 但是在物理层它们的位置没有顺序的限制。无序列表和前三种数据结构最大的不同在于 可以往无序列表中的任意位置添加和删除元素
2. 一个无序列表List应该要有以下的函数实现其功能:
1)List( ) 创建一个空的列表。不需要参数,并返回一个空的列表。
2)add(item) 增添一个新的元素到列表。需要增添的新元素作为参数,无返回值。
3)remove(item) 移除列表中的元素。 需要参数。 假设元素已经存在于列表中。
4)search (item) 查找列表中的元素。 需要参数 ,并返回布尔值。
5)is_empty( ) 查看列表是否为空。不需要参数,返回布尔值。
6)size( ) 返回列表中元素的数目。不需要参数,返回一个整数。
7)append(item) 在列表末尾增加一个新的元素作为集合中的最后一个元素。需要参数,无返回值。假设元素未存在于列表中。
8)index(item) 返回列表中元素的位置。需要参数,返回一个索引号。假设元素已经存在于列表中。
9)insert(pos,item) 在列表中的指定位置插入。需要参数,无返回值。假设元素未存在于列表中。
10)pop( ) 移除列表中末尾的。不需要参数,返回被移除的元素。假设列表至少有一个元素。
11)pop(pos) 移除列表中指定位置的元素。需要参数,返回被移除的元素。假设列表中存在该元素。
我们现在用Python来完善一个具备上述功能的UnorderedList类, 在此之前,我们先实现一个Node(节点)类:
 
class Node:
    def __init__(self,init_data):
        self.data = init_data
        self.next = None

    def get_data(self):
        return self.data

    def get_next(self):
        return self.next

    def set_data(self, new_data):
        self.data = new_data

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

temp = Node(93)
print(temp.get_data())
Node类的原理如下图所示
Node由两部分组成,一部分是当前位置的元素data,另一部分是指向下一个位置元素的next(类似C语言中的指针)。
为何我们会先引入Node类?因为Node类是构成UnorderedList类的基本元素。正因为Node有指向下一个位置的部分,只要我们将next指向的位置改变,就可以修改列表中的任意元素。比如,原本93指向的下一个元素是4,我们只需要将93指向5,然后5指向4,这样就可以在93和4之间添加一个新元素5。这就是链表结构的奇妙之处。
接下来我们用Python来完善UnorderedList类:
首先,我们的链表要有一个首元素,在链表内还没有元素的时候,首元素为空。
 
class UnorderedList:
    def __init__(self):
        self.head = None
其次,一个判断是否空链表的函数,只需要检查首元素。
 
def is_empty(self):
    return self.head == None
再次,一个为链表添加元素的函数。这里用到我们的之前定义的Node类,正如我们之前所举的例子:原本93指向的下一个元素是4,我们只需要将93指向5,然后5指向4,这样就可以在93和4之间添加一个新元素5。
这里我们只列出add函数,从首元素的位置为链表增添元素。若想要实现从指定位置插入元素,则可以按照相同的原理实现insert函数。
 
def add(self,item):
    temp = node.Node(item)
    temp.set_next(self.head)
    self.head = temp
然后,我们定义一个search函数实现查找功能。这里的查找方式为最常见的顺序查找法。
 
def search(self,item):
    current = self.head
    found = False
    while current != None and not found:
        if current.get_data() == item:
            found = True
        else:
            current = current.get_next()
    return  found
最后我们定义一个remove函数实现删除元素的功能。删除分为两部分,一部分是查找,另一部分是删除。删除类似于添加,如要删除93,5,4中的5,则需要将93指向5改成93指向4。
 
def remove(self,item):
    current = self.head
    previous = None
    found = False
    while not found:
        if current.get_data() == item:
            found = True
        else:
            previous = current
            current = current.get_next()
    if previous == None:    # 如果删除的是首元素,则设置下一个元素为首元素
        self.head = current.get_next()
    else:    # 如果删除的不是首元素,则前一个元素指向后一个元素(跳过了被删除的当前元素)
        previous.set_next(current.get_next())
到目前为止,我们基本实现了简单的链表结构。

五、 有序列表 (Ordered Lists)
1. 排序原则:数值按照从大到小或者从小到大的顺序在列表中排列
从图中可以看出,有序列表和无序列表相近,所不同的是列表中的元素按照一定的顺序排列。
2. 实现一个简单的Ordered List类
有序列表和无序列表的实现相近,但是在实现查找功能有微略不同。比如一个升序列表,查找5时,当查找到第一个大于5的元素时就停止了,因为后面的元素均大于5。因此,有序列表的好处在于其查找速度较快。
但是,有序列表添加元素的速度较慢。试想一下我们往一个有序列表[1,2,4,5]中添加3,则需要查找到4的位置,然后将2指向3,3指向4。与无序列表往首元素开始添加多了一个查找的过程。
 
class OrderedList:
    def __init__(self):
        self.head = None

    def search(self,item):
        current = self.head
        found = False
        stop = False
        while current != None and not found and not stop:
            if current.get_data() == item:
                found = True
            else:
                if current.get_data() > item:
                    stop = True
                else:
                    current = current.get_next()
        return found

    def add(self, item):
        current = self.head
        previous = None
        stop = False
        while current != None and not stop:
            if current.get_data()>item:
                stop = True
            else:
                previous = current
                current = current.get_next()
                
        temp = node.Node(item)
        if previous == None:
            temp.set_next(self.head)
            self.head = temp
        else:
            temp.set_next(current)
            previous.set_next(temp)
参考资料:Problem Solving with Algorithms and Data Structures
欢迎知乎栏目_Python编程与机器学习
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值