python实现常见数据结构及算法

1. 概念

  • 数据结构

    计算机存储和组织数据的方式。通俗的讲,计算机按不同数据结构存储数据,决定着其保存数据的形式及将数据存入和取出的效率,不同数据结构,存储数据和读取数据占用的时间和空间不同。

  • 算法

    计算机完成一个任务的整个流程即为一种算法。概念上讲,算法和数据结构没有任何联系,但一般地,使用不同的数据结构实现同一功能往往效率不同,因此,算法与数据结构密不可分。

  • 算法的五大特性
    • 输入: 算法具有0个或多个输入;
    • 输出: 算法至少有1个或多个输出;
    • 有穷性: 算法在有限的步骤之后会自动结束而不会无限循环,并且每一个步骤可以在可接受的时间内完成;
    • 确定性:算法中的每一步都有确定的含义,不会出现二义性;
    • 可行性:算法的每一步都是可行的,也就是说每一步都能够执行有限的次数完成;
  • 时间复杂度

    计算机实现某一算法所需的基本操作数。注意,时间复杂度并不是完成某一算法的耗时。所谓基本操作数,可以理解为计算机执行指令的最小单元,例如执行加法,减法,判断,等。通常采用大O表示法表示时间复杂度。

  • 大O表示法

    时间复杂度和大O表示法有专门的数学函数表达式解释,在此不作详解(看了又忘)。可以理解为,当要对数量为n的数据实现至某一规则时,所需最多操作数可以表示为与n相关的某个函数表达式的值。一般地,大O表示法只取决于表达式中次数最高项,次级次数项,常数项,系数等都忽略。

  • 时间复杂度分类
    • 最坏时间复杂度
    • 最优时间复杂度
    • 平均时间复杂度
  • 时间复杂度计算规则
    • 基本操作,即只有常数项,认为其时间复杂度为O(1);
    • 顺序结构,时间复杂度按加法进行计算;
    • 循环结构,时间复杂度按乘法进行计算;
    • 分支结构,时间复杂度取最大值;

2. 算法初体验

了解了一些基本概念,接下来对算法的作用有个初步体验。
现假设现要计算如下问题:
求满足条件的所有a,b,c组合:a+b+c=1000, a^2 + b^2 =c^2。

  • 方式一(直接上代码,逻辑比较简单):
import time


start_time = time.time()
for a in range(1001):
   for b in range(1001):
       for c in range(1002):
           if a+b+c == 1000 and a*a+b*b == c*c:
               print('a=%d,b=%d,c=%d' % (a, b, c))

end_time = time.time()
print('共耗时:%f' % (end_time-start_time))
  • 结果
a=0,b=500,c=500
a=200,b=375,c=425
a=375,b=200,c=425
a=500,b=0,c=500
共耗时:123.326442

Process finished with exit code 0
  • 方式二
import time


start_time = time.time()

for a in range(1001):
    for b in range(1001):
        c = 1000-a-b
        if a*a+b*b == c*c:
            print('a=%d,b=%d,c=%d' % (a, b, c))

end_time = time.time()
print('共耗时:%f' % (end_time - start_time))
  • 结果
a=0,b=500,c=500
a=200,b=375,c=425
a=375,b=200,c=425
a=500,b=0,c=500
共耗时:0.281858

Process finished with exit code 0

可以看出,为实现同一目标,计算机使用不同算法结果相差多个数量级。

  • 分析

根据上面时间复杂度计算规则,方式一时间复杂度为O(n^3), 而方式二时间复杂度为O(n^2)。因为此处n=1001,所以二者相差近3个数量级。

  • 常见时间复杂度大小

O(1) < O(log n) < O(n) < O(nlog n) < O(n^2) < O(n^3) < O(2^n) < O(n!) < O(n^n)
这里,log n 指以2为底,n的对数。

3. 常见数据结构

对算法有个初步的概念后,接下来了解下计算机中常见的数据结构。

  • 顺序表:元素顺序地存放在一块连续的存储区里;
    • 一体式顺序表:表头信息和内容相连;
    • 分离式顺序表:表头信息和内容不相连;

    python中list就是一种采用分离式技术实现的动态顺序表。

  • 链表:元素存放在通过链接构造起来的一系列存储块中;
    • 单向链表
    • 单向循环链表:尾节点的下个节点指向头节点的单向链表;
    • 双向链表:不仅上节点指向下节点,下节点同时指向上节点;
  • 栈:一种先进后出的数据结构;
  • 队列:一种先进先出的数据结构;

4. python实现常见数据结构

  • pyhton实现单向链表
class Node(object):
    """节点"""

    def __init__(self, item):
        self.item = item
        self.next = None  # 指向下一节点


class SingleLinkList(object):
    """单向链表"""

    def __init__(self):
        self.__head = None  # 头节点

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

    def length(self):
        """链表长度"""
        cur = self.__head
        count = 0
        while cur is not None:
            cur = cur.next
            count += 1
        return count

    def travel(self):
        """遍历链表"""
        cur = self.__head  # 创建游标指向头节点
        while cur is not None:
            print(cur.item, end=' ')
            cur = cur.next
        print()  # 换行

    def add(self, item):
        """链表头部添加节点"""
        node = Node(item)  # 创建节点
        node.next = self.__head  # 新节点下个元素指向头节点
        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

    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
            index = 0
            while index < pos-1:  # 注意这里是pos-1
                index += 1
                cur = cur.next
            node.next = cur.next
            cur.next = node

    def remove(self, item):
        """删除节点"""
        cur = self.__head  # 头节点
        pre = None  # 前一个节点
        while cur is not None:
            if cur.item == item:
                if pre is None:  # 首节点
                    self.__head = cur.next
                else:  # 中间节点(或尾节点)
                    pre.next = cur.next
                break
            # 链表继续后移
            pre = cur
            cur = cur.next

    def search(self, item):
        cur = self.__head
        while cur is not None:
            if cur.item == item:
                return True
            cur = cur.next
        return False

  • python实现单向循环列表
class Node(object):
    """单向链表节点"""

    def __init__(self, item):
        self.item = item
        self.next = None


class SinCycLinkList(object):
    """单向循环链表"""

    def __init__(self):
        self.__head = None

    def is_empty(self):
        return self.__head is None

    def length(self):
        if self.is_empty():
            return 0
        cur = self.__head
        count = 1
        while cur.next != self.__head:
            count += 1
            cur = cur.next
        return count

    def travel(self):
        cur = self.__head
        if self.is_empty():
            return
        while cur.next != self.__head:
            print(cur.item, end=' ')
            cur = cur.next
        print(cur.item)

    def add(self, item):
        node = Node(item)
        cur = self.__head
        if self.is_empty():
            self.__head = node
            node.next = node
            return
        while cur.next != self.__head:
            cur = cur.next
        node.next = self.__head
        self.__head = node
        cur.next = node

    def append(self, item):
        node = Node(item)
        if self.is_empty():
            self.add(item)
            return
        cur = self.__head
        while cur.next != self.__head:
            cur = cur.next
        node.next = self.__head
        cur.next = node

    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
            index = 0
            while index < pos-1:
                cur = cur.next
                index += 1
            node.next = cur.next
            cur.next = node

    def remove(self, item):
        if self.is_empty():
            return
        cur = self.__head
        pre = None
        # 要删除的是头节点的话需要知道尾节点,因此先判断是头节点的情况
        if cur.item == item:
            while cur.next != self.__head:
                pre = cur
                cur = cur.next
            # 循环执行完后,cur为尾节点
            if pre is None:  # 只有头节点
                self.__head = None
            else:
                cur.next = self.__head.next
                self.__head = self.__head.next
        else:
            while cur.next != self.__head:
                if cur.item == item:
                    pre.next = cur.next
                    return
                else:
                    pre = cur
                    cur = cur.next
            if cur.item == item:
                pre.next = self.__head

    def search(self, item):
        if self.is_empty():
            return False
        cur = self.__head
        while cur.next != self.__head:
            if cur.item == item:
                return True
            cur = cur.next
        if cur.item == item:
            return True
        return False

  • python实现双向链表
class DNode(object):
    """双向链表节点"""

    def __init__(self, item):
        self.item = item
        self.pre = None
        self.next = None


class DoubleLinkList(object):
    """双向链表"""

    def __init__(self):
        self.__head = None

    def is_empty(self):
        return self.__head is None

    def length(self):
        cur = self.__head
        count = 0
        while cur is not None:
            cur = cur.next
            count += 1
        return count

    def travel(self):
        cur = self.__head
        while cur is not None:
            print(cur.item, end=' ')
            cur = cur.next
        print()

    def add(self, item):
        node = DNode(item)
        if self.is_empty():
            self.__head = node
        else:
            node.next = self.__head
            self.__head.pre = node
            self.__head = node

    def append(self, item):
        if self.is_empty():
            self.append(item)
        else:
            cur = self.__head
            while cur.next is not None:
                cur = cur.next
            node = DNode(item)
            node.pre = cur
            cur.next = node

    def insert(self, pos, item):
        if pos <= 0:
            self.add(item)
        elif pos > self.length()-1:
            self.append(item)
        else:
            cur = self.__head
            index = 0
            while index < pos-1:
                cur = cur.next
                index += 1
            node = DNode(item)
            node.next = cur.next
            node.pre = cur
            cur.next.pre = node
            cur.next = node

    def remove(self, item):
        cur = self.__head
        while cur is not None:
            if cur.item == item:
                if cur.pre is None:  # 头节点
                    self.__head = cur.next
                else:
                    cur.pre.next = cur.next
                if cur.next is not None:
                    cur.next.pre = cur.pre
                return
            cur = cur.next

    def search(self, item):
        cur = self.__head
        while cur is not None:
            if cur.item == item:
                return True
            cur = cur.next
        return False

  • python实现栈
class Stack(object):
    """栈"""

    def __init__(self):
        """创建一个新的空栈"""
        self.__list = list()  # 栈底层使用list来实现

    def is_empty(self):
        """判空"""
        return self.__list == []

    def size(self):
        """返回栈中元素个数"""
        return len(self.__list)

    def push(self, item):
        """添加一个新的元素item到栈顶"""
        self.__list.append(item)

    def pop(self):
        """弹出栈顶元素"""
        if self.is_empty():
            return None
        return self.__list.pop()

    def peek(self):
        """返回栈顶元素"""
        if self.is_empty():
            return None
        return self.__list[self.size()-1]

  • python实现队列
class Queue(object):
    """队列"""

    def __init__(self):
        self.__list = list()  # 使用list实现队列

    def is_empty(self):
        return self.__list == []

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

    def enqueue(self, item):
        """往队列中添加一个元素"""
        self.__list.append(item)

    def dequeue(self):
        """从队列头中取出元素"""
        if self.is_empty():
            return None
        return self.__list.pop(0)

添加和获取元素也可以分别使用list.insert(0, item)list.pop()实现。因此实现队列时,考虑是插入多还是删除多来选择使用不同的方法(实际上这里取出次数<=添加次数)。

5. 常见排序算法

介绍常见排序算法前介绍一个概念:排序算法的稳定性。指根据算法排序后,原本相等的两个元素的先后位置是否改变,改变则称不稳定。作为了解即可,实际使用中用到稳定性的情况并不多。

  • 冒泡排序:相邻元素两两对比,将值大(小)的元素位置后移;
  • 选择排序:特定位置的元素与其他元素对比,确保该位置元素最大(小);
  • 插入排序:列表分为两部分,前者为已有序列表,将后者无序元素挨个和前者中元素比较,放置对应位置;
  • 快速排序:找一个基准元素,将所有小于基准元素的放置一侧,大于(等于)基准元素的放置另一侧,递归,直至列表大小为0或者1,即肯定有序;
  • 希尔排序:插入排序的改进版,先按一定间隔分组,每组插入排序,然后缩小间隔继续,直至间隔大小为1;
  • 归并排序:将数组分解最小之后,然后合并两个有序数组,基本思路是比较两个数组的最前面的数,谁小就先取谁,取了后相应的指针就往后移一位,然后再比较,直至一个数组为空,最后把另一个数组的剩余部分复制过来;

6. python实现常见排序算法

实现排序一般需要2-3层循环,对每层循环的作用应有清晰的逻辑,否则容易晕导致出错。可以先根据算法核心思想算出外层逻辑需要几次循环直接定义出,然后再定义内部每次循环的逻辑。

  • python实现冒泡排序
def bubble_sort(alist):
    # 因为冒泡排序每次将最大的数移动到最后,因此需要n-1次循环即可,因为其他排好后,第一个即是最小元素
    for i in range(len(alist)-1):  # n-1次循环
        # 每次循环旨在将前n-i个元素中的最大元素放置至最后
        # 由于是两两相邻比较,即拿到一个元素与该元素后元素比较,因此循环至n-i-1即可
        for j in range(len(alist)-i-1):
            # 核心思想为:将大的数向后移动
            if alist[j] > alist[j+1]:
                alist[j], alist[j+1] = alist[j+1], alist[j]

  • 稳定性:稳定;
  • 最坏时间复杂度:O(n^2);
  • 最优时间复杂度:O(n),表示遍历一次后发现没有需要交换的元素。当然,为实现次功能需要额外加判断条件及时退出循环,如下:
def bubble_sort(alist):
    # 因为冒泡排序每次将最大的数移动到最后,因此需要n-1次循环即可,因为其他排好后,第一个即是最小元素
    for i in range(len(alist) - 1):  # n-1次循环
        # 每次循环旨在将前n-i个元素中的最大元素放置至最后
        # 由于是两两相邻比较,即拿到一个元素与该元素后元素比较,因此循环至n-i-1即可
        count = 0  # 定义一个计数器,当前后元素发生位置变化时加1,如果最后计数器为0,说明本身就是正序的
        for j in range(len(alist) - i - 1):
            
            # 核心思想为:将大的数向后移动
            if alist[j] > alist[j + 1]:
                alist[j], alist[j + 1] = alist[j + 1], alist[j]
                count += 1
        if count == 0:
            break
           
  • python实现选择排序
def select_sort(alist):
    # 第一次将首位置换成最小值,以此类推,共需要n-1次
    for i in range(len(alist)-1):
        # 拿到第i个元素,与其之后的元素挨个比较,把较小值放置i位
        for j in range(i+1, len(alist)):
            if alist[i] > alist[j]:
                alist[i], alist[j] = alist[j], alist[I]

稳定性:不稳定,从后往前放置时,先满足条件的元素先被放置最后,会与相等的元素改变先后位置;
最坏时间复杂度:O(n^2);
最优时间复杂度:O(n^2):需要从首个位置排到最后个位置;

  • python实现插入排序
def insert_sort(alist):
    # 首次分为第一个元素和其余元素两部分,从第二个元素开始,与前面元素挨个比较,直到找到属于自己的位置。因此外层需要n-1次
    for i in range(len(alist)-1):
        # 将i+1位置的元素,向前挨个比较,找到自己的位置。即由第i个元素依次向前直至第0个元素
        for j in range(i+1, 0, -1):
            if alist[j] < alist[j-1]:
                alist[j], alist[j-1] = alist[j-1], alist[j]

稳定性:稳定;
最坏时间复杂度:O(n^2);
最优时间复杂度:O(n)。

  • python实现快速排序
"""
 快速排序和前面三种思想上有较大不同,不再是外层循环次数下,内层循环实现将某一元素放至某位置。其核心思想是以某个值作为基准值,
 将小于该值的元素放至左侧(交换位置),大于该值的元素放至右侧(交换位置),然后分别将左右两侧列表递归再次排序。
"""


# 为了实现递归而不用重新定义左右列表变量,将左右下标作为参数
def quick_sort(alist, left, right):
    # 1. 递归退出条件
    if left >= right:
        return
    # 2. 核心:
    # 以首值为基准值,判断元素大小交换位置
    mid_value = alist[0]
    while left < right:
        # 循环找出小于基准值的元素放至左侧
        while left < right and alist[right] >= mid_value:
            right -= 1
        alist[left] = alist[right]
        # 循环找出大于基准值的元素放至右侧
        while left < right and alist[left] < mid_value:
            left += 1
        alist[right] = alist[left]
    # 循环结束后,left=right,该位置放基准值
    alist[left] = mid_value
    # 对基准值左右两侧元素递归快排
    quick_sort(alist, 0, left-1)
    quick_sort(alist, left+1, right)

稳定性:不稳定,后前交互比较大小替换位置,必然不稳定;
最坏时间复杂度:O(n^2),最大需要递归n-1次,每次中,内层循环最大都要比较n次;
最优时间复杂度:O(nlogn),每次几乎分一半一半时,需要logn次循环;
PS:快速排序较前面三种排序难度提升,但很重要。

  • python实现希尔排序
"""
希尔排序是插入排序的改进版,先以一定步长将列表分为多个子列表,然后对每个子列表插入排序。执行完成后,缩小步长继续,直至步长为0。
这里可能会有疑惑,最终步长为1时就是插入排序,为什么还要多几次步长不为1的循环?
实际上,前面知道插入排序后面每个元素向前插入时,需要从前往后遍历比较,但如果比首个元素都小则可直接插入。我们可以理解为,对大部分
无序随机数列,步长取合适时,时间复杂度会有很大改善。
"""


def shell_sort(alist):
    # 取首次步长为列表长度一半
    gap = len(alist) // 2
    while gap > 0:
        # 内层类似插入排序,只不过步长为gap
        # 循环次数:gap->len
        for i in range(gap, len(alist)):
            # 内层:将第i个元素依次与前面元素比较调整位置,不过步长为gap
            for j in range(i, 0, -gap):
                if alist[j] < alist[j-gap]:
                    alist[j], alist[j-gap] = alist[j-gap], alist[j]
        # 排序后gap减小
        gap = gap // 2

稳定:不稳定;
最坏时间复杂度:O(n^2);
最优时间复杂度:(数学统计最优可达O(n^1.3))

  • python实现归并排序
"""
归并排序,如名字一样,是将序列一边合并一边排序。其核心思想为:
先将序列递归的折半拆分,直到长度为1,然后执行排序后作为本层递归的返回。
需注意归并排序每层递归都有返回值,因此比前面几个排序耗费空间。
"""


def merge_sort(alist):
    # 1. 递归退出条件
    if len(alist) <= 1:
        return alist  # 注意有返回值
    # 2.核心思想
    # 二分分解
    num = len(alist) // 2
    # 取左右两截(内部递归)
    left_list = merge_sort(alist[0:num])
    right_list = merge_sort(alist[num:])
    # 返回根据左右两截实现的排序序列
    return merge(left_list, right_list)


def merge(left_list, right_list):
    # 排序核心:将左右两个有序数列内依次取满足大小比较的元素,放置新序列
    # 定义左右两序列的其实元素位置
    left, right = 0, 0
    # 定义一个新序列接收排序后序列
    result = list()
    # 循环退出条件:一侧遍历完,将另一侧剩余元素直接添加至后面
    while left < len(left_list) and right < len(right_list):
        if left_list[left] < right_list[right]:
            result.append(left_list[left])
            left += 1
        else:
            result.append(right_list[right])
            right += 1
    result += left_list[left:]
    result += right_list[right:]
    return result

稳定性:稳定;
最坏时间复杂度:O(nlogn);
最优时间复杂度:O(nlogn);

  • 常见排序算法效率比较
排序方法 平均时间复杂度 最优时间复杂度 最坏时间复杂度 辅助空间 稳定性
冒泡排序 O(n^2) O(n) O(n^2) O(1) 稳定
选择排序 O(n^2) O(n^2) O(n^2) O(1) 不稳定
插入排序 O(n^2) O(n) O(n^2) O(1) 稳定
快速排序 O(nlogn) O(nlogn) O(n^2) O(logn)-O(n) 不稳定
希尔排序 O(nlogn)-O(n^2) O(n^1.3) O(n^2) O(1) 不稳定
归并排序 O(nlogn) O(nlogn) O(nlogn) O(n) 稳定

7. 常见搜索算法

常见算法除排序算法外还有就是搜索算法了。常见的搜索算法有针对有序序列的二分查找法,和针对二叉树的广度优先深度优先算法
树是另一种抽象的数据结构,主要有根节点和子节点等概念。一般主要使用的就是二叉树,即每个节点最多只有两个子节点的树。对树概念比较陌生的读者可以自行百度一波,树数据结构的使用可以参考下图来理解,另外xml,html等都是树结构。

树的使用.png

另外,对二叉树或完全二叉树和满二叉树(概念自行百度)有一些数学性质,如需了解可自行学习。

  • python实现二分查找
"""
二分查找法核心思想为:
找中间值将序列(等分地)分成两段,判断要找的元素比中间值大还是小,小的话在左侧段继续二分查找,大的在右侧。
python实现二分查找可以通过递归和非递归两种方式实现
"""


def binary_search(alist, item):
    """非递归实现二分查找"""
    # 定义出始末下标
    start = 0
    end = len(alist)-1
    # 循环结束条件:二分查找的序列长度为大于0
    while start <= end:
        # 找到中间值下标
        mid_point = (start+end) // 2
        if item == alist[mid_point]:
            return True
        elif item > alist[mid_point]:
            start = mid_point+1
        else:
            end = mid_point-1
    return False

def binary_search(alist, item):
    """递归实现二分查找"""
    # 递归结束条件:二分到长度为0时说明未找到
    if len(alist) == 0:
        return False
    mid_point = len(alist) // 2
    if alist[mid_point] == item:
        return True
    if alist[mid_point] > item:
        return binary_search(alist[:mid_point], item)
    else:
        return binary_search(alist[mid_point+1:], item)

最坏时间复杂度:O(logn);
最优时间复杂度:O(1);

  • python表示二叉树节点及实现树的创建
class Node(object):
    """二叉树节点"""
    # 二叉树节点三个属性:值,左子节点,右子节点
    def __init__(self, item):
        self.item = item
        self.lchild = None
        self.rchild = None


class Tree(object):
    """(满)二叉树"""
    # 二叉树初始化时需要根节点
    def __init__(self):
        self.__root = None

    def add(self, item):
        """添加元素"""
        node = Node(item)
        if self.__root is None:
            self.__root = node
        else:
            # 核心思想:将头节点加入至队列,先判断左节点,为空则将节点放置左侧;若不为空,判断右节点,为空则放置右节点。若左右都不为空,分别将左右
            # 节点加入至队列尾,再取队列首节点继续上面逻辑
            queue = list()
            queue.append(self.__root)
            while queue:
                cur = queue.pop()
                if cur.lchild is None:
                    cur.lchild = node
                    return
                if cur.rchild is None:
                    cur.rchild = node
                    return
                # 左右都不为空,将左右节点放置队列尾
                queue.append(cur.rchild)
                queue.append(cur.rchild)

  • python实现二叉树深度优先遍历
"""
二叉树深度优先遍历根据遍历顺序不同可分为三类:
先序遍历:先访问根节点,再访问左节点,最后右节点;
中序遍历:先访问左节点,再访问根节点,最优右节点;
后续遍历:先访问左节点,再当问右节点,最后根节点,注意左节点永远在右节点前,后续指根节点和右节点的顺序。
"""


def preorder(root):
    """递归实现先序遍历"""
    if root is None:
        return
    print(root.item)
    preorder(root.lchild)
    preorder(root.rchild)

def inorder(root):
    """递归实现中序遍历"""
    if root is None:
        return
    inorder(root.lchild)
    print(root.item)
    inorder(root.rchild)

def postorder(root):
    """递归实现后续遍历"""
    if root is None:
        return
    postorder(root.lchild)
    postorder(root.rchild)
    print(root.item)
  • python实现二叉树广度优先遍历
"""
广度优先遍历的核心:从根节点开始,从上到下,从左到右遍历整个树的节点
"""


def breadth_travel(root):
    if root is None:
        return
    queue = list()
    queue.append(root)
    while queue:
        node = queue.pop(0)
        print(node.item)
        if node.lchild is not None:
            queue.append(node.lchild)
        if node.rchild is not None:
            queue.append(node.rchild)
  • 整合上述方法
class Node(object):
    """二叉树节点"""
    # 二叉树节点三个属性:值,左子节点,右子节点
    def __init__(self, item):
        self.item = item
        self.lchild = None
        self.rchild = None


class Tree(object):
    """(满)二叉树"""
    # 二叉树初始化时需要根节点
    def __init__(self):
        self.root = None

    def add(self, item):
        """添加元素"""
        node = Node(item)
        if self.root is None:
            self.root = node
        else:
            # 核心思想:将头节点加入至队列,先判断左节点,为空则将节点放置左侧;若不为空,判断右节点,为空则放置右节点。若左右都不为空,分别将左右
            # 节点加入至队列尾,再取队列首节点继续上面逻辑
            queue = list()
            queue.append(self.root)
            while queue:
                cur = queue.pop(0)  # 注意是取第0个元素,与下面的append相斥
                if cur.lchild is None:
                    cur.lchild = node
                    return
                if cur.rchild is None:
                    cur.rchild = node
                    return
                # 左右都不为空,将左右节点放置队列尾
                queue.append(cur.lchild)
                queue.append(cur.rchild)

    def preorder(self, root):
        """递归实现先序遍历"""
        if root is None:
            return
        print(root.item)
        self.preorder(root.lchild)
        self.preorder(root.rchild)

    def inorder(self, root):
        """递归实现中序遍历"""
        if root is None:
            return
        self.inorder(root.lchild)
        print(root.item)
        self.inorder(root.rchild)

    def postorder(self, root):
        """递归实现后续遍历"""
        if root is None:
            return
        self.postorder(root.lchild)
        self.postorder(root.rchild)
        print(root.item)

    def breadth_travel(self, root):
        """广度优先遍历"""
        if root is None:
            return
        queue = list()
        queue.append(root)
        while queue:
            node = queue.pop(0)
            print(node.item)
            if node.lchild is not None:
                queue.append(node.lchild)
            if node.rchild is not None:
                queue.append(node.rchild)

深度优先一般用递归,广度优先一般用队列。一般情况下能用递归实现的算法大部分也能用堆栈来实现。

8. 结语

算法在编程语言中至关重要,但真正掌握好算法并不是一时半会儿能搞定的。理解掌握常用算法能对以后的积累和解决问题提供一定基础和思路。
上述算法比较简单,但掌握起来也并不容易。不需要记住每个的具体步骤,而且实现方式也不是唯一,但需要细心理解每个算法的核心思想,多写几次,才能达到比较熟练的水平。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 194,319评论 5 459
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 81,801评论 2 371
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 141,567评论 0 319
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 52,156评论 1 263
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 61,019评论 4 355
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 46,090评论 1 272
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 36,500评论 3 381
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 35,192评论 0 253
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 39,474评论 1 290
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 34,566评论 2 309
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 36,338评论 1 326
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 32,212评论 3 312
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 37,572评论 3 298
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 28,890评论 0 17
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,169评论 1 250
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 41,478评论 2 341
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 40,661评论 2 335

推荐阅读更多精彩内容