基础

1…Python的可变数据类型和不可变数据类型?

Python 中的可变数据类型有列表、字典和集合;不可变数据类型有整数、浮点数、字符串、元组和布尔值

可变对象可以在原来地址上修改元素,不可变则不行(即不能在自己身上增删改),若要修改可以使用对象拼接赋值给新的对象,总之不能修改自身

2.Python的内置数据结构?

列表,集合,元组,字典
list:有序可修改。通过索引进行查找、切片。使用方括号[ ]表示;
tuple:有序不可修改。可查询、切片操作。元组将多样的对象集合到一起,通过索引进行查找。用小括号( )表示;
set:集合,无序可修改,无重复,无索引
,不能跟列表一样切片。自动去重。使用set([ ])表示;
dict:字典是一组key-value
,通过key查找,无顺序,用{ }表示。

3.字符串逆序

1
2
s[::-1]
''.join(reversed(str(n)) 其中reversed返回迭代器对象

4.数值

没有最大整数def

1
float('inf')

5 .列表

  • lines=sorted(lines)
  • words.reverse() 原地翻转
  • ls.append(path[:]) :保存列表结果用这个。
  • ls.append(path)
  • ls.pop(0)
  • ls.sort() 原地排序

对这种二维列表去重

[[1, 2, 5], [1, 7], [1, 6, 1], [2, 6], [2, 1, 5], [7, 1]]

由于列表是不可哈希的类型,需要先将每个子列表转换为元组,再使用集合进行去重,最后再转换回列表。

为元组是可哈希的,可以作为集合的元素。然后,我们将这些元组放入集合中,集合会自动去重。

extend(iterable): 将可迭代对象中的元素添加到列表的末尾。

6.del 删除

在这个例子中,我们首先创建了一个列表[1, 2, 3]并让变量a指向它。然后,我们让变量b也指向这个列表。当我们使用del a时,我们只是删除了变量a,而列表[1, 2, 3]仍然存在,因为变量b仍然指向它。

所以,当我们说*“删除的是变量而不是数据*”时,我们的意思是del关键字删除的是变量(也就是数据的引用),而不是实际的数据对象。希望这个解释对你有所帮助!

1
2
3
a = [1, 2, 3]
b = a
del a
1
2
3
a = [0, 2, 2, 3]
a.remove(2) #remove只是列表的一个方法
print(a) # 输出 [0, 2, 3]

7.字符串方法

1
2
3
4
s.strip()
s=' '.join(t)
s.replace(' ',' ')
ls=s.split() #将字符串分割为单词列表

8.if not, 真假

FalseNone、空字符串""、数字0、空列表[\]、空字典{}或空元组()

if not stack: 当stack为NOne

9.is和== 的区别

is判断两个对象的内存地址是否相同 == 判断两个对象的值是否相同

10.字典

字典的键必须是不可变的类型。不可以使用列表作为键。字典键的常见类型包括整数、浮点数、字符串、元组等不可变类型。

1
2
3
4
5
6
7
8
9
# 字典的键可以是整数
my_dict = {1: 'one', 2: 'two', 3: 'three'}
# 字典的键可以是字符串
another_dict = {'apple': 3, 'banana': 5, 'orange': 2}
# 字典的键可以是元组
yet_another_dict = {(1, 2): 'tuple_key', (3, 4): 'another_tuple_key'}
# 使用元组作为键
my_dict = {('John', 25): 'value1', ('Alice', 30): 'value2'}

如何根据key进行排序?

sorted() 函数中指定要排序的元组列表和一个 key 参数,该参数用于指定排序的关键字。在这里,我们可以将 key 参数设置为 lambda x: x[0],以根据元组的第一个元素(即键)进行排序。

1
2
3
4
5
my_dict = {'apple': 10, 'orange': 5, 'banana': 20, 'grape': 7}

sorted_dict = dict(sorted(my_dict.items(), key=lambda x: x[0]))

print(sorted_dict)

lambda函数可以换:

1
2
3
4
def get_key(item):
return item[0]


方法

get(k,default)

11.推导

可以从一个数据序列构建另一个新的数据序列的结构体

1
2
3
4
5
6
7
8
9
10
11
names = ['Bob','Tom','alice','Jerry','Wendy','Smith']

# 列表推导式
new=[ name.upper() for name in names]
multiples = [i for i in range(30) if i % 3 == 0]
#字典推导式
dic={name:len(name) for name in names}
# 集合推导式
sett={name.upper() for name in names}
# 元组推导式
tt=(name.lower() for name in names)

12.常用的模块,标准库

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
import logging

logging.basicConfig(filename="test.log",level=logging.INFO)
logging.debug('debug message')
logging.info("info message")
logging.warn('warn message')
logging.error("error message")
logging.critical('critical message')

#
import os
import sys
dir(os)
help(sys)
#os 系统交互,环境变量
path='E:\\'
os.name
os.system('mkdir temp.foo')
os.getcwd()
os.listdir(path)
os.path.getmtime(path)
os.stat(path)

#日常的文件和目录管理任务,:
import shutil
shutil.copyfile('temp.txt', 'temp.out')
#shutil.move('temp.pkl', 'temp.move')
#从目录通配符搜索中生成文件列表:
import glob
glob.glob('*py')
#命令行参数
print(sys.argv)
sys.stdin
#字符串正则匹配
import re
re.findall(r'\bf[a-z]*', 'which foot or hand fell fastest')
re.sub(r'(\b[a-z]+) \1', r'\1', 'cat in the the hat')
# 数学math模块
import math
math.cos(math.pi/4)
math.log(1024,2)
#random提供了生成随机数的工具
import random
random.choice([1,2,3])
random.sample(range(100), 10)
random.random()
random.randrange(6)
#访问 互联网
#用于处理从 urls 接收的数据的 urllib.request
from urllib.request import urlopen
for line in urlopen('http://43.192.14.14'):
line=line.decode('utf-8')
print(line)
# requests更加简洁
import requests
res=requests.get('http://42.193.14.14/')
print(res.headers)
# 日期和时间datetime模块
import datetime
current_datetime=datetime.datetime.now()
print(current_datetime)
formatted_datetime = current_datetime.strftime("%Y-%m-%d %H:%M:%S")
print(formatted_datetime)

# 时间模块
import time
current_time = time.time() # 获取当前时间戳(从1970年1月1日开始的秒数)
print("Current Time:", current_time)
formatted_time = time.ctime(current_time) # 格式化为可读的时间字符串
print("Formatted Time:", formatted_time)

print("Start")
time.sleep(3) # 等待3秒
print("End after 3 seconds")


#数据压缩
import zlib
s=b'witch which has which witches wrist watch'
len(s)
t=zlib.compress(s)
len(t)
zlib.crc32(s)
# JSON
import json
data = {
'no' : 1,
'name' : 'Runoob',
'url' : 'http://www.runoob.com'
}
json_str=json.dumps(data)
print(repr(data))
print(json_str)

13.迭代器

迭代器是一个可以记住遍历位置的对象。
迭代器有两个基本的方法:iter()next()
许多内置对象都是可迭代的,例如列表、元组、字符串等。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
class  MyIterator:
def __init__(self,data):
self.data=data
self.index=0
# 返回迭代器自身
def __iter__(self):
return self
# 返回下一个元素
def __next__(self):
if self.index<len(self.data):
result = self.data[self.index]
self.index += 1
return result
else:
raise StopIteration
my_list=[1,2,3,4]
my_custom_iterator = MyIterator(my_list)
for element in my_custom_iterator:
print(element)

14. map(),filter(),reduce()

1
2
3
length=list(map(lambda x:len(x),names))
small=list(filter(lambda x:len(x)>4, names))
product=reduce(lambda x,y:x*y, nums, 1)

15.遍历技巧

1
2
for k in {'a':1,'b':2} :
print(k)

字典中遍历时,关键字和对应的值可以使用 items() 方法同时解读出来:

1
2
3
knights = {'gallahad': 'the pure', 'robin': 'the brave'}
for k,v in knights.items():
print(k,v)

序列中遍历时,索引位置和对应值可以使用 enumerate() 函数同时得到:

1
2
for i,v in enumerate(names):
print(i,v)

同时遍历两个或更多的序列,可以使用 zip() 组合:

1
2
3
4
questions = ['name', 'quest', 'favorite color']
answers = ['lancelot', 'the holy grail', 'blue']
for q,a in zip(questions,answers):
print("Que:{} Ans:{}".format(q, a))

​ 要按顺序遍历一个序列,使用 sorted() 函数返回一个已排序的序列,并不修改原值:

1
2
for f in sorted(set(names)):
print(f)

16.输入输出

读写文件

1
2
3
4
5
6
7
8
f=open('temp.txt','w')
f.write(' python open file \n nandwnowdoi')
f.close()

f=open('temp.txt','r')
str=f.read()
ls=f.readlines()
print(str,ls)

pickle 模块:基本的数据序列和反序列化。

1
2
3
4
5
6
7
8
9
10
11
import pickle
data1 = {'a': [1, 2.0, 3, 4+6j],
'b': ('string', u'Unicode string'),
'c': None}
pkl_file=open('temp.pkl','wb')
pickle.dump(data1, pkl_file)
pkl_file.close()

pkl_file = open('temp.pkl', 'rb')
data2=pickle.load(pkl_file)
pkl_file.close()

17.嵌套函数,外部变量

1
2
3
4
5
6
7
8
9
10
11
def outer_function():
outer_variable = 10

def inner_function():
nonlocal outer_variable
outer_variable = 20

inner_function()
print(outer_variable)

outer_function()

好避免在函数之间共享状态,而是通过函数参数和返回值来传递信息

但是res[] 列表可以

18.copy 拷贝:

深拷贝(deep copy)和浅拷贝(shallow copy)是两种不同的复制方式,它们主要针对复杂对象(例如嵌套的列表或字典)中的子对象的复制方式而言。

  1. 浅拷贝:只是复制了容器对象本身和对其元素(复杂对象)的引用。 即新对象修改元素会改变原始元素
    res.append(path.copy())res.append(path[:]) 都用于向列表 res 中添加列表 path 的副本。
  • path.copy():创建列表的浅拷贝,会复制列表中的元素。如果元素是对象如列表,则不会复制对象本身内容。
  • 实际上,对于包含整数、字符串这种的列表,copy [:]效果一样,
  1. 深拷贝:创建一个对象,递归复制对象内所有元素,是完全独立的副本
    path.deepcopy()实现深拷贝

19.转换

bin

20.集合

1
2
3
s.add(x)
s.remove( x )
s.clear()

21.循环

1
2
3
for i in range(6):
print(i)
i += 1 # 这一行对循环没有影响

20.生成器

生成器(Generator)是一种特殊的迭代器,它允许按需生成值,
生成器使用 yield 关键字来实现暂停和恢复执行,允许函数在每次产生值后暂停并在下一次调用时继续执行。
定义生成器:

  1. 生成器函数:使用 yield 关键字的函数。
1
2
3
4
5
6
7
8
def my_generator():
yield 1
yield 2
yield 3
gen = my_generator()
print(next(gen)) # 输出: 1
print(next(gen)) # 输出: 2
print(next(gen)) # 输出: 3
  1. ** 生成器表达式** : 类似于列表推导式,但使用圆括号。
1
2
3
4
gen = (x for x in range(1, 4))
print(next(gen)) # 输出: 1
print(next(gen)) # 输出: 2
print(next(gen)) # 输出: 3

yield和return:
相同点:都是返回程序的执行结果;
区别:yield返回结果但程序还没有结束,return返回结果表示程序已经结束执行

22.谈谈对不定⻓参数的理解?

在不确定需要传多少参数时使用,args表示位置参数,元组形式保存,kwargs表示关键字参数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
def cache(func):
# 缓存计算结果的字典
cached_results = {}

# 装饰器的包装函数
def wrapper(*args, **kwargs):
# 使用输入参数构建唯一的键
key = (args, tuple(kwargs.items()))

# 如果缓存中不存在对应键的结果
if key not in cached_results:
print('不在缓存中')
# 调用被装饰的函数,并将结果缓存起来
cached_results[key] = func(*args, **kwargs)

# 返回缓存中的结果
return cached_results[key]

# 返回装饰后的函数
return wrapper
@cache
def add(x, y):
print("Calculating result...")
return x + y

result1 = add(2, 3) # 计算并缓存结果
result2 = add(2, 3) # 直接从缓存中获取结果,不再计算
result3 = add(4, 5) # 计算并缓存新的结果

print(result1) # 输出: 5
print(result2) # 输出: 5
print(result3) # 输出: 9

23.多线程 threading 模块

  1. 方法:
    threading.Thread(target=modify_shared_resource):创建线程,指定启动执行函数
    run(): 用以表示线程活动的方法。
    start():启动线程活动。
    join([time]): 等待至线程中止。这阻塞调用线程直至线程的join() 方法被调用中止-正常退出或者抛出未处理的异常-或者是可选的超时发生。
    isAlive(): 返回线程是否活动的。
    getName(): 返回线程名。
    setName(): 设置线程名
    threading.Lock()
    threading.Semaphore(value=2)
    threading.Event()
  2. 案例:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
import threading
import time
exitFlag = 0

class MyThread(threading.Thread):
def __init__(self,threadID,name,delay):
threading.Thread.__init__(self)
self.threadID=threadID
self.name = name
self.delay = delay
def run(self):
print ("开始线程:" + self.name)
print_time(self.name, self.delay, 5)
print ("退出线程:" + self.name)
def print_time(threadName, delay, counter):
while counter:
if exitFlag:
threadName.exit()
time.sleep(delay)
print ("%s: %s" % (threadName, time.ctime(time.time())))
counter -= 1

# 创建新线程
thread1 = MyThread(1, "Thread-1", 1)
thread2 = MyThread(2, "Thread-2", 2)

# 开启新线程
thread1.start()
thread2.start()
thread1.join()
thread2.join()
print ("退出主线程")
  1. 线程同步
    (1) 互斥量
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
import threading
shared=0
mutex=threading.Lock()
def modift_shared():
global shared
with mutex:
# 互斥量保护区
shared+=1
threads=[]
for _ in range(5):
thread=threading.Thread(target=modift_shared)
thread.start()
threads.append(thread)
for thread in threads:
thread.join()
print("Final shared resource value:", shared)

(2)信号量

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
import threading

shared_resource = 0
semaphore = threading.Semaphore(value=2) # 允许同时两个线程访问

def modify_shared_resource():
global shared_resource
with semaphore:
# 信号量保护的临界区
shared_resource += 1

# 创建多个线程并启动
threads = []
for _ in range(5):
thread = threading.Thread(target=modify_shared_resource)
thread.start()
threads.append(thread)

# 等待所有线程执行完成
for thread in threads:
thread.join()

print("Final shared resource value:", shared_resource)

(3) 事件(Event):
事件用于线程之间的通信,一个线程可以等待事件的触发,而另一个线程可以触发事件。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
import threading

event = threading.Event()

def wait_for_event():
print("Thread 1 is waiting for the event.")
event.wait()
print("Thread 1 received the event.")

def set_event():
print("Thread 2 is setting the event.")
event.set()

# 创建两个线程并启动
thread1 = threading.Thread(target=wait_for_event)
thread2 = threading.Thread(target=set_event)
thread1.start()
thread2.start()

# 等待两个线程执行完成
thread1.join()
thread2.join()

(4).条件变量

(5).队列

24.with

with 语句是 Python 中用于简化资源管理的语法结构,通常用于对文件、网络连接、线程锁等资源进行管理。

25。collections

26. 面向对象

接口:

1
2
3
4
5
from abc import ABC, abstractmethod 
class Strategy(ABC):
@abstractmethod
def issue(self, *params):
pass

数据结构和算法

1.二叉树、

1.1二叉搜索树

  1. 对于根节点,左子树中所有节点的值 < 根节点的值 < 右子树中所有节点的值。
  2. 任意节点的左、右子树也是二叉搜索树,即同样满足条件 1.
  3. 中序遍历是一个升序序列

二叉搜索树

1.2 完全二叉树

除最底层最右边的节点没充满

  • 最后一个非叶子节点
  • ​ parent=(i-1)//2
  • 通过高度 计算结点个数
    • 左子树高度>右子树高度:右子树是满二叉
    • 左子树高度=右子树高度:左子树是满二叉

1.3 平衡二叉树

左右两个子树的高度差的绝对值不超过 1 。

1.4 遍历

  • 深度优先搜索(DFS):在DFS中,从起始节点开始,沿着一条路径尽可能深入直到到达叶子节点,然后回溯到前一个节点,继续探索其他路径。这一过程以递归或栈的形式实现。
    前序和后序都是DFS

  • 广度优先搜索
    从右到左层序遍历

  • 已知中序和后序遍历,如何确定这个二叉树?

计算每层平均值:DFS, BFS

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
# 使用深度优先搜索(DFS)计算每一层节点的平均值时,需要额外记录每层的节点值和节点数量。
# 节点携带 深度属性
def averageOfLevels(self, root: Optional[TreeNode]) -> List[float]:
result=[]
level_sum=[]
level_size=[]
def dfs(root,depth):
if not root:
return
if depth<len(level_size):
level_size[depth]+=1
level_sum[depth]+=root.val
else:
level_size.append(1)
level_sum.append(root.val)
dfs(root.left,depth+1)
dfs(root.right,depth+1)
dfs(root,0)
# 计算每一层的平均值
for i in range(len(level_sum)):
result.append(level_sum[i] / level_size[i])
return result

1.5 路径

  • 因为列表是可变对象,如果直接将 path 添加到 res,后续对 path 的修改会影响已经添加到 res 中的路径。
  • 使用 path.copy() 创建路径的副本,确保不同路径之间的独立性。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
# 回溯方法:所有路径
def back_paths(root:TreeNode):
res=[]
path=[root.val]
def backtrace(node):
if not node.left and not node.right:
res.append(path[:])
return
if node.left:
path.append(node.left.val)
backtrace(node.left)
path.pop()
if node.right:
path.append(node.right.val)
backtrace(node.right)
path.pop()
backtrace(root)
return res
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
# 检查路劲和
def hasPathSum( root: [TreeNode], targetSum: int) -> bool:
res=[]
# path是到当前node的路径
def dfs(node,path):
if not node:
return

path.append(node.val)
if sum(path)==targetSum:
res.append(path.copy())
print(node.val,path)

dfs(node.left,path)
dfs(node.right,path)
# 返回上层,弹出当前节点, 和回溯的pop如出一辙
path.pop()
dfs(root,[])
return len(res)>0

2.回溯法

简单来说,回溯算法采用了一种 「走不通就回退」 的算法思想。

回溯算法通常用简单的递归方法来实现,在进行回溯过程中更可能会出现两种情况:

  1. 找到一个可能存在的正确答案;
  2. 在尝试了所有可能的分布方法之后宣布该问题没有答案

全排列

img

模板:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
res = []    # 存放所欲符合条件结果的集合
path = [] # 存放当前符合条件的结果
def backtracking(nums,cond): # nums 为选择元素列表, cond 为相关条件参数
if 遇到边界条件: # 说明找到了一组符合条件的结果
res.append(path[:]) # 将当前符合条件的结果放入集合中
return

for i in range(len(nums)): # 枚举可选元素列表
path.append(nums[i]) # 选择元素
backtracking(nums) # 递归搜索
path.pop() # 撤销选择

backtracking(nums,cond)

子集:

img

  • backtracking(nums, index): 函数代表的含义是:在选择 nums[index] 的情况下,递归选择剩下的元素。

剪枝

3.栈

3.1最小栈

:能在常数时间内检索到最小元素的栈。

使用一个 min 变量记录当前栈中的最小值确实是一种方法。然而,当元素被弹出时,如果弹出的正好是最小值,那么我们就需要重新遍历整个栈找到新的最小值。这样在 pop 操作的时候就会变得更加耗时。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
class MinStack:

def __init__(self):
self.stack=[]
self.min_stack=[]

def push(self, val: int) -> None:
self.stack.append(val)
if not self.min_stack or val<=self.min_stack[-1]:
self.min_stack.append(val)

def pop(self) -> None:
poped=self.stack.pop()
if poped==self.min_stack[-1]:
self.min_stack.pop()

def top(self) -> int:
return self.stack[-1]

def getMin(self) -> int:
return self.min_stack[-1]

3.2单调栈:

单调递增栈:

  • 栈底到栈顶递增。
  • 新元素入栈,要保持递增,需要弹出所有比他大的元素
  • 可以快速找到它前面比它小的元素和后面比它小的元素。
  • 求解某个元素的下一个更大元素(Next Greater Element): 在一个数组中,找到每个元素右边第一个比它大的元素。

例子:每日温度

1
2
输入: temperatures = [73,74,75,71,69,72,76,73]
输出: [1,1,4,2,1,1,0,0]

找到每个元素下一个比他大的元素, 单调递减栈。
*stack*=[0(73)]*stack*=[2(75),3(71),4(69)]这种写法 好理解

模板

1
2
3
4
5
6
7
for i in nums:
while stack and n>nums[i]:
stack.pop()
if not stack:
break
# 比较元素,pop元素,栈顶元素 如下图
stack.apped()

image-20231211180435022

4.递归和迭代

递归,函数调用,栈帧:
参数压栈, 接受返回值,

就类似栈帧, 把调用参数逐个压栈,
多个参数可视为一个元组压栈.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
def maxDepth(self, root: Optional[TreeNode]) -> int:
if not root:
return 0
stack=[(root,1)]
maxdp=0
while stack:
node,depth=stack.pop(0)
if depth>maxdp:
maxdp=depth
if node.right:
stack.append((node.right,depth+1))
if node.left:
stack.append((node.left,depth+1))

return maxdp

5.贪心算法

贪心算法基于当前情况,做出下一步的最优解。因此,总是逐步迭代,问题规模逐渐变小。贪心算法每次局部最优,但不一定达到全局最优。需要证明达到,或者达到近似。

6.指针

快慢指针:
如果存在循环,终会相遇。
双指针:
明确含义
一般用while语句

滑动窗口

7.动态规划

7.1 五部曲:

  1. 确定dp数组(dp table)以及下标的含义
  2. 确定递推公式
  3. dp数组如何初始化:根据递推公式得到需要初始化的底层值
  4. 确定遍历顺序:不同遍历顺序?
  5. 举例推导dp数组:验证结果思路,然后才写代码

除了写循环,也可以写成递归

双层dp:
某一点的状态需要遍历,使用其他节点内容填充,
即:

1
2
3
for ele in list:
for ....:
dp[ele]=XXXX.

7.2案例

300. 最长递增子序列
718. 最长重复子数组

子集划分

给你一个 只包含正整数 的 非空 数组 nums 。请你判断是否可以将这个数组分割成两个子集,使得两个子集的元素和相等。

问题:是否存在一个子集的和为 nums和的一半?
dp[i][j]:前i个元素中是否存在一个子集和为j
dp[n][nums和/2]:最终结果
dp[i][0]:子集包含空集,和为0
状态转移:dp[i-1][(j)]
如果前i-1个元素存在子集和为j,那么前i个元素就存在子集和为j。 i元素不放入子集。
如果前i-1个元素存在子集和为j-nums[i],那么前i个元素也存在子集和为j。 i元素放入子集
如果前i-1个元素不存在子集和为j,且和不为j-nums[i],那么前i个元素也不存在和为j。

dp[i][j]=dp[i1][j]ordp[i1][jnums[i]]dp[i][j]=dp[i-1][j] \quad or\quad dp[i-1][j-nums[i]]

1049. 最后一块石头的重量 II

~~方法一:
dp[i][j]:前 i 个石头中选择若干个,使得它们的总重量不超过 j 时,可能的最大石头重量。
dp[n][sum//2]: 前n个元素,选择若干个,在其和不超过sum//2情况下,的最大和
结果:sum-dp[n][sum//2]-dp[n][sum//2]

1
dp[i][j]=max(dp[i-1][j],dp[i-1][j-stone]+stone)

方法二:
dp[i]:是否存在一些石头,重量和为i。
结果:找到最后一个true,即为最大重量

1
2
3
4
5
6
7
8
9
10
11
12
13
14
def lastStoneWeightII(stones):
total_weight = sum(stones)
target = total_weight
dp = [False] * (target + 1)
dp[0] = True
# #stone在序列中的情况下,判断dp[i]
for stone in stones:
for i in range(target, stone - 1, -1):
dp[i] = dp[i] or dp[i - stone]

for i in range(target, -1, -1):
if dp[i]:
return total_weight - 2 * i

方法三:
dp[i]:总重不超过i的情况下,最大重量

1
dp[j] = max(dp[j], dp[j - stone] + stone)

不同的二叉搜索树: [1…n]构成的…个数

卡特兰数:

c(n)=k=1n+1c(k1)c(nk)c(n)=\sum_{k=1}^{n+1} c(k-1) \cdot c(n-k)

判断子序列

给定字符串 s 和 t ,判断 s 是否为 t 的子序列。
字符串的一个子序列是原始字符串删除一些(也可以不删除)字符而不改变剩余字符相对位置形成的新字符串。(例如,"ace"是"abcde"的一个子序列,而"aec"不是)。

!()[https://raw.githubusercontent.com/ddongzi/ddongzi.github.io/main/image/markdown/20231220161833.png]

回文子串:

给你一个字符串 s ,请你统计并返回这个字符串中 回文子串 的数目。

回文字符串 是正着读和倒过来读一样的字符串。

子字符串 是字符串中的由连续字符组成的一个序列。

具有不同开始位置或结束位置的子串,即使是由相同的字符组成,也会被视作不同的子串。

dp定义:
dp[i]: s[i,j]是否存在回文子串。布尔类型
状态转移(递归):
dp[i][j]= s[i]==s[j] and dp[i+1][j-1]
特殊值处理,分情况:
i==j : True
j-i>=1:True
返回值:
用res 计数
初始化:
dp[i][j]:False
遍历顺序:
![[Pasted image 20231220175747.png]]

1
2
for i in range(n-1,-1,-1):
for j in range(i,n):

换零钱
dp[i]: 凑成i 元地最小硬币书

8.背包问题

背包问题是动态规划

8.1 01背包

问题描述:
有n件物品和一个最多能背重量为w 的背包。第i件物品的重量是weight[i],得到的价值是value[i] 。每件物品只能用一次,求解将哪些物品装入背包里物品价值总和最大。

例子:背包最大重量为4。

重量 价值
物品0 1 15
物品1 3 20
物品2 4 30
二维dp数组01背包:
1. dp数组定义,下标定义:
dp[i][j] 表示从下标为[0-i]的物品里任意取,放进容量为j的背包,价值总和最大是多少。
表示前i个物品,放进j容量背包地价值最大总和。
![[Pasted image 20231226231423.png]]
  1. 递推方式

dp[i][j] = max(dp[i - 1][j], dp[i - 1][j - weight[i]] + value[i]);
3. 初始化
dp[i][0]=0初始化
![[Pasted image 20231226231731.png]]
dp[0][j]初始化
![[Pasted image 20231226232510.png]]
其余值初始化:
由递推公式知道,是由上一层推导出来的,递推方向向右下,所以其余初始值都被覆盖,设置0即可
![[Pasted image 20231226232948.png]]

  1. 确定遍历顺序
    遍历顺序不影响公式推导,所需的数据都是固定左上角唯一的
    ![[Pasted image 20231226233928.png]]
  2. 举例推导dp数组
    做动态规划的题目,最好的过程就是自己在纸上举一个例子把对应的dp数组的数值推导一下,然后在动手写代码!
    ![[Pasted image 20231226234414.png]]

代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
limit=4
weights=[1,3,4]
values=[15,20,30]
m=len(weights)
n=limit+1

dp=[[0]*n for _ in range(m)]

for i in range(m):
dp[i][0]=0
for j in range(1,n):
if j>=weights[0]:
dp[0][j]=values[0]

for i in range(1,m):
for j in range(1,n):
dp[i][j]=dp[i-1][j]
if j-weights[i]>=0:
dp[i][j]=max(dp[i][j],dp[i-1][j-weights[i]]+values [i])
print(dp)

一维dp数组(滚动数组)
在使用二维数组的时候,递推公式:dp[i][j] = max(dp[i - 1][j], dp[i - 1][j - weight[i]] + value[i]); 其实可以发现如果把dp[i - 1]那一层拷贝到dp[i]上,不如只用一个一维数组了,只用dp[j](一维数组,也可以理解是一个滚动数组)。
这就是滚动数组的由来,需要满足的条件是上一层可以重复利用,直接拷贝到当前层。
本质是一层一层计算。
![[Pasted image 20231226232510.png]]

  1. dp数组定义:
    dp[j]表示:容量为j的背包,所背的物品价值可以最大为dp[j]。
  2. 递推公式:
1
dp[j] = max(dp[j], dp[j - weight[i]] + value[i]);
  1. 初始化:
    初始化为0, 右边通过左边地推而来
  2. 确定遍历顺序
1
2
3
4
5
for(int i = 0; i < weight.size(); i++) { // 遍历物品
for(int j = bagWeight; j >= weight[i]; j--) { // 遍历背包容量: 从右边到左,
dp[j] = max(dp[j], dp[j - weight[i]] + value[i]);
}
}

如果背包容量是正序遍历,显然dp[2]中物品0被重复放了2次,不符合只放一次的背包。

1
2
dp[1] = dp[1 - weight[0]] + value[0] = 15
dp[2] = dp[2 - weight[0]] + value[0] = 30

如果背包容量是倒序遍历,每次取得状态不会和之前取得状态重合,这样每种物品就只取一次了。

1
2
dp[2] = dp[2 - weight[0]] + value[0] = 15 (dp数组已经都初始化为0)
dp[1] = dp[1 - weight[0]] + value[0] = 15

此外,不可以颠倒内外部循环。
5. 举例推导dp数组
![[Pasted image 20231227114952.png]]

8.2 完全背包:

完全背包和01背包问题唯一不同的地方就是,每种物品有无限件
01背包和完全背包唯一不同就是体现在遍历顺序上

8.分治:

拆解问题,尤其是常规加上额外条件时

9.数组

元素是删除不了的,只能覆盖
滑动窗口
双指针

10.堆

堆是一种特殊的数据结构,它是一个完全二叉树,且父节点的值小于或等于其子节点的值。

heapq 是 Python 标准库中的一个模块,提供了一个实现堆队列算法的模块。堆是一种特殊的数据结构,它是一个完全二叉树,且父节点的值小于或等于其子节点的值。在 Python 的 heapq 模块中,堆总是以列表的形式表示,且堆的第一个元素是最小(或最大)的元素。
heapq 提供了一些用于在堆上进行操作的函数,包括将列表转换为堆、向堆中插入元素、从堆中弹出最小(或最大)元素等操作。其中,最小堆可以用于解决一些优先级队列的问题,而最大堆则相反。

1
2
3
4
5
import heapq
# 将列表转换为最小堆
heap = [3, 1, 4, 1, 5, 9, 2, 6, 5, 3, 5]
heapq.heapify(heap)
min_element = heapq.heappop(heap)

大根堆:
将元素的值取相反数插入堆 max_heap = [-x for x in max_heap]

堆是一种数据结构,具有以下主要特点:

  1. 有序性:

    • 堆是一个特殊的树形数据结构,通常是一个完全二叉树。
    • 堆中的每个节点都有一个键值,且具有堆属性,即对于每个节点 i,其父节点的键值要么小于等于 i 的键值(最大堆),要么大于等于 i 的键值(最小堆)。
  2. 堆的种类:

    • 最大堆: 每个节点的值都大于或等于其子节点的值,根节点是最大的。
    • 最小堆: 每个节点的值都小于或等于其子节点的值,根节点是最小的。
  3. 堆的表示:

    • 堆通常使用数组来表示。在数组中,如果节点 i 存储了一个元素,则其左子节点为 (2*i + 1),右子节点为 (2*i + 2),父节点为 ((i-1)//2)
  4. 插入和删除操作的复杂度:

    • 插入和删除堆中的元素的时间复杂度通常为 O(log n),其中 n 是堆中元素的数量。
    • 插入操作:将新元素插入到堆的末尾,然后通过一系列交换操作维持堆属性。
    • 删除操作:删除堆的根节点,将堆的最后一个元素移到根位置,然后通过一系列交换操作维持堆属性。
  5. 堆的应用:

    • 堆常用于优先队列的实现,其中最小堆可以用于实现最小优先队列,而最大堆可以用于实现最大优先队列。
    • 堆排序算法使用最大堆或最小堆来进行排序。
    • 堆还可以用于实现一些高效的算法,如Dijkstra算法和Prim算法。

总体而言,堆是一种非常有效的数据结构,特别适用于需要快速查找最大或最小元素的场景。

11.排序算法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
#每次选择最小的元素,放在已排序的部分的末尾。
def selection_sort(nums):
i,j=0,0
for i in range(len(nums)):
min_idx=i
for j in range(i+1,len(nums)):
if nums[j]<nums[min_idx]:
min_idx=j
nums[min_idx],nums[i]=nums[i],nums[min_idx]
return nums

#依次比较相邻的元素,将较大的元素交换到右侧。
def bubble_sort(nums):
i,j=0,0
n=len(nums)
for i in range(n):
for j in range(0,n-i-1):
if nums[j]>nums[j+1]:
nums[j],nums[j+1]=nums[j+1],nums[j]
return nums
# 将每个元素插入到已排序的部分的合适位置。
def insert_sort(nums):
i,j=0,0
n=len(nums)
for i in range(1,n):
j=i-1
key=nums[i]
while j>=0 and key<nums[j]:
nums[j+1]=nums[j]
j-=1
nums[j+1]=key
return nums
#: 通过选择一个基准元素,将数组分为两部分,然后递归地对每部分进行排序。时间复杂度平均为 O(n log n),最坏情况为 O(n^2)。
def quick_sort(nums):
if len(nums)<=1:
return nums
base=nums[len(nums)//2]
left=[x for x in nums if x<base]
middle=[x for x in nums if x==base]
right=[x for x in nums if x>base]
return quick_sort(left)+middle+quick_sort(right)
#它将数组分为两半,对每一半递归地进行排序,然后再将两个有序的子数组合并成一个有序的数组。
def merge_sort(arr):
if len(arr) > 1:
mid = len(arr) // 2
left_half = arr[:mid]
right_half = arr[mid:]

merge_sort(left_half)
merge_sort(right_half)

i = j = k = 0

# 合并两个有序数组
while i < len(left_half) and j < len(right_half):
if left_half[i] < right_half[j]:
arr[k] = left_half[i]
i += 1
else:
arr[k] = right_half[j]
j += 1
k += 1

# 处理剩余的元素
while i < len(left_half):
arr[k] = left_half[i]
i += 1
k += 1

while j < len(right_half):
arr[k] = right_half[j]
j += 1
k += 1

# 示例
arr = [64, 34, 25, 12, 22, 11, 90]
merge_sort(arr)
print("归并排序结果:", arr)

res=quick_sort([5,2,8,1,4,3,6])

12.广度优先和深度优先搜索

BFS和DFS 都是图搜索算法。
二叉树是特殊的图结构。

深度优先:
特点:认准一个方向搜索,直到碰壁后(到达终点后)再换方向。换方向是撤销原路径,改为下一个节点。 换方向过程就是回溯。 使用栈(或者递归调用)维护访问的节点。
场景:找到一条路径,或者找到所有路径

代码框架:(实际就是回溯)

1
2
3
4
5
6
7
8
9
10
11
12
dfs(参数) {
if (终止条件) {
存放结果;
return;
}

for (选择:本节点所连接的其他节点) {
处理节点;
dfs(图,选择的节点); // 递归
回溯,撤销处理结果
}
}

广度优先-
特点:从起点出发,以起始点为中心一圈一圈进行搜。搜索到终点即为最短路径。 搜索方向为四周
场景:寻找最短路径, 使用队列来维护节点, 常使用visited[]标记
![[Pasted image 20231226170753.png]]
![[Pasted image 20231226170825.png]]
代码框架:
使用队列实现

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
from collections import deque # 双端队列

dir = [(0, 1), (1, 0), (-1, 0), (0, -1)] # 创建方向元素

def bfs(grid, visited, x, y):

queue = deque() # 初始化队列
queue.append((x, y)) # 放入第一个元素/起点
visited[x][y] = True # 标记为访问过的节点

while queue: # 遍历队列里的元素

curx, cury = queue.popleft() # 取出第一个元素

for dx, dy in dir: # 遍历四个方向

nextx, nexty = curx + dx, cury + dy

if nextx < 0 or nextx >= len(grid) or nexty < 0 or nexty >= len(grid[0]): # 越界了,直接跳过
continue

if not visited[nextx][nexty]: # 如果节点没被访问过
queue.append((nextx, nexty)) # 加入队列
visited[nextx][nexty] = True # 标记为访问过的节点

岛屿数量

  1. 深度优先
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
def numIslands(self, grid: List[List[str]]) -> int:
m, n = len(grid), len(grid[0])
visited = [[False] * n for _ in range(m)]
dirs = [(-1, 0), (0, 1), (1, 0), (0, -1)] # 四个方向
result = 0

def dfs(x, y):
for d in dirs:
nextx = x + d[0]
nexty = y + d[1]
if nextx < 0 or nextx >= m or nexty < 0 or nexty >= n: # 越界了,直接跳过
continue
if not visited[nextx][nexty] and grid[nextx][nexty] == '1': # 没有访问过的同时是陆地的
visited[nextx][nexty] = True
dfs(nextx, nexty)

for i in range(m):
for j in range(n):
if not visited[i][j] and grid[i][j] == '1':
visited[i][j] = True
result += 1 # 遇到没访问过的陆地,+1
dfs(i, j) # 将与其链接的陆地都标记上 true

return result
  1. 广度优先
    ![[Pasted image 20231226175011.png]]
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
   def numIslands(self, grid: List[List[str]]) -> int:
        count=0
        m, n = len(grid), len(grid[0])
        directions = [(0, 1), (1, 0), (-1, 0), (0, -1)] # 创建方向元素
        visited=[[False]*n for _ in range(m)] # 记录访问标志
       
        def bfs(grid,visited,x,y): # 对一个陆地节点进行广度遍历,标记链接的陆地
       
            queue=[] # 初始化队列,访问状态
            queue.append((x,y))
            visited[x][y]=True

            while queue:
                curx,cury=queue.pop(0)
                for direct in directions:
                    next_x=curx+direct[0]
                    next_y=cury+direct[1]
                    if next_x<0 or next_x>=m or next_y<0 or next_y>=n: # 越界跳过
                        continue
                    if not visited[next_x][next_y] and grid[next_x][next_y]=='1': # 符合要求的节点处理
                        visited[next_x][next_y]=True
                        queue.append((next_x,next_y))

        for i in range(m):
            for j in range(n):
                if not visited[i][j] and grid[i][j]=='1':
                    count+=1
                    bfs(grid,visited,i,j)
        return count

面试题

2.什么是堆?什么是栈?栈和堆的区别是什么?

数据结构:
堆:是一种无序的数据结构,可以看成完全二叉树。
栈:后进先出的数据结构,

系统内存:
堆 :没有特定的操作顺序,一般用于动态分配内存空间。
栈:一般用于存储局部变量、函数调用以及程序执行过程中的临时数据。
最大区别在于它们的内存分配方式不同,栈由系统自动分配和释放,而堆的分配和释放需要程序员手动控制。

3.简述数组,链表,队列,堆栈的区别?

数组、链表、队列和堆栈都是常见的数据结构,它们在存储和操作数据上有不同的特点。

  1. 数组(Array):
    • 存储方式: 连续的内存块,通过索引直接访问元素。
    • 插入和删除: 插入和删除元素可能需要移动其他元素。
    • 适用场景: 适用于元素大小已知,且需要频繁随机访问的情况。
  2. 链表(Linked List):
    • 存储方式: 非连续的内存块,通过指针相连。
    • 插入和删除: 插入和删除元素相对较快,不需要移动其他元素。
    • 适用场景: 适用于元素大小不固定,需要频繁插入和删除的情况。
  3. 队列(Queue):
    • 操作方式: 先进先出(FIFO)。
    • 插入和删除: 插入(enqueue)在队尾进行,删除(dequeue)在队头进行。
    • 应用场景: 适用于需要按顺序处理的任务,如广度优先搜索。
  4. 栈(Stack):
    • 操作方式: 后进先出(LIFO)。
    • 插入和删除: 插入(push)和删除(pop)都在栈顶进行。
    • 应用场景: 适用于需要后进先出顺序的场景,如深度优先搜索,函数调用栈等。
      总结:
  • 数组适用于需要随机访问的场景,但插入和删除较为耗时。
  • 链表适用于频繁插入和删除的场景,但不支持随机访问。
  • 队列适用于按顺序处理的场景,先进先出。
  • 栈适用于后进先出的场景,如函数调用栈。

4.面向对象的三个特性是什么?

面向对象编程(Object-Oriented Programming,OOP)有三个主要的特性,通常被称为三大特性,它们是封装(Encapsulation)、继承(Inheritance)、多态(Polymorphism)。

  1. 封装(Encapsulation):
    • 封装是指将数据和操作数据的方法捆绑在一起的概念。
    • 封装通过将数据的内部细节隐藏起来,只暴露必要的接口,使得对象的使用者无需了解对象的内部实现细节。
    • 封装有助于提高代码的可维护性,降低了对象的耦合度。
  2. 继承(Inheritance):
    • 继承是指一个类(子类)可以继承另一个类(父类)的属性和方法。
    • 继承可以减少代码的重复性,提高代码的可重用性。
    • 子类可以通过继承扩展或修改父类的行为,实现了代码的扩展性和灵活性。
  3. 多态(Polymorphism):
    • 多态是指一个对象可以以多种形态存在。
    • 多态允许使用一个对象的通用接口来操作不同类型的对象,提高了代码的灵活性和可扩展性。
    • 多态包括编译时多态(静态多态,例如方法的重载)和运行时多态(动态多态,例如方法的重写、接口的实现)。
    • 不同的子类调用相同的父类,产生不同的结果。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
class Animal:
def make_sound(self):
pass
class Dog(Animal):
def make_sound(self):
return "Bark"
class Cat(Animal):
def make_sound(self):
return "Meow"
# 函数接受 Animal 类型的参数,但可以传入不同的子类对象
def animal_sound(animal):
return animal.make_sound()
# 创建不同的子类对象
dog = Dog()
cat = Cat()
# 调用 animal_sound 函数,传入不同的子类对象
print(animal_sound(dog)) # 输出: Bark
print(animal_sound(cat)) # 输出: Meow

这三个特性共同组成了面向对象编程的基础,它们使得程序更易于理解、维护和扩展。这些特性使得对象能够以更模块化、可重用的方式构建,并且有助于提高代码的质量和可维护性。

5. 什么是闭包?

闭包是指在一个函数内部定义另一个函数,并且这个内部函数能够访问外部函数的变量和参数,即使外部函数已经执行完毕,这些变量和参数仍然存在于内存中,并可以被内部函数使用。闭包常常用来实现一些高阶函数的功能,例如装饰器、回调函数等。运行时动态地改变或增强函数的行为。

  1. 工厂函数
1
2
3
4
5
6
7
8
def mutiply_by(factor):
def mutiply(x):
return x*factor
return mutiply
double=mutiply_by(2)
triple=mutiply_by(3)
print(double(2))
print(triple(2))
  1. #装饰器 是一种使用闭包的常见模式,它允许在不修改原始函数代码的情况下,向函数添加额外的功能。
    可以用于记录日志,记录时间
1
2
3
4
5
6
7
8
9
10
def my_decorator(func):
def wrapper():
print("Something is happening before the function is called.")
func()
print("Something is happening after the function is called.")
return wrapper
@my_decorator #相当于 say_hello = my_decorator(say_hello)
def say_hello():
print('hello')
say_hello()

@staticmethod 装饰器:

1
2
3
4
5
6
7
class MyClass:
@staticmethod
def my_static_method(arg1, arg2):
# 静态方法的实现
pass
# 调用静态方法,不需要创建类的实例
MyClass.my_static_method(value1, value2)

6. 匿名函数(lambda)/函数/闭包/对象在做实参时有什么区别?

函数:当函数作为参数传递时,仅传递其功能。
匿名函数:与函数类似,只有它们的功能作为参数传递。
闭包:当闭包作为参数传递时,函数和闭包内的数据都会被传递,从而可以同时传递功能和上下文。
对象:当实例对象作为参数传递时,它的方法和属性也会被传递。

7. 什么是进程、线程、协程、多进程、多线程?有什么区别?有什么使用场景?

  1. 概念
    进程:操作系统中进行资源分配和调度的基本单位,一个进程可以包含多个线程。每个进程都有独立的内存空间。
    线程:是操作系统能够进行运算调度的最小单位。一个进程可以包含多个线程,这些线程共享进程的内存空间和系统资源,但每个线程都拥有私有的信息。
    协程:一个线程可以包含多个协程,协程通常是在单个线程中运行的,但线程是由操作系统调度的,而协程则是由程序主动控制的。协程是轻量级的,在单个线程中运行,因此不存在资源竞争,死锁问题,
    多线程:指一个程序中创建多个线程,这些线程共享分配给进程的内存空间,可共享资源。
    多进程:是指在操作系统中创建多个独立的进程,每个进程有自己独立的内存空间和系统资源。这些进程通过进程间通信(IPC)来共享数据和协作完成任务。
  2. 场景
    进程:适用于需要进行大量计算或者需要充分利用CPU资源的场景,如并行计算、大数据处理等(cpu密集型任务)。
    线程:适用于不需要独立内存空间的场景,如Web服务器、桌面应用程序等。
    协程:适用于IO密集型的应用场景,如网络爬虫、异步编程等—》同样适用于线程。

8.什么是迭代器,为什么要使用它?什么是生成器,为什么要使用它?

拥有__iter__ ,next 魔法方法的对象就是迭代器,它是一个可以记住遍历的位置的对象。
生成器是特殊的迭代器,使用yield和next函数,有生成器表达式,生成器函数,都是为了节约内存。
区别:
在处理巨大的数据集合时,建议使用生成器来节省内存。
在遍历容器对象时,使用迭代器很方便。

9.什么是线程安全?

是多个线程同时访问共享数据时,不会出现任何问题或错误的特性。这意味着在一个多线程应用程序中,当多个线程同时访问共享资源(例如变量或对象)时,它们不会干扰彼此或产生无法预测的结果。

numpy

matplotlib

1
2
3
4
5
6
7
8
import matplotlib
import matplotlib.pyplot as plt
import numpy as np
fig = plt.figure()
ax = fig.add_subplot(111)
ax.scatter(mat[:,1], mat[:,2],15.0 * np.array(labels), 15.0*np.array(labels))

plt.show()

pandas

数据结构:
Series:带有索引的一组数据

环境

安装 Anaconda

  1. 官网下载 .sh 文件后,bash 运行即可

  2. [Optional] 区分 Ubuntu 自带 python 和 anaconda 中的 python

    Ubuntu22.04 中自带 python3,为方便起见,我们可以做一定的区分。在 ~/.bashrc 文件最后加入以下命令:

    1
    2
    3
    4
    alias python3="/usr/bin/python3"  # 给系统自带的python起一个别名,就叫python3
    export PATH="/home/jason/anaconda3/bin:$PATH" # anaconda3中的python

    BASH

    然后 source 一下让修改发生作用:

    1
    2
    3
    source ~/.bashrc

    BASH

    这样,输入 python3 时会自动替换为 /usr/bin/python3,即使用系统自带的 python;而直接输入 python 则是用当前环境下的 python.

  3. [Optional] 每次打开终端时,conda 会默认自动激活 base 环境,在命令提示符前有 (base) 标志。强迫症表示这太难看了,可以用以下命令禁止自动激活:

    1
    conda config --set auto_activate_base False