ICode9

精准搜索请尝试: 精确搜索
首页 > 编程语言> 文章详细

【日常系列】赏析《线性结构及其算法》(Python版)

2022-03-20 15:05:45  阅读:111  来源: 互联网

标签:head return Python self next 算法 data def 赏析


线性结构及其算法

一.数组篇

#数组练习说明
# #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

标签:head,return,Python,self,next,算法,data,def,赏析
来源: https://blog.csdn.net/qq_42889517/article/details/123612719

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有