classSolution(object): defstrStr(self, haystack:str, needle:str)->int: next = self.getnext(needle) h,n = 0,0 while h<len(haystack) and n<len(needle): if n==-1or haystack[h] == needle[n]: h += 1 n += 1 else: n = next[n] if n == len(needle): return h-n else: return -1
defgetnext(self,T:str)->List[int]: j=0 k=-1 next = [0for i inrange(len(T))] next[j]=k while j <len(T)-1: if k==-1or T[j]==T[k]: j+=1 k+=1 next[j] = k else: k = next[k] returnnext
deflongestCommonPrefix(self, strs:List(str))->str: min_str = min(strs, key=len) result = "" p = 0 while p < len(min_str): char = strs[0][p] for s in strs: if s[p] != char: return result result += char p += 1 return result
classSolution(object): deflongestPalindrome(self, s:str)->str: ans = "" for i inrange(len(s)): temp = self.search(s,i,i) iflen(temp) > len(ans): ans = temp temp = self.search(s,i,i+1) iflen(temp) > len(ans): ans = temp return ans
defsearch(self,s,first,end): while first>=0and end<len(s) and s[first] == s[end]: first -= 1 end += 1 return s[first+1:end]
classSolution(object): defarrayPairSum(self, nums:List[int])->int: ans = 0 nums = self.quick_sort(nums) print(nums) for i inrange(len(nums)//2): ans += nums[i*2] return ans
defquick_sort(self,array): iflen(array) <= 1: return array else: reference = array[0] small = [s for s in array[1:] if s < reference] equal = [e for e in array if e == reference] large = [l for l in array[1:] if l > reference] returnself.quick_sort(small) + equal + self.quick_sort(large)
deffindMaxConsecutiveOnes(self, nums: List[int]) -> int: a,b = 0,0# 分别存储当前最大连续数与历史最大连续数 for i inrange(len(nums)): if nums[i] == 0: if a > b: b = a a = 0 else: a += 1 returnmax(a,b)
# 快速排序 defquick_sort(array): iflen(array)<=1: # 当子序列长度<=1 return array # 排序完成,停止递归 else: small = [] # 由所有小于基准值的元素组成的子数组 equal = [] # 包括基准值在内并且和基准相等的元素 large = [] # 由所有大于基准值的元素组成的子数组 reference = array[0] # 选择第一个数作为基准值 # 遍历array中的每一个元素 for s in array: # 当前元素小于基准值时 if s < reference: # 当前元素插入small small.append(s) # 当前元素等于基准值 elif s == reference: equal.append(s) # 当前元素大于基准值 else: large.append(s) print(small,equal,large) # 调用自身,即为递归 return quick_sort(small) + equal + quick_sort(large)
实用版:
1 2 3 4 5 6 7 8 9
defquick_sort(array): iflen(array) <= 1: return array else: reference = array[0] small = [s for s in array[1:] if s < reference] equal = [e for e in array if e == reference] large = [l for l in array[1:] if l > reference] return quick_sort(small) + equal + quick_sort(large)
# 希尔排序 defshell_sort(array): # 设定初始增量d=n/2 d = len(array)//2 # 当增量大于0时执行,如果小于0,则退出循环 while d>0: # i 的取值范围为d到n for i inrange(d,len(array)): # 类似直接插入排序,比较当前值与指定增量的值 while i>=d and array[i-d]>array[i]: # 符号条件则交换 array[i],array[i-d] = array[i-d],array[i] # 取前一个指定增量的值,继续下一个判断 i -= d # 将增量取半,回到while循环 d = d//2 return array
# 计数排序 defcount_sort(arr): # 计算序列的最大值和最小值 maximum,minimum = max(arr),min(arr) # 申请额外的空间作为计数数组,大小为最大值减最小值+1 countArr = [0for i inrange(maximum-minimum+1)] # 申请一个存放已排序序列的等长空间 finalArr = [0for i inrange(len(arr))] # 统计各元素出现的次数,并将统计结果存入计数数组 for i in arr: # 出现一次就加1 countArr[i-minimum] += 1# 注意下标index = data - min # 对计数数组进行累加 for i inrange(1,len(countArr)): # 从第二个位置开始,每个每个位置的值等于其本身加上前者 countArr[i] += countArr[i-1] # 开始将元素反向填充到最终的数组中 for i in arr: finalArr[countArr[i-minimum]-1] = i # 填充了一个就减一 countArr[i-minimum] -= 1 return finalArr
# 桶排序 defbucket_sort(arr): maximum,minimum = max(arr),min(arr) # 确定桶的个数 buckets = [[] for i inrange(maximum-minimum)//10+1] for i in arr: # 计算各元素的所属的桶位 index = (i - minimum)//10 # list.append()---添加元素 buckets[index].append(i) # 将原数组清空 arr.clear() for i in buckets: # 桶内排序---堆排序(可以用别的替代) i = heap_sort(i) # 将已排序的桶重新放回已经清空的原数组中 arr.extend(i) return arr
# 基数排序 defradix_sort(arr): maximum = max(arr) # 确定待排序数组中的最大位数 d = 0# 用d记录最大位数 while maximum!=0: maximum = maximum//10 d += 1 # d轮排序 for i inrange(d): # 因为每一位数字都是0~9,所以建十个桶 s = [[] for k inrange(10)] for j in arr: # 从个位数开始注意进行分桶操作 s[int(j/(10**i))%10].append(j) # 用10个桶回填原数组 arr = [a for b in s for a in b] return arr
defrotate(self,matrix: List[List[int]])->List[List[int]]: rlists = list() for j inrange(len(matrix[0])): rlist = list() for i inrange(len(matrix)-1,-1,-1): rlist.append(matrix[i][j]) rlists.append(rlist) return rlists
这个实际上是通过更改读取顺序的方式,重新创建一个矩阵,但是不符合题目不额外使用空间的思想
方法二:
通过矩阵的几何特性来实现
1 2 3 4 5 6 7 8 9
defrotate(self,matrix: List[List[int]]): N = len(matrix) # 对角线互换 for i inrange(N): for j inrange(i): # 这里要仔细理解,因为接下来要进行互换,所以j要随i增大 matrix[i][j],matrix[j][i] = matrix[j][i],matrix[i][j] # 水平互换 for i inrange(N): matrix[i][:] = matrix[i][::-1]
defsetZeroes(self, matrix:List[List[int]]): M=len(matrix) N=len(matrix[0]) line=[0]*M row=[0]*N for i inrange(M): for j inrange(N): if matrix[i][j]==0: line[i] = 1 row[j] = 1 for i inrange(M): for j inrange(N): if line[i]==1or row[j]==1: matrix[i][j]=0
classSolution(object): deffindDiagonalOrder(self, mat:List[List[int]])->List[int]: ans = [] Xsize = len(mat) Ysize = len(mat[0]) tot = Xsize + Ysize - 1 x,y = 0,0 for i inrange(tot): if i%2==1: while y>=0and x<Xsize: ans.append (mat[x][y]) x += 1 y -= 1 if x<Xsize: y += 1 else: y += 2 x -= 1 else: while x>=0and y<Ysize: ans.append (mat[x][y]) x -= 1 y += 1 if y<Ysize: x += 1 else: x += 2 y -= 1 return ans
int ** pp; int *p; int q; // 数据一般情况下被动态分配 //此时假设p为数据存储的位置,也是p的数据被动态分配,导致数据的位置不确定 p = &q; //但是我们能找到一个二级指针来指向p的位置,pp自身的位置是硬编码,并不会改变 pp = &p; //也就是说只要我们能找到二级指针,就能根据指向,取到q中的数据
(3)那么我们现在进一步的思考,C语言将全局的变量存放在哪里?将局部变量又存放在哪里?每个函数开头的push bp mov bp sp又有什么意义?分析以下代码思考一下
1 2 3 4 5 6 7 8 9 10 11 12
int a1,a2,a3; voidf(void); main(){ int b1,b2,b3; a1=0xa1;a2=0xa2;a3=0xa3; b1=0xb1;b2=0xb2;b3=0xb3; } voidf(void){ int c1,c2,c3; a1=0x0fa1;a2=0x0fa2;a3=0x0fa3; c1=0xc1;c2=0xc2;c3=0xc3; }
image.png
我们看到
SUB SP,+06将栈顶下移了6个字节用来存放局部变量,为什么是存放局部变量而不是全局变量呢,在存储的过程中,我们可以看到对于全局变量,是使用直接定址的方法进行存储在程序的数据段中,而对于局部变量则是以栈底的相对位置进行访问。由此可以看出,局部变量以栈的形式存储在函数的同一个栈中。
在这里我们便可以理解push bp mov bp sp的意义,通俗来讲。这是因为全局变量被存储于数据段中,而局部变量被存储于栈段中,和函数功能存放在一起,而这段指令则是用于创建一个新的函数栈帧。
我在下一篇博客中会详细讲解这个过程。
(4)此时我们进一步思考,函数的返回值被存放在哪里?分析下面的程序
1 2 3 4 5 6 7 8 9 10
intf(void); int a,b,ab; main(){ int c; c=f(); } intf(void){ ab = a+b; return ab; }
而且相较于F程序,M程序的子程序结尾多了
RET PUSH BP MOV BP,SP这一部分的作用是用于函数返回,恢复原栈帧用的
现在我们可以好好分析一下二者的区别:
首先main函数被作为了一个子程序,且在编译时被添加了很多代码
f函数则是作为一个子程序被直接调用却没有返回
因此,问题出在main函数被编译连接的过程中,我们回想f函数编译失败的报错
Linker Error:Undefined symbol _main in module C0S我们可以猜测,在连接的过程中,连接器把main.obj与C0S.obj连接在了一起,得到我们的main.exe函数。此时我们可以进一步的推断,01FA地址以前的程序都是来自COS.obj中。接下来我们对此进行验证:
assume cs:code code segment a: db 1,2,3,4,5,6,7,8 b: dw 0 start: mov si,offset a mov bx,offset b s: mov al,cs:[si] mov ah,0 add cs:[bx],ax inc si loop s mov ax,4c00H int 21h code ends end start
这里的 abstartscode都是标号。这些标号仅仅表示了内存单元的地址
但是我们还有一种标号,这种标号不仅表示内存单元的地址,同时还表示了内存单元的长度
上面的程序可以写成下面的形式
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
assume cs:code code segment a db 1,2,3,4,5,6,7,8 b dw 0 start: mov si,offset a mov bx,offset b s: mov al,cs:[si] mov ah,0 add cs:[bx],ax inc si loop s mov ax,4c00H int 21h code ends end start
assume cs:code,ds:data data segment a db 1,2,3,4,5,6,7,8 b dw 0 data ends code segment start: mov ax,data mov ds,ax mov si,0 mov bx,8 s: mov al,a[si] mov ah,0 add b,ax inc si loop s mov ax,4c00H int 21h code ends end start
showsin: jmp short show table dw ag0,ag30,ag60,ag90,ag120,ag150,ag180 ;字符串偏移地址 ag0 db '0',0 ag30 db '0.5',0 ag60 db '0.866',0 ag90 db '1',0 ag120 db '0.866',0 ag150 db '0.5',0 ag180 db '0',0 show: push bx push es push si mov bx,0b800H mov es,bx ;用角度/30作为相对于table的偏移值,取得对应的字符串的偏移地址,放在bx中 mov ah,0 mov bl,30 div bl mov bl,al mov bh,0 add bx,bx ;注意每个标号实际上使双字节的 mov bx,table[bx] ;显示 mov si,160*12+40*2 shows: mov ah,cs:[bx] ;此时bx存储了表中的偏移地址 cmp ah,0 je showret mov es:[si],ah inc bx add si,2 jmp short shows showret: pop si pop es pop bx ret
mov ax,4c00h int 21h setscreen: jmp short set table dw sub1,sub2,sub3,sub4 set: push bx cmp ah,3 ja sret mov bl,ah mov bh,0 add bx,bx
call word ptr table[bx]
sret: pop bx ret
;清屏 sub1: push bx push cx push es mov bx,0b800h mov es,bx mov bx,0 mov cx,2000 sub1s: mov byte ptr es:[bx],' ' add bx,2 loop sub1s pop es pop cx pop bx ret ;设置前景色 sub2: push bx push cx push es mov bx,0b800h mov es,bx mov bx,1 mov cx,2000 sub2s: and byte ptr es:[bx],11111000b or es:[bx],al add bx,2 loop sub2s pop es pop cx pop bx ret ;设置背景色 sub3: push bx push cx push es mov cl,4 shl al,cl mov bx,0b800h mov es,bx mov bx,1 mov cx,2000 sub3s: and byte ptr es:[bx],10001111b or es:[bx],al add bx,2 loop sub3s pop es pop cx pop bx ret ;上移一行 sub4: push cx push si push di push es push ds mov si,0b800h mov es,si mov ds,si mov si,160 ;ds:si指向第n+1行 mov di,0 ;es:di指向第n行 cld mov cx,24 sub4s: push cx mov cx,160 rep movsb pop cx loop sub4s mov cx,80 mov si,0 sub4s1: mov byte ptr [160*24+si],' ' add si,2 loop sub4s1 pop ds pop es pop di pop si pop cx ret code ends end start