一 汉诺塔
b站网课看的这个
后面又找了个参考的
汉诺塔问题的详细讲解(python版)_汉诺塔python_羊老羊的博客-CSDN博客
两个代码的n都可以看作是第一个盘子里剩余的部分
递归的感觉就像是把重复部分写上,其他的让电脑自己算
二 顺序查找
网课:
没看懂 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.例子
网课:
三 列表排序
将列表排成有序的(内置的sort())
1.冒泡排序
网课:
定义:列表每两个相邻的数,如果后面比前面大,则交换这两个数(升序排列);一趟排序完成后,则无序区减少一个书,有序区增加一个数,要排n-1趟
代码:
最初始的版本:
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.选择排序
网课:
定义:
遍历一遍找到最小的数,再重复一遍找到第二小的数,依次循环
重点:
无序区,有序区,无序区最小数的位置
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)
四 插入排序
参考:Python实现插入排序_插入排序python_神树降临的博客-CSDN博客
原理:
-
将待排序列表的第一个元素当做已排序序列,第二个元素到最后一个元素当成未排序序列。
-
取未排序序列中的第一个数据,插入到已排序序列中顺序正确的位置。将未排序的第一个数据与相邻的前一个数据(已排序序列的最后一个数)据进行比较,如果顺序错误则交换位置,交换位置后继续与相邻的前一个数据进行比较,直到不需要交换则插入完成。每次插入数据后,已排序序列都是排好序的。
-
重复上一步,继续插入下一个数据。每进行一次插入,已排序序列的长度加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个元素
五 快速排序***
网课:
原理:
取一个元素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)堆的向下调整的性质
挨个出数:先排最上面,再挑最下面的,利用堆的向下调整依次出数,再挑选最上面的,直到所有数字都出列
构造堆:从最后一个非叶子结点开始调整为堆
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问题
网课:
各种方法的时间复杂度:
排序后切片 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))
七 归并排序***
网课:
思路:
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)
八 小结
网课:
排序的稳定性:
几个元素,被比较的属性相同,但是不改变原来它们的先后顺序
比如212,从大到小排是221,第一个2还要在第一个位置,只是第二个2和1交换了,稳定的排序第一个2永远在第二个2前面
挨个移动位置的稳定,不挨个换的不稳定
九 希尔排序
网课:
原理:
首先去一个整数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(插值)逐渐缩小,最终就排好序啦
十 计数排序
网课:
概念:先对列别排序,每种类别的元素有几个
代码:
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个数>
十二 基数排序
利用多关键字进行排序
代码:
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.列表
网课:
c和python列表的区别<c直接存,python存地址>
插入和删除元素的时间复杂度是O(n)<要把后面的数据往前挪>
2.栈
(1).概念
网课:
特点:后进先出
模拟代码:
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.队列
网课:
实现方式:环形队列
队列:先进先出
对空: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). 迷宫问题(栈)---深度优先搜索
网课:
找到一个能走的方向就一直走(先保存一个优先的方向,比如说按上下左右的优先顺序选择往哪个方向走),保存走过的路径,不能与之前走过的路径重复,如果不能走了就返回到上一个状态
就是深度优先搜索,一条路走到黑(用栈来做)
代码:
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.链表
(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).链表的插入和删除
网课:
插入:
删除:
(3).双链表
网课:
插入:
删除:
(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")#如果遍历完一轮都没找到就报这个错误