线性结构及其算法
一.数组篇
#数组练习说明
# #arr=[]
# arr=[0]*10 #数组长度为10
# #arr=list[]
#
# #增
# #结尾添加元素,o(1)
# arr.append(80)
# #开头添加元素,o(n)
# arr.insert(0,23)
# #指定索引添加元素(会自动扩容),o(n)
# arr.insert(1,222)
# print(arr)
#ArrayList
class ArrayList:
def __init__(self):
self.data=[]
def isEmpty(self):
return len(self.data)==0
def getSize(self):
return len(self.data)
#增(create)
#o(n)
def add(self,index,e):
self.data.insert(index,e)
#o(n)
def addFirst(self,e):
self.data.insert(0,e)
#o(1)
def addLast(self,e):
self.data.append(e)
#查(read/retrieve):o(1)
def get(self,index):
if index<0 or index>=self.getSize():
raise Exception("get failed,require index>=0 && index< size)")
return self.data[index]
def getLast(self):
return self.get(self.getSize()-1)
def getFirst(self):
return self.get(0)
#o(n)
def contains(self,e):
for num in self.data:
if num==e:
return True
return False
#o(n)
def find(self,e):
for i in range(self.getSize()):
if self.data[i]==e:
return i #返回找到元素的索引
return -1
#改(update)
def set(self,index,e):
if index<0 or index>=self.getSize():
raise Exception("set failed,require index>=0 && index<size")
self.data[index]=e
#删(delete)
#o(n)
def remove(self,index):
if index<0 or index>=self.getSize():
raise Exception("set failed,require index>=0 && index<size")
res=self.data[index]
#移动数据
for i in range(index+1,self.getSize()):
self.data[i-1]=self.data[i]
#注:需要删除数组最后一个元素
self.data.pop()
return res #返回删去的元素
#o(n)
def removeFirst(self):
return self.remove(0)
#o(1)
def removeLast(self):
return self.remove(self.getSize()-1)
#o(n)
def removeElement(self,e):
index=self.find(e)
if index!=-1:
self.data.remove(e)#python的remove方法删除第一次出现的元素#self.data.remove(index)
#拼接字符串
def toString(self):
res="Array:size={}\n".format(self.getSize())#元素个数
res+="["
for i in range(self.getSize()):
res+=str(self.data[i])
if i!=self.getSize()-1:
res+=","
res+="]"
return res
#Text
if __name__ == '__main__':
arrayList = ArrayList()
print(arrayList.isEmpty())
arrayList.addFirst(34)
arrayList.addLast(23)
arrayList.add(2, 50)
print(arrayList.isEmpty())
print(arrayList.toString())
arrayList.remove(1)
print(arrayList.toString())
arrayList.removeElement(1)
print(arrayList.toString())
arrayList1 = ArrayList()
arrayList1.add(0, "hello")
arrayList1.addLast("world")
print(arrayList1.toString())
二.链表篇
1.单向链表
class Node:
def __init__(self,e,next=None): #key 1
self.e=e#元素
self.next=next#索引
def toString(self):
return str(self.e)
class LinkedList:
def __init__(self): #key 2
self.dummyHead=Node(-1)#“尾节点”.next=头节点索引
self.size=0
def getsize(self):
return self.size
def isEmpty(self):
return self.size==0
#retrieve:返回指定索引节点值
#o(n)
def get(self,index):
if index<0 or index>=self.getsize():
raise Exception("get failed, require index>=0&&index<size")
curr=self.dummyHead.next#索引0,开始 #key 3
for i in range(0,index):
curr=curr.next #最后索引:(index-1)+1+0=index
#返回指定索引节点值
return curr.e
#o(1)
def getFirst(self0):
return self.get(0)
def getLast(self):
return self.get(self.size-1)
#update:修改指定索引节点值
#o(n)
def set(self,index,e):
if index<0 or index>=self.getsize():
raise Exception("get failed, require index>=0&&index<size")
curr=self.dummyHead.next#索引0,开始
for i in range(0,index):
curr=curr.next #最后索引:(index-1)+1+0=index
#修改指定索引节点值
curr.e=e
#create:指定索引处插入新节点
#o(n)
def add(self,index,e):
if index < 0 or index >= self.getsize():
raise Exception("get failed, require index>=0&&index<size")
prev=self.dummyHead
for i in range(0,index):
prev=prev.next#索引:index-1
#指定索引插入新节点
prev.next=Node(e,prev.next)#索引:index #key 4
#注:size+1
self.size+=1
#o(1)遍历 数组法:o(n)移位
def addFirst(self,e): #key 5
self.add(0,e)
#o(n) 数组法:o(1)
def addLast(self,e):
self.add(self.size,e)
#delete:删除指定索引处节点,并返回删除的节点值
def remove(self,index):
if index < 0 or index >= self.getsize():
raise Exception("get failed, require index>=0&&index<size")
prev=self.dummyHead
for i in range(0,index):
prev=prev.next
#定位(prev,del)
del_node=prev.next #key 6
#变位
prev.next=del_node.next
del_node.next=None
#注:size-1
self.size-=1
return del_node.e
#o(1)
def removeFirst(self):
self.remove(0)
#o(n)
def removeLast(self):
self.remove(self.size-1)
#o(n)
def removeElement(self,e):
if self.dummyHead.next is None:
raise Exception("removeElement failed, LinkedList is Empty")
#另一种循环遍历形式
#定位(prev,curr)
prev,curr=self.dummyHead,self.dummyHead.next #key 7
while curr:
if curr.e==e:
break
prev=curr
curr=curr.next
#变位
if curr:
prev.next=curr.next
curr.next=None
#是否存在指定元素
def contains(self,e):
curr=self.dummyHead.next
while curr:
if curr.e==e:
return True
curr=curr.next
return False
#列表可视化:a=>b=>c=>null
def toString(self):
res='' #stringbuilder
curr=self.dummyHead.next
while curr:
res+=curr.toString()+"=>"
curr=curr.next
res+="null"
return res
2.双向链表
class Node:
def __init__(self,e,prev=None,next=None): #key 1
self.e=e
self.prev=prev
self.next=next
class DoubleLinkedList:
def __init__(self): #key 2
self.first=None
self.last=None
self.size=0
def getSize(self):
return self.size
def isEmpty(self):
return self.size==0
#找到指定 index 的节点
#O(n)
def node(self,index): #key 3
if index < 0 or index >= self.getSize():
raise Exception("get failed, require index>=0&&index<size")
if index<self.size//2:
curr=self.first
for i in range(0,index):
curr=curr.next
else:
curr=self.last
for i in range(index+1,self.size):
curr=curr.prev
return curr
#retrieve:返回指定索引节点值
#o(n)
def get(self,index):
curr = self.node(index)
return curr.e
#o(1)
def getFirst():
return self.first
def getLast():
return self.last
#update:修改指定索引节点值
#o(n)
def set(self,index,e):
# if index<0 or index>=self.getsize():
# raise Exception("get failed, require index>=0&&index<size")
curr = self.node(index)
curr.e=e
#create:指定索引处插入新节点
# o(n)
def add(self, index, e):
if index < 0 or index >= self.getsize():
raise Exception("get failed, require index>=0&&index<size")
#找位(old,prev)
old_node=self.node(index)
prev=old_node.prev
#新建三节点(此时为去向关系,两个)
new_node=Node(e,prev,old_node) #key 4
#设置三节点关系(此时为回向关系,两个)
prev.next=new_node
old_node.prev=new_node
self.size+=1
def addFirst(self,e): #key 5
#新建一节点(前后为None)
new_node=Node(e)
if not self.first:
#头节点为空(没节点),插入节点即是头节点也是尾节点
self.last=new_node
else:#双向性:去向一个,回向一个
new_node.next=self.first
self.first.prev=new_node
self.first=new_node
self.size+=1
def addLast(self,e):
#新建节点(前后为None)
new_node=Node(e)
if not self.last:
#尾节点为空(没节点),插入节点即是尾节点也是头节点
self.first=new_node
else:#双向性:去向一个,回向一个
new_node.prev=self.last
self.last.next=new_node
self.last=new_node
self.size+=1
#delete:删除指定索引节点,并返回删除的节点值
def remove(self,index):
if index < 0 or index >= self.getsize():
raise Exception("get failed, require index>=0&&index<size")
#找位(prev,del,next)
del_node=self.node(index)
e=del_node.e
prev_node,next_node=del_node.prev,del_node.next #key 6
#新关联(双向性)
prev_node.next=next_node
next_node.prev=prev_node
#断开
del_node.next=None
del_node.prev=None
self.size-=1
return e
def removeFirst(self):
if not self.first:
raise Exception("不能在空链表中删除节点")
e=self.first.e
#找位(first,second) #key 7-1
second_node=self.first.next
#断开
if not second_node:
self.first=None
self.last=None
else:
self.first.next=None
second_node.prev=None
self.first=second_node
self.size-=1
return e
def removeLast(self):
if not self.Last:
raise Exception("不能在空链表中删除节点")
e=self.last.e
#找位(prev,last) #key 7-1
prev_node=self.last.prev
#断开
if not prev_node:
self.first=None
self.last=None
else:
prev_node.next=None
self.last.prev=None
self.last=prev_node
self.size-=1
return e
三.栈篇(先进后出)
1.数组实现栈(组尾作为栈顶)
class DynamicArrayStack:
def __init__(self):
self.data=[]
def getsize(self):
return len(self.data)
def isEmpty(self):
return len(self.data)==0
#o(1)
def push(self,e):
self.data.append(e)
def pop(self):
return self.data.pop()
def seek(self):
return self.data[self.getsize()-1]
2.链表实现栈(链头做栈顶)
from line.linkedlist.LinkedList import LinkedList
class LinkedListStack():
def __init__(self):
self.linkedList=LinkedList()
def getsize(self):
return self.linkedList.getSize()
def isEmpty(self):
return self.linkedList.isEmpty()
#o(1)
def push(self,e):
self.linkedList.addFirst()#self.linkedList.addLast(),o(n)
#o(1)
def pop(self):
return self.linkedList.removeFirst()#self.linkedList.removeLast(),o(n)
def peek(self):
return self.linkedList.getFirst()
四.队列篇(先进先出)
1.基本数组实现队列
class ArrayQueue:
def __init__(self):
self.data=[] #key 1
def getsize(self):
return len(self.data)
def isEmpty(self):
return len(self.data)==0
def getfront(self):
if self.isEmpty():
raise Exception("queue is empty")
return self.data[0]
#o(1)
def enqueue(self,e):
self.data.append(e)
#o(n)
def dequeue(self,e):
if self.isEmpty():
raise Exception("queue is empty")
res=self.data[0]
for i in range(1,len(self.data)): #key 2
self.data[i-1]=self.data[i]
self.data.pop()
return res#返回出队元素
def toString(self):
res="Queue:队首["
for i in range(self.getsize()):
res+=str(self.data[i])
if i!=self.getsize()-1:
res+=","
res+="]"
return res
2.循环数组实现队列(需要维护resize())
class LoopQueue:
def __init__(self,capacity=20):
self.data=[0]*capacity
self.head=self.tail=0 #key 1
self.size=0
#为了更好区分队满,队空情况:牺牲一个capacity
def getCapacity(self):
return len(self.data)-1 #key 2
def getSize(self):
return self.size
def isEmpty(self):
return self.head==self.tail #key 3
def getFront(self):
if self.isEmpty():
raise Exception("无队列元素")
return self.data[self.head]
def resize(self,new_capacity): #key 4
new_data=[None]*(new_capacity+1)
for i in range(self.size):
new_data[i]=self.data[(i+self.head)%len(self.data)]
self.data=new_data
self.head=0
self.tail=self.size
#o(1) 数组队列o(1)
def enqueue(self,e):
#判断队满,扩容 #key 5-1
if (self.tail+1)%len(self.data)==self.head:
self.resize(self.getCapacity()*2)
self.data[self.tail]=e #key 5-2
#注
self.size += 1
self.tail=(self.tail+1)%len(self.data)
#o(1) 数组队列o(n)
def dequeue(self):
if self.isEmpty():
raise Exception("队列无元素")
#record
res=self.data[self.head]
self.data[self.head]=None #key 6-1
#注
self.size-=1
self.head=(self.head+1)%len(self.data)
#缩容 #key 6-2
if self.size==self.getCapacity()//2:
self.resize(self.getCapacity()//2)
return res
def toString(self):
#属性
res="Queue:size={},capacity={}\n".format(self.size,self.getCapacity())
res+="队首:["
i=self.head
while i!=self.tail:#非空
res+=str(self.data[i])
i=(i+1)%len(self.data)
if (i+1)%len(self.data)!=self.tail:#终止
res+=","
res+="]"
return res
3.1单链表队列1
from line.linkedlist.LinkedList import LinkedList
class LinkedListQueue:
def __init__(self):
self.data=LinkedList()
def getsize(self):
return self.data.getSize()
def isEmpty(self):
return self.data.isEmpty()
def getFront(self):
if self.isEmpty():
raise Exception("queue is empty")
return self.data.getFirst()
#o(n)
def enqueue(self,e):
self.data.addLast(e)
#o(1)
def dequeue(self):
if self.isEmpty():
raise Exception("queue is empty")
return self.data.removeFirst()
def toString(self):
res="Queue:队首["
for i in range(self.getsize()):
res+=str(self.data.get(i))
if i!=self.data.getSize()-1:
res+=","
res+="]"
return res
3.2单链表队列2(优化)
class Node:
def __init__(self,e,next):
self.e=e
self.next=next
def toString(self):
return str(self.e)
class LinkedListQueue:
#区别于slef.dummyhead=Node[-1]
def __init__(self): #key 1
self.head=None
self.tail=None
self.size=0
def getSize(self):
return self.size
def isEmpty(self):
return self.size==0
#o(1) #使用self.tail,避免了Loop
#新建队尾节点方式
def enqueue(self,e):
new_node=Node(e)
if not self.tail:
self.head=new_node
self.tail=self.head
else:
self.tail.next=new_node
self.tail=new_node
#注
self.size+=1
#o(1)
def dequeue(self):
if self.isEmpty():
raise Exception("queue is empty")
#类比单链表中remove()代码
#定位
del_node=self.head
#换位
self.head=self.head.next
del_node.next=None
#只有一个节点的情况,即出队后为空(单链表代码中不考虑此情况,因为用的dummyhead)
if not self.head:
self.tail=None
#注
self.size-=1
return del_node.e
def toString(self):
res="Queue:队首["
curr=self.head
while curr:
res+=curr.toString()+"->"
curr=curr.next
res+="null]"
return res
对比性能:
(少数据量)链表队列2(优化>循环数组队列(需要维护resize()) >基本数组队列
(多数据量)循环数组队列(需要维护resize())> 链表队列2(优化>基本数组队列
四.算法篇
1.双指针技巧
1.1快慢指针(lc283)
from typing import List
#o(n)+o(1)
#交换法
def moveZeroes1(self,nums:List[int])->None:
slow=fast=0
while fast<len(nums):
if nums[fast]!=0:#减少交换次数
nums[slow],nums[fast]=nums[fast],nums[slow]
slow+=1
fast+=1
#赋值法(优化)
def moveZeroes2(self,nums:List[int])->None:
slow=fast=0
while fast<len(nums):
if nums[fast]!=0:#减少赋值次数
nums[slow]=nums[fast]
slow+=1
fast+=1
for i in range(slow,len(nums)):
nums[i]=0
1.2对撞指针
from typing import List
class Solution:
def reverseString(self,s:List[str])->None:
left,right=0,len(s)-1
while left<right:
s[left],s[right]=s[right],s[left]
left,right=left+1,right-1
s=["h","e","l","l","o"]
Solution().reverseString(s)
print(s)
2.递归编程技巧
def walkStair(n: int) -> int:
if n == 1:
return 1
if n == 2:
return 2
return walkStair(n - 1) + walkStair(n - 2)
def fibonacci(n: int) -> int:
if n == 1:
return 1
# bug 修复:斐波那契数列:1,1,2,3,5,8,13.....
if n == 2:
return 1
return fibonacci(n - 1) + fibonacci(n - 2)
#1+2+3+...+n
def sum(n: int) -> int:
if n == 1:
return 1;
tmp=sum(n - 1)
return n + tmp
def sum1(n: int) -> int:
if n == 1:
return 1;
res = n + sum2(n - 2)
return res
def sum2(n: int) -> int:
if n == 1:
return 1;
res = n + sum(n - 3)
return res
#递归:2-1-1-2
def a(times: int) -> None:
if times == 0:
return
print("前参数 times = {}".format(times))
a(times - 1)
print("后参数 times = {}".format(times))
#栈溢出(无终止条件)
def a() -> None:
print("调用方法 a()")
a()
print("调用本身结束")
3.排序算法
基本排序
3.1冒泡排序(Bubble)
#时间复杂度o(n^2),空间复杂度o(1)
def sort(data):
if data is None or len(data)<=1:
return
for round in range(1,len(data)+1):
has_swap=False#本身就已经排好序,就一轮比较结束循环
#控制每轮比较次数
compare_times=len(data)-round
for i in range(compare_times):
if data[i]>data[i+1]:
data[i],data[i+1]=data[i+1],data[i]
has_swap=True
if not has_swap:
break
if __name__=='__main__':
data=[12,23,36,9,24,42]
sort(data)
print(data)
3.2选择排序(Selection )
#时间复杂度o(n^2),空间复杂度o(1)
def sort(data):
if data is None or len(data)<=1:
return
for i in range(len(data)):
#在[i,len(data))中寻最小值
min_num_index =i
for j in range(i+1,len(data)):
if data[j]<data[min_num_index]:
min_num_index=j
#交换
data[i],data[min_num_index]=data[min_num_index],data[i]
if __name__=='__main__':
data=[12,23,36,9,24,42]
sort(data)
print(data)
3.3.1插入排序(Insertion,间隔为1 )
#o(n^2),o(1)
#交换法
def sort1(data):
if data is None or len(data)<=1:
return
for i in range(1,len(data)):
# 较小元素向前换位
tmp=data[i]
for j in range(i,0,-1):
if tmp<data[j-1]:
data[j],data[j-1]=data[j-1],data[j]
else:
break
#赋值法(优化)
def sort2(data):
if data is None or len(data)<=1:
return
for i in range(1,len(data)):
# 较大元素向后赋值
tmp=data[i]
j=i
while j>0:
if data[j-1]>tmp:
data[j]=data[j-1]
else:
break
j -= 1
#注
data[j]=tmp
if __name__=='__main__':
data=[12,23,36,9,24,42]
sort2(data)
print(data)
3.3.2希尔排序(优化的插入排序,间隔为h)(Shell)
优点:处理多数据数据
#o(n^3/2),o(n)
def sort1(data):
if data is None or len(data)<=1:
return
# list=[1,4,13,,,,,,]
list=[]
if len(data)<3:
list.append(1)
h=k=1
while h<=len(data)//3:
list.append(h)
k+=1
h = (pow(3, k) - 1) // 2
#希尔排序
for k in range(len(list)-1,-1,-1):
h=list[k]#选间隔
for i in range(h,len(data)):
for j in range(i,h-1,-h):#j,j-h
if data[j]<data[j-h]:
data[j],data[j-h]=data[j-h],data[j]
else:
break
#o(n^3/2),o(1)(通过近似公式,优化空间复杂度)
def sort2(data):
if data is None or len(data)<=1:
return
#递增序列:h=1,4,13,,,,,,
h=1
while h<len(data)//3:
h=3*h+1
while h>=1:
for i in range(h,len(data)):
for j in range(i,h-1,-h):
if data[j]<data[j-h]:
data[j],data[j-h]=data[j-h],data[j]
else:
break
h//=3
if __name__=='__main__':
data=[2, 5, 1, 23, 22, 33, 56, 12, 5, 3, 5, 6, 8, 2, 3, 4]
sort1(data)
print(data)
sort2(data)
print(data)
3.1-3.3性能比较(o(n^2))
插入(不稳定-相同元素会改变位序)>选择>冒泡
3.4归并排序(Merge,稳定)
#自顶朝下SORT,o(nlogn),o(n)
def sortUB(data):
if data is None or len(data)<=1:
return
tmp=[0]*len(data)
#总问题
sortR(data,0,len(data)-1,tmp)
#子问题
def sortR(data,left,right,tmp):
#终止条件
if left==right:
return
#子问题分解
mid=(left+right)//2
sortR(data,left,mid,tmp)
sortR(data,mid+1,right,tmp)
#合并两个有序数组
merge1(data,left,mid,right,tmp)
#自顶朝下,合并两个有序数组(data->tmp->data)
def merge1(data,left,mid,right,tmp):
tmp_pos,i,j=left,left,mid+1
#写法一
#将两区间的元素按照顺序拷贝到临时的数组中
while i<=mid and j<=right:
if data[i]<=data[j]:
tmp[tmp_pos]=data[i]
tmp_pos,i=tmp_pos+1,i+1
else:
tmp[tmp_pos]=data[j]
tmp_pos,j=tmp_pos+1,j+1
#如果左边还有元素,则直接将左边的元素拷贝到临时数组(注:已有序)
while i<=mid:
tmp[tmp_pos]=data[i]
tmp_pos,i=tmp_pos+1,i+1
#如果右边还有元素,则直接将右边的元素拷贝到临时数组
while j<=right:
tmp[tmp_pos] = data[j]
tmp_pos, j = tmp_pos + 1, j + 1
#写法2
# for tmp_pos in range(left, right + 1):
# if i == mid + 1:
# tmp[tmp_pos] = data[j]
# j += 1
# elif j == right + 1:
# tmp[tmp_pos] = data[i]
# i += 1
# elif data[i] <= data[j]:
# tmp[tmp_pos] = data[i]
# i += 1
# else:
# tmp[tmp_pos] = data[j]
# j += 1
#拷贝()
for tmp_pos in range(len(tmp)):
data[left]=tmp[tmp_pos]
left+=1
#合并两个有序数组(tmp->data)
def merge2(data,left,mid,right,tmp):
for i in range(left,right+1):
tmp[i]=data[i]
#写法一:
i,j=left,mid+1
for k in range(left,right+1):
if i==mid+1:
data[k] = tmp[j]
j += 1
elif j==right+1:
data[k] = tmp[i]
i += 1
elif tmp[i]<=tmp[j]:
data[k] = tmp[i]
i += 1
else:
data[k]=tmp[j]
j+=1
#写法二:
# while i<=mid and j<=right:
# if tmp[i]<=tmp[j]:
# data[k]=tmp[i]
# k,i=k+1,i+1
# else:
# data[k]=tmp[j]
# k,j=k+1,j+1
# # 如果左边还有元素,则直接将左边的元素拷贝到data数组(注:已有序)
# while i <= mid:
# data[k] = tmp[i]
# k, i = k + 1, i + 1
# # 如果右边还有元素,则直接将右边的元素拷贝到data数组
# while j <= right:
# data[k] = tmp[j]
# k , j = k + 1, j + 1
#自底朝上SORT,o(nlogn),o(n)
def sortBU(data):
if data is None or len(data)<=1:
return
tmp=[0]*len(data)
size=1#子数组长度
while size<len(data):
#left,mid,right
for left in range(0,len(data)-size,2*size):
mid=left+size-1
right=min(left+2*size-1,len(data)-1)
#合并
merge1(data,left,mid,right,tmp)
size+=size
if __name__ == '__main__':
data = [34, 33, 12, 78, 21, 1, 98, 100]
sortUB(data)
print(data)
3.5.1快速排序之二向切分(Quick 1)
缺点:不适用于很多重复元素的数组
#o(nlogn)
def sort(data):
if data is None or len(data)<=1:
return
sortR(data,0,len(data)-1)
#递归子问题
def sortR(data,lo,hi):
#终止条件
if lo>=hi:
return
#公式
j=partition(data,lo,hi)
sortR(data,lo,j-1)
sortR(data,j+1,hi)
def partition(data,lo,hi):
pivot=data[hi]
#快慢指针(两个:great作为快指针,less作为慢指针)
less=great=lo
while great<=hi-1:
if data[great]<pivot:
data[less],data[great]=data[great],data[less]
less+=1
great+=1
data[less],data[hi]=data[hi],data[less]
return less
if __name__=='__main__':
data=[34,33,12,78,21,1,98,100]
sort(data)
print(data)
3.5.2快速排序之三向切分(Quick 2,优化Quick 1)
#o(nlogn)
def sort(data):
if data is None or len(data)<=1:
return
sortR(data,0,len(data)-1)
def sortR(data,lo,hi):
#终止条件
if lo >=hi:
return
#公式
j,k=partition(data,lo,hi)
sortR(data,lo,j-1)
sortR(data,k+1,hi)
def partition(data,lo,hi):
# 快慢指针(三个)
pivot = data[hi]
i = lo # 快指针
less, great = lo, hi # 慢指针
while i <= great:
if data[i] < pivot:
data[i], data[less] = data[less], data[i]
i, less = i + 1, less + 1
elif data[i] > pivot:
data[i], data[great] = data[great], data[i]
great = great - 1#注意:交换后data[i]=pivot->else()
else:
i += 1
return less,great
if __name__ == '__main__':
data = [34, 33, 12, 78, 21, 1, 98, 100]
sort(data)
print(data)
其他排序
3.6桶排序(Bucket)
#线性时间复杂度o(n)(n≈m时,o(nlogn/m))
from line.algo.sort import QuickSorter
def sort(data):
if data is None or len(data)<=1:
return
#创建m个空bucket(bucket_num)
max_value=data[0]
for d in data:
max_value=max(max_value,d)
bucket_num=max_value//10+1
buckets=[None]*bucket_num
#元素添加至对应bucket
for i in range(len(data)):
#计算当前元素应该被分配到哪一个桶里
index=data[i]//10
#注
if buckets[index]==None:
buckets[index]=[]
buckets[index].append(data[i])
#对每一bucket中元素排序
for i in range(bucket_num):
bucket_data=buckets[i]
if bucket_data is not None:
QuickSorter.sort(bucket_data)
#从buckets中拿到排序后的数组
index=0
for i in range(bucket_num):
#第i个桶
bucket_data=buckets[i]
#在其取元素
if bucket_data is not None:
for j in range(len(bucket_data)):
data[index]=bucket_data[j]
index+=1
if __name__ == '__main__':
data=[2, 5, 1, 23, 22, 33, 56, 12, 5, 3, 5, 6, 8, 2, 3, 4]
sort(data)
print(data)
3.7计数排序(Counting,稳定)
适用:数组个数n > 数据范围k(max-min)
#线性时间复杂度o(n)
def sort(data):
if data is None or len(data)<=1:
return
#计算数据范围(max-min),并初始化计数器
max_num=min_num=data[0]
for i in range(1,len(data)):
max_num=max(max_num,data[i])
min_num=min(min_num,data[i])
count=[0]*(max_num-min_num+1)#例如:4-3=1,但两个数,所以+1
#计数与计数累加
for i in range(len(data)):
count[data[i]-min_num]+=1
for i in range(1,len(count)):#注:len(count)
count[i]+=count[i-1]
#利用计数信息求出排序数组(先保存至output中),并拷贝至data[i]
output=[0]*len(data)
for i in range(len(data)-1,-1,-1): #顺序遍历会导致不稳定
k=count[data[i]-min_num]-1#注:计算对应索引=计数值-1
output[k]=data[i]
#注
count[data[i]-min_num]-=1
for i in range(len(data)):
data[i]=output[i]
if __name__ == '__main__':
data = [4, 2, -2, 8, 3, 3, 1]
sort(data)
print(data)
3.8基数排序(Radix)
#线性时间复杂度o(n),空间复杂度o(n)
def sort(data):
if data is None or len(data)<=1:
return
#找到最大值,从而找到最大位数
max_num=data[0]
for i in range(len(data)):
max_num=max(max_num,data[i])
#exp:方便对应到正确的位
exp=1
while max_num//exp>0:#例如88/100=0
countSort(data,exp)
exp*=10
#分别对个位进行计数排序
def countSort(data,exp):
#位数digit:0-9
count=[0]*10
#计数与计数累加
for i in range(len(data)-1,-1,-1):
digit=(data[i]//exp)%10
count[digit]+=1
for i in range(1,len(count)):
count[i]+=count[i-1]
#利用计数信息求出排序数组(先保存至output中),并拷贝至data[i]
output=[0]*len(data)
for i in range(len(data)-1,-1,-1):
digit=(data[i]//exp)%10
k=count[digit]-1
output[k]=data[i]
#注
count[digit]-=1
for i in range(len(data)):
data[i]=output[i]
if __name__ == '__main__':
data = [4512, 4231, 31923, 2165, 1141]
sort(data)
print(data)
4.二分查找算法
4.1基本二分查找
#适用:有序数组(这里以升序列表为例)
#o(logn),o(1)
#方法1:不断找中间值
def contains(nums,target):
if nums is None or len(nums)==0:
return False
left,right=0,len(nums)-1
while left<=right:
#溢出bug:mid=(left+right)/2(因为int最大值2^31-1=2147483647)
mid=left+(right-right)//2
if target==nums[mid]:
return True
elif target<nums[mid]:
right=mid-1
else:
right=mid+1
return False
#方法二:递归写法
def containsR(nums,target):
if nums is None or len(nums)==0:
return False
return contains_help(nums,0,len(nums)-1,target)
def contains_help(nums,left,right,target):
#终止条件
if left>right:
return False
#公式
mid=left+(right-left)//2
if nums[mid]==target:
return True
elif target<nums[mid]:
return contains_help(nums,left,right-1,target)
else:
return contains_help(nums,left-1,right,target)
4.2二分查找变形
#返回第一个等于Target的index
def firstTargetIndex(nums,target):
if nums is None or len(nums)==0:
return
left,right=0,len(nums)-1
while left<=right:
mid=left+(right-left)//2
if target==nums[mid]:
if mid==0 or nums[mid-1]!=target:
return mid
else:
right=mid-1
elif target>nums[mid]:
left=mid+1
else:
right=mid-1
return -1
#返回第一个大于或等于Target的index
def firstGEtargetIndex(nums,target):
if nums is None or len(nums)==0:
return
left,right=0,len(nums)-1
while left<=right:
mid=left+(right-left)//2
if nums[mid]>=target:
if mid==0 or nums[mid-1]<target:
return mid
else:
right=mid-1
else:
left=mid+1
return -1
#返回最后一个等于Target的index
def lastTargetIndex(nums,target):
if nums is None or len(nums)==0:
return
left,right=0,len(nums)-1
while left<=right:
mid=left+(right-left)//2
if nums[mid]==target:
if mid==len(nums)-1 or nums[mid+1]!=target:
return mid
else:
left=mid+1
elif nums[mid]>target:
right=mid-1
else:
left=mid+1
return -1
# 返回最后一个小于或等于Target的index
def lastLETargetIndex(nums,target):
if nums is None or len(nums)==0:
return
left,right=0,len(nums)
while left<=right:
mid=left+(right-left)//2
if nums[mid]<=target:
if mid==len(nums)-1 or nums[mid+1]>target:
return mid
else:
left=mid+1
else:
right=mid-1
#left>tight没有元素
return -1
4.3IP地址解析器
#空间换时间o(logn)
class IpLocation:
def __init__(self,start_ip,end_ip,location_city):
self.start_ip=start_ip
self.end_ip=end_ip
self.location_city=location_city
#数据预处理
class IpLocationParser:
def __init__(self):
self.sorted_ip_locations=[]
# 1. 读取文件,解析 ip 地址段
with open("D:\install by myself\BaiduNetdiskDownload\新建文件夹(桌面)\douma-algo-master\data\ip_location.txt",'r',encoding="utf-8") as reader:
lines=reader.readlines()
for line in lines:
temps=line.split(" ")#key point
ip_location=IpLocation(self.ip2Score(temps[0]),self.ip2Score(temps[1]),temps[2])
self.sorted_ip_locations.append(ip_location)
# 2. 按照起始 ip 进行升序排列
# 时间复杂度:O(nlogn)
self.sorted_ip_locations.sort(key=lambda x:x.start_ip)
#将ip转成长整型
def ip2Score(self,ip):
temps1=ip.split(".")
score=256*256*256*int(temps1[0])\
+256*256*int(temps1[1])\
+356*int(temps1[2])\
+int(temps1[3])
return score
# 二分查找指定 ip 对应的城市
# 时间复杂度:O(logn)
def getIpLocation(self, ip):
score1 = self.ip2Score(ip)
# 在 sortedIpLocations 中找到最后一个 startIp 小于等于 score 的这个 ip 段
left, right = 0, len(self.sorted_ip_locations) - 1
while left <= right:
mid = left + (right - left) // 2
if score1 >= self.sorted_ip_locations[mid].start_ip:
if mid == len(self.sorted_ip_locations) - 1 or self.sorted_ip_locations[mid + 1].start_ip > score1:
#注
if score1 <= self.sorted_ip_locations[mid].end_ip:
return self.sorted_ip_locations[mid].location_city
else:
# bug 修复:
# 如果查询的 ip > (mid 对应 ip 段的 startIp) 且 ip < (mid + 1 对应 ip 段的 startIp),
# 但是如果 ip > (mid 对应 ip 段的 endIp),说明 ip 超出了 mid 对应的 ip 段,又不属于 mid + 1 对应 ip 段直接退出即可
break
else:
left = mid + 1
else:
right = mid - 1
return None
if __name__ == '__main__':
te = IpLocationParser()
print(te.getIpLocation("202.101.48.198"))
5.基于链表的算法
5.1基础知识
编写节点类,数组转成链表
class ListNode:
def __init__(self,val,next=None):
self.val=val
self.next=next
def toString(self):
res = ""
curr = self#当前节点
while curr:
res += str(curr.val) + "->"
curr = curr.next
res+="null"
return res
def fromArray(arr):
if arr is None or len(arr)==0:
return None
#创建表头
head=ListNode(val=arr[0])
#创建后面节点
curr=head
for i in range(1,len(arr)):
curr.next=ListNode(val=arr[i])
curr=curr.next
return head
if __name__=='__main__':
head=fromArray([23,12,11,34])
print(head.toString())
链表节点数
from line.algo.linkedlist import ListNode
def count(head):
if head is None:
return 0
# python的for循环不太好遍历链表
cnt,curr=0,head
while curr:
cnt,curr=cnt+1,curr.next
return cnt
def countTarget(head,target):
if head is None:
return 0
cnt,curr=0,head
while curr:
if curr.val==target:
cnt+=1
curr=curr.next
return cnt
if __name__ == '__main__':
head = ListNode.fromArray([23, 12, 11, 34, 12])
print(countTarget(head,12))
lc206(反转链表)
from line.algo.linkedlist import ListNode
class Solution:
# 方法一:迭代解法
def reverseList1(self,head:ListNode)->ListNode:
if head is None or head.next is None:
return head
prev,curr=None,head
while curr is not None:
next=curr.next#"临时储存"
curr.next=prev
prev=curr
curr=next
return prev
# 解法二;递归解法
def reverseList2(self,head:ListNode)->ListNode:
#终止条件
if head is None or head.next is None:
return head
#公式
#递
p=self.reverseList2(head.next)
#归
next=head.next
next.next=head
head.next=None
return p
5.2快慢指针
lc876(返回中间节点)
from line.algo.linkedlist.ListNode import ListNode
class Solution:
def middleNode(self,head:ListNode)->ListNode:
if not head or not head.next:
return head
#慢1快2
slow=fast=head
while fast and fast.next:#key:考虑奇偶
slow,fast=slow.next,fast.next.next
return slow
lc19(删除链表的倒数第N个节点)
from line.algo.linkedlist.ListNode import ListNode
class Solution:
def removeNthFromEnd(self, head: ListNode, n: int) -> ListNode:
dummy_node = ListNode(-1)
#找位
slow=fast=dummy_node
#1.fast先走n+1步
while n>=0:
fast=fast.next
n-=1
#2.slow与fast同步走,等到fast==None,此时slow指向delnode.prev
while fast!=None:
slow,fast=slow.next,fast.next
#删除
del_node=slow.next
slow.next=del_node.next
del_node.next=None
return dummy_node.next
5.3链表排序
lc21(合并两个有序链表)
from line.algo.linkedlist.ListNode import ListNode
class Solution:
# 迭代解法
def mergeTwoLists1(self, l1: ListNode, l2: ListNode) -> ListNode:
if not l1:
return l2
if not l2:
return l1
dummy_node=ListNode(-1)
curr=dummy_node
#l1,l2先各指向head
while l1 and l2:
if l1.val<=l2:
curr.next=l1
l1=l1.next
else:
curr.next=l2
l2=l2.next
curr=curr.next
#注
if l1:
curr.next=l1
if l2:
curr.next=l1
return dummy_node.next
# 递归解法
def mergeTwoLists2(self, l1: ListNode, l2: ListNode) -> ListNode:
#终止条件
if not l1:
return l2
if not l2:
return l1
#公式
if l1.val<=l2.val:
l1.next=self.mergeTwoLists2(l1.next,l2)
return l1
else:
l2.next=self.mergeTwoLists2(l1,l2.next)
return l2
lc23(合并k个升序链表)
import heapq
from typing import List
from line.algo.linkedlist.ListNode import ListNode
class Solution:
#方法一:顺序合并
#o(k^2*n),o(1)
def mergeKLists1(self,lists):
if len(lists)==0:
return None
outputList=lists[0]
for i in range(1,len(lists)):
outputList=self.mergeTwoLists(outputList,lists[i])#体现顺序性
return outputList
def mergeTwoLists(self,l1:ListNode,l2:ListNode)->ListNode:
#迭代解法
if not l1:
return l2
if not l2:
return l1
dummy_node=ListNode(-1)
curr=dummy_node
while l1 and l2:
if l1.val<=l2.val:
curr.next=l1
l1=l1.next
else:
curr.next=l2
l2.next=l2
curr=curr.next
if l1:
curr.next=l1
if l2:
curr.next=l2
return dummy_node.next
#方法二:递归(分治思想)
#时间复杂度:o(k*n*logk),o(logk)-申请栈
def mergeKLists(self,lists:List[ListNode])->ListNode:
if len(lists)==0:
return None
return self.merge(lists,0,len(lists)-1)
def merge(self,lists:List[ListNode],left:int,right:int)->ListNode:
#终止条件(only one list)
if left==right:
return lists[left]
#公式
mid=left+(right-left)//2
merged_left_list=self.merge(lists,left,mid)
merged_right_list=self.merge(lists,mid+1,right)
return self.mergeTwoLists(merged_left_list,merged_right_list)
lc147(对链表进行插入排序)
from line.algo.linkedlist.ListNode import ListNode
class Solution:
def insertionSortList(self,head:ListNode)->ListNode:
if not head and not head.next:
return head
#创建虚拟节点
dummy_node=ListNode[-1]
dummy_node.next=head
prev,curr=head,head.next
while curr:
if curr.val>=prev.val:
prev=curr
curr=curr.next
else:
p=dummy_node
while p.next.val<curr.val:
p=p.next
#将 curr 插入到 p 和 p.next 之间
prev.next=curr.next
curr.next=p.next
p.next=curr
return dummy_node.next
lc148(排序列表)
from line.algo.linkedlist.ListNode import ListNode
class Solution:
#解法一:递归实现
def sortList1(self,head:ListNode)->ListNode:
if not head or not head.next:
return head
#寻找的右表头节点
slow,fast=head,head.next
while fast and fast.next:
slow,fast=slow,fast.next.next
right_head=slow.next
slow.next=None
#子问题
left,right=self.sortList1(head),self.sortList1(right_head)
return self.mergeTwoLists(left,right)
def mergeTwoLists(self, l1: ListNode, l2: ListNode) -> ListNode:
if not l1: return l2
if not l2: return l1
dummy_node = ListNode(-1)
curr = dummy_node
while l1 and l2:
if l1.val <= l2.val:
curr.next = l1
l1 = l1.next
else:
curr.next = l2
l2 = l2.next
curr = curr.next
if l1: curr.next = l1
if l2: curr.next = l2
return dummy_node.next
#解法二:自底朝上实现归并排序
def sortList2(self,head:ListNode)->ListNode:
if not head or not head.next:
return head
dummy_node=ListNode(-1)
dummy_node.next=head
#计算链表的长度
length=0
while head:
length+=1
head=head.next
#分别按step=1,2,4 (两两,四四,八八合并)
step=1
while step<length:
prev,curr=dummy_node,dummy_node.next
while curr:
left=curr
right=self.split(left,step)
#通过split,得到下次处理的链表头
curr=self.split(right,step)
#合并 left 和 right 链表,并获得合并后的最后一个节点(以进行下次合并)
prev=self.merge(left,right,prev)
#step*=2
step<<=1
return dummy_node.next
#通过split(.next)step-1次,寻找到right节点(即分开后,右边链表的头节点)
def split(self,node:ListNode,step:int)->ListNode:
if not node:
return None
for i in range(step-1):
#有了这个判断,最后curr=“right”==None,合并结束
if node.next:
node=node.next
right=node.next
node.next=None
return right
def merge(self,left:ListNode,right:ListNode,prev:ListNode)->ListNode:
curr=prev
while left and right:
if left.val<=right.val:
curr.next=left
left=left.next
else:
curr.next=right
right=right.next
curr=curr.next
if left:
curr.next=left
if right:
curr.next=right
#
while curr.next:
curr=curr.next
return curr