牛客的剑指题目:https://www.nowcoder.com/ta/coding-interviews
dict = {"a" : "apple", "b" : "banana", "c" : "grape", "d" : "orange"}
#输出key的列表
dict.keys()
输出:dict_keys(['a', 'b', 'c', 'd'])
#输出value的列表
dict.values()
#每个元素是一个key和value组成的元组,以列表的方式输出
dict.items()
输出:dict_items([('a', 'apple'), ('b', 'banana'), ('c', 'grape'), ('d', 'orange')])
for k in dict:
print ("dict[%s] =" % k,dict[k])
输出:
dict[a] = apple
dict[c] = grape
dict[d] = orange
dict[w] = watermelon
for (k, v) in dict.items():
print ("dict[%s] =" % k, v)
输出:
dict[a] = apple
dict[c] = grape
dict[d] = orange
dict[w] = watermelon
dict["a"]
输出:'apple'
dict["a"][0]
输出:'a'
字典的增加:
dict["w"] = "watermelon"
dict输出为:{'a': 'apple', 'b': 'banana', 'c': 'grape', 'd': 'orange', 'w': 'watermelon'}
字典的删除:
dict.pop("b")
输出为:'banana'
可以直接用+,具体题目见《左旋转字符串》
采用 join() 函数。
s = ['s', 'd', 's', 'd', 'v', 'f', 'h', 'b', 'f', 'g', 'h', 'N', 'I', 'O']
s = ''.join(s)
输出:'sdsdvfhbfghNIO'
str.strip()
lower():转换字符串中所有大写字符为小写。
isalnum():检测字符串是否由字母和数字组成。
数据转换为字符串,再逆序输出:int(str(x)[::-1])
节点的定义
class item (object):
def __init__(self, data):
self.data = data
self.next = None
class Solution:
# 返回从尾部到头部的列表值序列,例如[1,2,3]
def printListFromTailToHead(self, listNode):
# write code here
cur = listNode
res = []
while cur:#while循环后接的要与下面的对应,要么都为cur,要么都为listNode。
res.append(cur.val)#不能插入cur,而是要插入cur.val。
cur = cur.next
return res[::-1]
链表为dum={1,2,3,4},则dum.next = {2,3,4}
cur = tmp = ListNode(0)#伪头结点的设置
解题思路:见双指针
主要是注意当两个链表没有公共点,且链表长度不相等时,如果不相交说明两个指针走过的次数都为m+n,他们会同时到达链尾,也就是它们同时会指向NULL,达到跳出循环的条件。
- 无环:表示为单向链表。
- 公共节点:但由于是单向链表的节点,每个节点只有一个next,因此从第一个公共节点开始,之后他们的所有节点都是重合的,不可能再出现分叉。
- c = a if a>b else b的扩展写法
a, b, c = 1, 2, 3
if a>b:
c = a
else:
c = b
- None 不在链表{1,2}中,
p2 = {1,2}
while None is not p2为真,则None is not p2。
def FindFirstCommonNode(self , pHead1 , pHead2 ):
p1,p2 = pHead1,pHead2
while p1 != p2:
p1 = p1.next if p1 else pHead2#if后面不要用pHead1,会陷入死循环,因为没有指向pHead1.next。
p2 = p2.next if p2 else pHead1
return p2
两个值互换:
迭代解法
总共初始化定义两个变量,总共有三个变量。
这类链表题目一般都是使用双指针法解决的,例如寻找距离尾部第K个节点、寻找环入口、寻找公共尾部入口等。
最容易想到的方法是遍历所有节点,每次遍历到一个节点时,判断该节点此前是否被访问过。
class Solution:
def EntryNodeOfLoop(self, pHead):
# write code here
seen = set()
while pHead:
if pHead in seen:#遍历所有节点,每次遍历到一个节点时,判断该节点此前是否被访问过。
return pHead
else:
seen.add(pHead)
pHead = pHead.next
return None
set():表示一个集合,集合添加、删除
a = set('boy')
#集合添加
a.add('python')
#集合删除
a.update('python')
引入了一个新的概念,哑结点:是在处理与链表相关的操作时,设置在链表头之前的指向链表头的节点,用于简化与链表头相关的操作。
如果ListNode为{1,2,3,3,4,4,5}
dummy = ListNode(n)
输出为n
dummy = ListNode(pHead.val)
输出为链表对应的第一个节点。
#定义节点
class Node(object):
"""单链表的节点"""
def __init__(self,item):
self.item = item
self.next = None
class List(object):
"""单链表"""
def __init__(self):
self._head = None
def is_empty(self):
"""1、判断链表是否为空"""
return self._head is None
def length(self):
"""2、链表长度"""
cur = self._head
count = 0
while cur:
count += 1
cur = cur.next
return count
def items(self):
"""3、遍历链表"""
cur = self._head
if self.length() == 0:
return None
else:
for i in range(self.length()):
print(cur.item)
cur = cur.next
print('\n')
def add(self,item):
"""4、向头部添加元素"""
node = Node(item)
# 新结点指针指向原头部结点
node.next = self._head
# 头部结点指针修改为新结点
self._head = node
def append(self,item):
"""5、链表尾部添加元素"""
node = Node(item)
if self.is_empty():
self._head = node
else:
cur = self._head
while cur.next:
cur = cur.next
cur.next = node
if __name__ == "__main__":
link_list = List()
link_list.append(1)
link_list.append(2)
link_list.append(4)
link_list.append(8)
link_list.items()
为了实现队列的先进先出,用两个栈实现。先在第一个栈中进行全部元素压入;再将全部元素弹出到第二个栈中;实现先入先出。
例如:原始数组为1,2,3.进入第一个栈顺序为:1,2,3;第一个栈的出栈顺序为:3,2,1;进入第二个栈顺序为:3,2,1;第二个栈的出栈顺序为:1,2,3;
而在队列中:原始数组为1,2,3;进入队列顺序顺序为:1,2,3;出队列顺序为:1,2,3;
要注意如果第二个栈不为空的话,要先将第二个栈的元素弹出。
注意第二个栈的的条件:当栈1为空;当栈2不为空;其它的话则直接弹出。
数据栈 A : 栈 A 用于存储所有元素,保证入栈 push() 函数、出栈 pop() 函数、获取栈顶 top() 函数的正常逻辑。
辅助栈B:栈 BB 中存储栈 AA 中所有 非严格降序 的元素,则栈 AA 中的最小元素始终对应栈 BB 的栈顶元素,即 min() 函数只需返回栈 BB 的栈顶元素即可。
push函数:重点为保持栈 BB 的元素是 非严格降序 的。
如果辅助栈B为空:x 压入栈 B。
如果辅助栈B的栈顶 > x:x 压入栈 B。
如果辅助栈B的栈顶 < x : 大于栈B的栈顶的数,无需再压入栈B,可以舍弃,将辅助栈B的栈顶在放入其中。因为要保证栈B的降序,
POP函数:重点为保持栈 A,B 的 元素一致性 。
弹出栈 A,B栈顶元素
top函数:获取数据栈A栈顶元素
min函数:获取辅助栈B栈中最小元素
先遍历pushV,将pushV的元素按顺序放入到新栈stack中,直到stack 的栈顶元素 == 弹出序列元素 popped[i],开始执行while循环,出栈与 i++,直到stack的栈顶元素 != 弹出序列元素 popped[i] 。
再将pushV中的下一个元素放入到栈stack中,循环步骤1.
在树的实现中,一定考虑使用递归方法时,if语句,是用来表示递归结束的条件
主要是将左右节点交换。
结束函数的执行,从函数返回:
return 与return None相同,返回值为None
a = False
if not a: (这里因为a是False,所以not a就是True)
print "hello"
这里就能够输出结果hello
代码中经常会有变量是否为None的判断,有三种主要的写法:
第一种是if x is None
;是最好的写法,清晰,不会出现错误,以后坚持使用这种写法。
第二种是 if not x:
;必须清楚x等于None, False, 空字符串"", 0, 空列表[], 空字典{}, 空元组()时对你的判断没有影响才行。
第三种是if not x is None
(这句这样理解更清晰if not (x is None)
) 。
在python中 None, False, 空字符串"", 0, 空列表[], 空字典{}, 空元组()都相当于False ,即:
not None == not False == not '' == not 0 == not [] == not {} == not ()
但是:
>>> x = 0
>>> y = None
>>> z = []
第一种写法的例子:
>>> x is None
False
>>> y is None
True
>>> z is None
False
第二种写法的例子:
>>> not x
True
>>> not y
True
>>> not z
True
第三种写法的例子:
>>> not x is None
True
>>> not y is None
False
>>> not z is None
True
(2).判断元素是否在列表或者字典中:如果a不在列表b中,那么就执行冒号后面的语句
a = 5
b = [1, 2, 3]
if a not in b:
print "hello"
def KthNode(self, pRoot, k):
# write code here
#这里的k == 0,可以替换为not k。因为只要k为0,not k返回值为True。
if not pRoot or k == 0:
return
res = []
def Traversal(node):
#这里的node is None,可以替换为not node。因为只要node为None,node is None返回值为True。
if len(res) >= k or node is None:
return
Traversal(node.left)
res.append(node)
Traversal(node.right)
Traversal(pRoot)
if len(res) < k:
return
return res[k-1]
平衡二叉树(Balanced Binary Tree),具有以下性质:它是一棵空树或它的左右两个子树的高度差的绝对值不超过1,并且左右两个子树都是一棵平衡二叉树。
注:我们约定空树是平衡二叉树。
此树的深度 等于 左子树的深度 与 右子树的深度 中的 最大值 +1 。
(1)、在类中一个函数调用另一个函数:只要在那个被调用的函数前加self即可
class Fun(object): #定义类
def __init__(self,x): #初始化参数
self.x = x
def fun1(self,y): #定义第一个函数,self表示继承上面的参数x,而y是fun1自己的一个参数,不是类的参数
a = y**2+2+self.x
return a
def fun2(self): #定义第二个函数
b = self.fun1(3)+3 #调用fun1,同时参数y实例化为3
return b
def fun3(self):
return(self.fun1(3)+3) #便捷的输出,不想再定义一个参数来储存
ans = Fun(1) #实例化,参数x实例化为1,但参数y是没有实例化的,因为y是fun1自己的参数
ans1 = ans.fun1(2) #将参数y赋值为2
ans2 = ans.fun2() #要有括号
ans3 = ans.fun3()
print(ans1,ans2,ans3)
>>>>> 7 15 15 #结果
(2)、在类外,直接调用即可。嵌套函数也是直接调用。
def test1():
print("*" * 50)
def test2():
print("-" * 50)
# 函数的潜逃调用
test1()
test2()
class Solution:
def high(self,root):
if root == None:#递归结束的条件
return 0
return max(self.high(root.left), self.high(root.right))+1
def IsBalanced_Solution(self, pRoot):
if pRoot == None:
return True
if not self.IsBalanced_Solution(pRoot.left):#先序遍历递归,判断 当前子树的左子树 是否是平衡树
return False
if not self.IsBalanced_Solution(pRoot.right):#先序遍历递归,判断 当前子树的右子树 是否是平衡树;
return False
isbalance = self.high(pRoot.left) - self.high(pRoot.right)
if abs(isbalance) <= 1:
return True
return False
算法流程:
1、特例处理: 当树的根节点为空,则直接返回空列表 [] ;
2、初始化: 打印结果空列表 res ,包含根节点的双端队列 deque ;
3、BFS 循环: 当 queue 为空时跳出;
(1)、新建列表 tmp ,用于临时存储当前层打印结果;
(2)、当前层打印循环: 循环次数为当前层节点数(即 queue 长度);
A. 出队: 队首元素出队,记为 node;
B. 添加子节点: 若 node 的左(右)子节点不为空,则加入 deque ;
(3)、打印: 若之前的res长度为奇数(即本层为偶数层),将tmp倒序插入; 否则,tmp正序插入;
4、返回值: 返回打印结果列表 res 即可;
class Solution:
def Print(self, pRoot):
# write code here
if not pRoot:
return []
queue = [pRoot]#这里插入的是树的根节点,而不是整棵树。
res = []
#这里的while循环是用来判断树的层数是否已经没有了。在最后一层的下一层则跳出循环。
while queue:
tmp = []
#len(queue)表示的是每一层节点的长度,而不是整个树的层数。用for来遍历
for _ in range(len(queue)):
#queue.pop(0)保证每次的queue都只包含该层节点,而非所有层的节点。
cur_node = queue.pop(0)
tmp.append(cur_node.val)
if cur_node.left:
queue.append(cur_node.left)
if cur_node.right:
queue.append(cur_node.right)
if len(res)%2 == 1:
res.append(tmp[::-1])
else:
res.append(tmp)
return res
仔细观察,可以把中序(DBHEIAFCG)下一结点归为几种类型:
1、有右子树,下一结点是右子树中的最左结点,例如 B,下一结点是 H
2、无右子树,且结点是该结点父结点的左子树,则下一结点是该结点的父结点,例如 H,下一结点是 E
3、无右子树,且结点是该结点父结点的右子树,则一直沿着父结点追朔,直到找到某个结点是其父结点的左子树,如果存在这样的结点,那么这个结点的父结点就是我们要找的下一结点。例如 I,下一结点是 A;例如 G,并没有符合情况的结点,所以 G 没有下一结点
class Solution:
def GetNext(self, pNode):
# write code here
if not pNode:
return None
#pNode是一个节点,而不是一棵树。
#如果有右子树,则找右子树中最左的子节点
if pNode.right != None:#单纯的if pNode.right无法运行,得加上!= None。
res = pNode.right
while res.left != None:
res = res.left
return res
#无右子树,则向上找第一个是其父节点的左节点的点,这个父节点就是遍历序列中的下一个节点
while pNode.next:#pNode.next表示该节点的父节点
#判断该节点是父节点的左子树还是右子树,即该节点的父节点的左子树是否等于该节点。
if pNode.next.left == pNode:
return pNode.next
pNode = pNode.next #沿着父节点向上遍历
return #到了根节点仍没找到,则返回空
为什么先开启左子树递归而非右子树:因为无论是中序遍历还是前序遍历都是左子树在前,右子树在后。
str.index(str, beg=0, end=len(string))
str -- 指定检索的字符串
beg -- 开始索引,默认为0。
end -- 结束索引,默认为字符串的长度
f = [0]+[1]*number
f[i] = 2*f[i-1]
dp = [0]*(len(array))
dp[0] = array[0]
dp[i] = max(dp[i-1]+array[i],array[i])
可以采用递归,但是可能会因为计算量过大,超时。
class Solution:
def Fibonacci(self, n):
# write code here
if n <= 1:
return n
a,b = 0,1
s = 0
for i in range(2,n+1):
**#下面这三行代码交换,可以用a, b = b, a + b,这也是状态转移方程**
s = a+b
a = b
b = s
return s
def Fibonacci(self, n):
# write code here
a,b = 0,1
for _ in range(n):#当n=0时,循环不执行,直接输出a=0.
c= a+b #下面三行代码也可以表示为:a,b = b,a+b
a = b
b = c
return a
主要采用逆向思维的方法:如果我从第n个台阶进行下台阶,下一步有2中可能,一种走到第n-1个台阶,一种是走到第n-2个台阶。所以f[n] = f[n-1] + f[n-2].
不知道范围的时候,可以从用以下方法找出循环的范围:
a0 = 1,b0 = 1,→ f(1) = 1
a1 = 1,b1 = 2,→ f(2) = 2
所以用b作为返回值,且n比a,b对应的值要大1 ,所以循环取(0,n-1)
此题主要是找出n与n-1的关系,f[n-1] = f[n-2] + f[n-3] + … + f[0],优化可得f[n] = 2*f[n-1]。
动态规划初始化:
def jumpFloorII(self, number):
# write code here
f = [0]+[1]*number
for i in range(2,number+1):
f[i] = 2*f[i-1]
return f[number]
主要是找到前后关系,一般来说,写出前四个f(n)就可以得到关系:f[n] = f[n-1] + f[n-2]
//:取整除 - 返回商的整数部分(向下取整),例如:6 / 4 =1.5,int(7.2/2)=3
/:除 - x除以y。例如:6 // 4 = 1,得到的结果是整数。
算法思路:
初始化数组的长度:B = [1]*28,则数组的长度为28,每个元素的值都为1。
class Solution:
def multiply(self, A):
# write code here
l = len(A)
B,tmp = [1]*l,1
if l <= 1:
return None
for i in range(1,l):
B[i] = B[i-1] * A[i-1]#计算上三角
for i in range(l-2,-1,-1):#逆序输出索引:l-2,l-3,...,0.
tmp = tmp*A[i+1]#计算下三角,从下往上计算
B[i] = B[i]*tmp#逆序输出B的值,
return B
数组是 numpy 中的,求解数组的行和列:行:len(b) ,b.shape返回的是行和列,b.size返回的是总长度。
求解List的长和宽:使用的函数为len(a),len(a[0])
注意数组本身已经是旋转数组了,这个原始数组是递增的。
滑动窗口的性质:
滑动窗口需要解决的难题:
窗口何时扩大,何时缩小?(左右边界的移动条件)
当窗口的和小于 target 的时候,窗口的和需要增加,所以要扩大窗口,窗口的右边界向右移动;
当窗口的和大于 target 的时候,窗口的和需要减少,所以要缩小窗口,窗口的左边界向右移动;
当窗口的和恰好等于 target 的时候,我们需要记录此时的结果。设此时的窗口为 [i, j)[i,j),那么我们已经找到了一个 ii 开头的序列,也是唯一一个 ii 开头的序列,接下来需要找 i+1i+1 开头的序列,所以窗口的左边界要向右移动。
滑动窗口能找到全部的解吗
输出的两个数字不一定是连续的,所以不能用滑动窗口来解决。
1.初始化:指针i指向数组首, 指针j指向数组尾部
2. 如果arr[i] + arr[j] == sum , 说明是可能解。
通过下列代码,找出最小值。
min_ = float('inf')#表示无穷大,最小值的初始化可以为无穷大。最大值的初始化可设置为0.
if array[i] * array[j] < min_:
res = [array[i] , array[j]]
min_ = array[i] * array[j]
力扣题解动态规划具体步骤
class Solution:
def FindGreatestSumOfSubArray(self, array):
# write code here
for i in range(1,len(array)):
array[i] = array[i] + max(array[i-1],0)#每循环替换一次,array[i] 中的值就会被替换,已经是某个连续子数组的和了。
return max(array) # max(array)可以找出连续子数组的最大和
动态规划的定义:dp = [0]*(len(array))
dp[0] = array[0]
dp[i] = max(array[i],array[i]+dp[i-1]),主要是用来判断dp[i-1]的值是否为正,如果为负的话,舍弃之前的序列和,重新开始。
def FindGreatestSumOfSubArray(self, array):
# write code here
dp = [0]*(len(array))
dp[0] = array[0]
res = []
for i in range(1,len(array)):
dp[i] = max(dp[i-1]+array[i],array[i])
return max(dp)
与操作(&):两位同时为“1”,结果才为“1”,否则为0
或运算符(|):参加运算的两个对象只要有一个为1,其值为1。
异或运算符(^):参加运算的两个对象,如果两个相应位为“异”(值不同),则该位结果为1,否则为0。
运算规则:0^ 0=0; 0 ^ 1=1; 1^ 0=1; 1^1=0;
取反运算符(~):对一个二进制数按位取反,即将0变1,1变0。
左移运算符(<<):将一个运算对象的各二进制位全部左移若干位(左边的二进制位丢弃,右边补0)
例:a = a << 2 将a的二进制位左移2位,右补0;
右移运算符(>>):将一个数的各二进制位全部右移若干位,正数左补0,负数左补1,右边丢弃。
127的补码:0111 1111 右移一位: 0011 1111 右移二位: 0001 1111
-128的补码:1000 0000 右移一位: 1100 0000 右移二位: 1110 0000
向下整除 n // 2等价于 右移一位 n >> 1 ;
取余数 n%2 等价于 判断二进制最右一位值 n&1 ;可以判断是奇数还是偶数。n&1为真 等价于 n%2==1,为奇数。
不管是n是奇数还是偶数,倒数第一轮n的值都会变成0,跳出循环。
那么倒数第二轮的值,都会是1,会将x的偶数的最终幂结果转化到res中去。
两数字的二进制形式a,b,其和s = a+b。
通过下图可知,进位与无进位不可能同时出现同为1的情况:
无进位和 n 与进位 c 的计算公式如下:
class Solution:
def add(self, a: int, b: int) -> int:
x = 0xffffffff
a, b = a & x, b & x
while b != 0:
a, b = (a ^ b), (a & b) << 1 & x
return a if a <= 0x7fffffff else ~(a ^ x)
定义数组 dp,其中 dp[i] 表示第 i 个丑数,第 n 个丑数即为 dp[n] 。由于最小的丑数是 11,因此 dp[1]=1。
定义三个指针 p2,p3,p5,初始值都为1。
通过三个指针,得到下一个丑数,即找出 1 与 2,3,5 相城的最小值,dp[i] = min( dp[p2] * 2, dp[p3] * 3, dp[p5] * 5 ),然后分别比较 dp[i] 与 dp[p2] * 2, dp[p3] * 3, dp[p5] * 5 中哪个相等,如果相等则将对应的指针加 1。 例如:p2+1=2,则在下一次循环中可以得到dp[p2] * 2,即dp[2] * 2,以此循环可以保证每一个丑数都有一次机会与2相乘,一次机会与3相乘,一次机会与5相乘。并且可以保证不同丑数乘以2,3,5时的比较结果。
重复循环2—n次。
三个指针的使用:首先三个指针 dp[p2]、dp[p3]、dp[p5] 都指向1,乘以2,3,5之后得到2,3,5。
pointer2, 指向1, 2, 3, 4, 5, 6中,还没使用乘2机会的丑数的位置。该指针的前一位已经使用完了乘以2的机会。
pointer3, 指向1, 2, 3, 4, 5, 6中,还没使用乘3机会的丑数的位置。该指针的前一位已经使用完了乘以3的机会。
pointer5, 指向1, 2, 3, 4, 5, 6中,还没使用乘5机会的丑数的位置。该指针的前一位已经使用完了乘以5的机会。
if not index:
return 0 #需要返回0的时候,return后面一定不能为空。
dp = [1]*index#对于动态规划的定义方法。
p2=p3=p5=0
for i in range(1,index):
n2,n3,n5 = dp[p2]*2,dp[p3]*3,dp[p5]*5
dp[i] = min(n2,n3,n5)
if dp[i] == n2:
p2 += 1
if dp[i] == n3:
p3 += 1
if dp[i] == n5:
p5 += 1
return dp[index-1]
32位二进制数就是有32个0或者1:全1表示为x = 0xffffffff。
正数的补码还是其本身;负数的补码是其反码+1。
Python 中的整型也是用 补码形式存储的,正数和负数都是。
Python 中 bin 一个正数(十进制表示)结果就是它的二进制补码表示形式;但是Python 中 bin 一个负数(十进制表示),输出的是它的原码的二进制表示加上个负号。
正负数补码计算方法:
负数补码计算:
x = 0xffffffff
n = x&n
n = bin(n)
正数补码计算:
n = bin(n)
not c in dic 整体返回一个布尔值,表示如果c 不在 dic 中,则返回 False 。c in dic 表示如果c 在 dic 中,则返回 True。这两者表示的情况刚好相反。
字符流:表示源源不断的向池子中添加字符,而不是一次性将整个字符全部添加完成。
输入字符流为"google"时,表示先输入“g→‘go’→“goo”→goog…→"google",对应题目中,如果只出现一次,则返回第一个只出现一次的字符;如果当前字符流没有存在出现一次的字符,返回#字符。则前面字符串对应返回的字符为:“g→‘gg’→“ggg”→ggg#…→"ggg#ll"
Insert函数表示遍历字符串,FirstAppearingOnce作用是找出第一个不重复的字符。
算法流程:
①、遍历输入字符串,将遍历结果放入新的字符串和字典中
②、字典的键为字符,值为字符出现的次数。(如果重复出现则值加1)
③、用新字符串中的字符对应字典中的键,循环遍历的返回值为第一次只出现一次的字符(比如‘go’,返回值为’gg’,因为第一个不重复的字符为g,所以后面返回的也全为g)。
④、否则,遍历完之后,没有只出现一次的字符,则返回#。
python 的内置函数:s = s.replace(" “,”%20") :用空格替换 %20
for 循环使用 enumerate:前面的元素表示索引,后面的数字表示元素。
Python中有join()和os.path.join()两个函数,具体作用如下:
join()函数 。语法: ‘sep’.join(seq);上面的语法即:以sep作为分隔符,将seq所有的元素合并成一个新的字符串
返回值:返回一个以分隔符sep连接各个元素后生成的字符串。
os.path.join()函数;语法: os.path.join(path1[,path2[,…]])
返回值:将多个路径组合后返回
def LeftRotateString(self, s, n):
# write code here
if n > len(s):
return s
s1 = s[n:]#表示从n到最后那个数。
s2 = s[:n]#表示索引值从0到n-1.
return s1+s2
字符类型:
空格 「 」、数字「 0—9 」 、正负号 「 ± 」 、小数点 「 .」 、幂符号 「 eE 」
状态定义:
按照字符串从左到右的顺序,定义以下 9 种状态。
f(n, m),表示长度为n的序列,删除n-1次,最后留下元素的序号;则f(n-1, m)表示长度为n-1的序列,删除n-2次,最后留下元素的序号;…;则 f(1, m) = 0。(其中最后留下元素的序号都表示删除元素后,新序列中的下标,而不是长度为n的序列中,对应的初始下标,本题要求的是初始下标对应的值)
长度为n的序列,第1次删除,划掉了下标为(m-1)%n的数字,剩下n-1个数;下一次开始的下标为 m % n。
假设 x = f(n - 1, m),表示长度为n-1时,最终留下的元素的序号为x(此时的x是第一次删除后n-1个序列中,新的排序的下标,新排序第一个数为 m % n 开始)。
x = f(n - 1, m)也表示从 m % n 开始往后数 x 个数,即 m % n + x,在初始序列中的下标为 f(n, m) = (m % n + x) % n = (m + x) % n
快排的原理:在数组中任意选择一个数字作为基准,用数组的数据和基准数据进行比较,比基准数字大的数字的基准数字的右边,比基准数字小的数字在基准数字的左边。时间复杂度O(nlogn)
def fast_sort(num):
if len(num)<=1:
return num
else:
left,right = [],[]
base = num[len(num)//2]
num.remove(base)
for i in num:
if i<= base:
left.append(i)
else:
right.append(i)
return fast_sort(left)+[base]+fast_sort(right)
num = [2,4,8,7,6,5]
print(fast_sort(num))
冒泡排序重复地走访需要排序的元素列表,依次比较两个相邻的元素,如果顺序(如从大到小或从小到大)错误就交换它们的位置。重复地进行直到没有相邻的元素需要交换,则元素列表排序完成。冒泡排序总的平均时间复杂度为:O(n2)
def _sort(num):
for i in range(len(num)-1):
for j in range(len(num)-1-i):
if num[j] > num[j+1]:
num[j],num[j+1] = num[j+1],num[j]
return num
num = [2,4,8,7,6,5]
print(_sort(num))
**改进版:**上述代码中加入了一个标志位 isSorted ,利用布尔变量 isSorted 作为标记。如果在本轮排序中,元素有交换,则说明数列无序;如果没有元素交换,说明数列已然有序,直接跳出大循环。
在基础版中已经知道就算当前数列中的某几个元素之间是有序的(如最后的4、5),元素遍历依然会执行。而我们改进版就是为了解决这个问题。
def _sort(num):
for i in range(len(num)-1):
isSorted = True
for j in range(len(num)-1-i):
if num[j] > num[j+1]:
num[j],num[j+1] = num[j+1],num[j]
isSorted = False
if isSorted:
break
return num
num = [2,4,8,7,6,5]
print(_sort(num))
复杂度为O(nlog^n)
def marge(list):
n = len(list)
if n<=1:
return list
mid = n//2
#left采用归并排序之后,形成有序的新列表
left = marge(list[:mid])
# right采用归并排序之后,形成有序的新列表
right = marge(list[mid:])
#将两个有序的子序列合并为一个新的整体
#排序形成的新数组,是之前拆分开的数组之间的排序。
left_p,right_p = 0,0
res = []
while left_p < len(left) and right_p < len(right):
if left[left_p] < right[right_p]:
res.append(left[left_p])
left_p += 1
else:
res.append(right[right_p])
right_p += 1
res += left[left_p:]
res += right[right_p:]
return res
num = [2,4,8,7,6,5]
print(marge(num))
插入排序是将元素列表中未排序的数据依次插入到有序序列中。从元素列表的第一个数据开始(将第一个数据视为已排序序列),按顺序将后面未排序的数据依次插入到前面已排序的序列中。对每一个未排序数据进行插入,是将该数据依次与相邻的前一个数据进行比较,如果顺序错误则交换位置,直到不需要交换则该数据插入完成。
# coding=utf-8
def insertion_sort(array):
for i in range(len(array)):
cur_index = i
#未排序的第一个数据与已排序的最后一个数据进行比较。cur_index - 1 >= 0表示插入的数据已经通过交换位置到达了数列的最前端
while array[cur_index - 1] > array[cur_index] and cur_index - 1 >= 0:
array[cur_index],array[cur_index - 1] = array[cur_index - 1],array[cur_index]
cur_index -= 1
return array
if __name__ == '__main__':
array = [10,17,50,7,30,24,27,45,15,5,36,21]
print(insertion_sort(array))