leetcode算法学习笔记-复杂度与排序算法(python版)

数组

数组是存放在连续内存空间上的相同类型数据的集合。
数组可以方便的通过下标索引的方式获取到下标对应的数据。

需要两点注意的是:数组下标都是从0开始的。。数组内存空间的地址是连续的。
正是因为数组在内存空间的地址是连续的,所以我们在删除或者增添元素的时候,就难免要移动其他元素的地址。

数组的元素是不能删的,只能覆盖。

在C++中二维数组是连续分布的。像Java是没有指针的,同时也不对程序员暴露其元素的地址,寻址操作完全交给虚拟机。而对于python,使用嵌套列表实现的二维数组,每个一维数组内部是连续分布的,但各个数组分布不连续,使用NumPy库实现的多维数组,它们被存储在一段连续的内存块中,因此它们的地址是连续的。

复杂度

选择排序

实现数组升序

过程描述:从第i个数开始,从i+1到数组最后一个数依次与i比较,将最小的数与i互换位置。

                  .....i+1...................i+2........................................................................i+1.............

                  ...

时间复杂度O(n^2):(i历遍数组n+n-1+....1)+(j历遍i后数n-1+n-2+...+1)+(i与最小数互换n)

空间复杂度O(1):只对i, j, min_num赋值

class select_min:
    def __init__(self,ary):
        self.ary = ary
        
    def num_select(self):
        ##历遍整个数组,获得第i个值
        for i in range(len(self.ary)):
            
            ##数组无值直接返回
            if self.ary == None :
                return self.ary
            
            ##使用中间变量储存值,方便操作
            min_num = self.ary[i]
            
            ##历遍i后的所有值,进行比较,将最小值与第i个值位置互换
            for j in range(i+1,len(self.ary)):
                if self.ary[j] < min_num:
                    min_num = self.ary[j]
                    self.ary[i], self.ary[j] = self.ary[j], self.ary[i]
        return self.ary

if __name__ == "__main__":
    ary = [1,9,5,6,8,3,4,22,4,5,66]
    s = select_min(ary)
    print(s.num_select())

冒泡排序

实现数组升序

过程描述:历遍数组,两两进行比较,并将大的数往右移动一格。

历遍数组过程:第一轮0~N-1,第二轮0~N-2....

时间复杂度:O(n^2):(i历遍数组n-1+n-2+...)+(最坏情况交换n-1+n-2+....)

class bubble_sort(object):
    def __init__(self, ary):
        self.ary = ary

    def sort(self):
        if self.ary == None or len(self.ary) == 0: # 如果数组为空或长度为0
            return self.ary
        for k in range(len(self.ary)-1): # 遍历数组,最后一个数移动后不动
            for i in range(len(self.ary)-k-1): # 遍历数组
                if self.ary[i] > self.ary[i+1]: # 如果当前元素大于下一个元素
                    self.ary[i], self.ary[i+1] = self.ary[i+1], self.ary[i] # 交换两个元素的位置

        return self.ary

if __name__ == '__main__':
    ary = []
    print(bubble_sort(ary).sort())

插入知识点

异或运算:两数不同为1,两数相同为0。可理解为无进位相加,即1^1=0等同于二进制1+1=10,但不进位,等于0。

异或运算的数学性质:

(1)0^N=N,N^N=0

(2)符合交换律与结合律:a^b = b^a,a^b^c = a^(b^c)

(3)异或的结果与顺序无关。

由此我们可以重写交换数模块:

class bubble_sort(object):
    def __init__(self, ary):
        self.ary = ary

    def sort(self):
        if self.ary == None or len(self.ary) == 0: # 如果数组为空或长度为0
            return self.ary
        for k in range(len(self.ary)-1): # 遍历数组,最后一个数移动后不动
            for i in range(len(self.ary)-k-1): # 遍历数组
                if self.ary[i] > self.ary[i+1]: # 如果当前元素大于下一个元素
                    self.ary[i], self.ary[i+1] = self.exchange_num(self.ary[i], self.ary[i+1]) # 交换两个元素
        return self.ary

    ##重写交换数模块
    def exchange_num(self, a, b):
        a = a ^ b #a=a^b,b=b
        b = a ^ b #a=a^b,b=a^b^b=a^0=a
        a = a ^ b #a=a^b^a=b^0=b,b=a
        return a, b


if __name__ == '__main__':
    ary = [5,2,9,6,8,3,4,4]
    print(bubble_sort(ary).sort())

第一种交换方法只适用于python编程语言,因为它利用了Python中的元组解包(tuple unpacking)特性。在 Python 中,可以在一行代码中同时交换两个变量的值,而不需要临时变量。而在许多编程语言中,变量赋值并不直接支持像 Python 这样的元组解包。例如,在 C、Java 和 C++ 中,必须使用临时变量来交换两个值,因为它们不具备像 Python 那样的解包能力。在这些语言中,你不能在一行代码中直接用多元赋值交换变量的值。

异或运算的其他运用:

已知有一个数组,有一种数出现了奇数次,其他数均出现了偶数次。

(1)如何找到奇数次的那个数?

(2)如果奇数次的数变为了两种,如何找到奇数次的那两个数?

要求:时间复杂度O(n),空间复杂度O(1)

(1)

思路:

偶数次异或=0,奇数次异或=奇数次的数。

如[1,2,1,3,1,1,2,3,3],用异或计算时等同1^1^1^1^2^2^3^3^3=0^0^3=3

class find_single_odd_num:
    def __init__(self, ary):
        self.ary = ary

    def EO(self):
        a = 0
        for b in self.ary:
            a ^= b
        return a

if __name__ == '__main__':
    ary = [1,2,1,3,1,1,2,3,3]#1^1^1^1^2^2^3^3^3=0^0^3=3
    print(find_single_odd_num(ary).EO())

(2)

思路:

我们将这个数组设为ori,用8位表示一个字节,设a,b为奇数次数,经过异或运算后最后会剩a^b不等于0,即结果的二进制表示必有1,a与b的二进制表示也不同,一定会有第k位一个是0一个是1,所以我们可以用第k为为1或0来区分a,b。

我们再设一个oth数组,这个数组只包含k位为1(为0同理)的数,对这个数组进行异或运算肯定会是a(或b),再将该数组(oth)的异或计算结果与原(ori)异或计算结果进行异或计算,得到的数会是a^a^b=b(或b^a^b=a)。

class find_twice_odd_num:
    def __init__(self, ary):
        self.ary = ary

    def EO(self):
        a = 0
        b = 0

        for i in self.ary:
            b ^= i #a^b


        right_one = b & (~b + 1) #寻找结果的二进制中最右侧的1,如b = 11101000, right_one = 00001000

        for j in self.ary:
            ##将数组中与right_one与运算结果为1的数异或到a中
            if (j & right_one) == 1:
                a ^= j #a
        return a, a ^ b #a=a,a^a^b=b

if __name__ == '__main__':
    ary = [1,2,1,3,1,1,3,3]
    print(find_twice_odd_num(ary).EO())

插入排序

实现数组升序

过程描述:从0开始逐渐增加历遍范围(0~0,0~1...,0~N),在每个范围内执行比较和移动数的操作。

从最右侧开始与左边的数比较,比左边的数小就交换位置,直到不比左边的数小或者到数组左侧尽头。

时间复杂度:O(n^2):(历遍范围n)+(最坏的交换情况,1+2+...+n-1)在一些良好数据排列下,会优于前两种。

空间复杂度:O(1)

class insertion_sort:
    def __init__(self, ary):
        self.ary = ary

    def exchange(self):
        for n in range(1, len(self.ary)):
            j = n
            while j > 0 and self.ary[j] < self.ary[j-1]: #直到不比左边的数小或者到数组左侧尽头
                self.ary[j], self.ary[j-1] = self.ary[j-1], self.ary[j] #偷懒了,建议用位运算交换
                j -= 1 #向左迭代

        return self.ary


if __name__ == '__main__':
    ary = [7,6,5,4,3,2,1]
    print(insertion_sort(ary).exchange())

二分法

题型1:在升序数组中搜索指定数的位置

过程描述:从数组中心取数与指定数比大小,

中心数<指定数:指定数在右侧数组内,取右侧数组right,取right数组的中心数再次比较,以此类推。

中心数>指定数:指定数在左侧数组内,取左侧数组left,取left数组的中心数再次比较,以此类推。

中心数=指定数:直接返回

时间复杂度:O(log2 n):n个数每次切一刀,相当于2^k=n,k就是log2 n。

class search_num:
    def __init__(self, ary, num):
        self.ary = ary
        self.num = num

    def search(self):

        if self.ary == None or len(self.ary) < 1:
            return print('Please input an array with values.')

        left = 0
        right = len(self.ary)-1

        while left <= right:
            mid = (left + right)//2

            if self.ary[mid] < self.num:
                left = mid + 1

            elif self.ary[mid] > self.num:
                right = mid - 1

            else:
                return print(f'{self.num} is at location {mid+1}')


        return print(f'{self.num} not found in the array.')



if __name__ == '__main__':
    ary = []
    num = 7
    search_num(ary, num).search()

题型2:在升序数组中,找到大于等于指定数的最左侧位置。

过程描述:类似于题型1,不赘述

时间复杂度:O(log2 n)

class SearchNum:
    def __init__(self, ary, num):
        self.ary = ary
        self.num = num

    def search_left(self):
        # 检查数组是否为空
        if self.ary is None or len(self.ary) < 1:
            return print('Please input an array with values.')

        left, right = 0, len(self.ary)-1

        while left < right:
            mid = (left + right) // 2

            if self.ary[mid] < self.num:
                left = mid + 1  # 移动到右半部分
            else:
                right = mid  # mid可能是答案,继续在左半部分查找

        # left是第一个大于或等于num的位置
        if left < len(self.ary):
            return print(f'The leftmost position of {self.num} is at location {left+1}.')
        else:
            return print(f'No element greater than or equal to {self.num} found.')


if __name__ == '__main__':
    ary = [1,3,5,7,7,7,7,7,7,7,7,9,13] 
    num = 7  
    SearchNum(ary, num).search_left()  

题型3:无序数组,相邻数皆不相等,找局部最小值

过程描述:

0位置数小于1位置数,直接返回。

n-1位置数小于n-2位置数,直接返回。

前两种情况不符合,中间必存在局部最小。

取中点数与两侧数比较,如果符合局部最小直接返回。不符合取比中点数小的那一侧继续比较,直到符合为止。

时间复杂度:O(log2 n)

class SearchMinNum:
    def __init__(self, ary):
        self.ary = ary

    def search_min(self):
        # 检查数组是否为空
        if self.ary is None or len(self.ary) < 1:
            return print('Please input an array with values.')


        left, right = 0, len(self.ary)-1

        while left < right:

            if self.ary[left] < self.ary[left+1]:
                return print(left)

            elif self.ary[right] < self.ary[right - 1]:
                return print(right)

            else:
                mid = (left + right)//2
                if self.ary[mid] < self.ary[mid-1] and self.ary[mid] > self.ary[mid-1]:
                    return print(mid)
                elif self.ary[mid] > self.ary[mid-1]:
                    right = mid
                else:
                    left = mid


if __name__ == '__main__':
    ary = [9,8,7,6,5,4,13,19]

    SearchMinNum(ary).search_min()  # 创建搜索对象并执行搜索

对数器

用来测试代码对错

思路:有两套独立方法用于实现某个功能,我们随机生成多次两套相同数据,将数据载入到两个方法中,用两个方法互相验证结果。

以上内容对应b站1.认识复杂度和简单排序算法_哔哩哔哩_bilibili,是自己对课程的理解。编程语言使用的是python。左老师讲的很仔细,很推荐去看看,给左老师个三连,配合食用更佳。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值