【数据结构与算法系列】初级篇:赏析《线性结构及其算法》(Python版)

本文深入探讨了数据结构中的线性结构,包括数组和ArrayList的实现,以及单向链表、双向链表的创建、插入、删除操作。此外,文章还介绍了栈和队列的基本概念,如数组栈、循环数组队列和链表队列。在排序算法方面,详细阐述了冒泡、选择、插入、希尔、快速和基数排序的工作原理及性能比较。最后,涉及了二分查找在链表中的应用,如寻找中间节点和删除指定位置节点。
摘要由CSDN通过智能技术生成

线性结构及其算法

一.数组篇

#数组练习说明
# #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
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值