前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >数据结构与算法基础-(2)

数据结构与算法基础-(2)

作者头像
用户10920432
发布2024-01-18 17:00:55
1200
发布2024-01-18 17:00:55
举报
文章被收录于专栏:Python数据结构与算法

"时间复杂度"回顾💯

5ceb6ac91b5b4fe49a64ced7252d0078.png
5ceb6ac91b5b4fe49a64ced7252d0078.png

执行次数函数( Asymptotic Notation )举例

阶( Big-0 Notation )

非正式术语

12

O(1)

常数阶

2n+3

O(n)

线性阶

3n^2+2n+1

O(n^2)

平方阶

5log2n+20

O(logn)

对数阶

2n+3nlog2n+19

O(nlogn)

nlogn阶

6n^3+2n^2+3n+4

O(n^3)

立方阶

2^n

O(2^n)

指数阶

由图可知,所消耗的时间从小到大:

O(1)<O(logn)<O(n)<O(nlogn)<O(n^2)<O(n^3)<O(2^n)<O(n!)<O(n^n)

下图表示常见的时间复杂度

85332f4948d242b4b19d10908732283a.jpeg
85332f4948d242b4b19d10908732283a.jpeg

空间复杂度💥

空间复杂度指运行完一个程序所需内存的大小。

利用程序的空间复杂度可以对程序的运行所需要的内存多少有个预先估计。一个程序执行时除了需要存储空间和存储本身所使用的指令、常数、变量和输入数据外,还需要一些对数据进行操作的工作单元和存储一些为现实计算所需信息的辅助空间。

程序执行时所需存储空间包括以下两部分。

(1)固定部分。这部分空间的大小与输入/输出的数据的个数多少、数值无关。主要包括指令空间(即代码空间)、数据空间《常量、简单变量)等所占的空间。这部分属于静态空间。 (2)可变空间,这部分空间的主要包括动态分配的空间,以及递归栈所需的空间等这部分的空间大小与算法有关。

例如: 要判断某年是不是闰年,你可能会花一点心思来写一个算法,每给一个年份,就可以通过这个算法计算得到是否闰年的结果。 另外一种方法是,事先建立一个有 2050 个元素的数组,然后把所有的年份按下标的数字对应,如果是闰年,则此数组元素的值是 1,如果不是元素的值则为 0。

这样,所谓的判断某一年是否为闰年就变成了查找这个数组某一个元素的值的问题。

第一种方法相比起第二种来说很明显非常节省空间,但每一次查询都需要经过一系列的计算才能知道是否为闰年。

第二种方法虽然需要在内存里存储 2050 个元素的数组,但是每次查询只需要一次索引判断即可。

这就是通过一笔空间上的开销来换取计算时间开销的小技巧。到底哪一种方法好?其实还是要看你用在什么地方~~

一个算法所需的存储空间用 f(n)表示。

S(n)=O(f(n))

n为问题的规模,S(n)表示空间复杂度。

代码语言:javascript
复制
# 空间复杂度
def reserve(a,b):
    n=len(a)
    for i in range(n):
        b[i]=a[n-1-i]

上方的代码中,当程序调用 reserse()方法时,

要分配的内存空间包括: 引用 a、引用 b、局部变量n、局部变量i。因此 f(n)=4 ,4 为常量。

所以该算法的空间复杂度 S(n)=O(1)

空间复杂度的计算方式和时间复杂度类似

算法:独立解决问题的一种思想

大O数量级(大O记法):评判算法复杂度的指标

897358516c064e238c330adaab21530a.png
897358516c064e238c330adaab21530a.png

“变位词”判断问题⭐

“变位词”是指两个词之间存在组成字母的重新排列关系 如: heart和earth,python和typhon

解法1:逐字检查法🔥

思路:

645afc6dc23c4952bc1aa48cde363432.png
645afc6dc23c4952bc1aa48cde363432.png
f0702955022b455d94e9500a42164373.png
f0702955022b455d94e9500a42164373.png

代码块:

代码语言:javascript
复制
def anagramSolution1(s1,s2):
    alist = list(s2)
    pos1 = 0
    stillOK = True
    while pos1 < len(s1) and stillOK:
        pos2 = 0
        found = False
        while pos2 < len(alist) and not found:
            if s1[pos1] == alist[pos2]:
                found = True
            else:
                pos2 = pos2 + 1
        if found:
            alist[pos2] = None  #找到就利用None打勾
        else:
            stillOK = False
        pos1 = pos1 + 1
    return stillOK

print(anagramSolution1("ad","ca"))

运行过程:

ad967c765c5e4736bd4b5f933a9454e7.gif
ad967c765c5e4736bd4b5f933a9454e7.gif
03cad5b732114a2b90c882279c74f205.png
03cad5b732114a2b90c882279c74f205.png
a58ecf79a320486fb033c8c269e34d19.png
a58ecf79a320486fb033c8c269e34d19.png

以最差的情况,上面变位词全都是逆序的,我们通过逐字查找的过程可知到,从s1的a开始到s2中依次查找a需要找4次 从S1的b开始到S2中依次查找b需要找3次...依此类推 一共要找4+3+2+1=10次 如果有n个元素则需要n+(n-1)+...1 利用等差数列求和可得:

47569398816a4ffdb52df42a74dcbc2a.png
47569398816a4ffdb52df42a74dcbc2a.png

解法2:排序比较法🔥

思路:
4eeea9293def4883a1b0c21195476980.png
4eeea9293def4883a1b0c21195476980.png

代码块:

代码语言:javascript
复制
def anagramSolution2(s1,s2):
    #转为列表
    alist1 = list(s1)
    alist2 = list(s2)
    
    #分别排序
    alist1.sort()
    alist2.sort()
    pos = 0
    matches = True
    while pos < len(s1) and matches:
        #逐个对比
        if alist1[pos] == alist2[pos]:
            pos = pos + 1
        else:
            matches = False
    return matches

print(anagramSolution2("abcde", "edcba"))
运行过程:
43b77bcaee5d4dccb49e432d5e041e23.gif
43b77bcaee5d4dccb49e432d5e041e23.gif

粗看上去,本算法只有一个循环,最多执行n次,数量级是O(n)

但循环前面的两个sort并不是无代价的~

排序算法采用不同的解决方案,其运行时间数量级差不多是0(n2)或者0(n log n),大过循环的O(n)

所以本算法时间主导的步骤是排序步骤 本算法的运行时间数量级就等于排序过程的数量级O(n log n)

python中的sorted()函数对字符串进行排序,判断是否两个字符串排序后相等来判断是否为变位词。

例如,判断字符串"race"和"care"是否为变位词:

代码语言:javascript
复制
str1 = "race"
str2 = "care"

if sorted(str1) == sorted(str2):
    print("两个字符串是变位词")
else:
    print("两个字符串不是变位词")
代码语言:javascript
复制
def is_anagram(str1, str2):
    # 如果两个字符串长度不同,则它们不可能是变位词
    if len(str1) != len(str2):
        return False

    # 两个字符串排序后比较
    str1_sorted = sorted(str1)
    str2_sorted = sorted(str2)
    for i in range(len(str1)):
        if str1_sorted[i] != str2_sorted[i]:
            return False
    return True

# 示例:
print(is_anagram("listen", "silent"))  # True
print(is_anagram("python", "java"))    # False

在上面的示例中,我们定义了一个名为 is_anagram 的函数,输入两个字符串 str1str2,如果两个字符串的长度不同,则它们不可能是变位词,返回False。并使用Python的sorted函数将这两个字符串排序。对于两个已排序的字符串,我们使用for循环逐个比较它们的字符。如果有任何不相等的字符,则这两个字符串不是变位词。如果所有字符都相等,则这两个字符串是变位词,返回True

解法3:暴力法🔥

(由于它不是一个好方法,所以了解即可)

思路:
6f0a56b8470b4b51a63ac1517c38b3c7.png
6f0a56b8470b4b51a63ac1517c38b3c7.png
488de26891ac41b9ae2a24e8518045d0.png
488de26891ac41b9ae2a24e8518045d0.png

解法4:计数比较法🔥

思路:
be27ea84b0f3461fae865433e70b9132.png
be27ea84b0f3461fae865433e70b9132.png

ord()函数:返回的是 字符的 Unicode值

将 对应字符的Unicode - a的Unicode 就能得到 每个字符变成 0 - 25的数字

代码块:
代码语言:javascript
复制
def anagraamSolution4(s1,s2):
    #计数器1
    c1 = [0] * 26
    #计数器2
    c2 = [0] * 26
    #对 s1 进行计数
    for i in range (len(s1)):
        pos = ord(s1[i]) - ord("a")
        c1[pos] = c1[pos] + 1
    #对 s2 进行计数
    for i in range (len(s2)):
        pos = ord(s2[i]) - ord("a")
        c2[pos] = c2[pos] + 1
    #进行每一位字符的比较
    j = 0
    stillOK = True
    while j < 26 and stillOK:
        if c1[j] == c2[j]:
            j = j + 1
        else:
            stillOK = False
    return stillOK
print(anagraamSolution4("apple","pleap"))
运行过程:
代码语言:javascript
复制
1bacde5b4a054c81acb21719a5b5b6db.gif
1bacde5b4a054c81acb21719a5b5b6db.gif
399beba6ec0044c7ad3ec0cc859284fb.png
399beba6ec0044c7ad3ec0cc859284fb.png
2291d1433b1043e3ad33688c34c3620f.png
2291d1433b1043e3ad33688c34c3620f.png

拓展 sort.() 和 sorted的区别🪄

sort()和sorted()的区别✨

接收 sort() 的返回值,可以发现是None

代码语言:javascript
复制
list1 = [1, 3, 2, 5]
list2 = list1.sort()
print(list2)

输出:

None

打印一下排序前、后的「内存地址」,可以发现 地址没有改变!

代码语言:javascript
复制
list1 = [1, 3, 2, 5]
print(id(list1))
list1.sort()
print(id(list1))

输出:

1465837605120 1465837605120

sort() 的设计思想就是「修改」原列表,而不是返回新的列表; 它不会创建新的列表,从而节省「效率」; 当然,这也意味着原列表被修改了,使用时要留意这一点; sorted() 是 sort() 的扩展函数,可以对列表的元素排序,同时不会修改原列表。

代码语言:javascript
复制
list1 = [1, 3, 2, 5]
list2 = sorted(list1)
print(list1)
print(list2)

输出:

[1, 3, 2, 5] [1, 2, 3, 5]

从结果可以看到, sorted() 创建了新的列表,用来保存排序后的列表

其他类型排序✨

sort() 只能对列表排序,而 sorted() 能对可迭代对象排序;

所以,字符串、元组、字典等类型想排序,可以用 sorted().

代码语言:javascript
复制
str1 = "312"
print(sorted(str1))

tuple1 = (5, 1, 3)
print(sorted(tuple1))

dict1 = {"key1": 1, "key2": 2}
print(sorted(dict1))

输出:

['1', '2', '3'] [1, 3, 5] ['key1', 'key2']

从输出结果可以发现,字符串、元组、字典类型排序后,返回的是列表类型;并且字典只对键排序,不对值排序。

本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2023-12-23,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • "时间复杂度"回顾💯
  • 空间复杂度💥
  • “变位词”判断问题⭐
    • 解法1:逐字检查法🔥
      • 解法2:排序比较法🔥
        • 思路:
        • 运行过程:
      • 解法3:暴力法🔥
        • 思路:
      • 解法4:计数比较法🔥
        • 思路:
        • 代码块:
        • 运行过程:
    • 拓展 sort.() 和 sorted的区别🪄
      • sort()和sorted()的区别✨
        • 其他类型排序✨
        领券
        问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档