Python 数据结构与算法

一 汉诺塔

b站网课看的这个

06 汉诺塔问题_哔哩哔哩_bilibili

后面又找了个参考的

汉诺塔问题的详细讲解(python版)_汉诺塔python_羊老羊的博客-CSDN博客

两个代码的n都可以看作是第一个盘子里剩余的部分

递归的感觉就像是把重复部分写上,其他的让电脑自己算

二 顺序查找

网课:

07 顺序查找_哔哩哔哩_bilibili

没看懂 enumerate(li)是什么,所以我又搜了

python enumerate函数的用法_嘎嘎_哈的博客-CSDN博客

总之就是,使用这个列表/字符串的下标(第一个参数)+对应的内容(第二个参数)

for ind,v in enumerate(li):

基本是这样用的

三 二分查找

1.介绍

网课:

09二分查找介绍_哔哩哔哩_bilibili

定义:从有序列表的初始候选值li[0:n]开始,通过对待查找的值与候选区中间值的比较,可以使候选区减少一半

大概就是一个东西,切一半再切一半再切一半。。

实现的原理就是,设置两个指针left,right,n为列表含有元素的个数;left=0,right=n-1(left的值+right的值)/2,得出相应大小的元素的位置mid(有0.5的话就向小一个取);将mid的值与目标值比较,如果比目标值大,right=中间数所在的前一个位置;如果比目标值小,left=中间数所在的后一个位置。

left和right其中一个等于目标值的时候就已经找到了

找不到值的时候,会出小left>right或者right<left

时间复杂度O(logn)

2.例子

网课:

10二分查找代码_哔哩哔哩_bilibili

三 列表排序

将列表排成有序的(内置的sort())

1.冒泡排序

网课:

13冒泡排序介绍_哔哩哔哩_bilibili

定义:列表每两个相邻的数,如果后面比前面大,则交换这两个数(升序排列);一趟排序完成后,则无序区减少一个书,有序区增加一个数,要排n-1趟

代码:

14冒泡排序_哔哩哔哩_bilibili

最初始的版本:

import random
def bubble_sort(li):
    for i in range(len(li)-1): #第i趟
        for j in range(len(li)-i-1): #定义箭头
            if li[j] > li[j+1]: #升序,小于号就是降序
                li[j], li[j+1]=li[j+1], li[j]#交换
li =[random.randint(0,10000) for i in range(1000)] #生成1000个0~10000的整数
print(li)
bubble_sort(li)
print(li)

时间复杂度O(n^2)

改良版:

如果在n-1趟之内就已经排好序了

def bubble_sort(li):
    for i in range(len(li)-1): #第i趟
        exchange =False
        for j in range(len(li)-i-1): #定义箭头
            if li[j] > li[j+1]: #升序,小于号就是降序
                li[j], li[j+1]=li[j+1], li[j]#交换
                exchange =True 
        if not exchange: #如果第i趟没有发生交换,则说明已经排好序了
        return

2.选择排序

网课:

15选择排序_哔哩哔哩_bilibili

定义:

遍历一遍找到最小的数,再重复一遍找到第二小的数,依次循环

重点:

无序区,有序区,无序区最小数的位置

def select_sort(li): #最小的数和第一个数交换位置,第二小的数和第二个数交换。。
    for i in range(len(li)-1): #i是第几趟
        min_loc = i #每一趟最小值的位置 <设定一个初始值>
        for j in range(i+1,len(li)):#从第i+1个数开始比
            if li[j] < li[min_loc]:
                min_loc = j #新的最小值的数的下标
            li[i], li[min_loc] =li[min_loc], li[i]#交换

时间复杂度O(n^2)

四 插入排序

网课:16插入排序_哔哩哔哩_bilibili

参考:Python实现插入排序_插入排序python_神树降临的博客-CSDN博客

原理:

  1. 将待排序列表的第一个元素当做已排序序列,第二个元素到最后一个元素当成未排序序列。

  2. 取未排序序列中的第一个数据,插入到已排序序列中顺序正确的位置。将未排序的第一个数据与相邻的前一个数据(已排序序列的最后一个数)据进行比较,如果顺序错误则交换位置,交换位置后继续与相邻的前一个数据进行比较,直到不需要交换则插入完成。每次插入数据后,已排序序列都是排好序的。

  3. 重复上一步,继续插入下一个数据。每进行一次插入,已排序序列的长度加1,未排序序列的长度减1,直到列表中的所有数据都插入到已排序序列了,则列表排序完成。

代码

def insert_sort(li):
    for i in range(1,len(li)): #i表示摸到的牌的下标 j是有序区
        tmp=li[i]
        j = i- 1 #j指手里的牌(最开始第一个的牌)
        while j>=0 and li[j] > tmp : #j=-1 则说明已经移到了第一个位置,li[j] > tmp 就说明顺序正好是对的,然后放tmp的右边
            li[j+1] = li[j] #每次都往后移
            j -= 1
        li[j+1]=tmp #把tmp插入到j的后面
        print(li)

时间复杂度O(n^2), 每一次最多移动1/2*n个元素

五 快速排序***

网课:

17快速排序原理介绍_哔哩哔哩_bilibili

原理:

取一个元素p(第一个元素),使元素p归位; 列表被p分成两部分,左边都比p小,右边都比p大; 递归完成排序

代码:

def partition(li,left,right):
    tmp = li[left]
    while left < right:
        while left < right and li[right] >= tmp: #从右边找比左边tmp小的数
            right -= 1   #往左边走一次
        li[left] = li[right] #把右边的值写道左边的空位上
        while left < right and li[left] <= tmp:
            left += 1
        li[right] = li[left] #把左边的值写到右边空位上
    li[left] = tmp #把tmp归位
    return left

def quick_sort(li,left,right):
    if left < right: #至少两个元素
        mid = partition(li,left,right)
        quick_sort(li,left,mid-1)
        quick_sort(li,mid+1,right)

时间复杂度:O(nlogn)-------partition的复杂度是n,运行了logn次

不好的地方是会占用系统资源

空间复杂度:平均走logn层,所以为O(logn),最坏情况下全部都遍历一边,为O(n)

六 树(堆排序)

1.一些概念

度是这个结点有一个子结点

(1)堆的向下调整的性质

23堆排序的过程演示_哔哩哔哩_bilibili

挨个出数:先排最上面,再挑最下面的,利用堆的向下调整依次出数,再挑选最上面的,直到所有数字都出列

构造堆:从最后一个非叶子结点开始调整为堆

2.代码

第一段代码:

这个代码就是使根元素的子元素都比它小(或者是根元素最小,没有子元素)
def sift(li,low,high): #li:列表 low:堆的根节点位置 high:堆的最后于各元素的位置
    i = low #i最开始指向根节点
    j = 2*i+1 #j开始是左孩子
    tmp = li[low] #把堆顶存起来
    while j <= high #只要j位置有数(如果j>high,就说明j已经到列表之外了)
        if j+1<=high and li[j+1] > li[j]: #如果右孩子有并且比较大(就是两个中选择更大的)
            j=j+1  #j指向右孩子
        if li[j] > tmp:
            li[i] = li[j] #把大的跟小的互换位置
            i= j #往下看一层
            j =2*i+1
        else: #tmp比它现在所在位置的两个孩子都大,把tmp放到i的位置上,直接结束(顺序正好是对的)
            li[i] = tmp #把tmp放到某一级领导位置上
            break
    else:
        li[i] = tmp #i已经指向与high同级的地方(叶子结点),此时就把tmp移到i的位置

第二段代码:

high如果放到最后一个元素上,所起的作用和放在右树那边的high是相同的 因为如果i已经移动到右数的右叶子,那么j肯定也是大于放在最后一个元素的high的(因为这样的话j和左边的high所处的级数相同,但是下标比左high大,说明已经超出列表的范围)
def heap_sort(li):
    n = len(li)
    for i in range((n-2)//2, -1, -1): #(n-2)//2开始,0结束,步长为-1;n-1是最后一个元素的下标;
        #孩子找父亲的公式是(i-1)//2,把n-1带入,所以是(n-2)//2  <最下面结点的上一层结点,从这一小块树开始>
        # i代表建堆的时候那块树的部分的根的下标
        sift(li,i, n-1) #high如果放到最后一个元素上,所起的作用和放在右树那边的high是相同的 因为如果i已经移动到右数的右叶子
                        #那么j肯定也是大于放在最后一个元素的high的
        #堆建完了
        

第三段代码:(是heap_sort的一部分)

挨个出数

是i-1的理由

for i in range(n-1,-1,-1):
    #i指向当前堆的最后一个元素(到这里堆就排完了,从大到小,把最小的和最大的两个元素交换)
    li[0],li[i]=li[i],li[0] #触发堆的向下调整的性质,并且把当前无序区最大的数排进来
    sift(li,0,i-1) # 因为最后一个已经是排好序的了,i需要往前一位进入无序区排(成为新的high)
#每一次循环排好一个数

(因为最后一个已经没有了,i需要往前一位)(这就是新的high了)

时间复杂度O(nlogn) <sift是nlogn,heap_sort部分也是nlogn>

Python右内置函数heapq

3.topk问题

网课:

29topk问题_哔哩哔哩_bilibili

各种方法的时间复杂度:

排序后切片 O(nlogn)

冒泡,选择,插入排序 O(kn)

堆排序 O(nlogk)

堆排序思路:

取列表前k个元素建立一个小根堆,堆顶就是目前第k大的数;依次向后遍历原列表,对于列表中的元素,如果小于堆顶,则忽略该元素;如果大于堆顶,则将堆顶更换为该元素,并且对堆进行一次调整。遍历列表所有元素后,倒序弹出堆顶。

代码:

def sift(li,low,high): #li:列表 low:堆的根节点位置 high:堆的最后于各元素的位置
    i = low #i最开始指向根节点
    j = 2*i+1 #j开始是左孩子
    tmp = li[low] #把堆顶存起来
    while j <= high: #只要j位置有数(如果j>high,就说明j已经到列表之外了)
        if j+1<=high and li[j+1] < li[j]: #如果右孩子有并且比较小(就是两个中选择更小的)
            j=j+1  #j指向右孩子
        if li[j] < tmp:
            li[i] = li[j] #把大的跟小的互换位置
            i= j #往下看一层
            j =2*i+1
        else: #tmp比它现在所在位置的两个孩子都小,把tmp放到i的位置上,直接结束(顺序正好是对的)
            li[i] = tmp #把tmp放到某一级领导位置上
            break
    else:
        li[i] = tmp #i已经指向与high同级的地方(叶子结点),此时就把tmp移到i的位置

def topk(li,k):
    heap=li[0:k]  #取出k个元素
    for i in range ((k-2)//2,-1,-1): #建堆
        sift(heap,i,k-1)
    for i in range(k,len(li)-1): #遍历列表中的所有元素,如果比根大,那么就加入进堆中并调整堆的顺序
        if li[i] > heap[0]:
            heap[0] = li[i]
            sift(heap,0,k-1)
    for i in range(k - 1, -1, -1): #出数
        # i指向当前堆的最后一个元素
        heap[0], heap[i] = heap[i], heap[0]  # 触发堆的向下调整的性质,并且把当前无序区最大的数排进来
        sift(heap, 0, i - 1)  # 因为最后一个已经是排好序的了,i需要往前一位进入无序区排(成为新的high)
         # 每一次循环排好一个数
    return heap

import random
li = list(range(1000))
random.shuffle(li)
print(topk(li,10))

七 归并排序***

网课:

32归并排序归并_哔哩哔哩_bilibili

思路:

1.分解:将列表越分越小,直至分成一个元素

2.终止条件:一个元素是有序的

3.合并:将两个有序列表归并,列表越来越大

代码:

def merge(li,low,mid,high):
    i=low #i和j充当指针的作用,i在左j在右,如果i的数更小,那么i往左,j同理
          #一直到分界线mid和high
    j=mid+1
    ltmp=[] #把比较完的元素放到这个空列表中
    while i<=mid and j<=high:#只要左右两边都有数
        if li[i]<li[j]:
            ltmp.append(li[i])
            i+=1
        else:
            ltmp.append(li[j])
            j+=1
        #while运行完,肯定有一部分没数了,让有数的地方继续输出
    while i<=mid:
        ltmp.append(li[i])
        i+=1
    while j<=high:
        ltmp.append(li[j])
        j+=1
    li[low:high+1]=ltmp#从low到high这一段范围里

def merge_sort(li,low,high): #low是最小元素, high是最大元素
    if low<high:#至少有两个元素
        mid = (low+high)//2
        merge_sort(li,low,mid) #把左边一部分排序,左边一部分再分成两部分排序....low,mid,high会发生改变,这是结果出现的原因
                               #一直到有一方只剩一个元素为止
        merge_sort(li,mid+1,high)#左边那一层的结束,右边那一层的开始,类似于堆的结构!!

        merge(li,low,mid,high)#运行到每一层的最后,就会排序一次

merge就是把列表拆分的过程,下图为示意图

时间复杂度:

一次归并复杂度是O(n),有logn层,总的复杂度是O(nlogn)

空间复杂度:

开了ltmp,所以为O(n)

八 小结

网课:

34NB三人组小结_哔哩哔哩_bilibili

排序的稳定性:

几个元素,被比较的属性相同,但是不改变原来它们的先后顺序

比如212,从大到小排是221,第一个2还要在第一个位置,只是第二个2和1交换了,稳定的排序第一个2永远在第二个2前面

挨个移动位置的稳定,不挨个换的不稳定

九 希尔排序

网课:

35希尔排序_哔哩哔哩_bilibili

原理:

首先去一个整数d1=n/2,将元素分为d1个组,每组相邻两元素之间距离为d1,在各组内进行直接插入排序

取第二个整数d2=d1/2,重复上述分组排序过程,直到d1=1,即所有元素在同一组内进行直接插入排序

希尔排序每趟并不使某些元素有序,而是使整体数据越来越接近有序;最后一趟排序使得所有数据有序

先使得每层数都是有序的

分完了之后,再使得每层有序最后一趟相邻两数交换代码:

def insert_sort_gap(li,gap):
    for i in range(gap,len(li)): #i表示摸到的牌的下标 j是有序区
        tmp=li[i]
        j = i- gap #j指手里的牌(最开始第一个的牌)
        while j>=0 and li[j] > tmp : #j<0 则说明已经移到了第一个位置,li[j] > tmp 就说明顺序正好是对的,然后放tmp的右边
            li[j+gap] = li[j] #每次都往后移
            j -= gap
        li[j+gap]=tmp #把tmp插入到j的后面

def shell_sort(li):
    d=len(li)//2
    while d>=1:
        insert_sort_gap(li,d)
        d //= 2

与插入排序的区别就是,插入排序使一个个插,而希尔排序是跳过一大段来插入,然后把gap(插值)逐渐缩小,最终就排好序啦

十 计数排序

网课:

37计数排序_哔哩哔哩_bilibili

概念:先对列别排序,每种类别的元素有几个

代码:

def count_sort(li,max_count=100): #max_count是类别的个数
    count = [0 for _ in range(max_count+1)]#从创建max_count个0
    for val in li:
        count[val] += 1 #count当中对应的val计数+1
    li.clear() #li被计数完成就可以清空了
    for ind,val in enumerate(count):
        for i in range(val):#对于每种ind(ind本来就是有序排序),把它里面的数全部都加进来
            li.append(ind)
import random
li=[random.randint(0,100) for _ in range(1000)] #意思是生成1000个0到100的数
print(li)
count_sort(li)
print(li)

时间复杂度O(n)

限制是要知道数的范围

十一 桶排序

思路:

把元素分在不同的桶里,再对每个桶中的元素排序(可以用插入排序)

代码:

def bucket_sort(li,n=100,max_num=10000):#n是桶的个数,最大值是10000
    buckets= [[] for _ in range(n)] #创建桶 这个用法是创建n个列表元素<创建一个列表有n个元素>,0~n-1
    for var in li:
        i = min(var//(max_num//n),n-1)#i表示把元素放在哪个桶内<现在有99个桶,每个桶是0~100>
                                      #eg. 0-->0, 86-->0, 101-->1
                                      #如果是第10000个数,也要放在第99号桶,避免出现漏出的情况
        buckets[i].append(var) #把var加入到桶里,append是向后插入
        for j in range(len(buckets[i])-1,0,-1): #所以这时候的冒泡要反着写
            if buckets[i][j]<buckets[i][j-1]:
                buckets[i][j], buckets[i][j-1]=buckets[i][j-1],buckets[i][j]
            else:
                break
    sorted_list = []
    for buc in buckets:#buc是一个列表,里面是n个重复的值
        sorted_list.extend(buc)
    return sorted_list

平均时间复杂度O(n+k) <n是数组长度,k是桶的个数>

最坏时间复杂度O((n^2) * k)<如果特别不巧全都在一个桶里,那样就没有意义分桶了>

空间复杂度O(nk)<k个桶,每个桶里n个数>

十二 基数排序 

网课:40基数排序介绍_哔哩哔哩_bilibili

利用多关键字进行排序

代码:

def radix_sort(li):
    max_num=max(li) #最大值, 是99就做2次,888做3次,几位数就做几次
    it = 0; #迭代的次数
    while 10**it <= max_num:
        buckets =[[] for _ in range(10)]#分10个桶,每次运行到这里的时候桶里的数都会刷新
        for var in li:
            # 987 it=1 987//10-->98 98%10=8  it=2 987//100-->9
            digit = (var //10**it)%10 #取每个数不同的位进行排序
            buckets[digit].append(var)#排序好几次,每个数字所在的每个位分别代表这个数的属性 eg:51,既在一号桶里,也在五号桶里
            #比如:4个数,53,62,99,71,0号桶:1,3,6,9;1号桶:5,6,7,9
        # 分桶完成
        li.clear()#每次分完也会把li清空
        for buc in buckets:
            li.extend(buc)#将这次分完的结果放到li里,也就是每次都在更新,li的顺序越来越整齐
        #把数重新写回li
        it += 1

和桶排序的区别是,桶排序只要装入一次桶,然后在桶内排序;而基数排序是多次装桶,最后桶里的数,桶和桶之间都是有序的

时间复杂度:O(kn):k表示最大的位数,n是每一次执行所执行的次数(for循环复杂度是n)

空间复杂度O(k+n)

十三 一些补充

python 类中的self:

Python self 理解_一只特立独行的猪工作室的博客-CSDN博客

十四 数据结构

1.列表

网课:

48列表_哔哩哔哩_bilibili

c和python列表的区别<c直接存,python存地址>

插入和删除元素的时间复杂度是O(n)<要把后面的数据往前挪>

2.栈

(1).概念

网课:

49栈的介绍_哔哩哔哩_bilibili

特点:后进先出

模拟代码:

class Stack:
    def __init__(self):
        self.stack =[] #添加stack属性
    def push(self,element):
        self.stack.append(element)
    def pop(self):
        return self.stack.pop()
    def get_top(self):
        if len(self.stack) > 0:
            return self.stack[-1]
        else:
            return None

(2).应用

括号匹配问题:

进一个括号,看栈顶是否和它匹配,匹配就出栈,不匹配就继续留在栈里

代码:

class Stack:
    def __init__(self):
        self.stack =[] #添加stack属性
    def push(self,element):
        self.stack.append(element)
    def pop(self):
        return self.stack.pop()
    def get_top(self):
        if len(self.stack) > 0:
            return self.stack[-1]
        else:
            return None
    def is_empty(self):
        return len(self.stack)==0

def brace_match(s):
    match ={'}':'{',']':'[',')':'('}
    stack=Stack()
    for ch in s:
        if ch in {'(','[','{'}:
            stack.push(ch)
        else:#ch in {'}',']',')'}
            if stack.is_empty():
                return False
            elif stack.get_top()==match[ch]:
                stack.pop()
            else:# stack.get_top 不匹配
                return False
    if stack.is_empty():
        return True
    else:
        return False

print(brace_match('[({})]'))

3.队列

网课:

51队列的介绍_哔哩哔哩_bilibili

实现方式:环形队列

队列:先进先出

对空:rear=front

对满:(rear+1)%maxsize=front

让11+1变成0  rear=(rear+1)%maxsize, front同理

插入元素,rear按照进队顺序往前,指向最后一个进队的元素

排出元素,front指向对头元素,也是与rear一个方向移动

代码:

class Queue:
    def __init__(self,size=100):
        self.queue =[0 for _ in range(size)]
        self.size =size
        self.rear = 0 #队尾,进队
        self.front = 0 #队首,出队
    def push(self,element):
        if not self.is_fulled():
            self.rear = (self.rear+1) %self.size
            self.queue[self.rear]=element
        else:
            raise IndexError("Queue is fulled")
    def pop(self):
        if not self.is_empty():
            self.front = (self.front+1)%self.size
            return self.queue[self.front]
        else:
            raise IndexError("Queue is empty.")
    #判断对空
    def is_empty(self):
        return self.rear == self.front
    #判断对满
    def is_fulled(self):
        return (self.rear +1)%self.size == self.front

q = Queue(5)
for i in range(4):
    q.push(i)
print(q.is_fulled())
print(q.pop())
q.push(4)

4.栈和队列的应用

(1). 迷宫问题(栈)---深度优先搜索

网课:

54栈和队列的应用:迷宫问题_哔哩哔哩_bilibili

找到一个能走的方向就一直走(先保存一个优先的方向,比如说按上下左右的优先顺序选择往哪个方向走),保存走过的路径,不能与之前走过的路径重复,如果不能走了就返回到上一个状态

就是深度优先搜索,一条路走到黑(用栈来做)

代码:

maze=[
    [1,1,1,1,1,1,1,1,1,1],
    [1,0,0,1,0,0,0,1,0,1],
    [1,0,0,1,0,0,0,1,0,1],
    [1,0,0,0,0,1,1,0,0,1],
    [1,0,1,1,1,0,0,0,0,1],
    [1,0,0,0,1,0,0,0,0,1],
    [1,0,1,0,0,0,1,0,0,1],
    [1,0,1,1,1,0,1,1,0,1],
    [1,1,0,0,0,0,0,0,0,1],
    [1,1,1,1,1,1,1,1,1,1]
]#1表示不能走,0表示能走
dirs=[#返回一个元组
      #lambda [arg1 [,arg2,.....argn]]:expression
    lambda x,y:(x+1,y),
    lambda x,y:(x-1,y),
    lambda x,y:(x,y-1),
    lambda x,y:(x,y+1)
] #表示四个方向
def maze_path(x1,y1,x2,y2): #(x1,y1)是起点位置,(x2,y2)是终点位置
    stack = []
    stack.append((x1,y1))
    while(len(stack)>0): #如果栈空了就说明没有通路
        curNode=stack[-1] #指向栈顶,栈后进先出<就像是列表的最后一个元素就是栈顶>,
                          #curNode是一个元组,curNode[0]==x,curNode[1]==y表示当前所在的位置<最新的位置>
        if curNode[0]==x2 and curNode[1]==y2:#说明找到了
            for p in stack:
                print(p) #打印路径
            return True
        #x,y四个方向: x-1;x+1;y-1;y+1
        for dir in dirs:
            nextNode=dir(curNode[0],curNode[1])#先按照上的方向走,这个dir走完了之后才换下一个dir,里面把curNode带进去
                                               #计算下一个Node的位置,nextnode也是一个有两个元素的元组
            #如果下一个点能走
            if maze[nextNode[0]][nextNode[1]] == 0:
                stack.append(nextNode)
                maze[nextNode[0]][nextNode[1]] =2 #2表示走过,这样就不会再走一遍走过的路
                break
        else:
            maze[nextNode[0]][nextNode[1]] =2#表示一个都找不到了
            stack.pop()
    else:
        print("没有路")
        return False #没有路

maze_path(1,1,8,8)

(2)迷宫问题(队列)---广度优先搜索

网课:56使用队列进行迷宫问题:介绍_哔哩哔哩_bilibili

                                                                                                                这里的是出列的情况,框里                                                                                                                 的就是在队列里的,队中                                                                                                                   只保存当前的点的位置

同时考虑所有的能走的路,一套路可能会分成几条路,但是所有的路不会再走其他路走过的路

先到的路径最短

要保存路径,还需要一个列表,上面代表元素,下面代表这个元素,是由哪一个元素推到过来的的列表索引值

代码:

from collections import deque
maze=[
    [1,1,1,1,1,1,1,1,1,1],
    [1,0,0,1,0,0,0,1,0,1],
    [1,0,0,1,0,0,0,1,0,1],
    [1,0,0,0,0,1,1,0,0,1],
    [1,0,1,1,1,0,0,0,0,1],
    [1,0,0,0,1,0,0,0,0,1],
    [1,0,1,0,0,0,1,0,0,1],
    [1,0,1,1,1,0,1,1,0,1],
    [1,1,0,0,0,0,0,0,0,1],
    [1,1,1,1,1,1,1,1,1,1]
]#1表示不能走,0表示能走
dirs=[#返回一个元组
      #lambda [arg1 [,arg2,.....argn]]:expression
    lambda x,y:(x+1,y),
    lambda x,y:(x-1,y),
    lambda x,y:(x,y-1),
    lambda x,y:(x,y+1)
] #表示四个方向
def print_r(path):
    curNode = path[-1] #说明最后一个元素就是终点了
    realpath=[]
    while curNode[2]!=-1: #当还没有走到终点的时候,就一直往回退
        realpath.append((curNode[0],curNode[1]))#这个是走到终点了才会用的function,所以realpath可以直接append curnode
                                                #里的信息
        curNode=path[curNode[2]]#curnode[2]记录的是相应元组的索引值,通过path可以返回传送到被推导的上一个
                                #curnode
    realpath.append(curNode[0:2])#把起点放进去<此时已经回退到最后一个位置>
    realpath.reverse()
    for node in realpath:
        print(node)
def maze_path_queue(x1,y1,x2,y2):
    queue = deque()
    queue.append((x1,y1,-1)) #表示最初的点,因为之前没有元素推导出它,所以下标为-1
                             #这里储存的还是一个元组
    path =[] #0,1号位置就是x,y下标,2号位置存的是谁带它来的
    while len(queue) > 0:#队空了就没有路了
        curNode=queue.pop()#完成了出队,并把结点存到了curNode,curNode在这里是有3个元素的元组
                           #第一个和第二个元素是x,y,第三个是推导出这个xy的前一个位置的索引值
                           #curnode的值会不断更新,然后curnode的值会放到path中
        path.append(curNode)#把后面的结点都放进去
        if curNode[0]==x2 and curNode[1]==y2:
            #到终点
            print_r(path)
            return True
        for dir in dirs:#搜索4个方向
            nextNode=dir(curNode[0],curNode[1]) #看能不能走
            if maze[nextNode[0]][nextNode[1]]==0:
                queue.append((nextNode[0],nextNode[1],len(path)-1))
                #nextnode是由curnode推导出来的,三号位是curnode在path里的下标,这个时候curnode存在path的最后一个位置
                #同时后续结点进队
                maze[nextNode[0]][nextNode[1]] = 2 #标志为已经走过
    else:
        print("没有路")
        return False
maze_path_queue(1,1,8,8)

5.链表

网课:59 链表创建和遍历_哔哩哔哩_bilibili

(1). 链表的创建方法

头插法:设置头结点head,让一个新的元素指向原来的头,再让head指向这个新的头

尾插法:设置头尾节点,向后插入一个新的元素,再把尾结点指向这个新的元素

代码:

class Node:
    def __init__(self,item):
        self.item=item
        self.next=None
def create_linklist_head(li):
    head=Node(li[0])
    for element in li[1:]:#因为0上已经有头节点了,lk的元素传入到element中
        node=Node(element)#在element处创建一个新结点
        node.next=head
        head=node
    return head
def print_linklist(lk):
    while lk:#只要lk不是none
        print(lk.item,end=",")
        lk=lk.next
def create_linklist_tail(li):
    head=Node(li[0])
    tail=head
    for element in li[1:]:
        node=Node(element)
        tail.next=node
        tail=node
    return head
lk=create_linklist_tail([1,2,3,4])
print(lk.next.next.item)

(2).链表的插入和删除

网课:

60 链表的插入和删除_哔哩哔哩_bilibili

插入:

删除:

(3).双链表

网课:

61 双链表_哔哩哔哩_bilibili

插入:

删除:

(4). 总结

6.哈希表:直接寻址表+哈希

1)直接寻址表

key是几就放在什么位置上<这个例子只有key>

14%7=0,放在0号位上;22%7=1,放在1号位置上;3%7=3,放在3号位置上......

EG:线性探查:1号位被占用了,就查2号位,依次往后,

         到空的位置还没有出现,则说明这个数 不存在<存在会被放在空位置上>...

         二次探查:1号位被占用了,就查2号位,0号位...<跳着找>

         二度哈希:用好几个函数试)

EG:比如说要查496,496%16=0,在0号位上找<每一位上都以链表的形式存储>

一个基本的哈希表的代码:

class LinkList:
    class Node:
        def __init__(self,item=None):
            self.item=item
            self.next=None
    class LinkListIterator:#迭代器类
        def __init__(self,node):
            self.node=node
        def __next__(self):#用于返回迭代对象内部的下一个元素值
            if self.node:#如果node不是空就返回node
                cur_node=self.node
                self.node=cur_node.next
                return cur_node.item
            else:
                raise StopIteration#如果没有元素可供返回,则抛出 StopIteration异常
        def __iter__(self):#用于返回一个迭代器对象
            return self
    def __init__(self,iterable=None):
        self.head=None
        self.tail=None
        if iterable:
            self.extend(iterable)
    def append(self,obj):#尾插
        s=LinkList.Node(obj)#创建节点
        if not self.head:#如果为空,s为头尾节点
            self.head=s
            self.tail=s
        else:
            self.tail.next=s
            self.tail=s
    def extend(self,iterable):
        for obj in iterable:
            self.append(obj)
    def find(self,obj):
        for n in self:
            if n==obj:
                return True
        else:
            return False
    def __iter__(self):
        return self.LinkListIterator(self.head)
    def __repr__(self):#打印的时候转化成字符串
        return "<<"+",".join(map(str,self))+">>"
    """
    语法:  'sep'.join(seq)
    参数说明
    sep:分隔符。可以为空
    seq:要连接的元素序列、字符串、元组、字典
    上面的语法即:以sep作为分隔符,将seq所有的元素合并成一个新的字符串
    
    map()函数语法:
    第一个参数function以参数序列中的每一个元素调用function函数,
    返回包含每次function函数返回值的新列表
  map(function,iterable,...)
        """
#类似于集合的关系
class HashTable:
    def __init__(self,size=101):
        self.size=size
        self.T=[LinkList() for i in range(self.size)]#创建size个空链表,LinkList是上面的类
    def h(self,k):
        return k%self.size
    def insert(self,k):
        i=self.h(k)
        if self.find(k):
            print("重复的插入")
        else:
            self.T[i].append(k)
    def find(self,k):
        i=self.h(k)
        return self.T[i].find(k)


7.树​​​​​​​

根节点:最上面的那个点

叶子节点:没有孩子的结点

树的深度:有几层就是多深<比如说这个树的深度是4>

树的度:哪个结点的度最大,树的度就是多少<比如说F有3个分叉(这就是度),那么这个树的度就是3>

孩子节点/父结点:A是D的父结点,D是A的子结点

子树:E,I,J,P,Q是一个子树

模拟树的代码:

class Node:
    def __init__(self,name,type=dir):
        self.name=name
        self.type=type #"dir"or"file"
        self.children=[]
        self.parent=None
    def __repr__(self):
        """
        _repr__是Python中的一个特殊方法,
        用于返回对象的可打印字符串表示形式
        """
        return self.name
class FileSyetemTree:
    def __init__(self):
        self.root=Node("/")
        self.now=self.root
    def mkdir(self,name):
        #name以“/”结尾
        if name[-1]!="/":
            name+="/"
        node=Node(name)
        self.now.children.append(node)#在现在结点的子节点下面加结点
        node.parent=self.now#结点往下移动,和最深一层的结点保持距离
    def ls(self):
        return self.now.children#返回节点内容
    def cd(self,name):
        #只支持向下走一层
        if name[-1]!="/":
            name+="/"
        #如果少写了这个部分就自动补全<好像是这样?>
        if name=="../":
            self.now=self.now.parent#返回上一层
            return#空返回<不知道有啥用。。>
        for child in self.now.children:
            if child.name==name:
                self.now=child
                return#运行完一个就会返回<结束>
        raise ValueError("invalid dir")#如果遍历完一轮都没找到就报这个错误
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值