将最近遇到的一些难题整理一下,以供参考,持续更ing~
目录
字母分离,小写在前,大写在后,顺序不变,空间复杂度为O(1)
数据结构
双指针处理两数相加之和,时间复杂度O(n)
def twoSum(self, numbers, target):
"""
167. 两数之和 II - 输入有序数组
给定一个已按照升序排列 的有序数组,找到两个数使得它们相加之和等于目标数。
函数应该返回这两个下标值 index1 和 index2,其中 index1 必须小于 index2。
说明:
返回的下标值(index1 和 index2)不是从零开始的。
你可以假设每个输入只对应唯一的答案,而且你不可以重复使用相同的元素。
输入: numbers = [2, 7, 11, 15], target = 9
输出: [1,2]
解释: 2 与 7 之和等于目标数 9 。因此 index1 = 1, index2 = 2 。
思路:利用双指针,左右递进,可实现O(n)时间复杂度,类似于快排
:param numbers:
:param target:
:return:
"""
l = 0
r = len(numbers) - 1
while l < r:
if numbers[l] + numbers[r] == target:
return [l+1, r+1]
elif numbers[l] + numbers[r] < target:
l += 1
else:
r -= 1
return []
字母分离,小写在前,大写在后,顺序不变,空间复杂度为O(1)
def Resort(self, string):
if len(string) < 2:
return string
arr = list(string)
n = 0
for i in arr:
if i>='A'and i<='Z':
n += 1
i = 0
flag = True
while i < n and flag:
flag = False
for j in range(0,len(arr)-i-1):
if arr[j+1]>='a' and arr[j+1]<='z'and arr[j]>='A'and arr[j]<='Z':
k = arr[j]
arr[j] = arr[j+1]
arr[j+1] = k
flag = True
i += 1
return ''.join(arr)
求逆序对(递归调用,O(n*logn))
#coding:utf-8
from functools import cmp_to_key
class Solution():
def __init__(self):
self.count = 0
def Partition(self, left, right):
l = 0
r = 0
result = []
while l<len(left) and r<len(right):
if left[l] < right[r]:
result.append(left[l])
l += 1
else:
self.count += (len(left)-l)
result.append(right[r])
r += 1
result += left[l:]
result += right[r:]
return result
def Merge(self, array):
if len(array) == 1:
return array
m = int(len(array)/2)
left = self.Merge(array[:m])
right = self.Merge(array[m:])
return self.Partition(left, right)
if __name__ == "__main__":
sl = Solution()
nums = [1,2,3,4,5,6,7,0]
print(sl.Merge(nums))
print(sl.count)
二叉树前、中、后非递归遍历
def ProOrder(root):
ans = []
stack = []
while root or stack:
while root:
ans.append(root.val)
stack.append(root)
root = root.left
if stack:
root = stack.pop()
root = root.right
return ans
def MidOrder(root):
ans = []
stack = []
while root or stack:
while root:
stack.append(root)
root = root.left
if stack:
root = stack.pop()
ans.append(root.val)
root = root.right
return ans
def PostOrder(root):
ans = []
stack = []
while root or stack:
while root:
ans.append(root.val)
stack.append(root)
root = root.right
if stack:
root = stack.pop()
root = root.left
return ans[::-1]
最大子数组和(滴滴)
def maxSubArray(self,array):
"""
描述:输入一个整数数组,返回最大连续子数组和
输入 [1, -2, 3, -4, 5, 6, -7]
输出 11
:param array:
:return:max
"""
max, cur = -0xffff, 0
for i in range(len(array)):
if cur > 0:
cur += array[i]
else:
cur = array[i]
if(max<cur):
max = cur
return max
最小两个连续子数组和
#coding:__utf-8__
def min_shulie(lists):
"""
描述:给定了一个序列,找出其中两组排列最小的连续序列之和相加
:param lists:
:return:
"""
Min, cur =0xffff, 0
for i in lists:
if cur >= 0:
cur = i
else:
cur += i
if cur < Min:
Min = cur
return Min
def liangxulie(lists):
res = []
buf = []
for i in range(1,len(lists)-1):
if lists[i] > 0:
buf.append(min_shulie(lists[:i]))
buf.append(min_shulie(lists[i+1:]))
res.append(buf[0]+buf[1])
buf = []
res.sort()
print(res[0])
if __name__ == "__main__":
lists = [2,-2,1,-3,3,-9,5,-5,-6,-8]
liangxulie(lists)
最长重复子串(阿里)
def maxRepat(self,String):
"""
描述:输入一个字符串,求出最长重复子串
输入:"hello, my name is chenchi. is chenchi."
输出:" is chenchi.",注:空格也要算。
:param String:
:return:
"""
if not String or len(String) == 0:
return None
String = list(String)
max, count = -0xffff, 0
for i in range(1,len(String)):
for j in range(len(String)-i):
if String[i+j] == String[j]:
count += 1
else:
count = 0
if max < count:
max = count
first = j - count +1
return ''.join(String[first:first+max+1])
加密解密(头条)
def jiami(self):
#滑窗法求解
# N, K = map(int,input().split(' '))
N, K = 7, 4
secret = "1110100110"
#result = "1001010"
res = [int(secret[0])]
for i in range(1,N-1):
pre = int(secret[i-1])
now = int(secret[i])
val = pre^now
if i >=K:
val ^= res[i-K]
res.append(val)
res.append(secret[-1])
print(''.join([str(x)for x in res]))
return
#加密
# for i in range(K):
# res.append('0b'+string+'0'*i)
# result = int(res[0],2)
# # print(result)
# for j in range(1,K):
# result = result^(int(res[j],2))
# print(bin(result).replace('0b', ''))
# return
分糖果(头条)
#(贪心算法)
#描述:要求对于每个学生获得得分数,所得糖果左右分高者要得的糖果比分低者多
def candy(selfs):
arr = [1,0,2]
nums = [1]*len(arr)
for i in range(len(arr)-1):
if arr[i] < arr[i+1]:
nums[i+1] += 1
for i in range(len(arr)-1,0,-1):
if arr[i] < arr[i-1]:
nums[i-1] = max(nums[i]+1, nums[i-1])
print(sum(nums))
return
最长无重复字符子串(头条)
def maxUnrepat(self,string):
lists = list(string)
if not len(lists):
return 0
res = [] # 每一次的长度
buf = [] # 存取总共的不重复长度
start = 0
for i in range(len(lists)):
if lists[i] not in res:
res.append(lists[i])
else:
start = res.index(lists[i])
buf.append(res)
res = res[start + 1:]
res.append(lists[i])
buf.append(res)
max_len = len(buf[0])
max_string = buf[0]
for i in buf:
if max_len < len(i):
max_len = len(i)
max_string = i
return ''.join(max_string)
求取货币兑换方式(平安科技)
输入 n=100,a1=0,a2=0,a3=5,a4=6,a5=3,a6=2
输出 8
package com.chenyao.demo;
import java.util.*;
import java.util.concurrent.CountDownLatch;
public class money_distribute {
/**
* input:n=100,a1=0,a2=0,a3=5,a4=6,a5=3,a6=2
* output:8
* */
public static void main(String[] args){
Scanner input = new Scanner(System.in);
String s1 = input.nextLine(); //识别到回车作为结束,行输入(next()为字符输入识别,一旦不是字符就停止)
String[] s2 = s1.split("\\=|,");//正则表达式实现 分割操作
int n = Integer.parseInt(s2[1]);
int[] num = new int[6];
for(int i = 0;i<6;i++){
num[i] = Integer.parseInt(s2[3+i*2]); //字符串转整型
}
int[] moneyValue = {1,5,10,20,50,100};
int m = num.length-1;
int count = 0;
count = Count_money_distribute(n,num,m,moneyValue);
System.out.println(count);
}
private static int Count_money_distribute(int n,int[] num, int m, int[] moneyValue){
if((n<0)||(m<0))
return 0;
if (n==0)
return 1;
int count = 0;
for(int i=0;i<=num[m];i++) //递归通过逐渐降低n,获得兑换种类
count = count + Count_money_distribute(n-i*moneyValue[m],num,m-1,moneyValue);
return count;
}
}
约瑟夫环问题
/********
* 描述:约瑟夫环问题,让一群小朋友围成一个圈,从一个小朋友按1,2,3,、、、标号,
* 让他们报数,doom=3,就按照1,2,3报数,喊到doom值该人出圈,从下一个继续从1报数,知道最后剩下一个人
* 输入:count:小朋友总人数,doom:去人间隔
* 输出:剩下人的标号
* *******/
#环形链表法,本程序为变种,从第一个开始杀死,找到最后的幸存者,
#
def josephus(n, k):
if k==1:
print('survive:', n)
return
alive = n
preIndex, curIndex = 0, 1
List = [(i+1)%alive for i in range(n)] #储存下一个地址的位置
List[-1] = 1 #先把最后一个地址的位置制成第二个数字位置
alive -= 1 #总数减掉1
while alive > 0:
num = k%alive - 1 #计算下一个的偏移量,通常会考虑是否大于k的情况
if num == -1:
length = alive - 1
else:
length = num
for index in range(0, length):
preIndex = curIndex
curIndex = List[curIndex]
print(curIndex+1)
alive -= 1
List[preIndex] = List[curIndex] #该处指向了下下个点位置
curIndex = List[curIndex]
return
private static int joseph(int count,int doom){
/********
* 描述:约瑟夫环问题,让一群小朋友围成一个圈,从一个小朋友按1,2,3,、、、标号,
* 让他们报数,doom=3,就按照1,2,3报数,喊到doom值该人出圈,从下一个继续从1报数,知道最后剩下一个人
* 输入:count:小朋友总人数,doom:去人间隔
* 输出:剩下人的标号
* *******/
int alive = count; //幸存的人数
int[] List = new int[count]; //构建链表,当前各自储存下一个点值
int preIndex = 0,curIndex = 0;
for (int i=0;i<count;i++){
List[i] = (i+1)%alive;
}
while(alive>0){
int num = doom%alive - 1; //直接计算出移动的人数
for(int index=0;index<(num == -1?alive -1:num);index++){
preIndex = curIndex;
curIndex = List[curIndex];
}
//计算出当前出去人的位置
if(alive == 1)
System.out.print(" "+(curIndex+1)+" " );
else
System.out.print(" "+(curIndex+1)+" " );
alive --;
//出圈操作,就是让链表下一个储存位置更新
List[preIndex] = List[curIndex];
curIndex = List[curIndex];
}
return List[curIndex];
}
字符串匹配
KMP算法,查找字符串T在字符串S中所在起始位置
private static void get_next(char[] T,int[] next){
int i,j;
i = 1;
j = 0;
next[1] = 0;
while(i < T[0]){
/*T[i]表示后缀的单个字符*/
/*T[j]表示前缀的单个字符*/
if(j == 0 || T[i] == T[j]){
++i;
++j;
if(T[i] == T[j])
next[i] = next[j];
else
next[i] = j;
}
else
j = next[j];
}
}
private static int Index_KMP(char[] S,char[] T,int pos){
int i = pos;
int j = 1;
int[] next = new int[10];
get_next(T,next);
while(i <= S[0] && j <= T[0]){
if(j == 0 || S[i] == T[j]){
++i;
++j;
}
else {
j = next[j];
}
}
if(j > T[0])
return (i-T[0]);
else
return 0;
}
堆排序
讲解:https://www.jianshu.com/p/d174f1862601
from collections import deque
def swap(L, i, j):
L[i], L[j] = L[j], L[i]
return L
def heap_adjust(L, start, end):
temp = L[start]
i = start
j = 2 * i
while j <= end:
if j < end and L[j] < L[j+1]:
j += 1
if temp < L[j]:
L[i] = L[j]
i = j
j = 2 * i
else:
break
L[i] = temp
def heap_sort(L):
L_length = len(L) - 1
first_sort_count = L_length // 2
for i in range(first_sort_count):
heap_adjust(L, first_sort_count-i, L_length)
for i in range(L_length - 1):
L = swap(L, 1, L_length - i)
heap_adjust(L, 1, L_length - i - 1)
return [L[i] for i in range(1,len(L))]
if __name__ == "__main__":
L = deque([50, 16, 30, 10, 60, 90, 2, 80, 70])
L.appendleft(0)
print(heap_sort(L))
快排(两种方式)
def swap(self,lists,low,high):
if low==high:
return
k=lists[low]
lists[low]=lists[high]
lists[high]=k
return
def Partition(self,lists,low,high):
"""
采用二分递归的Partition
1.选择主元素(一般是数组末尾元素)
2.定义2个index:i记录不大于主元素的个数;j当前遍历值
以主元素为基准,小于主元素的位于主元素左侧,反之位于主元素右侧
:param lists:
:param low:
:param high:
:return:
"""
i = low
for j in range(low,high):
if lists[j] <= lists[high]:
sl.swap(lists,j,i)
i+=1
sl.swap(lists,i,high)
return i
"""
该种方式:通过先从底下遍历,遇到大的再从上边遍历,左右交换,知道左右碰头,再跟总值交换
"""
# pivot = high
# pivotKey = lists[high]
# while low<high:
# #从左向右遍历
# while low<high and lists[low]<=pivotKey:
# low += 1
# print("low:",low,"high:",high,"pivot:",pivot,"左边遍历:》》",lists)
# while low<high and lists[high]>=pivotKey:
# high -= 1
# print("low:",low,"high:",high,"pivot:",pivot,"右边遍历:》》",lists)
# sl.swap(lists, low, high)
# print("low:",low,"high:",high,"pivot:",pivot,"交换后:》",lists)
# sl.swap(lists,low,pivot)
# print("low:",low,"high:",high,"pivot:",pivot,"结束一轮:>>",lists)
# return low
def Quick_sort(self,lists,low,high):
if len(lists) <= 1:
return lists
if low<high:
pivot=sl.Partition(lists,low,high)
sl.Quick_sort(lists,low,pivot-1)
sl.Quick_sort(lists,pivot+1,high)
return lists
电话号码=》单词
def getoutput(self,Input):
n = len(Input)
M = {
'2':['A','B','C' ],
'3':['D','E','F'],
'4':['G','H','I'],
'5':['J','K','L'],
'6':['M','N','O'],
'7':['P','Q','R','S'],
'8':['T','U','V'],
'9':['W','X','Y','Z']
}
Buf = []
res = []
def partition(k):
if k == n:
Buf.append(copy.deepcopy(res))
return
for i in M[Input[k]]:
res.append(i)
partition(k+1)
res.pop(-1)
partition(0)
print(Buf)
return
二叉树非递归前中后序遍历
# -*- 二叉树 begin -*-
# 前序遍历:根结点->左子树->右子树
# 中序遍历:左子树->根结点->右子树
# 后序遍历:左子树->右子树->根结点
class Node(object):
"""节点类"""
def __init__(self, value=None, left=None, right=None):
self.value = value
self.left = left
self.right = right
class Tree(object):
"""定义二叉树类"""
def __init__(self):
self.root = Node()
self.nodeList = [] # 用以存放相对的父节点,用以开辟其子节点
def add(self, value):
"""添加节点"""
node = Node(value)
if self.root.value == None: # 判断树是否是空的
self.root = node
self.nodeList.append(self.root)
else:
parentNode = self.nodeList[0] # 给nodeList里面相对的父节点添加其子节点
if parentNode.left == None:
parentNode.left = node
self.nodeList.append(parentNode.left)
else:
parentNode.right = node
self.nodeList.append(parentNode.right)
# 此时parentNode 已有两个节点,所以对它的构造完成,移除它
self.nodeList.pop(0)
def front_search(self, root):
if root is None:
return None
stack = []
val_buf = []
while root or stack:
while root:
val_buf.append(root.value)
stack.append(root.right)
root = root.left
root = stack.pop()
return val_buf
def mid_search(self, root):
if not root:
return None
stack = []
val_buf = []
while root or stack:
while root:
stack.append(root)
root = root.left
root = stack.pop()
val_buf.append(root.value)
root = root.right
return val_buf
def back_search(self, root):
if root is None:
return None
stack = []
val_buf = []
while root or stack:
while root:
val_buf.append(root.value)
stack.append(root.left)
root = root.right
root = stack.pop()
return val_buf[::-1]
围棋问题
解析:找出包围的围棋并修改,该问题类型属于岛屿包围问题,采用DFS求解,相关解析可参考leetcode200
class Solution:
def daoyu(self, grid):
if not grid:
return 0
m = len(grid)
n = len(grid[0])
self.direction = [(1, 0), (-1, 0), (0, -1), (0, 1)]
self.Mark = [[False] * n for _ in range(m)]
self.res = []
self.buf = []
for i in range(m):
for j in range(n):
if not self.Mark[i][j] and grid[i][j] == 'O':
if self.__dfs(i, j, m, n, grid):
self.res.append(self.buf)
return
def __dfs(self, i, j, m, n, grid):
if i == 0 or i == m-1 or j == 0 or j == n-1:
if grid[i][j] == 'X':
return True
else:
self.Mark[i][j] = True
return False
elif not self.Mark[i][j] and grid[i][j] == 'X':
return True
elif self.Mark[i][j]:
return True
self.buf.append((i,j))
self.Mark[i][j] = True
up = self.__dfs(i-1, j, m, n, grid)
down = self.__dfs(i+1, j, m, n, grid)
left = self.__dfs(i, j-1, m, n, grid)
right = self.__dfs(i, j+1, m, n, grid)
return up & down & left & right
if __name__ == "__main__":
sl = Solution()
grid = [['X', 'X', 'X', 'X'],
['X', 'O', 'O', 'X'],
['X', 'X', 'O', 'X'],
['X', 'O', 'X', 'X']]
print("_________原始矩阵_________")
for i in grid:
print(i)
sl.daoyu(grid)
print("_________标记_________")
for i in sl.Mark:
print(i)
print("_________修改位置坐标_________")
for i in sl.res:
print(i)
print("_________修改后矩阵_________")
for i in sl.res[0]:
grid[i[0]][i[1]] = 'X'
for i in grid:
print(i)
DFS求解最短路径问题
题目描述:在一个M*N的矩阵中,有一只猫‘2’和老鼠‘3’,存在着障碍物‘1’,需要经过‘0’找到老鼠抓住他,请问最短路径为多少
解题思路,建立一个Mark标记矩阵,单次路径走过的点记录,注意上下左右移动可以使用directions元组技巧,进行深度遍历,最终找到所有路径。
def cat_mouse(self, grid):
cat = (-1,0)
mouse = (-1,0)
self.directions = [(1, 0), (-1, 0), (0, -1), (0, 1)]
i = 0
for row in grid:
if '2' in row:
cat = (i,row.index('2'))
if '3' in row:
mouse = (i,row.index('3'))
i += 1
if cat[0] == -1 or mouse[0] == -1:
return 0
m = len(grid)
n = len(grid[0])
Mark = [[False] * n for _ in range(m)]
self.minpath = float('inf')
def dfs(i, j, distance,Mark):
if self.minpath < distance:
return
for direction in self.directions:
i_t = i + direction[0]
j_t = j + direction[1]
if i_t < 0 or i_t == m or j_t < 0 or j_t == n:
continue
if (i_t,j_t) == mouse:
self.minpath = min(self.minpath, distance+1)
return
if Mark[i_t][j_t] == False and grid[i_t][j_t] == '0':
Mark[i_t][j_t] = True
dfs(i_t, j_t, distance+1, Mark)
Mark[i_t][j_t] = False
return
dfs(cat[0], cat[1], 0, Mark)
return self.minpath
if __name__ == "__main__":
sl = Solution()
grid = [['2', '0', '0', '1'],
['0', '1', '0', '1'],
['1', '0', '0', '0'],
['3', '0', '1', '0']]
print("_________原始矩阵_________")
for i in grid:
print(i)
print(sl.cat_mouse(grid))