python 常见的算法 & 数据结构

常见的算法 & 数据结构

时间复杂度

  • 评判规则:量化算法执行步骤的数量
  • 最重要的项 :时间复杂度表达式中最有意义的项(如果最高阶项存在,则省去最高阶项前面的系数。)
  • 大O记法:就是使用时间复杂度衡量算法的好坏的表现形式
分析算法时,需要考虑的
  • 算法完成工作最少需要多少基本操作,即最优时间复杂度

  • 算法完成工作最多需要多少基本操作,即最坏时间复杂度

  • 算法完成工作平均需要多少基本操作,即平均时间复杂度

    对于最坏时间复杂度,提供了一种保证,表明算法在此种程度的基本操作中一定能完成工作。

    ​ 因此,我们主要关注算法的最坏情况,亦即最坏时间复杂度。

时间复杂度的基本计算规则
  1. 基本操作,即只有常数项,认为其时间复杂度为O(1)
  2. 顺序结构,时间复杂度按加法进行计算
  3. 循环结构,时间复杂度按乘法进行计算
  4. 分支结构,时间复杂度取最大值
  5. 判断一个算法的效率时,往往只需要关注操作数量的最高次项,其它次要项和常数项可以忽略
  6. 在没有特殊说明时,我们所分析的算法的时间复杂度都是指最坏时间复杂度

img

算法是解决实际问题而设计的,数据结构是算法处理问题的载体。

  • timeit 模块︰该模块可以用来测试一段python代码的执行速度/时长。
  • Timer类该类是 timeit 模块中专门用于测量python代码的执行速度/时长的。原型为: class
    timeit.Timer(stmt=‘pass’, setup=‘pass’)
    • stmt 参数:表示即将进行测试的代码块语句。
    • setup:运行代码块语句时所需要的设置。
    • timeit函数: timeit.Timer.timeit(number=100000),该函数返回代码块语句执行number次的平均耗时。
from timeit import Timer

def text1():
    list = []
    for i in range(100):
        list.append(i)
    return list

def text2():
    list = [i for i in range(100)]
    return list

if __name__ == '__main__':
    timer1 = Timer(stmt='text1', setup='from __main__ import text1')
    t1 = timer1.timeit(100)

    timer2 = Timer(stmt='text2', setup='from __main__ import text2')
    t2 = timer2.timeit(100)

    print(t1, t2)

特性:栈(stacks)是一种只能通过访问其一端来实现数据存储与检索的线性数据结构,具有”后进先出“的特征

应用︰每个web浏览器都有一个返回按钮。当你浏览网页时,这些网页被放置在一个栈中(实际是网页的网址)。你现在查看的网页在顶部,你第一个查看的网页在底部。如果按 “返回” 按钮,将按相反的顺序浏览刚才的页面。

  • Stack() 创建一个空的新栈。它不需要参数,并返回一个空栈。
  • push(item) 将一个新项添加到栈的顶部。它需要item做参数并不返回任何内容。
  • pop() 从栈中删除顶部项。它不需要参数并返回item。栈被修改。
  • peek() 从栈返回顶部项,但不会删除它。不需要参数。不修改栈。
  • isEmpty() 测试栈是否为空。不需要参数,并返回布尔值。
  • size() 返回栈中的item数量。不需要参数,并返回一个整数。
# 栈 先进后出
class Stack():
    def __init__(self):
        self.items = []

    def push(self, item):  # 向栈里添加元素
        self.items.append(item)

    def pop(self):  # 从栈底取元素
        return self.items.pop()

    def peek(self):  # 返回栈顶元素下标
        return len(self.items) - 1

    def isEmpty(self):  # 判断是否为空
        return self.items == []

    def size(self):  # 大小
        return len(self.items)
    
s = Stack()
s.push(1)
s.push(2)
s.push(3)
print(s.pop())
print(s.pop())
print(s.pop())

队列

概念:队列是项的有序结合,其中添加新项的一端称为队尾,移除项的一端称为队首。当一个元素从队尾进入队列时,一直向队首移动,直到它成为下一个需要移除的元素为止。最近添加的元素必须在队尾等待。集合中存活时间最长的元素在队首,这种排序成为 FIFO,先进先出,也被成为先到先得。。python库from collections import deque可以实现。

  • Queue() 创建一个空的新队列。它不需要参数,并返回一个空队列。
  • enqueue(item) 将新项添加到队尾。它需要item作为参数,并不返回任何内容。
  • dequeue() 从队首移除项。它不需要参数并返回item。队列被修改。
  • isEmpty () 查看队列是否为空。它不需要参数,并返回布尔值。
  • size() 返回队列中的项数。它不需要参数,并返回一个整数。
# 队列  先进先出
class Queue():
    def __init__(self):
        self.items = []

    def enqueue(self, item):  # 进去
        self.items.insert(0, item)

    def dequeue(self):  # 取出
        return self.items.pop()

    def isEmpty(self):  # 是否为空
        return self.items == []

    def size(self):  # 大小
        return len(self.items)


k = Queue()
k.enqueue(1)
k.enqueue(2)
k.enqueue(3)
print(k.dequeue())
print(k.dequeue())
print(k.dequeue())

两个队列生成一个栈
q1 = Queue()
q2 = Queue()
alist = [1,2,3,4,5]
#将数据加入队列
for i in alist:
    q1.enqueue(i)

while True:  
    
    while q1.size() > 1:
        item = q1.dequeue()
        q2.enqueue(item)
    print(q1.dequeue())
    
    q1,q2 = q2,q1
    
    if q1.size() == 0:
        break
烫手的山芋
  • 案例:烫手的山芋

    • 烫手山芋游戏介绍:6个孩子围城一个圈,排列顺序孩子们自己指定。第一个孩子手里有一个烫手的山芋,需要在计时器计时 1秒 后将山芋传递给下一个孩子,依次类推。规则是,在计时器每计时 7 秒时,手里有山芋的孩子退出游戏。该游戏直到剩下一个孩子时结束,最后剩下的孩子获胜。请使用队列实现该游戏策略,排在第几个位置最终会获胜。

    • 分析:
      在一轮游戏中山芋会被传递6次
      山芋传递的次数不受孩子个数的影响
      山芋传递六次后一轮游戏结束,淘汰一个孩子游戏继续
      队列∶先进先出,只可以从对头取元素,从队尾添加元素。
      准则:保证队头孩子手里面有山芋(谁手里有山芋谁作为队头)
      	方便删除元素。最终7秒到的时候需要将手里有山芋的孩子从队列中剔除。
      

      在这里插入图片描述

# 烫手的山芋
kids = ['A', 'B', 'c', 'D', 'E', 'F']
queue = Queue()
for kid in kids:
    queue.enqueue(kid)  # 将6个孩子添加到了队列中
while queue.size() > 1:
    # 山芋传递的次数
    for i in range(6):
        # 让队头的孩子出队列在入队列
        kid = queue.dequeue()
        queue.enqueue(kid)
    # 当6次循环结束后,说明山芋被传递了6次,说明一轮游戏结束
    # —轮游戏结束后,将队头孩子淘汰即可
    queue.dequeue()
# 当while循环结束后,游戏结束,队列中仅剩的孩子就是获胜者
print('获胜者:', queue.dequeue())
  • 使用栈实现队列
    • 使用两个站如何实现队列
      • 使用两个栈实现先进先出的特性
双端队列

一.双端队列(Deque)

- 概念:deque(也称为双端队列)是与队列类似的项的有序集合。它有两个端部,首部和尾部,并且项在集合中保持不变。

- 特性:deque 特殊之处在于添加和删除项是非限制性的。可以在前面或后面添加新项。同样,可以从任一端移除现有项。在某种意义上,这种混合线性结构提供了单个数据结构中的栈和队列的所有能力。

- 注意:即使 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中的项数。它不需要参数,并返回一个整数。
    
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)

内存

  • 计算机的作用

    • 存储和运算二进制的数据
  • 计算机如何实现 1+ 1的操作?

    • 将 1 加载到计算机内存中,然后基于计算机的加法寄存器对指定内存中存储的数据进行加法运算。
  • 变量的概念

    • 本质讲,变量指的就是计算机中的某一块内存空间。

    • 内存空间有两个固有的属性

      • 地址︰使用16进制的数表示

        • 作用:方便cup寻址。门牌号。
      • 大小: bit, byte, kb , mb , gb , tb

        • 决定该块内存存储的数值的范围
  • 理解 a=10 的内存图(引用,指向)

    • 引用︰就是变量,通常将变量表示的就是一块内存空间的地址。
    • 指向︰如果一个变量或者引用存储表示了某块内存空间的地址后,则该变量或引用指向了该块内存空间。
  • 不同数据占用内存空间的大小

    • bit (位):1 bit 只能存储一位二进制的数
    • byte(字节):8 bit
  • 给相同的数据类型的数据开辟固有大小的内存空间

    • 整形数据:4 byte
    • 浮点型:4, 8 byte

顺序表

在这里插入图片描述

  • 多数据类型顺序表的内存图(内存非连续开辟 )

在这里插入图片描述

  • 顺序表的弊端:顺序表的结构需要预先知道数据大小来申请连续的存储空间,而进行数据扩充时又需要进行数据的搬迁(时间复杂度是相当高的)

链表

相对于顺序表,链表结构可以充分利用计算机内存空间,实现灵活的内存动态管理且进行扩充是不需要进行数据搬迁。

链表(Linked list) 是一种常见的基础数据结构,是一种线性表,但是不像 顺序表一样连续存储数据,而是每一个节点(数据存储单元)里存放下一个节点的信息(即地址),非连续开辟数据空间并连到一起

  • is_empty(): 链表是否为空

  • length ( ): 链表长度

  • travel ( ): 遍历整个链表

  • add(item): 链表头部添加元素

  • append(item): 链表尾部添加元素

  • insert(pos, item): 指定位置添加元素

  • remove(item): 删除节点

  • search(item): 查找节点是否存在

# 节点的封装
class Node():
	def __init__(self, item):
		self.item = item
		self.next = None
        
# 链表的封装
class Link():
	def __init__(self):  # 构建一个空链表
		self._head = None  # _head永远指向链表中的第一个节点,None表示没有节点
	
    def add(self, item):   # 向链表的头部添加节点(inster(0, item))
        node = Node(item)
        node.next = self._head
        self._head = node     # 向头部添加元素
        
    def travel(self):   # 遍历
        cur = self._head
        while cur:
            print(cur.item)
            cur = cur.next
            
    def is_empty(self):
        return self._head == None   # 如果链表的头部为空,则链表为空
    
    def length(self):    # 获取链表的长度
        cur = self._head
        count = 0
        while cur:
            count += 1
            cur = cur.next
        print(count)
        
    def append(self, item):   # 往最后添加一个节点
        node = Node(item)
        cur = self._head
        if cur == None:
            cur = node
        pre = None   # pre 指向为 cur 的 前一位
        while cur:
            pre = cur
            cur = cur.next
        # 循环体结束后,pre成为最后一个节点。
        pre.next = node  # 添加成功
        
    def search(self, item):  # 查询
        cur = self._head
        find = False
        while cur:
            if cur.item == item:
                find = True
                break
            else:
                cur.next
        return find
        
    def reverse(self):  # 链表倒序
        pre = self._head
        cur = pre.next
        node_text = cur.next

        pre.next = None
        
        while True:
            cur.next = pre
            pre = cur
            cur = node_text
            if node_text != None:
                node_text = node_text.next
            else:
                break
        self._head = pre
        
    def remove(self, item):  # 删除节点
        pre = None
        cur = self._head
        if cur.item == item:
            self._head = cur.next
            return
        else:
            while cur is not None:
                pre = cur
                cur = cur.next
                if cur.item == item:
                    pre.next = cur.next
                    return
                
    def insertTo(self,item,index):  # 插入
        cur = self._head
        ex = 0
        node = Node(item)
        #插入到第一个节点位置
        if index <= 0:
            self.add(item)
        #插入到最后一个节点位置
        elif index >= self.length():
            self.append(item)
        else:
            while cur is not None:
                pre = cur 
                cur = cur.next
                #此处插入的一定不是第一个节点和最后一个节点位置,因此index要减1
                if ex == index-1:
                    node.next = cur
                    pre.next = node
                    break
                ex += 1

在这里插入图片描述

在这里插入图片描述

链表排序

思路:

  • 3,8,5,7,6,将这几个值封装到5个节点中,首先将3对应的节点插入到链表中。然后插入后序节点的时候,都需要先进行判断将节点合适的位置找到,然后将其插入到合适的为中。
两个队列生成一个链表
# 两个队列生成一个链表
items = [1,2,3,4,5,6,7]
q1 = Queue()
q2 = Queue()
for item in items:
    q1.enqueue(item)
while True:
    while q1.size() > 1:
        item = q1.dequeue()
        q2.enqueue(item)
    print(q1.dequeue())
    q1, q2 = q2, q1
    if q1.size() == 0:
        break
数组和链表的区别
1.链表是什么

链表是一种上一个元素的引用指向下一个元素的存储结构,链表通过指针来连接元素与元素;

链表是线性表的一种,所谓的线性表包含顺序线性表链表,顺序线性表是用数组实现的,在内存中有顺序排列,通过改变数组大小实现。而链表不是用顺序实现的,用指针实现,在内存中不连续。意思就是说,链表就是将一系列不连续的内存联系起来,将那种碎片内存进行合理的利用,解决空间的问题。

所以,链表允许插入和删除表上任意位置上的节点,但是不允许随即存取。链表有很多种不同的类型:单向链表、双向链表及循环链表。

2.单向链表

img

单向链表包含两个域,一个是信息域,一个是指针域。也就是单向链表的节点被分成两部分,一部分是保存或显示关于节点的信息,第二部分存储下一个节点的地址,而最后一个节点则指向一个空值。

3.双向链表

img

从上图可以很清晰的看出,每个节点有2个链接,一个是指向前一个节点(当此链接为第一个链接时,指向的是空值或空列表),另一个则指向后一个节点(当此链接为最后一个链接时,指向的是空值或空列表)。意思就是说双向链表有2个指针,一个是指向前一个节点的指针,另一个则指向后一个节点的指针。

# coding=utf-8
# 双向链表


class Node:
    """节点"""
    def __init__(self, item):
        self.item = item
        self.prev = None
        self.next = None


class DLinkList:
    """双向链表"""
    def __init__(self):
        self._head = None

    def is_empty(self):
        """判断链表是否为空"""
        return self._head is None

    def length(self):
        """获取链表长度"""
        if self.is_empty():
            return 0
        else:
            cur = self._head
            count = 1
            while cur.next is not None:
                count += 1
                cur = cur.next

            return count

    def travel(self):
        """遍历链表"""
        print("↓↓" * 10)
        if self.is_empty():
            print("")

        else:
            cur = self._head
            print(cur.item)
            while cur.next is not None:
                cur = cur.next
                print(cur.item)
        print("↑↑" * 10)

    def add(self, item):
        """链表头部添加节点"""
        node = Node(item)
        if self.is_empty():
            self._head = node
        else:
            cur = self._head

            node.next = cur
            cur.prev = node
            self._head = node

    def append(self, item):
        """链表尾部添加节点"""
        node = Node(item)
        if self.is_empty():
            self._head = node
        else:
            cur = self._head
            # 遍历找到最后一个节点
            while cur.next is not None:
                cur = cur.next

            # 在尾节点添加新的节点
            cur.next = node
            node.prev = cur

    def insert(self, pos, item):
        """指定位置添加"""
        # 头部添加
        if pos <= 0:
            self.add(item)

        # 尾部添加
        elif pos > (self.length() - 1):
            self.append(item)

        # 其他位置添加
        else:
            node = Node(item)

            cur = self._head
            cur_pos = 0
            while cur.next is not None:
                if cur_pos == (pos - 1):
                    # 与下一个节点互相指向
                    node.next = cur.next
                    cur.next.prev = node
                    # 与上一个节点互相指向
                    cur.next = node
                    node.prev = cur
                cur_pos += 1
                cur = cur.next

    def remove(self, item):
        """删除节点"""
        if self.is_empty():
            return
        else:
            cur = self._head
            # 删除首节点
            if cur.item == item:
                self._head = cur.next
                cur.next.prev = None

            # 删除其他节点
            else:
                while cur.next is not None:
                    if cur.item == item:
                        # 删除之前:1 ←→ [2] ←→ 3
                        # 删除之后:1 ←→ 3
                        cur.prev.next = cur.next
                        cur.next.prev = cur.prev
                    cur = cur.next

                # 删除尾节点
                if cur.item == item:
                    cur.prev.next = None


    def search(self, item):
        """查找节点是否存在"""
        if self.is_empty():
            return -1
        else:
            cur = self._head
            cur_pos = 0
            while cur.next is not None:
                if cur.item == item:
                    return cur_pos

                cur_pos += 1
                cur = cur.next

            if cur_pos == (self.length() - 1):
                return -1


if __name__ == "__main__":
    ll = DLinkList()
    ll.add(1)       # 1
    ll.add(2)       # 2 1
    ll.append(3)    # 2 1 3
    ll.insert(2, 4) # 2 1 4 3
    ll.insert(4, 5) # 2 1 4 3 5
    ll.insert(0, 6) # 6 2 1 4 3 5
    print("length:", ll.length())   # 6
    ll.travel()                 # 6 2 1 4 3 5
    print("search(3)", ll.search(3))
    print("search(4)", ll.search(4))
    print("search(10)", ll.search(10))
    ll.remove(1)
    print("length:", ll.length())
    ll.travel()
    print("删除首节点 remove(6):")
    ll.remove(6)
    ll.travel()
    print("删除尾节点 remove(5):")
    ll.remove(5)
    ll.travel()
4.循环链表

img

循环链表就是首节点和末节点被连接在一起。循环链表中第一个节点之前就是最后一个节点,反之亦然。

# coding=utf-8
# 单向循环链表


class Node:
    """节点"""
    def __init__(self, item):
        self.item = item
        self.next = None

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


class SinCycLinkedList:
    """单向循环链表"""
    def __init__(self):
        self._head = None

    def is_empty(self):
        """判断链表是否为空"""
        return self._head is None

    def length(self):
        """链表长度"""
        if self.is_empty():
            return 0
        count = 1
        cur = self._head
        while cur.next != self._head:
            # print("cur", cur.item)
            count += 1
            cur = cur.next
        return count

    def travel(self):
        """遍历"""
        if self.is_empty():
            return

        cur = self._head
        print(cur.item)
        while cur.next != self._head:
            cur = cur.next
            print(cur.item)

    def add(self, item):
        """在头部添加一个节点"""
        node = Node(item)
        if self.is_empty():
            self._head = node
            node.next = self._head
        else:
            node.next = self._head
            cur = self._head
            while cur.next != self._head:
                cur = cur.next

            cur.next = node
            self._head = node

    def append(self, item):
        """在尾部添加一个节点"""
        node = Node(item)
        if self.is_empty():
            self._head = node
            node.next = self._head
        else:
            cur = self._head
            # print(type(cur), cur.item, cur.next)
            while cur.next != self._head:
                cur = cur.next

            # print(cur.item)
            cur.next = node
            node.next = self._head

    def insert(self, pos, item):
        """指定位置pos添加节点"""
        if pos <= 0:
            self.add(item)
        elif pos > (self.length() - 1):
            self.append(item)
        else:
            node = Node(item)
            cur = self._head
            cur_pos = 0
            while cur.next != self._head:
                if (pos - 1) == cur_pos:
                    node.next = cur.next
                    cur.next = node
                    break
                cur_pos += 1
                cur = cur.next

    def remove(self, item):
        """删除一个节点"""
        if self.is_empty():
            return

        pre = self._head
        # 删除首节点
        if pre.item == item:
            cur = pre
            while cur.next != self._head:
                cur = cur.next

            cur.next = pre.next     # 删除首节点(跳过该节点)
            self._head = pre.next   # 重新指定首节点

        # 删除其他的节点
        else:
            cur = pre
            while cur.next != self._head:
                if cur.next.item == item:
                    cur.next = cur.next.next
                cur = cur.next

    def search(self, item):
        """查找节点是否存在"""
        if self.is_empty():
            return -1

        cur_pos = 0
        cur = self._head
        if cur.item == item:
            return cur_pos

        while cur.next != self._head:
            if cur.item == item:
                return cur_pos
            cur_pos += 1
            cur = cur.next

        if cur_pos == self.length() - 1:
            return -1


if __name__ == "__main__":
    ll = SinCycLinkedList()
    ll.add(1)       # 1
    ll.add(2)       # 2 1
    # ll.travel()
    ll.append(3)    # 2 1 3
    ll.insert(2, 4) # 2 1 4 3
    ll.insert(4, 5) # 2 1 4 3 5
    ll.insert(0, 6) # 6 2 1 4 3 5
    print("length:", ll.length())        # 6
    ll.travel()                           # 6 2 1 4 3 5
    print("search(3)", ll.search(3))     # 4
    print("search(7)", ll.search(7))     # -1
    print("search(6)", ll.search(6))    # 0
    print("remove(1)")
    ll.remove(1)
    print("length:", ll.length())       # 6 2 4 3 5
    print("remove(6)")
    ll.remove(6)
    ll.travel()
5.数组和链表的区别?

不同:链表是链式的存储结构;数组是顺序的存储结构

链表通过指针来连接元素与元素,数组则是把所有元素按次序依次存储。

链表的插入删除元素相对数组较为简单,不需要移动元素,且较为容易实现长度扩充,但是寻找某个元素较为困难;

数组寻找某个元素较为简单,但插入与删除比较复杂,由于最大长度需要再编程一开始时指定,故当达到最大长度时,扩充长度不如链表方便。
相同:两种结构均可实现数据的顺序存储,构造出来的模型呈线性结构

6.链表的应用、代码实践

约瑟夫问题:

传说在公园1世纪的犹太战争中,犹太约瑟夫是公元一世纪著名的历史学家。在罗马人占领乔塔帕特后,39 个犹太人与约瑟夫及他的朋友躲到一个洞中,39个犹太人决定宁愿死也不要被敌人俘虏,于是决定了一个流传千古的自杀方式,41个人排成一个圆圈,由第1个人开始报数,每报到第3人该人就必须自杀,然后再由下一个人重新报数,直到所有人都自杀身亡为止。然而约瑟夫和他的朋友并不想遵从这个约定,约瑟夫要他的朋友先假装遵从,他将朋友与自己安排在第_个和第_个位置,于是逃过了这场死亡游戏,你知道安排在了第几个嘛?

针对以上问题,使用单向循环链表的方式求解:

二叉树

二叉树的插入
class Node():
	def __init__(self, item):
		self.item = item
		self.left = None   # 指向该节点的左节点
		self.right = None  # 指向该节点的右节点

class Tree():
    def __init__(self):  # 构建一个空树
        self.root = None
        
    def insert(self,item):    # 向树中插入一个节点
        node = Node(item)
        # 如果树为空
        if self.root == None:
            self.root = node
            return
        # 树不为空
        cur = self.root
        a = [cur]
        while True:
            b = a.pop(0)
            if b.left != None:
                a.append(b.left)
            else:
                b.left = node
                break
            if b.right != None:
                a.append(b.right)
            else:
                b.right = node
                break

二叉树的遍历
# 广度遍历
def travel(self):
    cur = self.root
    q = [cur]
    while q:
        n = q.pop(0)
        print(n.item)
        if n.left != None:
            q.append(n.left)
            if n.right != None:
                q.append(n.right)

tree = Tree()
tree.insert(1)
tree.insert(2)
tree.insert(3)
tree.insert(5)
tree.insert(6)

tree.travel()

# 深度遍历
# 参数root表示的是子树的根节点,需要给递归调用的forWord传入不同的子树的根节点
    def forword(self, root):  # 前
        if root == None:
            return
        print(root.item)
        # print(root.left.item)
        self.forword(root.left)
        # print(root.right.item)
        self.forword(root.right)

    def middle(self, root):  # 中
        if root == None:
            return
        self.middle(root.left)
        print(root.item)
        self.middle(root.right)

    def back(self, root):    # 后
        if root == None:
            return
        self.back(root.left)
        self.back(root.right)
        print(root.item)

深度遍历的实现思路:
深度遍历是需要作用到每一颗子树中
子树和子树之间的区别体现在根节点中。
如果写一个函数,该函数可以将一个子树中的节点进行遍历,则将该函数作用到其他子树中就可以将整棵树进行深度遍历。

排序二叉树

在这里插入图片描述

class Node():
    def __init__(self, item):
        self.item = item
        self.left = None  # 指向该节点的左节点
        self.right = None  # 指向该节点的右节点

class SortTree():
    def __init__(self):
        self.root = None

    def add(self, item):
        node = Node(item)
        if self.root == None:   # 树为空的情况
            self.root = node
            return
        cur = self.root

        while True:         # 非空
            if cur.item > item:  # 插入的节点值小于 根节点, 该节点需要插入根节点的左侧
                if cur.left == None:  # 左侧为空,直接插入
                    cur.left = node
                    break
                else:   # 左侧非空
                    cur = cur.left
            else:    # 插入的节点值大于 根节点, 该节点需要插入根节点的右侧
                if cur.right == None:
                    cur.right = node
                    break
                else:
                    cur = cur.right

    def middle(self, root):  # 中排序
        if root == None:
            return
        self.middle(root.left)
        print(root.item)
        self.middle(root.right)


list = [3, 8, 5, 7, 6, 2, 1]
tree1 = SortTree()
for i in list:
    tree1.add(i)
tree1.middle(tree1.root)

二分查找
  • 二分查找只可以作用于有序序列中
def find(alist, item):
    find = False
    low = 0
    high = len(alist) - 1

    while low <= high:
        mid = (low + high) // 2
        if item < alist[mid]:
            high = mid - 1
        elif item > alist[mid]:
            low = mid + 1
        else:
            find = True
            break
    return find

alist = [1,2,3,4,5]
print(find(alist, 4))

排序算法

冒泡算法
def sort(list):
    for j in range(len(list)-1):
        if list[j] > list[j+1]:
            list[j], list[j+1] = list[j+1], list[j]
    return list

# 先找到最大值放在最后面
def sort(alist):
    for j in range(len(alist) - 1):
        for i in range(len(alist) - 1 - j):
            if alist[i] > alist[i + 1]:
                alist[i], alist[i + 1] = alist[i + 1], alist[i]
    return alist

# 先找到最大值,再找第二大,第三大。。。。。

list = [2, 4, 6, 3, 7, 9, 5, 1, 2, 2]
print(sort(list))
选择排序

先找出第一个最大值:把列表中的第一个值当成最大值,再跟第二个值相比较,再第二个与第三个,第三个与第四个相比较。。。。。最后找到最大值

def sort(list):
    max_index = 0  # max_index 是最大值的下标,假设最开始最大值为下标为 0 的数
    for i in range(len(list)-1):
        if list[max_index] < list[i+1]:
            max_index = i+1
    list[len(list) - 1], list[max_index] = list[max_index], list[len(list) - 1]
    return list

list1 = [7, 6, 10, 3, 8, 2, 4, 9, 1, 5]
print(sort(list1))

循环列表,先找到第一个最大值,再找到第二个最大值,第三个最大值。。。。

def sort(list):
    for j in range(len(list) - 1):
        max_index = 0  # max_index 是最大值的下标,假设最开始最大值为下标为 0 的数
        for i in range(len(list) - 1 - j):
            if list[max_index] < list[i + 1]:
                max_index = i + 1
        list[len(list) - 1 - j], list[max_index] = list[max_index], list[len(list) - 1 - j]
    return list


list = [7, 6, 3, 5, 8, 2, 4, 9, 1, 10]
print(sort(list))

插入排序

思路:
需要将原始序列分成两部分:有序部分,无序部分
– 将无序部分中的元素逐一插入到有序部分中

  • 注意:初始情况下,有序部分为乱序序列的第一个元素,无序部分为乱序序列的n-1个元素

  • 乱序序列:.[3,8,5,Z,61.

  • [3…8,5,7,6], 3就是初始的有序部分,8,5,7,6就是初始的无序部分

  • .[3,8…5,7,6].

  • .[3,5,8…7,6].

  • .[3,5,7,8…6].

  • .[3,5,6,7,8…].

# [2.....5,7,8,3。。。。]  最开始前list[0]为有序序列,后面的为无需序列
# [2,5.....7,8,3。。。。]
# [3,5,8.....7,6。。。。]
# [3,5,7,8.....6。。。。].
# [3,5,6,7,8....。。。。]

前面的为有序,后面的为无序

# list[i-1]  i-1:有序序列的最后一个下标
# list[i]	 i:无需序列的第一个下标


# 先找到第一个最大值

def sort(list):
    for i in range(1, len(list)):
        if list[i-1] > list[i]:
            list[i-1], list[i] = list[i], list[i-1]
    return list

list = [7, 6, 3, 5, 8, 2, 4, 9, 1, 10]
print(sort(list))


# 全部循环排序(完整代码)
def sort(list):
    for i in range(1, len(list)):
        while i > 0:
            if list[i - 1] > list[i]:
                list[i - 1], list[i] = list[i], list[i - 1]
                i -= 1
            else:
                break
    return list


list = [7, 6, 3, 5, 8, 2, 4, 9, 1, 10]
print(sort(list))
希尔排序

希尔排序(Shell Sort)是插入排序的一种。也称缩小增量排序,是直接插入排序算法的一种更高效的改进版本,该方法的基本思想是:先将整个待排元素序列分割成若干个子序列(由相隔某个“增量(gap)”的元素组成的)分别进行直接插入排序,然后依次缩减增量再进行排序,待整个序列中的元素基本有序(增量足够小)时,再对全体元素进行一次直接插入排序。因为直接插入排序在元素基本有序的情况下(接近最好情况),效率是很高的,因此希尔排序在时间效率比直接插入排序有较大提高。

在这里插入图片描述

def sort(list):
    gap = len(list) // 2   # 增量
    while gap >= 1:
        for i in range(gap, len(list)):
            while i > 0:
                if list[i - gap] > list[i]:
                    list[i - gap], list[i] = list[i], list[i - gap]
                    i -= gap
                else:
                    break
        gap //= 2
    return list

list1 = [7, 6, 3, 5, 8, 2, 4, 9, 1, 10]
print(sort(list1))
快速排序
def sort(list, left, right):
    low = left  # 下标为 0
    high = right  # 下标为list的最后一位
    if low > high:
        return

    mid = list[low]

    while low < high:
        while low < high:
            if list[high] > mid:
                high -= 1
            else:
                list[low] = list[high]
                break
        while low < high:
            if list[low] < mid:
                low += 1
            else:
                list[high] = list[low]
                break

        if low == high:
            list[low] = mid
            break

    sort(list, left, high-1)   # 递归,执行左侧序列
    sort(list, high+1, right)  # 递归,执行右侧序列

    return list


list1 = [7, 6, 3, 5, 8, 2, 4, 9, 1, 10]
print(sort(list1, 0, len(list1)-1))

(完!)

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值