数据结构与算法(python版)

最近学习数据结构,对于从未接触过数据结构的我来说,老师不仅讲解理论,还有代码的逐层分析,非常不错,受益匪浅!!!(以下是学习记录)

黑马程序员孔德海老师教程

重点+基础语法

  1. python语言只要是给出的变量名,里边存储的都是地址值(可以把类,函数,付给变量a)。
    如a=5 a=“s” a=Person() a=f 方法

java基本数据类型中,变量a存放的是数值。引用数据类型(对象,数组,集合)的变量a存放的是地址。

#1
strat=time.time()

时间复杂度(主要关注最坏时间复杂度)

请添加图片描述

不同方法间的复杂度

import time
start= time.time()
for i in range(0,1001):
    for j in range(0,1001):
        for k in range(0,1001):
            if i+j+k==1000 and i**2+j**2==k**2:
                print(i,j,k)
end = time.time()
print("总开销:",end-start)#总开销: 126.49699997901917

start1= time.time()
for i in range(0,1001):
    for j in range(0,1001):
        k=1000-i-j
        if i**2+j**2==k**2:
            print(i,j,k)
end1= time.time()
print("总开销:",end1-start1)#总开销: 1.0120000839233398
  1. T(n)时间复杂度,n执行的步数
    大O表示法,只记录最显著特征O(n)=nxn
    在这里插入图片描述
  2. 最坏时间复杂度(平常说的就是这个)
    比如对一个list排序,无序的复杂度要高于有序

基本步骤:顺序,条件(取T最大值),循环
li.append()不能看成一步,只有分析函数中的封装才能看到append的时间复杂度
在这里插入图片描述
在这里插入图片描述
请添加图片描述

在这里插入图片描述

time.it,list,dic内置函数

from timeit import Timer
def test3():
   l = [i for i in range(1000)]
t3 = Timer("test3()", "from __main__ import test3")#1函数名,2import,因为这个Timer不一定在这里运行
print("comprehension ",t3.timeit(number=10000), "seconds")#test3()执行10000次后,10000次总的执行时间

import time
start=time.time()#从1970年到现在的计时秒数
end=time.time()-start#返回秒

对于上例使用不同方法实现时

1.
('list(range(1000)) ', 0.014147043228149414, 'seconds')
('l = [i for i in range(1000)]', 0.05671119689941406, 'seconds')
('li.append ', 0.13796091079711914, 'seconds')
('concat加 ', 1.7890608310699463, 'seconds')
2.由于数据的存储方式不同,导致插头部和尾部
append()#2.159s
insert(0,i)#30.00s
pop(end)#0.00023,对尾部弹出
pop(0)#1.91

在这里插入图片描述

数据结构

数据是一个抽象的概念,将其分类后得到程序语言的基本类型,如int,float,char。数据结构指对数据的一种封装组成,如高级数据结构list,字典。数据结构就是一个类的概念,数据结构有顺序表、链表、栈、队列、树

算法复杂度只考虑的是运行的步骤,数据结构要与数据打交道。数据保存的方式不同决定了算法复杂度
在这里插入图片描述
程序 = 数据结构 + 算法
总结:算法是为了解决实际问题而设计的,数据结构是算法需要处理的问题载体

抽象数据类型(Abstract Data Type)
抽象数据类型(ADT)的含义是指一个数学模型以及定义在此数学模型上的一组操作。
在这里插入图片描述

== 代码编写注意==
在这里插入图片描述
2.对于链表要先接入新的node,再打断原来的,要注意顺序

下面讲的各种表都是一种数据的封装结构

顺序表

顺序表+链表=线性表:一根线串起来,两种表都是用来存数据的

顺序表的2个形式

  1. 计算机存储
    计算机最小寻址单位是1字节,就是一个字节,才有一个地址,所有的地址都是统一大小0x27 4个字节
    在这里插入图片描述
  2. 元素内置顺序表
    对于存放一个含有相同类型元素的list来讲,用顺序表封装成一个数据结构
    在这里插入图片描述
    在这里插入图片描述
  3. 元素外置顺序表
    在这里插入图片描述
    元素内置顺序表,是指存储的数据类型都是一样,这样为每个元素开辟的空间都是一样大的,在根据index找元素的时候
    如list1=[1,2,3], 可以根据list的首地址0x12,很容易计算要查询元素的物理地址=0x12+index x 4.
    元素外置, 指存的数据类型不一样,如list2=[1,3,“b”,4,“ccc”]
    在这里插入图片描述

顺序表的结构与实现

在这里插入图片描述
对于python来讲已经做好封装,不需要写容量,元素个数
在这里插入图片描述
在这里插入图片描述
so常用的是分离式
在这里插入图片描述

顺序表数据区扩充

如果数据要来回增,删,导致空间不稳定,所以有了策略
在这里插入图片描述

顺序表的操作

在这里插入图片描述
在这里插入图片描述

python_list使用顺序表数据结构

1.表头与数据分离式 2.因为一个list中有int,也有字符所以用的是元素外置 3.动态顺序中的倍数扩充
在这里插入图片描述

链表

# cur做判断,逻辑(不是指针)走到最后一个元素时,里面的方法体是执行的
# 用cur.next做判断,while循环中是不执行的
# 如果发现少一次执行语句,可以手动打印,就不用完全在while循环中执行

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
java,c语言是需要中间变量temp,才能完成交换,只有python可以直接交换
在这里插入图片描述

单链表的实现

在这里插入图片描述
在这里插入图片描述

cur:指针地址
cur.elment:地址中的元素
cur=self._head先提出等式右边地址,再给cur

多写几次单链表,有助理解,链表的运行过程

1. """单链表的结点,里面存放element区和next"""相当于元祖(element,nextclass SingleNode(object):
    def __init__(self,item):
        # _item存放数据元素
        self.item = item
        # _next是下一个节点的标识
        self.next = None
2."""单链表数据结构类,具有以下功能"""相当于python中的list数据结构类,同时它也具有以下功能
class SingleLinkList(object):
    """存放一个属性P,好用来指向第一个节点"""
    def __init__(self,):
        self._head = None# _ 私有属性,head=None说明P指向的单链表中没有node

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

    def length(self):
        """链表长度"""
        # current游标用来遍历节点,初始时指向头节点
        cur = self._head#直接指到第一个节点
        count = 0
        # 尾节点指向None,当未到达尾部时
        while cur != None:
            count += 1
            # 将cur后移一个节点
            cur = cur.next #相当于指向下一个节点
        return count

    def travel(self):
        """遍历链表"""
        cur = self._head#直接指到第一个节点
        while cur != None:
            print cur.item,#打印第一个节点
            cur = cur.next#相当于指向下一个节点
        print ""
    
       def add(self, item):
        """头部添加元素"""
        # 先创建一个保存item值的节点
        node = SingleNode(item)
        # 将新节点的链接域next指向头节点,即_head指向的位置,有先后,否则会丢失链
        node.next = self._head
        # 将链表的头_head指向新节点
        self._head = node
        
    def append(self, item):
        """尾部添加元素"""
        node = SingleNode(item)
        # 先判断链表是否为空,若是空链表,则将_head指向新节点
        if self.is_empty():
            self._head = node#append时候,我只要保证把我的node地址给你就行,node.next不用管
        # 若不为空,则找到尾部,将尾节点的next指向新节点
        else:
            cur = self._head
            while cur.next != None:
                cur = cur.next
            cur.next = node#直接将node送给cur.next,内部操作就是把node结点地址给他

if __name__=="__main__":
    li=SingleLinkList()
    print(li.is_empty())
    print(li.length())
    node = SingleNode(3)
    li.append(11111)
    li.travel()
  
  3.使用
   node =Node(100)
   single_obj=SingleLinkList(node)#创建node,让单链表指向它
   single_obj=SingleLinkList()#直接创建空链表

指定位置添加元素

![在这里插入图片描述](https://img-blog.csdnimg.cn/20200805105253710.png?x-os s-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L20wXzQ2MjA0MjI0,size_16,color_FFFFFF,t_70)

    def insert(self, pos, item):
        """指定位置添加元素"""
        # 若指定位置pos为第一个元素之前,则执行头部插入
        if pos <= 0:
            self.add(item)
        # 若指定位置超过链表尾部,则执行尾部插入
        elif pos > (self.length()-1):
            self.append(item)
        # 找到指定位置
        else:
            node = SingleNode(item)
            count = 0
            # pre用来指向指定位置pos的前一个位置pos-1,初始从头节点开始移动到指定位置
            pre = self._head
            while count < (pos-1):
                count += 1
                pre = pre.next
            # 先将新节点node的next指向插入位置的节点
            node.next = pre.next
            # 将插入位置的前一个节点的next指向新节点
            pre.next = node

查找节点是否存在

在这里插入图片描述

def search(self,item):
        """链表查找节点是否存在,并返回True或者False"""
        cur = self._head
        while cur != None:#一直往后走,起到遇到none停止
            if cur.item == item:
                return True
            cur = cur.next
        return False

删除节点

在这里插入图片描述

def remove(self,item):
        """删除节点"""
        cur = self._head
        pre = None
        while cur != None:
            # 找到了指定元素
            if cur.item == item:
                # 如果第一个就是删除的节点
                if not pre:
                    # 将头指针指向头节点的后一个节点
                    self._head = cur.next
                else:
                    # 将删除位置前一个节点的next指向删除位置的后一个节点
                    pre.next = cur.next
                break
            else:
                # 继续按链表后移节点
                pre = cur
                cur = cur.next

链表与顺序表的对比

链表只能记录头节点地址,因此在访问,尾部插,中间查时,都需要从头节点,遍历过去,所以复杂度O(n)

  1. 链表可以利用离散的空间,顺序表只能开辟完整的连续空间,只要list不够,必须重新开辟
  2. 链表时间花费到遍历地址上,顺序表花费到表的复制搬迁
    在这里插入图片描述

双向链表

对于前面单向的,当前node不能查看其前面node的信息,所以引入双向。添加,删除node时,要保证每个node中的P,N都 给上值,如:中间插入
在这里插入图片描述
头部插入元素
在这里插入图片描述

def add(self, item):
        """头部插入元素"""
        node = Node(item)
        if self.is_empty():
            # 如果是空链表,将_head指向node
            self._head = node
        else:
            # 将node的next指向_head的头节点
            node.next = self._head
            # 将_head的头节点的prev指向node
            self._head.prev = node
            # 将_head 指向node
            self._head = node

尾部插入元素
在这里插入图片描述

def append(self, item):
        """尾部插入元素"""
        node = Node(item)
        if self.is_empty():
            # 如果是空链表,将_head指向node
            self._head = node
        else:
            # 移动到链表尾部
            cur = self._head
            while cur.next != None:
                cur = cur.next
            # 将尾节点cur的next指向node
            cur.next = node
            # 将node的prev指向cur
            node.prev = cur

中间插入
在这里插入图片描述
在这里插入图片描述

单向循环链表

由于是循环列表,最后的尾结点不再是None结束,而是指向self._head
由于一开始将cur = self._head设成游标,所以判断语句只能是cur.next
while cur.next != self._head
使用cur.next不能进到循环体,会导致少一次打印,可以手动打印
在这里插入图片描述

length(self)返回链表长度

在这里插入图片描述

def length(self):
        """返回链表的长度"""
        # 如果链表为空,返回长度0
        if self.is_empty():
            return 0
        count = 1
        cur = self._head#指针启动地址
        while cur.next != self._head:#因为循环链表最后一个node要带有self._head,所以通过判断是否有self._head来看是否一个循环结束
            count += 1
            cur = cur.next
        return count

头部添加节点

在这里插入图片描述

def add(self, item):
        """头部添加节点"""
        node = Node(item)
        if self.is_empty():
            self._head = node
            node.next = self._head
        else:
            #添加的节点指向_head
            node.next = self._head
            # 移到链表尾部,将尾部节点的next指向node
            cur = self._head
            while cur.next != self._head:
                cur = cur.next
            cur.next = node
            #_head指向添加node的
            self._head = node

栈=杯
顺序表,链表是用来存数据的
栈和队列不考虑数据是如何存放的,栈stack,队列是一种容器,执行什么样的操作,抽象结构。
6*(2+3)
在这里插入图片描述
在这里插入图片描述

#利用前面封装好的顺序表的二次开发
class Stack(object):
    """栈"""
    def __init__(self):#初始化的时候要生成一个容器,顺序表就是python中的list,然后再执行操作,可将list设成私有的,self.__items = []这样就不能修改原来的list容器
         self.items = []

    def is_empty(self):
        """判断是否为空"""
        return self.items == []#右边是一个判断返回True
        
#对于存放的数据结构是顺序表来讲,尾部压栈出栈复杂度为o(1),对于链表来讲,头部复杂度为o(1),所以要考虑
    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)

if __name__ == "__main__":
    stack = Stack()
    stack.push("hello")
    stack.push("world")
    stack.push("itcast")
    print stack.size()
    print stack.peek()
    print stack.pop()
    print stack.pop()
    print stack.pop()

队列

在这里插入图片描述
在这里插入图片描述

#因为出入队总有一个复杂度为N,所以考虑是出队用的多,还是入队多,再写程序
class Queue(object):
    """队列"""
    def __init__(self):#空列表保存队列
        self.items = []

    def is_empty(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)

if __name__ == "__main__":
    q = Queue()
    q.enqueue("hello")
    q.enqueue("world")
    q.enqueue("itcast")
    print q.size()
    print q.dequeue()
    print q.dequeue()
    print q.dequeue()

双端队列
两个栈尾部放一起
在这里插入图片描述
是上面的扩展,含有的函数功能更多了
在这里插入图片描述

排序

排序算法的稳定性
在这里插入图片描述

冒泡排序O(n2)稳定

在这里插入图片描述

#可以将这个算法操作在顺序表上(改变存储位置),也可以操作在链表上(改变node节点)
#不论list是什么要,O(n)=n^2
def bubble_sort(alist):
    for j in range(len(alist)-1,0,-1):#n-1,n-2,,,1
        # j表示每次遍历需要比较的次数,是逐渐减小的
        for i in range(j):
            if alist[i] > alist[i+1]:
                alist[i], alist[i+1] = alist[i+1], alist[i]

li = [54,26,93,17,77,31,44,55,20]
bubble_sort(li)
print(li)

#加个检测是否交换的优化的count,这样复杂度为n
#[1,2,3,4,5,9,8]
def bubble_sort(alist):
    for j in range(len(alist)-1,0,-1):#n-1,n-2,,,1
        # j表示每次遍历需要比较的次数,是逐渐减小的
        count=0
        for i in range(j):
            if alist[i] > alist[i+1]:
                alist[i], alist[i+1] = alist[i+1], alist[i]
                count+=1
        if 0 == count:
            contine
li = [54,26,93,17,77,31,44,55,20]
bubble_sort(li)
print(li)

冒泡是稳定的

选择排序O(n2)不稳定

比如[5,5,2],就是不稳定的
选择排序,插入排序,希尔排序是把序列分成两部分,把其中的一部分元素往另外一部分做
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

list=[54,226,93,17,77,31,44,55,21]
def selec_sort(list):
    for j in range(len(list)-1):
        min=list[j]
        for i in range(j+1,len(list)):
            if list[i]<min:
                min=list[i]
                list[j],list[i]=list[i],list[j]

    print(list)
selec_sort(list)

插入排序O(n2)稳定

在这里插入图片描述
在这里插入图片描述

#这个想法像选择排序,每次对比选出最大,没有插序的相邻之间对比,向前移
list=[54,226,93,17,77,44,4,55,21]
    #0,   1,  2,  3  4, 5, 6 ,7, 8
def insert_sort(list):
    for j in range(1,len(list)):
        max=list[j]
        for i in range(0,len(list)-1,1):#先写第一个循化,可以直接用下表list[1],然后再加上外循环,看看j与list[1]下标的关系
            if list[i]>max:
                max=list[i]
                list[j],list[i]=list[i],list[j]
    print(list)
insert_sort(list)
#version1插序的相邻之间对比,向前移,用下面的方法
def insert_sort(alist):
    # 从第二个位置,即下标为1的元素开始向前插入
    for i in range(1, len(alist)):
        # 从第i个元素开始向前比较,如果小于前一个元素,交换位置
        for j in range(i, 0, -1):
            if alist[j] < alist[j-1]:
                alist[j], alist[j-1] = alist[j-1], alist[j]

alist = [54,26,93,17,77,31,44,55,20]
insert_sort(alist)
print(alist)

#version2
#对于上面的用for j in range(i, 0, -1):来控制i,与i-1,i-2的比较,操作比较复杂
#这里用 while i>0: i-=1 来控制指针向0的方向移动,随着i的变动,每次只比较i与i-1
def insert_sort(alist):
    # 从第二个位置,即下标为1的元素开始向前插入
    for i in range(1, len(alist)):
        # 从第i个元素开始向前比较,如果小于前一个元素,交换位置
        while i>0:
            if alist[i] < alist[i-1]:
                alist[i], alist[i-1] = alist[i-1], alist[i]
                i-=1
            else:
                break
alist = [54,26,93,1711,77,31,44,55,20]
insert_sort(alist)
print(alist)

希尔排序

在这里插入图片描述

def shell_sort(list):
    n=len(list)
    gap=n//2 #gap=4

    while gap>=1:#gap一直变化到1
        for j in range(gap,n):#for控制的是gap=4时的4个子序列
            i=j
            while i >0:#跟之前插入排序一样,对于一个序列的具体操作,与普通插入的区别就是gap步长
                if list[i]<list[i-gap]:
                    list[i],list[i-gap]=list[i-gap],list[i]
                    i-=gap
                else:
                    break
        gap//=2#gap减半

list = [54,26,93,17,77,31,44,55,20]
shell_sort(list)
print(list)

快速排序(不稳定)

快速排序(英语:Quicksort),又称划分交换排序(partition-exchange sort),通过一趟排序将要排序的数据分割成独立的两部分,其中一部分的所有数据都比另外一部分的所有数据都要小,然后再按此方法对这两部分数据分别进行快速排序,整个排序过程可以递归进行,以此达到整个数据变成有序序列。
注意
1.version2 中,两个while有一个是等号,保证相同大小的元素放到mid的同一边,这是快排设计的一个思想在这里插入图片描述
用version2的思想写代码
在这里插入图片描述

def quick_sort(alist, start, end):
    """快速排序"""

    # 递归的退出条件
    if start >= end:
        return

    # 设定起始元素为要寻找位置的基准元素
    mid = alist[start]

    # low为序列左边的由左向右移动的游标
    low = start

    # high为序列右边的由右向左移动的游标
    high = end

    while low < high:
        # 如果low与high未重合,high指向的元素不比基准元素小,则high向左移动
        while low < high and alist[high] >= mid:#加=号表示将2个一样的值放到一边,不加的话,最终迭代后也会正确
            high -= 1
        # 将high指向的元素放到low的位置上
        alist[low] = alist[high]

        # 如果low与high未重合,low指向的元素比基准元素小,则low向右移动
        while low < high and alist[low] < mid:
            low += 1
        # 将low指向的元素放到high的位置上
        alist[high] = alist[low]

    # 退出循环后,low与high重合,此时所指位置为基准元素的正确位置
    # 将基准元素放到该位置
    alist[low] = mid

    # 对基准元素左边的子序列进行快速排序
    quick_sort(alist, start, low-1)#这里要保证使用同一个list,这样产生的多个子序列下标是保持不变的,才能恢复成一个list

    # 对基准元素右边的子序列进行快速排序
    quick_sort(alist, low+1, end)


alist = [54,26,93,17,77,31,44,55,20]
quick_sort(alist,0,len(alist)-1)
print(alist)

归并排序

在这里插入图片描述

在这里插入图片描述

def merge_sort(alist):
    if len(alist) <= 1:#递归的终止条件 
        return alist
    # 二分分解
    num = len(alist)/2
    left = merge_sort(alist[:num]) 
    right = merge_sort(alist[num:])
    # 合并
    return merge(left,right)

def merge(left, right):
    '''合并操作,将两个有序数组left[]和right[]合并成一个大的有序数组'''
    #left与right的下标指针
    l, r = 0, 0
    result = []
    while l<len(left) and r<len(right):
        if left[l] < right[r]:
            result.append(left[l])
            l += 1
        else:
            result.append(right[r])
            r += 1
    result += left[l:]#当上面的while循环出来时,一个list的元素是取完的,保证将另外一个list余下的元素append
    result += right[r:]
    return result#是一个新list,不再使用之前list下标

alist = [54,26,93,17,77,31,44,55,20]
sorted_alist = mergeSort(alist)
print(sorted_alist)

在这里插入图片描述

递归时要注意缩进,这样可以帮助理解
在这里插入图片描述

在这里插入图片描述

排序算法效率比较

在这里插入图片描述

搜索

二分法
前提,二分查找只能作用于有序列表
在这里插入图片描述
在这里插入图片描述

递归法:当list处理后得到的list,还想再进行同样的操作,使用递归,但是不能无限递归下去,所以终止条件很重要,在这个例子中,要么alist[midpoint]=item,return True,要么 if len(alist) = 0: return False,用来退出递归

#递归法,每次生成一个新的list
def binary_search(alist, item):
    if len(alist) == 0:
        return False
    else:
        midpoint = len(alist)//2
        if alist[midpoint]==item:
          return True
        else:
          if item<alist[midpoint]:
            return binary_search(alist[:midpoint],item)
          else:
            return binary_search(alist[midpoint+1:],item)

testlist = [0, 1, 2, 8, 13, 17, 19, 32, 42,]
print(binary_search(testlist, 3))
print(binary_search(testlist, 13))

binary_search([0, 1, 2, 8, 13, 17, 19, 32, 42,],item=3)
    midpoint=4
    3<13
    binary_search([0, 1, 2, 8],item=3)
        midpoint=2
        3>2
        binary_search([8],item=3)
            midpoint=1//2=0
            3<8
                binary_search([],item=3)#a=[1,2,3,4] print(a[5:])会返回空列表
                   return False

非递归
递归法,调用函数自身,每次生成一个新的list,然后在list上操作.非递归法使用的是同一个list,所以first,last下标很重要,在while循环中,不断更新first,last值来实现对于list的取值控制
在这里插入图片描述

def binary_search(alist, item):
      first = 0
      last = len(alist)-1
      while first<=last:
          midpoint = (first + last)/2
          if alist[midpoint] == item:
              return True
          elif item < alist[midpoint]:
              last = midpoint-1
          else:
              first = midpoint+1
    return False
testlist = [0, 1, 2, 8, 13, 17, 19, 32, 42,]
print(binary_search(testlist, 3))
print(binary_search(testlist, 13))

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
请添加图片描述
在这里插入图片描述
和前面的二分查找是一样的
请添加图片描述

树的存储

在这里插入图片描述

在这里插入图片描述

应用

请添加图片描述

二叉树的节点表示以及树的创建

在这里插入图片描述

 self.root = root#保存一个根节点,可以有,也可以没有,如果有的话说明self.root后面跟着一串数据,然后.append,在这个基础上进行加.如果self.root=None,self.root = node,为树创建第一个节点

在这里插入图片描述
A为第一层,2^ 0, 2^1, ,2^2
写完代码后,要分析下特殊情况是否可以跑通,若不能的话,再写特殊语句对待
在这里插入图片描述
广度优先的add实现的二茶树->考察队列,先入先出

#树就是链表的扩充,链表有一个element,和next,但是树有两个分支,所以lchild,rchild.
class Node(object):
    """节点类"""
    def __init__(self, elem, lchild=None, rchild=None):
        self.elem = elem
        self.lchild = lchild
        self.rchild = rchild

#树的创建,创建一个树的类,并给一个root根节点,一开始为空,随后添加节点
class Tree(object):
    """树类"""
    def __init__(self, root=None):
        self.root = root#保存一个根节点,可以有,也可以没有,如果有的话说明self.root后面跟着一串数据,然后.append,在这个基础上进行加.如果self.root=None,self.root = node,为树创建第一个节点

    def add(self, elem):#广度优先的添加方式创建的二茶树
        """为树添加节点"""
        node = Node(elem)
        #如果树是空的,则对根节点赋值,若没有这行,直接走到下面会出错
        if self.root == None:
            self.root = node
        else:
            queue = []
            queue.append(self.root)
            #对已有的节点进行层次遍历
            while queue:#          通过不断的压入队列,里面的数将会越来越多,不会为0
                #弹出队列的第一个元素
                cur = queue.pop(0)
                if cur.lchild == None:
                    cur.lchild = node
                    return
                elif cur.rchild == None:
                    cur.rchild = node
                    return
                else:
                    #如果左右子树都不为空,加入队列继续判断
                    queue.append(cur.lchild)
                    queue.append(cur.rchild)

二叉树的遍历

广度优先遍历(层次遍历)

def breadth_travel(self):
        """利用队列实现树的层次遍历"""
        if self.root == None:
            return
        queue = []
        queue.append(root)
        while queue:#这个queue最后是要为空的(上面的那么add的queue,由于满足条件就会return,所以不会空)
            node = queue.pop(0)
            print node.elem,
            if node.lchild != None:
                queue.append(node.lchild)
            if node.rchild != None:
                queue.append(node.rchild)

深度优先遍历

在这里插入图片描述
每次把框缩小一开始0为根节点,然后1为根节点,然后3为根节点,根节点一直变动
在这里插入图片描述
在这里插入图片描述

def preorder(self, root):
      """递归实现先序遍历"""
      if root == None:#控制迭代结束
          return
      print root.elem
      self.preorder(root.lchild)
      self.preorder(root.rchild
      
def inorder(self, root): 
      """递归实现中序遍历"""
      if root == None:
          return
      self.inorder(root.lchild)
      print root.elem
      self.inorder(root.rchild)
      
def postorder(self, root):
      """递归实现后续遍历"""
      if root == None:
          return
      self.postorder(root.lchild)
      self.postorder(root.rchild)
      print root.elem

根据数据画树图

一定要中序:用来分割数据
前、后序:用来找根
在这里插入图片描述

树的补充

只列出大纲,后序学习

二叉排序树(BST)

在这里插入图片描述

平衡二叉树(AVL)

特点:1.是二叉排序树 ⒉满足每个结点的平衡因子绝对值<=1
请添加图片描述
请添加图片描述

红黑树

红黑树是一个“适度平衡”的二叉搜索树,而非如AVL一般“严格”的平衡。(说它不严格是因为它不是严格控制左、右子树高度或节点数之差小于等于1。)
面试常问:什么是红黑树?
在这里插入图片描述
1.每个节点只能是红色或者黑色。
2.根节点必须是黑色。
3.红色节点的子节点只能是黑色。
4.从任一节点到其每个叶子节点,的所有路径上,包含黑色节点的数目相同。

多路查找树

B-树(多路平衡查找树)

视频增删查
B树又称为多路平衡查找树,是一种组织和维护外存文件系统非常有效的数据结构。比如数据库的索引。

请添加图片描述
请添加图片描述
请添加图片描述

请添加图片描述
请添加图片描述
请添加图片描述
请添加图片描述

B+树

b+树和b树的区别_B+树和B/B树的区别?Mysql为啥用B+树来做索引?
b+树适合范围查找,比如找年龄在18-22岁的人的信息,先用随机查找找到18,再用顺序查到到22,速度极快,这也是为啥Mysql用B+树来做索引。
通过该指针可以实现随机查找,通过该指针可以实现顺序查找
主要区别:
1.所有关键字都会在在叶子节点出现
2.内部节点(非叶子节点)不存储数据(b树内部节点是存储data的),数据只在叶子节点存储(叶子节指其下没有子树了)
3.所有叶子结点构成了一个链指针,而且所有的叶子节点按照顺序排列。

B+树比B树有什么优势?
1.每一层更宽,更胖,存储的数据更多。因为B+树只存储关键字,而不存储多余data.所以B+树的每一层相比B树能存储更多节点。
2.所有的节点都会在叶子节点上出现,查询效率更稳定。因为B+树节点上没有数据,所以要查询数据就必须到叶子节点上,所以查询效率比B树稳定。而在 B 树中,非叶子节点也会存储数据,这样就会造成查询效率不稳定的情况,有时候访问到了非叶子节点就可以找到关键字,而有时需要访问到叶子节点才能找到关键字。
3.查询效率比B树高。因为B+树更矮,更胖,所以和磁盘交互的次数比B树更少,而且B+树通过底部的链表也可以完成遍历,但是B树需要找到每个节点才能遍历,所以B+树效率更高。

总结:
1.存的多 2.查询稳定 3.查的快

  • 108
    点赞
  • 1111
    收藏
    觉得还不错? 一键收藏
  • 20
    评论
### 回答1: Python是一种高级编程语言,它提供了丰富的数据结构和算法库,方便开发者进行各种数据处理和算法实现。以下是关于Python数据结构和算法的一些重要特点和示例: 1. 数据结构: - 列表(List)是Python中最常用的数据结构之一,它可以存储多个元素并支持常见的增删改查操作。 - 元组(Tuple)类似于列表,但是是不可变的,一旦创建就无法修改。 - 字典(Dictionary)是一种键值对存储结构,可以通过键快速查找对应的值。 - 集合(Set)是一种无序且不重复的数据结构,可以进行各种集合操作,如并集、交集、差集等。 2. 算法: - 排序算法:Python提供了多种排序算法,如冒泡排序、选择排序、插入排序、快速排序等。 - 搜索算法:Python的内置函数和库中包含了各种搜索算法,如线性搜索、二分搜索等。 - 图算法:Python的标准库中提供了图相关的数据结构和算法,包括图的表示和遍历、最短路径算法等。 - 动态规划:Python支持动态规划算法的实现,可以解决一些复杂的问题,如背包问题、最长公共子序列等。 Python还有许多其他的数据结构和算法,可以根据具体需求进行选择和应用。此外,Python拥有简洁明确的语法和丰富的第三方库支持,使得数据结构和算法的实现更加简单和高效。无论是初学者还是专业开发者,都可以利用Python轻松应对各种数据处理和算法挑战。 ### 回答2: Python是一种功能强大且易于学习的编程语言,它提供了许多内置的数据结构和算法,使得开发者能够轻松地处理和操作数据。 首先,关于数据结构Python提供了许多常见的数据结构类型,如列表、元组、字典和集合等。这些数据结构可以方便地存储和操作数据。例如,列表可以用于存储一系列元素,可以通过索引访问和修改元素。字典可以将键和值对应起来,方便通过键来访问和修改值。集合可以用于存储一组元素,并且支持常见的集合操作,如并集、交集和差集等。 其次,Python还提供了许多常见的算法,可用于解决各种问题。例如,排序算法是处理数据的常见操作,Python提供了多种排序算法,如快速排序和归并排序等。搜索算法是另一类常见的算法,Python提供了线性搜索和二分搜索等算法,用于在有序或无序的数据集中查找特定元素。此外,Python还提供了诸如递归、动态规划和贪心等算法,用于解决更加复杂的问题。 使用Python进行数据结构和算法的开发具有许多优点。首先,Python具有直观和简洁的语法,使得编码和调试变得更加容易。其次,Python具有丰富的标准库和第三方库,提供了大量可用的数据结构和算法实现,开发者可以直接使用这些库来解决问题,避免了重复造轮子的工作。此外,Python还具有良好的可移植性和可扩展性,可以轻松地将代码移植到不同的平台和环境中。 总之,Python提供了丰富的数据结构和算法,在实际开发中可以方便地处理和操作数据。通过使用这些数据结构和算法,开发者可以更高效地解决问题,提高代码的可读性和可维护性。 ### 回答3: Python是一种简洁、高效且易于学习的编程语言,它可以很好地支持各种数据结构和算法。Python提供了许多内置的数据结构,如列表、元组、字典和集合,这些数据结构可以方便地存储和操作数据。 在Python中,我们可以使用列表来存储多个元素,列表可以动态增加和删除元素,还可以使用索引访问列表的元素。元组与列表类似,但是元组是不可变的,即一旦创建后就无法修改。字典是一种键值对的数据结构,通过键来访问相应的值,字典可以很方便地实现查找和映射功能。集合是一种无重复元素的数据结构,可以进行交集、并集和差集等常见的集合操作。 除了内置的数据结构外,Python还提供了丰富的库和模块来支持各种算法。例如,NumPy和SciPy库提供了许多用于科学计算和统计分析的函数和数据结构。Pandas库提供了高效的数据处理和分析工具,非常适合处理大规模的数据集。另外,Python还有许多专门用于算法和数据结构的第三方库,如NetworkX用于图算法、Python-Levenshtein用于字符串相似度计算等。 在算法方面,Python具有强大的能力,并且易于实现和调试。Python可以很方便地实现各种排序算法,如冒泡排序、快速排序和归并排序等。此外,Python还支持递归算法,可以高效地解决许多问题。Python还提供了各种内置的算法函数,如查找最大值、最小值和求和等。对于复杂的算法问题,Python还可以通过调用C或C++编写的函数或库来提高执行效率。 总而言之,Python提供了丰富的数据结构和算法支持,使程序员可以很方便地处理和分析数据,实现各种复杂的算法。无论是初学者还是专业的数据科学家,Python都是一个理想的选择。
评论 20
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值